diff --git a/config/config.exs b/config/config.exs
index 367d586..31b56d7 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -40,10 +40,12 @@ config ExIndexea.Config,
config :ex_indexea,
repo_index: 1027,
- issue_index: 1028,
+ issue_index: 1115,
creator_id: 1072,
owner_id: 1066,
- open_search_id: 1074
+ issue_owner_id: 1067,
+ open_search_id: 1074,
+ issue_search_id: 1154
config :compass_admin, :configurations, %{
nginx_config: "/path/to/nginx/nginx.conf",
@@ -59,6 +61,7 @@ config :compass_admin, CompassAdmin.Agents.FrontendAgent,
config :compass_admin, CompassAdmin.Agents.BackendAgent,
input: [""],
execute: "pyinfra prod-nodes.py prod-backend-deploy.py"
+
config :compass_admin, CompassAdminWeb.ConfigurationLive,
execute:
"cd {config_dir} && git config user.name {username} && git config user.email {useremail} && git add {config_path} && git commit -m '{commit_message}' && git push"
diff --git a/lib/compass_admin/user.ex b/lib/compass_admin/user.ex
index ed7122c..b00ce7e 100644
--- a/lib/compass_admin/user.ex
+++ b/lib/compass_admin/user.ex
@@ -58,4 +58,6 @@ defmodule CompassAdmin.User do
def super_role, do: 10
def admin_role, do: 65535
+
+ def is_admin?(user), do: user.role_level >= admin_role()
end
diff --git a/lib/compass_admin_web/live/backoffice/layout.ex b/lib/compass_admin_web/live/backoffice/layout.ex
index 3c2ea5b..7ee5464 100644
--- a/lib/compass_admin_web/live/backoffice/layout.ex
+++ b/lib/compass_admin_web/live/backoffice/layout.ex
@@ -101,9 +101,19 @@ defmodule CompassAdminWeb.Live.Backoffice.Layout do
},
%{
label: "Gitee",
- link: "/admin/gitee",
- icon: gitee_icon()
- }
+ icon: gitee_icon(),
+ expanded: true,
+ links: [
+ %{
+ label: "Repositories",
+ link: "/admin/gitee/repos"
+ },
+ %{
+ label: "Issues",
+ link: "/admin/gitee/issues"
+ }
+ ]
+ },
]
end
end
diff --git a/lib/compass_admin_web/live/gitee_issues_live.ex b/lib/compass_admin_web/live/gitee_issues_live.ex
new file mode 100644
index 0000000..8c9bf9e
--- /dev/null
+++ b/lib/compass_admin_web/live/gitee_issues_live.ex
@@ -0,0 +1,380 @@
+defmodule CompassAdminWeb.GiteeIssuesLive do
+ use CompassAdminWeb, :live_view
+
+ alias CompassAdmin.User
+ alias ExIndexea.Client
+ alias ExIndexea.Config
+ alias CompassAdminWeb.Helpers
+
+ @default_size 20
+ @default_page 1
+ @default_search_mode "full_search"
+
+ require Logger
+
+ @impl true
+ def mount(_params, %{"current_user" => current_user}, socket) do
+ if User.is_admin?(current_user) do
+ {
+ :ok,
+ socket
+ |> assign(:prepare_creator_ids, [])
+ |> assign(:can_delete_by_owner_id, false)
+ |> assign(:can_delete_by_creator_id, false)
+ |> assign(:can_delete_by_creator_docs_count, false)
+ |> assign(:loading_delete_by_creator_docs_count, false)
+ |> assign(:search_mode, @default_search_mode)
+ }
+ else
+ {:ok, put_flash(socket, :error, "You don't have permissions to access this page.")}
+ end
+ end
+
+ @impl true
+ def handle_params(params, _uri, socket) do
+ cached_page = socket.assigns[:meta][:current_page] || @default_page
+ cached_search_mode = socket.assigns[:search_mode] || @default_search_mode
+
+ query = Map.get(params, "query") || get_in(params, ["search_field", "query"])
+ current_page = (Map.get(params, "page") || cached_page) |> binary_to_integer
+
+ search_mode =
+ Map.get(params, "search_mode") || get_in(params, ["search_field", "search_mode"]) ||
+ cached_search_mode
+
+ {:noreply,
+ list_records(socket, %{
+ from: (current_page - 1) * @default_size,
+ size: @default_size,
+ q: query,
+ page: current_page,
+ search_mode: search_mode
+ })}
+ end
+
+ @impl true
+ def handle_event(
+ "search",
+ %{"search_field" => %{"query" => query}},
+ socket
+ ) do
+ {:noreply, push_patch(socket, to: cached_url(socket, query, @default_page))}
+ end
+
+ def handle_event("validate", %{"bulk_field" => %{"owner_id" => owner_id}}, socket) do
+ {:noreply, assign(socket, :can_delete_by_owner_id, is_number_string(owner_id))}
+ end
+
+ def handle_event("validate", %{"bulk_field" => %{"creator_docs_count" => docs_count}}, socket) do
+ docs_count = string_to_number(docs_count)
+
+ creator_ids =
+ if docs_count > 0 do
+ get_in_attempt(socket.assigns[:aggs], ["creator_id", "buckets"], [])
+ |> Enum.filter(fn row -> row["doc_count"] >= docs_count end)
+ |> Enum.map(fn row -> row["key"] end)
+ else
+ []
+ end
+
+ {:noreply,
+ socket
+ |> assign(:can_delete_by_creator_docs_count, docs_count > 0)
+ |> assign(:prepare_creator_ids, creator_ids)}
+ end
+
+ def handle_event("validate", _, socket) do
+ {:noreply, socket}
+ end
+
+ def handle_event("bulk", %{"bulk_field" => %{"creator_id" => creator_id}}, socket) do
+ {:noreply,
+ if(is_number_string(creator_id),
+ do: bulk_delete(socket, :creator_id, %{creator_id: String.to_integer(creator_id)}),
+ else: socket
+ )}
+ end
+
+ def handle_event("bulk", %{"bulk_field" => %{"owner_id" => owner_id}}, socket) do
+ {:noreply,
+ if(is_number_string(owner_id),
+ do: bulk_delete(socket, :issue_owner_id, %{owner_id: String.to_integer(owner_id)}),
+ else: socket
+ )}
+ end
+
+ def handle_event("bulk", %{"bulk_field" => %{"creator_docs_count" => _}}, socket) do
+ if connected?(socket), do: Process.send_after(self(), :bulk, 1000)
+
+ creator_ids = socket.assigns[:prepare_creator_ids] || []
+
+ Enum.map(creator_ids, fn creator_id ->
+ Logger.info("delete #{creator_id}")
+ bulk_delete(socket, :creator_id, %{creator_id: creator_id})
+ end)
+
+ {:noreply,
+ socket
+ |> put_flash(:info, "Deleted Successfully.")
+ |> assign(:loading_delete_by_creator_docs_count, false)
+ |> push_patch(to: cached_url(socket))}
+ end
+
+ def handle_event("close_modal", _, socket) do
+ {:noreply, push_patch(socket, to: cached_url(socket))}
+ end
+
+ def handle_event("switch_mode", %{"mode" => mode}, socket) do
+ {:noreply, push_patch(socket, to: cached_url(socket, nil, nil, mode))}
+ end
+
+ @impl true
+
+ def handle_info(:bulk, socket) do
+ {:noreply, assign(socket, :loading_delete_by_creator_docs_count, true)}
+ end
+
+ def handle_info(_msg, socket) do
+ {:noreply, socket}
+ end
+
+ @impl true
+ def render(assigns) do
+ ~H"""
+ <%= if live_flash(@flash, :error) == nil do %>
+
+
+ <.button
+ class="float-right w-48"
+ phx-click="switch_mode"
+ phx-value-mode={if @search_mode == "open_search", do: "full_search", else: "open_search"}
+ label={if @search_mode == "open_search", do: "Open Search Mode", else: "Full Fields Mode"}
+ />
+ <%= if @live_action == :bulk do %>
+ <.modal max_width="lg" title="Bulk Operations">
+
+ <%= if @aggs != %{} do %>
+
+
+ <%= for header <- Map.keys(@aggs) do %>
+ <.table>
+ <.tr>
+ <.th><%= header %>
+ <.th>Docs Count
+
+
+ <%= for record <- @aggs[header]["buckets"] do %>
+ <.tr>
+ <.td class="text-sm font-medium text-gray-500 truncate hover:text-clip">
+ <%= Helpers.truncate(record["key"]) %>
+
+ <.td class="text-sm font-medium text-gray-500 truncate hover:text-clip">
+ <%= record["doc_count"] %>
+
+
+ <% end %>
+
+ <% end %>
+ <% end %>
+
+
+ <.button color="white" phx-click="close_modal" label="Close" />
+
+
+ <% end %>
+
<%= "About #{@meta[:total]} records in the results" %>
+ <.table class="mb-2">
+ <.tr>
+ <%= for header <- @headers do %>
+ <.th><%= header %>
+ <% end %>
+
+
+ <%= for record <- @records do %>
+ <.tr>
+ <%= for header <- @headers do %>
+ <.td class="text-sm font-medium text-gray-500 truncate hover:text-clip">
+ <%= Helpers.truncate(record[header]) %>
+
+ <% end %>
+
+ <% end %>
+
+
+
+ <.pagination
+ link_type="live_patch"
+ path={
+ fn page ->
+ "/admin/gitee/issues?page=#{page}&query=#{@meta[:query]}&search_mode=#{@search_mode}"
+ end
+ }
+ current_page={@meta[:current_page]}
+ total_pages={@meta[:total_pages]}
+ />
+
+ <% end %>
+ """
+ end
+
+ defp config(key) do
+ Application.get_env(:ex_indexea, key)
+ end
+
+ def list_records(socket, params) do
+ query = get_in(params, [:q])
+ current_page = get_in(params, [:page]) || @default_page
+ search_mode = get_in(params, [:search_mode]) || @default_search_mode
+
+ case do_list_recods(search_mode, params) do
+ {200, data, _resp} ->
+ total = get_in(data, ["hits", "total", "value"]) || 0
+ hits = get_in(data, ["hits", "hits"]) || []
+ aggs = get_in(data, ["aggregations"]) || %{}
+
+ get_fields = fn row ->
+ base = Map.get(row, "_source") || Map.get(row, "fields") || %{}
+
+ for {k, v} <- base,
+ into: %{},
+ do: {k, if(is_list(v) && length(v) == 1, do: List.first(v), else: v)}
+ end
+
+ records =
+ Enum.map(hits, fn hit ->
+ hit
+ |> get_fields.()
+ |> FlattenMap.flatten()
+ end)
+
+ headers = if List.first(records), do: Map.keys(List.first(records)), else: []
+
+ assign(socket,
+ records: records,
+ headers: headers,
+ aggs: aggs,
+ meta: %{
+ current_page: current_page,
+ total_pages: ceil(total / @default_size),
+ query: query,
+ total: total
+ },
+ search_mode: search_mode
+ )
+
+ {_, reason, _response} ->
+ put_flash(socket, :error, "#{inspect(reason)}")
+ end
+ end
+
+ defp do_list_recods("open_search", params) do
+ ExIndexea.Queries.search_query(client(), Config.app(), config(:issue_search_id), params)
+ end
+
+ defp do_list_recods(_, params) do
+ ExIndexea.Records.list(client(), Config.app(), config(:issue_index), params)
+ end
+
+ defp bulk_delete(socket, key, params) do
+ case ExIndexea.Records.delete_by_query(
+ client(),
+ Config.app(),
+ config(:issue_index),
+ config(key),
+ params
+ ) do
+ {200, _data, _resp} ->
+ socket
+ |> put_flash(:info, "Deleted Successfully.")
+ |> push_patch(to: cached_url(socket))
+
+ {_, reason, _response} ->
+ socket
+ |> put_flash(:error, "#{inspect(reason)}")
+ |> push_patch(to: cached_url(socket))
+ end
+ end
+
+ defp is_number_string(text) do
+ case Integer.parse(text) do
+ {_int, ""} ->
+ true
+
+ _ ->
+ false
+ end
+ end
+
+ defp string_to_number(text) do
+ case Integer.parse(text) do
+ {int, ""} ->
+ int
+
+ _ ->
+ 0
+ end
+ end
+
+ def get_in_attempt(data, keys, default) do
+ case get_in(data, keys) do
+ nil -> default
+ result -> result
+ end
+ end
+
+ defp binary_to_integer(nil), do: 0
+ defp binary_to_integer(binary_or_int) when is_number(binary_or_int), do: binary_or_int
+
+ defp binary_to_integer(binary_or_int) when is_binary(binary_or_int),
+ do: String.to_integer(binary_or_int)
+
+ defp cached_url(socket, query \\ nil, page \\ nil, search_mode \\ nil, suffix \\ "") do
+ query = query || socket.assigns[:meta][:query]
+ cached_page = page || socket.assigns[:meta][:current_page] || @default_page
+ search_mode = search_mode || socket.assigns[:search_mode] || @default_search_mode
+
+ "/admin/gitee/issues/#{suffix}?page=#{cached_page}&query=#{query}&search_mode=#{search_mode}"
+ end
+
+ defp client() do
+ Client.new(%{access_token: Config.access_token()})
+ end
+end
diff --git a/lib/compass_admin_web/live/gitee_live.ex b/lib/compass_admin_web/live/gitee_repos_live.ex
similarity index 96%
rename from lib/compass_admin_web/live/gitee_live.ex
rename to lib/compass_admin_web/live/gitee_repos_live.ex
index 1db74e3..abd7330 100644
--- a/lib/compass_admin_web/live/gitee_live.ex
+++ b/lib/compass_admin_web/live/gitee_repos_live.ex
@@ -1,4 +1,4 @@
-defmodule CompassAdminWeb.GiteeLive do
+defmodule CompassAdminWeb.GiteeReposLive do
use CompassAdminWeb, :live_view
alias CompassAdmin.User
@@ -14,7 +14,7 @@ defmodule CompassAdminWeb.GiteeLive do
@impl true
def mount(_params, %{"current_user" => current_user}, socket) do
- if current_user.role_level >= User.admin_role() do
+ if User.is_admin?(current_user) do
{
:ok,
socket
@@ -159,7 +159,7 @@ defmodule CompassAdminWeb.GiteeLive do
<.button
link_type="live_patch"
- to={"/admin/gitee/bulk?page=#{@meta[:current_page]}&query=#{@meta[:query]}&search_mode=#{@search_mode}"}
+ to={"/admin/gitee/repos/bulk?page=#{@meta[:current_page]}&query=#{@meta[:query]}&search_mode=#{@search_mode}"}
label="Bulk"
/>
@@ -258,7 +258,7 @@ defmodule CompassAdminWeb.GiteeLive do
link_type="live_patch"
path={
fn page ->
- "/admin/gitee?page=#{page}&query=#{@meta[:query]}&search_mode=#{@search_mode}"
+ "/admin/gitee/repos?page=#{page}&query=#{@meta[:query]}&search_mode=#{@search_mode}"
end
}
current_page={@meta[:current_page]}
@@ -385,7 +385,7 @@ defmodule CompassAdminWeb.GiteeLive do
cached_page = page || socket.assigns[:meta][:current_page] || @default_page
search_mode = search_mode || socket.assigns[:search_mode] || @default_search_mode
- "/admin/gitee/#{suffix}?page=#{cached_page}&query=#{query}&search_mode=#{search_mode}"
+ "/admin/gitee/repos/#{suffix}?page=#{cached_page}&query=#{query}&search_mode=#{search_mode}"
end
defp client() do
diff --git a/lib/compass_admin_web/router.ex b/lib/compass_admin_web/router.ex
index b407268..126b997 100644
--- a/lib/compass_admin_web/router.ex
+++ b/lib/compass_admin_web/router.ex
@@ -33,8 +33,11 @@ defmodule CompassAdminWeb.Router do
live "/configurations/#{key}", ConfigurationLive, key
end
- live "/gitee", GiteeLive, :index
- live "/gitee/bulk", GiteeLive, :bulk
+ live "/gitee/repos", GiteeReposLive, :index
+ live "/gitee/repos/bulk", GiteeReposLive, :bulk
+
+ live "/gitee/issues", GiteeIssuesLive, :index
+ live "/gitee/issues/bulk", GiteeIssuesLive, :bulk
live "/deployments/backend", BackendDeploymentLive, :index
live "/deployments/frontend", FrontendDeploymentLive, :index