Files
claper/bootstrap_admin.exs
Linus GasserandClaude Opus 5.5 5295915dff feat: bootstrap admin account from ADMIN_EMAIL
Create/promote the ADMIN_EMAIL account on every start, before the seeds,
so the public admin@claper.co/claper default is never created.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 15:58:31 +01:00

60 lines
2.1 KiB
Elixir

# Ensures that the account ADMIN_EMAIL exists and is an admin. Runs on every
# start, between the migrations and the seeds:
# - missing account: created with the admin_pass secret as password
# - account without admin role: promoted to admin
# - existing admin: left untouched, the password is never overwritten
#
# Because the account exists before the seeds run, the seeds never create the
# default admin@claper.co/claper account.
# Without ADMIN_EMAIL nothing is done and the upstream seeds apply.
alias Claper.Accounts
alias Claper.Accounts.Role
alias Claper.Repo
case System.get_env("ADMIN_EMAIL", "") |> String.trim() do
"" ->
IO.puts("ADMIN_EMAIL not set, skipping admin bootstrap")
email ->
Application.load(:claper)
{:ok, _, _} =
Ecto.Migrator.with_repo(Repo, fn _repo ->
# The roles are normally created by the seeds, which run after this.
admin_role =
Repo.get_by(Role, name: "admin") ||
Repo.insert!(%Role{name: "admin", permissions: %{"all" => true}})
Repo.get_by(Role, name: "user") || Repo.insert!(%Role{name: "user", permissions: %{}})
case Accounts.get_user_by_email(email) do
nil ->
pw = File.read!("/run/secrets/admin_pass") |> String.trim()
case Accounts.register_user(%{
email: email,
first_name: "Claper",
last_name: "Admin",
password: pw,
role_id: admin_role.id,
confirmed_at: NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
}) do
{:ok, _} ->
IO.puts("Created admin #{email} with the admin_pass secret as password")
{:error, changeset} ->
raise "Could not create admin #{email}: #{inspect(changeset.errors)}"
end
user ->
if user.role_id == admin_role.id do
IO.puts("Admin #{email} exists, nothing to do")
else
{:ok, _} = Accounts.assign_role(user, admin_role)
IO.puts("Promoted #{email} to admin")
end
end
end)
end