3.4 KiB · text History b868907
defmodule GitGudWeb.AuditLive.Repo do
  @moduledoc """
  `/r/:owner/:name/settings/audit[/:category[/before|after/:id]]` — who
  changed what in one repository (`GitGud.Audit`).

  For the owner of a personal repository, an admin of the org that
  owns an org repository, and instance admins — in sudo mode, checked
  again on every `handle_params` (see `GitGudWeb.AuditLive.Org`). A
  team or collaborator admin of an org repository doesn't see it: the
  log is the org's to show. No client addresses or user agents here.
  """
  use GitGudWeb, :live_view

  alias GitGud.Repositories
  alias GitGud.Repositories.Storage
  alias GitGudWeb.AuditLive.Page
  alias GitGudWeb.RepoAdmin

  @categories ~w(secrets settings repositories branch_protection deploy_keys webhooks
                 credentials runners ci moderation)

  @impl true
  def mount(%{"owner" => owner, "name" => name}, _session, socket) do
    repo =
      Repositories.get_repository_by_path!(owner, name)
      |> GitGud.Repo.preload([:owner, :organization])

    if allowed?(repo, socket.assigns.current_scope.user) do
      {:ok,
       socket
       |> assign(:repo, repo)
       |> assign(:handle, Storage.repo_handle(repo))
       |> GitGudWeb.RepoLive.Header.assign_chrome(repo)
       |> assign(:page_title, "Audit log — #{owner}/#{name}")
       |> Page.setup(@categories)}
    else
      {:ok, RepoAdmin.deny(socket, repo)}
    end
  end

  @impl true
  def handle_params(params, uri, socket) do
    %{repo: repo, handle: handle, current_scope: %{user: user}} = socket.assigns
    # Re-read: the repository may have moved to an org, or the org's
    # admins changed, since the page opened.
    fresh =
      GitGud.Repo.preload(GitGud.Repo.get(GitGud.Repositories.Repository, repo.id), :organization)

    if fresh && allowed?(fresh, user),
      do:
        Page.params(
          socket,
          params,
          uri,
          ~p"/r/#{handle}/#{repo.name}/settings/audit",
          {:repo, repo}
        ),
      else: {:noreply, RepoAdmin.deny(socket, repo)}
  end

  @impl true
  def handle_event("filter", %{"filter" => params}, socket), do: Page.filter(socket, params)
  def handle_event(_event, _params, socket), do: {:noreply, socket}

  @impl true
  def handle_info({:audit, id}, socket) do
    %{repo: repo, current_scope: %{user: user}} = socket.assigns
    fresh = GitGud.Repo.get(GitGud.Repositories.Repository, repo.id)
    Page.live(socket, id, fresh != nil and allowed?(fresh, user))
  end

  defp allowed?(repo, user), do: GitGud.Audit.readable?(user, {:repo, repo})

  @impl true
  def render(assigns) do
    ~H"""
    <Layouts.app flash={@flash} current_scope={@current_scope}>
      <div class="space-y-6">
        <GitGudWeb.RepoLive.Header.header
          repo={@repo}
          handle={@handle}
          current_scope={@current_scope}
          has_packages?={@has_packages?}
          can_admin?={@can_admin?}
          latest_run={@latest_run}
          open_pulls={@open_pulls}
          security_alerts={assigns[:security_alerts]}
        />
        <.repo_settings_header handle={@handle} repo={@repo} current={:audit} />
        <Page.body
          base={@base}
          filters={@filters}
          filter_form={@filter_form}
          categories={@categories}
          refines={@refines}
          repo_options={@repo_options}
          entries={@streams.entries}
          older={@older}
          newer={@newer}
        />
      </div>
    </Layouts.app>
    """
  end
end