"""add users, refresh_tokens, activity_log; FK existing tables to users Revision ID: 002 Revises: 001 Create Date: 2026-05-27 The Phase 1-3 schema from the OAuth plan, adapted to this repo's existing tables. SQLite doesn't enforce FK constraints by default but the columns and indexes are still useful for query planning. """ from typing import Sequence, Union import sqlalchemy as sa from alembic import op revision: str = "002" down_revision: Union[str, None] = "001" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: op.create_table( "users", sa.Column("id", sa.String(), primary_key=True), sa.Column("email", sa.String(), nullable=False, unique=True), sa.Column("display_name", sa.String(), nullable=False), sa.Column("avatar_url", sa.String(), nullable=True), sa.Column("google_id", sa.String(), nullable=True, unique=True), sa.Column( "role", sa.String(), nullable=False, server_default=sa.text("'user'"), ), sa.Column("created_at", sa.DateTime(), nullable=False), sa.Column("last_login_at", sa.DateTime(), nullable=True), ) op.create_table( "refresh_tokens", sa.Column("id", sa.String(), primary_key=True), sa.Column( "user_id", sa.String(), sa.ForeignKey("users.id"), nullable=False, ), sa.Column("token_hash", sa.String(), nullable=False, unique=True), sa.Column("device", sa.String(), nullable=True), sa.Column("created_at", sa.DateTime(), nullable=False), sa.Column("expires_at", sa.DateTime(), nullable=False), sa.Column("revoked_at", sa.DateTime(), nullable=True), ) op.create_index( "ix_refresh_tokens_user_id", "refresh_tokens", ["user_id"] ) op.create_table( "activity_log", sa.Column("id", sa.String(), primary_key=True), sa.Column( "user_id", sa.String(), sa.ForeignKey("users.id"), nullable=False, ), sa.Column("action", sa.String(), nullable=False), sa.Column("resource", sa.String(), nullable=False), sa.Column("resource_id", sa.String(), nullable=True), sa.Column("metadata_json", sa.Text(), nullable=True), sa.Column( "source", sa.String(), nullable=False, server_default=sa.text("'web'"), ), sa.Column("ip_address", sa.String(), nullable=True), sa.Column("user_agent", sa.Text(), nullable=True), sa.Column("created_at", sa.DateTime(), nullable=False), ) op.create_index( "ix_activity_log_user_id", "activity_log", ["user_id"] ) op.create_index( "ix_activity_log_created_at", "activity_log", ["created_at"] ) op.create_index( "ix_activity_log_resource", "activity_log", ["resource", "resource_id"], ) def downgrade() -> None: op.drop_index("ix_activity_log_resource", table_name="activity_log") op.drop_index("ix_activity_log_created_at", table_name="activity_log") op.drop_index("ix_activity_log_user_id", table_name="activity_log") op.drop_table("activity_log") op.drop_index("ix_refresh_tokens_user_id", table_name="refresh_tokens") op.drop_table("refresh_tokens") op.drop_table("users")