"""invites: single-use, expiring registration links an admin issues Revision ID: 0032 Revises: 0031 Create Date: 2026-10-07 Until now the only way to add a second person was to re-open registration to the whole internet while they signed up (#2939 ยง1). An invite lets one person register while registration stays closed. Only the token's SHA-256 hash is stored. ## Downgrade Drops the table. Accounts created through invites are untouched; the record of who invited them goes with it. """ import sqlalchemy as sa from alembic import op from sqlalchemy.dialects.postgresql import CITEXT, UUID revision = "0032" down_revision = "0031" branch_labels = None depends_on = None def upgrade() -> None: op.create_table( "invites", sa.Column("id", UUID(as_uuid=True), primary_key=True), sa.Column("token_hash", sa.Text(), nullable=False, unique=True), sa.Column("created_by", UUID(as_uuid=True), sa.ForeignKey("users.id", ondelete="SET NULL"), nullable=True), sa.Column("email", CITEXT(), nullable=True), sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False), sa.Column("redeemed_at", sa.DateTime(timezone=True), nullable=True), sa.Column("redeemed_by", UUID(as_uuid=True), sa.ForeignKey("users.id", ondelete="SET NULL"), nullable=True), sa.Column("revoked_at", sa.DateTime(timezone=True), nullable=True), ) def downgrade() -> None: op.drop_table("invites")