fix(users): repair create delete and duplicate email handling

This commit is contained in:
Schubert Ferenc 2026-07-11 17:14:41 +02:00
parent 155fdbb16a
commit 0bbcaba211
22 changed files with 773 additions and 107 deletions

View file

@ -0,0 +1,108 @@
from __future__ import annotations
from alembic import op
import sqlalchemy as sa
revision = "202607110006"
down_revision = "202607110005"
branch_labels = None
depends_on = None
ALLOWED_ROLES = ("admin", "pruefer", "mitarbeiter", "leser")
def _other_columns_use_userrole(connection) -> bool:
result = connection.execute(
sa.text(
"""
SELECT EXISTS (
SELECT 1
FROM pg_attribute a
JOIN pg_class c ON c.oid = a.attrelid
JOIN pg_type t ON t.oid = a.atttypid
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE t.typname = 'userrole'
AND NOT (n.nspname = 'public' AND c.relname = 'users' AND a.attname = 'role')
AND a.attnum > 0
AND NOT a.attisdropped
)
"""
)
)
return bool(result.scalar())
def _normalize_roles(connection) -> None:
connection.execute(
sa.text(
"""
UPDATE users
SET role = CASE upper(role::text)
WHEN 'ADMIN' THEN 'admin'
WHEN 'PRUEFER' THEN 'pruefer'
WHEN 'EMPLOYEE' THEN 'mitarbeiter'
WHEN 'MITARBEITER' THEN 'mitarbeiter'
WHEN 'AUDITOR' THEN 'leser'
WHEN 'LESER' THEN 'leser'
ELSE role::text
END
"""
)
)
invalid_roles = connection.execute(
sa.text(
"""
SELECT DISTINCT role::text
FROM users
WHERE role::text NOT IN ('admin', 'pruefer', 'mitarbeiter', 'leser')
"""
),
).scalars().all()
if invalid_roles:
raise RuntimeError(f"Unknown user roles encountered during migration: {', '.join(sorted(invalid_roles))}")
def upgrade() -> None:
connection = op.get_bind()
op.alter_column(
"users",
"role",
existing_type=sa.Enum(name="userrole"),
type_=sa.String(length=50),
existing_nullable=False,
existing_server_default=None,
postgresql_using="role::text",
nullable=False,
)
_normalize_roles(connection)
op.execute(sa.text("ALTER TABLE users ALTER COLUMN role SET DEFAULT 'mitarbeiter'"))
if not _other_columns_use_userrole(connection):
op.execute(sa.text("DROP TYPE IF EXISTS userrole"))
def downgrade() -> None:
connection = op.get_bind()
op.execute(
sa.text(
"CREATE TYPE userrole AS ENUM ('admin', 'pruefer', 'mitarbeiter', 'leser')"
)
)
invalid_roles = connection.execute(
sa.text(
"""
SELECT DISTINCT role
FROM users
WHERE role NOT IN ('admin', 'pruefer', 'mitarbeiter', 'leser')
"""
),
).scalars().all()
if invalid_roles:
raise RuntimeError(f"Cannot downgrade user roles with invalid values: {', '.join(sorted(invalid_roles))}")
op.alter_column(
"users",
"role",
existing_type=sa.String(length=50),
type_=sa.Enum("admin", "pruefer", "mitarbeiter", "leser", name="userrole"),
nullable=False,
)
op.execute(sa.text("ALTER TABLE users ALTER COLUMN role SET DEFAULT 'mitarbeiter'::userrole"))

View file

@ -0,0 +1,25 @@
"""add deleted_at to users for soft delete
Revision ID: 202607110007
Revises: 202607110006
Create Date: 2026-07-11 00:07:00.000000
"""
from __future__ import annotations
from alembic import op
import sqlalchemy as sa
revision = "202607110007"
down_revision = "202607110006"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column("users", sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True))
def downgrade() -> None:
op.drop_column("users", "deleted_at")