"""002_multitenant_workspace Revision ID: 002 Revises: 001 Create Date: 2026-06-09 Alembic 002: 多租户基础(workspaces/workspace_members/user_api_keys) matrix_accounts 二期预留,一期不建。 user_api_keys 不存 url 字段(token站固定自家站,基石B)。 """ 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( "workspaces", sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), sa.Column("name", sa.String(128), nullable=False), sa.Column("slug", sa.String(64), nullable=False), sa.Column("is_active", sa.Boolean(), nullable=False, server_default="1"), sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False), sa.PrimaryKeyConstraint("id"), sa.UniqueConstraint("slug"), ) op.create_table( "workspace_members", sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), sa.Column("workspace_id", sa.BigInteger(), nullable=False), sa.Column("user_id", sa.BigInteger(), nullable=False), sa.Column("role", sa.Enum("admin", "supervisor", "operator", name="userrole"), nullable=False), sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False), sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"], ondelete="CASCADE"), sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"), sa.PrimaryKeyConstraint("id"), sa.UniqueConstraint("workspace_id", "user_id", name="uq_workspace_member"), ) op.create_index("idx_workspace_members_workspace_id", "workspace_members", ["workspace_id"]) op.create_table( "user_api_keys", sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), sa.Column("user_id", sa.BigInteger(), nullable=False), sa.Column("workspace_id", sa.BigInteger(), nullable=False), sa.Column("provider", sa.String(32), nullable=False), sa.Column("encrypted_key", sa.String(512), nullable=False), sa.Column("key_last4", sa.String(4), nullable=False), sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False), sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"), sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"], ondelete="CASCADE"), sa.PrimaryKeyConstraint("id"), sa.UniqueConstraint("user_id", "workspace_id", "provider", name="uq_user_workspace_provider"), ) op.create_index("idx_user_api_keys_workspace_id", "user_api_keys", ["workspace_id"]) def downgrade() -> None: op.drop_index("idx_user_api_keys_workspace_id", "user_api_keys") op.drop_table("user_api_keys") op.drop_index("idx_workspace_members_workspace_id", "workspace_members") op.drop_table("workspace_members") op.drop_table("workspaces") op.execute("DROP TYPE IF EXISTS userrole")