"""initial schema - auth tables Revision ID: 0001_initial Revises: Create Date: 2026-03-25 """ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql revision = "0001_initial" down_revision = None branch_labels = None depends_on = None def upgrade() -> None: # ── companies ───────────────────────────────────────────────────────────── op.create_table( "companies", sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True), sa.Column("name", sa.String(255), nullable=False), sa.Column("slug", sa.String(100), nullable=False, unique=True), sa.Column("plan", sa.String(50), server_default="trial"), sa.Column("logo_url", sa.Text), sa.Column("country", sa.String(10), server_default="DE"), sa.Column("state", sa.String(10)), sa.Column("settings", postgresql.JSONB, server_default="{}"), ) # ── departments (before users – FK target) ───────────────────────────────── op.create_table( "departments", sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True), sa.Column("company_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("companies.id", ondelete="CASCADE"), nullable=False), sa.Column("name", sa.String(255), nullable=False), sa.Column("manager_id", postgresql.UUID(as_uuid=True)), # FK added after users ) # ── users ───────────────────────────────────────────────────────────────── op.create_table( "users", sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True), sa.Column("company_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("companies.id", ondelete="CASCADE")), sa.Column("department_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("departments.id", ondelete="SET NULL")), sa.Column("email", sa.String(255), nullable=False, unique=True), sa.Column("password_hash", sa.Text, nullable=False), sa.Column("first_name", sa.String(100), nullable=False), sa.Column("last_name", sa.String(100), nullable=False), sa.Column("role", sa.Enum( "SUPER_ADMIN", "COMPANY_ADMIN", "HR", "MANAGER", "EMPLOYEE", name="userrole", ), nullable=False), sa.Column("kiosk_pin_hash", sa.Text), sa.Column("kiosk_qr_token", sa.Text, unique=True), sa.Column("is_active", sa.Boolean, server_default="true"), sa.Column("invite_token_hash", sa.Text), sa.Column("invite_expires", sa.DateTime(timezone=True)), sa.Column("last_login", sa.DateTime(timezone=True)), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()), ) op.create_index("ix_users_email", "users", ["email"]) # Now add FK for departments.manager_id → users op.create_foreign_key( "fk_departments_manager", "departments", "users", ["manager_id"], ["id"], ondelete="SET NULL", ) # ── sessions ────────────────────────────────────────────────────────────── op.create_table( "sessions", sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True), sa.Column("user_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=False), sa.Column("refresh_token_hash", sa.Text, nullable=False, unique=True), sa.Column("device", sa.String(255)), sa.Column("ip", sa.String(45)), sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()), ) op.create_index("ix_sessions_user_id", "sessions", ["user_id"]) # ── password_resets ─────────────────────────────────────────────────────── op.create_table( "password_resets", sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True), sa.Column("user_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=False), sa.Column("token_hash", sa.Text, nullable=False, unique=True), sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False), sa.Column("used_at", sa.DateTime(timezone=True)), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()), ) op.create_index("ix_password_resets_user_id", "password_resets", ["user_id"]) # ── audit_logs ──────────────────────────────────────────────────────────── op.create_table( "audit_logs", sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True), sa.Column("company_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("companies.id", ondelete="SET NULL")), sa.Column("user_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("users.id", ondelete="SET NULL")), sa.Column("action", sa.String(100), nullable=False), sa.Column("entity_type", sa.String(100)), sa.Column("entity_id", postgresql.UUID(as_uuid=True)), sa.Column("old_value", postgresql.JSONB), sa.Column("new_value", postgresql.JSONB), sa.Column("ip", sa.String(45)), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), index=True), ) op.create_index("ix_audit_logs_company_id", "audit_logs", ["company_id"]) op.create_index("ix_audit_logs_user_id", "audit_logs", ["user_id"]) def downgrade() -> None: op.drop_table("audit_logs") op.drop_table("password_resets") op.drop_table("sessions") op.drop_constraint("fk_departments_manager", "departments", type_="foreignkey") op.drop_table("users") op.execute("DROP TYPE IF EXISTS userrole") op.drop_table("departments") op.drop_table("companies")