Files
webmail-client/backend/migrations/versions/0016_admin_bootstrap.py
T

56 lines
1.8 KiB
Python
Raw Normal View History

"""bootstrap administrator + registration gate
The first account created on a fresh install becomes the administrator and
registration closes behind it, so an exposed port cannot be claimed by a
stranger. Existing installs keep their oldest account as administrator and start
closed; an admin reopens registration from the admin panel.
Revision ID: 0016
Revises: 0015
Create Date: 2026-09-15
"""
from __future__ import annotations
from collections.abc import Sequence
from alembic import op
revision: str = "0016"
down_revision: str | None = "0015"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
op.execute("ALTER TABLE users ADD COLUMN is_admin boolean NOT NULL DEFAULT false")
op.execute(
"""
CREATE TABLE app_settings (
id integer PRIMARY KEY DEFAULT 1,
registration_open boolean NOT NULL DEFAULT false,
updated_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT ck_app_settings_singleton CHECK (id = 1)
)
"""
)
# Existing deployment: the oldest account is the one that set the instance up,
# so it becomes the administrator. Ties on created_at break by id.
op.execute(
"""
UPDATE users SET is_admin = true
WHERE id = (SELECT id FROM users ORDER BY created_at, id LIMIT 1)
"""
)
# Fresh install (no users yet) starts open so the first signup can happen;
# anything else starts closed.
op.execute(
"INSERT INTO app_settings (id, registration_open) "
"VALUES (1, NOT EXISTS (SELECT 1 FROM users))"
)
def downgrade() -> None:
op.execute("DROP TABLE IF EXISTS app_settings")
op.execute("ALTER TABLE users DROP COLUMN IF EXISTS is_admin")