56 lines
1.8 KiB
Python
56 lines
1.8 KiB
Python
"""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")
|