defmodule GitGudWeb.RepoLive.Settings do
@moduledoc """
General repo settings: description, visibility, default branch — and
a Danger Zone with deletion behind a name-confirmation gate.
Auth: repo owner (user-owned) or org admin (org-owned). Anyone else
is redirected with a 404-ish error.
"""
use GitGudWeb, :live_view
alias GitGud.Organizations
alias GitGud.Repositories
alias GitGud.Repositories.Storage
@impl true
def mount(%{"owner" => owner, "name" => name}, _session, socket) do
repo =
Repositories.get_repository_by_path!(owner, name)
|> GitGud.Repo.preload([:owner, :organization])
user = socket.assigns.current_scope && socket.assigns.current_scope.user
cond do
is_nil(user) ->
{:ok,
socket
|> put_flash(:error, "Sign in to manage repository settings.")
|> redirect(to: ~p"/users/log-in")}
not can_admin?(user, repo) ->
{:ok,
socket
|> put_flash(:error, "You don't have access to manage this repository.")
|> redirect(to: ~p"/r/#{owner}/#{name}")}
true ->
{:ok,
socket
|> assign(:repo, repo)
|> assign(:handle, Storage.repo_handle(repo))
|> GitGudWeb.RepoLive.Header.assign_chrome(repo)
|> assign(:page_title, "Settings — #{owner}/#{name}")
|> assign(:delete_confirm, "")
|> assign(:form, to_form(blank_changeset(repo)))}
end
end
defp can_admin?(user, %{owner_id: uid, organization_id: nil}), do: user.id == uid
defp can_admin?(user, %{organization: %{} = org}),
do: Organizations.org_admin?(org, user)
defp can_admin?(_, _), do: false
defp blank_changeset(repo),
do: Ecto.Changeset.change(repo, %{})
@impl true
def handle_event("save", %{"repository" => attrs}, socket) do
case Repositories.update_repository(socket.assigns.repo, attrs) do
{:ok, repo} ->
{:noreply,
socket
|> assign(:repo, GitGud.Repo.preload(repo, [:owner, :organization]))
|> assign(:form, to_form(blank_changeset(repo)))
|> put_flash(:info, "Settings saved.")}
{:error, cs} ->
{:noreply, socket |> assign(:form, to_form(cs)) |> put_flash(:error, "Couldn't save.")}
end
end
def handle_event("delete_confirm", %{"value" => v}, socket) do
{:noreply, assign(socket, :delete_confirm, v)}
end
def handle_event("delete", _params, socket) do
expected = socket.assigns.repo.name
if socket.assigns.delete_confirm == expected do
{:ok, _} = Repositories.delete_repository(socket.assigns.repo)
{:noreply,
socket
|> put_flash(:info, "Repository deleted.")
|> push_navigate(to: ~p"/repositories")}
else
{:noreply,
put_flash(
socket,
:error,
"Type the repository name exactly to confirm deletion."
)}
end
end
@impl true
def render(assigns) do
~H"""
<Layouts.app flash={@flash} current_scope={@current_scope}>
<div class="space-y-10">
<GitGudWeb.RepoLive.Header.header
repo={@repo}
handle={@handle}
current_scope={@current_scope}
has_packages?={@has_packages?}
can_admin?={@can_admin?}
latest_run={@latest_run}
/>
<.repo_settings_header handle={@handle} repo={@repo} current={:general} />
<section class="space-y-4 max-w-2xl">
<h2 class="font-semibold">General</h2>
<.form for={@form} phx-submit="save" class="space-y-3">
<.input field={@form[:description]} type="text" label="Description" />
<.input
field={@form[:visibility]}
type="select"
label="Visibility"
options={[
{"Public — anyone can read", "public"},
{"Internal — any signed-in user can read", "internal"},
{"Private — only collaborators", "private"}
]}
/>
<.input field={@form[:default_branch]} type="text" label="Default branch" />
<div>
<button class="btn btn-primary btn-sm" type="submit">Save</button>
</div>
</.form>
</section>
<section class="space-y-3 border border-error/40 rounded-lg p-4 max-w-2xl">
<h2 class="font-semibold text-error">Danger zone</h2>
<p class="text-sm opacity-80">
Deleting a repository permanently removes its git data, issues,
pull requests, wiki, secrets, and runs. This cannot be undone.
</p>
<p class="text-sm">
To confirm, type the repository name
<code class="font-mono">{@repo.name}</code>
below.
</p>
<input
type="text"
class="input input-bordered w-full font-mono"
value={@delete_confirm}
phx-keyup="delete_confirm"
phx-debounce="100"
autocomplete="off"
/>
<button
type="button"
class="btn btn-error btn-sm"
phx-click="delete"
data-confirm={"Permanently delete #{@repo.name}? This cannot be undone."}
disabled={@delete_confirm != @repo.name}
>
Delete this repository
</button>
</section>
</div>
</Layouts.app>
"""
end
end
5.3 KiB · text
5af55d9