DealDocumentScreening/migrations/versions/0003_web_auth.py

56 lines
1.9 KiB
Python

"""Email/password columns on users + webUI auth (Stage 4).
Revision ID: 0003
Revises: 0002
Create Date: 2026-08-12
Adds nullable email/password_hash/reset columns to the existing Telegram-only
users table. Existing Telegram rows remain valid (telegram_id still satisfies
the new users_identity_present check). Email is unique; both telegram_id and
email may co-exist on one row (linking the two identities later is a no-op).
"""
from __future__ import annotations
from collections.abc import Sequence
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "0003"
down_revision: str | None = "0002"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
op.add_column("users", sa.Column("email", sa.Text(), nullable=True))
op.add_column("users", sa.Column("password_hash", sa.Text(), nullable=True))
op.add_column("users", sa.Column("password_reset_token_hash", sa.Text(), nullable=True))
op.add_column(
"users", sa.Column("password_reset_expires_at", sa.DateTime(timezone=True), nullable=True)
)
op.add_column(
"users",
sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.text("true")),
)
op.create_unique_constraint("users_email_unique", "users", ["email"])
# A user must have at least one identity anchor (telegram_id OR email).
op.create_check_constraint(
"users_identity_present",
"users",
"telegram_id IS NOT NULL OR email IS NOT NULL",
)
def downgrade() -> None:
op.drop_constraint("users_identity_present", "users", type_="check")
op.drop_constraint("users_email_unique", "users", type_="unique")
op.drop_column("users", "is_active")
op.drop_column("users", "password_reset_expires_at")
op.drop_column("users", "password_reset_token_hash")
op.drop_column("users", "password_hash")
op.drop_column("users", "email")