chore: consolidate platform split checks
This commit is contained in:
@@ -7,6 +7,7 @@ from sqlalchemy import engine_from_config, pool
|
||||
|
||||
from govoplan_access.backend.db import models as access_models # noqa: F401 - populate access metadata
|
||||
from govoplan_core.admin import models as core_admin_models # noqa: F401 - populate core admin metadata
|
||||
from govoplan_core.core import change_sequence as core_change_sequence_models # noqa: F401 - populate core metadata
|
||||
from govoplan_core.core.migrations import migration_metadata_plan
|
||||
from govoplan_core.db.base import Base
|
||||
from govoplan_core.server.default_config import get_server_config
|
||||
|
||||
@@ -94,10 +94,10 @@ def _scrub_policy_column(table_name: str) -> None:
|
||||
def upgrade() -> None:
|
||||
inspector = sa.inspect(op.get_bind())
|
||||
tables = set(inspector.get_table_names())
|
||||
for table_name in ("system_settings", "tenants"):
|
||||
for table_name in ("core_system_settings", "tenancy_tenants"):
|
||||
if table_name in tables and "settings" in {column["name"] for column in inspector.get_columns(table_name)}:
|
||||
_scrub_settings_table(table_name)
|
||||
for table_name in ("users", "groups", "campaigns"):
|
||||
for table_name in ("access_users", "access_groups", "campaigns"):
|
||||
if table_name in tables and "mail_profile_policy" in {column["name"] for column in inspector.get_columns(table_name)}:
|
||||
_scrub_policy_column(table_name)
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ def upgrade() -> None:
|
||||
batch_op.add_column(sa.Column("locked_by_user_id", sa.String(length=36), nullable=True))
|
||||
batch_op.create_foreign_key(
|
||||
op.f("fk_campaign_versions_locked_by_user_id_users"),
|
||||
"users",
|
||||
"access_users",
|
||||
["locked_by_user_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
|
||||
@@ -17,67 +17,67 @@ depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
with op.batch_alter_table("users") as batch_op:
|
||||
with op.batch_alter_table("access_users") as batch_op:
|
||||
batch_op.add_column(sa.Column("auth_provider", sa.String(length=50), nullable=False, server_default="local"))
|
||||
batch_op.add_column(sa.Column("password_hash", sa.String(length=500), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_login_at", sa.DateTime(timezone=True), nullable=True))
|
||||
|
||||
op.create_table(
|
||||
"user_group_memberships",
|
||||
"access_user_group_memberships",
|
||||
sa.Column("id", sa.String(length=36), nullable=False),
|
||||
sa.Column("tenant_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("user_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("group_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["group_id"], ["groups.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["user_id"], ["access_users.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["group_id"], ["access_groups.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("tenant_id", "user_id", "group_id", name="uq_user_group_memberships"),
|
||||
)
|
||||
op.create_index(op.f("ix_user_group_memberships_tenant_id"), "user_group_memberships", ["tenant_id"])
|
||||
op.create_index(op.f("ix_user_group_memberships_user_id"), "user_group_memberships", ["user_id"])
|
||||
op.create_index(op.f("ix_user_group_memberships_group_id"), "user_group_memberships", ["group_id"])
|
||||
op.create_index(op.f("ix_access_user_group_memberships_tenant_id"), "access_user_group_memberships", ["tenant_id"])
|
||||
op.create_index(op.f("ix_access_user_group_memberships_user_id"), "access_user_group_memberships", ["user_id"])
|
||||
op.create_index(op.f("ix_access_user_group_memberships_group_id"), "access_user_group_memberships", ["group_id"])
|
||||
|
||||
op.create_table(
|
||||
"user_role_assignments",
|
||||
"access_user_role_assignments",
|
||||
sa.Column("id", sa.String(length=36), nullable=False),
|
||||
sa.Column("tenant_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("user_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("role_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["role_id"], ["roles.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["user_id"], ["access_users.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["role_id"], ["access_roles.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("tenant_id", "user_id", "role_id", name="uq_user_role_assignments"),
|
||||
)
|
||||
op.create_index(op.f("ix_user_role_assignments_tenant_id"), "user_role_assignments", ["tenant_id"])
|
||||
op.create_index(op.f("ix_user_role_assignments_user_id"), "user_role_assignments", ["user_id"])
|
||||
op.create_index(op.f("ix_user_role_assignments_role_id"), "user_role_assignments", ["role_id"])
|
||||
op.create_index(op.f("ix_access_user_role_assignments_tenant_id"), "access_user_role_assignments", ["tenant_id"])
|
||||
op.create_index(op.f("ix_access_user_role_assignments_user_id"), "access_user_role_assignments", ["user_id"])
|
||||
op.create_index(op.f("ix_access_user_role_assignments_role_id"), "access_user_role_assignments", ["role_id"])
|
||||
|
||||
op.create_table(
|
||||
"group_role_assignments",
|
||||
"access_group_role_assignments",
|
||||
sa.Column("id", sa.String(length=36), nullable=False),
|
||||
sa.Column("tenant_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("group_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("role_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["group_id"], ["groups.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["role_id"], ["roles.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["group_id"], ["access_groups.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["role_id"], ["access_roles.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("tenant_id", "group_id", "role_id", name="uq_group_role_assignments"),
|
||||
)
|
||||
op.create_index(op.f("ix_group_role_assignments_tenant_id"), "group_role_assignments", ["tenant_id"])
|
||||
op.create_index(op.f("ix_group_role_assignments_group_id"), "group_role_assignments", ["group_id"])
|
||||
op.create_index(op.f("ix_group_role_assignments_role_id"), "group_role_assignments", ["role_id"])
|
||||
op.create_index(op.f("ix_access_group_role_assignments_tenant_id"), "access_group_role_assignments", ["tenant_id"])
|
||||
op.create_index(op.f("ix_access_group_role_assignments_group_id"), "access_group_role_assignments", ["group_id"])
|
||||
op.create_index(op.f("ix_access_group_role_assignments_role_id"), "access_group_role_assignments", ["role_id"])
|
||||
|
||||
op.create_table(
|
||||
"auth_sessions",
|
||||
"access_auth_sessions",
|
||||
sa.Column("id", sa.String(length=36), nullable=False),
|
||||
sa.Column("tenant_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("user_id", sa.String(length=36), nullable=False),
|
||||
@@ -89,42 +89,42 @@ def upgrade() -> None:
|
||||
sa.Column("ip_address", sa.String(length=100), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["user_id"], ["access_users.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("token_hash"),
|
||||
)
|
||||
op.create_index(op.f("ix_auth_sessions_tenant_id"), "auth_sessions", ["tenant_id"])
|
||||
op.create_index(op.f("ix_auth_sessions_user_id"), "auth_sessions", ["user_id"])
|
||||
op.create_index(op.f("ix_auth_sessions_token_hash"), "auth_sessions", ["token_hash"])
|
||||
op.create_index(op.f("ix_auth_sessions_expires_at"), "auth_sessions", ["expires_at"])
|
||||
op.create_index(op.f("ix_auth_sessions_revoked_at"), "auth_sessions", ["revoked_at"])
|
||||
op.create_index(op.f("ix_access_auth_sessions_tenant_id"), "access_auth_sessions", ["tenant_id"])
|
||||
op.create_index(op.f("ix_access_auth_sessions_user_id"), "access_auth_sessions", ["user_id"])
|
||||
op.create_index(op.f("ix_access_auth_sessions_token_hash"), "access_auth_sessions", ["token_hash"])
|
||||
op.create_index(op.f("ix_access_auth_sessions_expires_at"), "access_auth_sessions", ["expires_at"])
|
||||
op.create_index(op.f("ix_access_auth_sessions_revoked_at"), "access_auth_sessions", ["revoked_at"])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index(op.f("ix_auth_sessions_revoked_at"), table_name="auth_sessions")
|
||||
op.drop_index(op.f("ix_auth_sessions_expires_at"), table_name="auth_sessions")
|
||||
op.drop_index(op.f("ix_auth_sessions_token_hash"), table_name="auth_sessions")
|
||||
op.drop_index(op.f("ix_auth_sessions_user_id"), table_name="auth_sessions")
|
||||
op.drop_index(op.f("ix_auth_sessions_tenant_id"), table_name="auth_sessions")
|
||||
op.drop_table("auth_sessions")
|
||||
op.drop_index(op.f("ix_access_auth_sessions_revoked_at"), table_name="access_auth_sessions")
|
||||
op.drop_index(op.f("ix_access_auth_sessions_expires_at"), table_name="access_auth_sessions")
|
||||
op.drop_index(op.f("ix_access_auth_sessions_token_hash"), table_name="access_auth_sessions")
|
||||
op.drop_index(op.f("ix_access_auth_sessions_user_id"), table_name="access_auth_sessions")
|
||||
op.drop_index(op.f("ix_access_auth_sessions_tenant_id"), table_name="access_auth_sessions")
|
||||
op.drop_table("access_auth_sessions")
|
||||
|
||||
op.drop_index(op.f("ix_group_role_assignments_role_id"), table_name="group_role_assignments")
|
||||
op.drop_index(op.f("ix_group_role_assignments_group_id"), table_name="group_role_assignments")
|
||||
op.drop_index(op.f("ix_group_role_assignments_tenant_id"), table_name="group_role_assignments")
|
||||
op.drop_table("group_role_assignments")
|
||||
op.drop_index(op.f("ix_access_group_role_assignments_role_id"), table_name="access_group_role_assignments")
|
||||
op.drop_index(op.f("ix_access_group_role_assignments_group_id"), table_name="access_group_role_assignments")
|
||||
op.drop_index(op.f("ix_access_group_role_assignments_tenant_id"), table_name="access_group_role_assignments")
|
||||
op.drop_table("access_group_role_assignments")
|
||||
|
||||
op.drop_index(op.f("ix_user_role_assignments_role_id"), table_name="user_role_assignments")
|
||||
op.drop_index(op.f("ix_user_role_assignments_user_id"), table_name="user_role_assignments")
|
||||
op.drop_index(op.f("ix_user_role_assignments_tenant_id"), table_name="user_role_assignments")
|
||||
op.drop_table("user_role_assignments")
|
||||
op.drop_index(op.f("ix_access_user_role_assignments_role_id"), table_name="access_user_role_assignments")
|
||||
op.drop_index(op.f("ix_access_user_role_assignments_user_id"), table_name="access_user_role_assignments")
|
||||
op.drop_index(op.f("ix_access_user_role_assignments_tenant_id"), table_name="access_user_role_assignments")
|
||||
op.drop_table("access_user_role_assignments")
|
||||
|
||||
op.drop_index(op.f("ix_user_group_memberships_group_id"), table_name="user_group_memberships")
|
||||
op.drop_index(op.f("ix_user_group_memberships_user_id"), table_name="user_group_memberships")
|
||||
op.drop_index(op.f("ix_user_group_memberships_tenant_id"), table_name="user_group_memberships")
|
||||
op.drop_table("user_group_memberships")
|
||||
op.drop_index(op.f("ix_access_user_group_memberships_group_id"), table_name="access_user_group_memberships")
|
||||
op.drop_index(op.f("ix_access_user_group_memberships_user_id"), table_name="access_user_group_memberships")
|
||||
op.drop_index(op.f("ix_access_user_group_memberships_tenant_id"), table_name="access_user_group_memberships")
|
||||
op.drop_table("access_user_group_memberships")
|
||||
|
||||
with op.batch_alter_table("users") as batch_op:
|
||||
with op.batch_alter_table("access_users") as batch_op:
|
||||
batch_op.drop_column("last_login_at")
|
||||
batch_op.drop_column("password_hash")
|
||||
batch_op.drop_column("auth_provider")
|
||||
|
||||
78
alembic/versions/2e3f4a5b6c7d_namespace_platform_tables.py
Normal file
78
alembic/versions/2e3f4a5b6c7d_namespace_platform_tables.py
Normal file
@@ -0,0 +1,78 @@
|
||||
"""namespace platform-owned tables
|
||||
|
||||
Revision ID: 2e3f4a5b6c7d
|
||||
Revises: 1b2c3d4e5f70
|
||||
Create Date: 2026-07-09 00:00:00.000000
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision = "2e3f4a5b6c7d"
|
||||
down_revision = "1b2c3d4e5f70"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
_TABLE_RENAMES = (
|
||||
("tenants", "tenancy_tenants"),
|
||||
("accounts", "access_accounts"),
|
||||
("users", "access_users"),
|
||||
("groups", "access_groups"),
|
||||
("roles", "access_roles"),
|
||||
("system_role_assignments", "access_system_role_assignments"),
|
||||
("user_group_memberships", "access_user_group_memberships"),
|
||||
("user_role_assignments", "access_user_role_assignments"),
|
||||
("group_role_assignments", "access_group_role_assignments"),
|
||||
("api_keys", "access_api_keys"),
|
||||
("auth_sessions", "access_auth_sessions"),
|
||||
("system_settings", "core_system_settings"),
|
||||
("governance_templates", "admin_governance_templates"),
|
||||
("governance_template_assignments", "admin_governance_template_assignments"),
|
||||
)
|
||||
|
||||
|
||||
def _row_count(bind: sa.Connection, table_name: str) -> int:
|
||||
return int(bind.execute(sa.text(f"SELECT COUNT(*) FROM {table_name}")).scalar_one())
|
||||
|
||||
|
||||
def _rename_tables(renames: tuple[tuple[str, str], ...]) -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
tables = set(inspector.get_table_names())
|
||||
|
||||
old_tables_to_drop: set[str] = set()
|
||||
new_tables_to_drop: set[str] = set()
|
||||
for old_name, new_name in renames:
|
||||
if old_name not in tables or new_name not in tables:
|
||||
continue
|
||||
if _row_count(bind, old_name) == 0:
|
||||
old_tables_to_drop.add(old_name)
|
||||
elif _row_count(bind, new_name) == 0:
|
||||
new_tables_to_drop.add(new_name)
|
||||
else:
|
||||
raise RuntimeError(f"Cannot rename non-empty {old_name} over non-empty {new_name}")
|
||||
|
||||
for old_name, _new_name in reversed(renames):
|
||||
if old_name in old_tables_to_drop:
|
||||
op.drop_table(old_name)
|
||||
tables.remove(old_name)
|
||||
for _old_name, new_name in reversed(renames):
|
||||
if new_name in new_tables_to_drop:
|
||||
op.drop_table(new_name)
|
||||
tables.remove(new_name)
|
||||
|
||||
for old_name, new_name in renames:
|
||||
if old_name not in tables:
|
||||
continue
|
||||
op.rename_table(old_name, new_name)
|
||||
tables.remove(old_name)
|
||||
tables.add(new_name)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
_rename_tables(_TABLE_RENAMES)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
_rename_tables(tuple((new_name, old_name) for old_name, new_name in reversed(_TABLE_RENAMES)))
|
||||
@@ -32,7 +32,7 @@ def upgrade() -> None:
|
||||
sa.Column("retained_until", sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("tenant_id", "checksum_sha256", "size_bytes", name="uq_file_blobs_tenant_checksum_size"),
|
||||
)
|
||||
@@ -55,10 +55,10 @@ def upgrade() -> None:
|
||||
sa.Column("metadata", sa.JSON(), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["created_by_user_id"], ["users.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["owner_group_id"], ["groups.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["owner_user_id"], ["users.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["created_by_user_id"], ["access_users.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["owner_group_id"], ["access_groups.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["owner_user_id"], ["access_users.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
for col in ["tenant_id", "owner_type", "owner_user_id", "owner_group_id", "current_version_id", "display_path", "filename", "created_by_user_id", "deleted_at"]:
|
||||
@@ -80,9 +80,9 @@ def upgrade() -> None:
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["blob_id"], ["file_blobs.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["created_by_user_id"], ["users.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["created_by_user_id"], ["access_users.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["file_asset_id"], ["file_assets.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("file_asset_id", "version_number", name="uq_file_versions_asset_number"),
|
||||
)
|
||||
@@ -101,9 +101,9 @@ def upgrade() -> None:
|
||||
sa.Column("revoked_at", sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["created_by_user_id"], ["users.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["created_by_user_id"], ["access_users.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["file_asset_id"], ["file_assets.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("file_asset_id", "target_type", "target_id", "revoked_at", name="uq_file_shares_active_target"),
|
||||
)
|
||||
@@ -136,7 +136,7 @@ def upgrade() -> None:
|
||||
sa.ForeignKeyConstraint(["file_asset_id"], ["file_assets.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["file_blob_id"], ["file_blobs.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["file_version_id"], ["file_versions.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("campaign_job_id", "file_version_id", "filename_used", "use_stage", name="uq_campaign_attachment_uses_job_file_stage"),
|
||||
)
|
||||
|
||||
111
alembic/versions/3f4a5b6c7d8e_core_change_sequence.py
Normal file
111
alembic/versions/3f4a5b6c7d8e_core_change_sequence.py
Normal file
@@ -0,0 +1,111 @@
|
||||
"""add core change sequence
|
||||
|
||||
Revision ID: 3f4a5b6c7d8e
|
||||
Revises: 2e3f4a5b6c7d
|
||||
Create Date: 2026-07-09 00:00:00.000000
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision = "3f4a5b6c7d8e"
|
||||
down_revision = "2e3f4a5b6c7d"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
inspector = sa.inspect(op.get_bind())
|
||||
if "core_change_sequence" not in inspector.get_table_names():
|
||||
op.create_table(
|
||||
"core_change_sequence",
|
||||
sa.Column("id", sa.BigInteger().with_variant(sa.Integer(), "sqlite"), autoincrement=True, nullable=False),
|
||||
sa.Column("tenant_id", sa.String(length=36), nullable=True),
|
||||
sa.Column("module_id", sa.String(length=100), nullable=False),
|
||||
sa.Column("collection", sa.String(length=150), nullable=False),
|
||||
sa.Column("resource_type", sa.String(length=100), nullable=False),
|
||||
sa.Column("resource_id", sa.String(length=255), nullable=False),
|
||||
sa.Column("operation", sa.String(length=30), nullable=False),
|
||||
sa.Column("actor_type", sa.String(length=30), nullable=True),
|
||||
sa.Column("actor_id", sa.String(length=255), nullable=True),
|
||||
sa.Column("payload", sa.JSON(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id", name=op.f("pk_core_change_sequence")),
|
||||
)
|
||||
|
||||
inspector = sa.inspect(op.get_bind())
|
||||
indexes = {item["name"] for item in inspector.get_indexes("core_change_sequence")}
|
||||
for column in (
|
||||
"tenant_id",
|
||||
"module_id",
|
||||
"collection",
|
||||
"resource_type",
|
||||
"resource_id",
|
||||
"operation",
|
||||
"actor_type",
|
||||
"actor_id",
|
||||
"created_at",
|
||||
):
|
||||
name = op.f(f"ix_core_change_sequence_{column}")
|
||||
if name not in indexes:
|
||||
op.create_index(name, "core_change_sequence", [column], unique=False)
|
||||
for name, columns in (
|
||||
("ix_core_change_sequence_tenant_module_id", ["tenant_id", "module_id", "id"]),
|
||||
("ix_core_change_sequence_collection_id", ["collection", "id"]),
|
||||
("ix_core_change_sequence_resource", ["module_id", "resource_type", "resource_id"]),
|
||||
):
|
||||
if name not in indexes:
|
||||
op.create_index(name, "core_change_sequence", columns, unique=False)
|
||||
|
||||
inspector = sa.inspect(op.get_bind())
|
||||
if "core_change_sequence_retention_floor" not in inspector.get_table_names():
|
||||
op.create_table(
|
||||
"core_change_sequence_retention_floor",
|
||||
sa.Column("id", sa.BigInteger().with_variant(sa.Integer(), "sqlite"), autoincrement=True, nullable=False),
|
||||
sa.Column("tenant_key", sa.String(length=36), nullable=False),
|
||||
sa.Column("module_id", sa.String(length=100), nullable=False),
|
||||
sa.Column("collection", sa.String(length=150), nullable=False),
|
||||
sa.Column("min_valid_sequence", sa.BigInteger().with_variant(sa.Integer(), "sqlite"), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id", name=op.f("pk_core_change_sequence_retention_floor")),
|
||||
sa.UniqueConstraint("tenant_key", "module_id", "collection", name="uq_core_change_sequence_retention_scope"),
|
||||
)
|
||||
indexes = {item["name"] for item in inspector.get_indexes("core_change_sequence_retention_floor")}
|
||||
if "ix_core_change_sequence_retention_scope" not in indexes:
|
||||
op.create_index(
|
||||
"ix_core_change_sequence_retention_scope",
|
||||
"core_change_sequence_retention_floor",
|
||||
["tenant_key", "module_id", "collection"],
|
||||
unique=False,
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
inspector = sa.inspect(op.get_bind())
|
||||
if "core_change_sequence_retention_floor" in inspector.get_table_names():
|
||||
indexes = {item["name"] for item in inspector.get_indexes("core_change_sequence_retention_floor")}
|
||||
if "ix_core_change_sequence_retention_scope" in indexes:
|
||||
op.drop_index("ix_core_change_sequence_retention_scope", table_name="core_change_sequence_retention_floor")
|
||||
op.drop_table("core_change_sequence_retention_floor")
|
||||
if "core_change_sequence" not in inspector.get_table_names():
|
||||
return
|
||||
indexes = {item["name"] for item in inspector.get_indexes("core_change_sequence")}
|
||||
for name in (
|
||||
"ix_core_change_sequence_resource",
|
||||
"ix_core_change_sequence_collection_id",
|
||||
"ix_core_change_sequence_tenant_module_id",
|
||||
op.f("ix_core_change_sequence_created_at"),
|
||||
op.f("ix_core_change_sequence_actor_id"),
|
||||
op.f("ix_core_change_sequence_actor_type"),
|
||||
op.f("ix_core_change_sequence_operation"),
|
||||
op.f("ix_core_change_sequence_resource_id"),
|
||||
op.f("ix_core_change_sequence_resource_type"),
|
||||
op.f("ix_core_change_sequence_collection"),
|
||||
op.f("ix_core_change_sequence_module_id"),
|
||||
op.f("ix_core_change_sequence_tenant_id"),
|
||||
):
|
||||
if name in indexes:
|
||||
op.drop_index(name, table_name="core_change_sequence")
|
||||
op.drop_table("core_change_sequence")
|
||||
@@ -31,10 +31,10 @@ def upgrade() -> None:
|
||||
sa.Column("metadata", sa.JSON(), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["created_by_user_id"], ["users.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["owner_group_id"], ["groups.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["owner_user_id"], ["users.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["created_by_user_id"], ["access_users.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["owner_group_id"], ["access_groups.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["owner_user_id"], ["access_users.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
for col in ["tenant_id", "owner_type", "owner_user_id", "owner_group_id", "path", "created_by_user_id", "deleted_at"]:
|
||||
|
||||
@@ -24,7 +24,7 @@ def upgrade() -> None:
|
||||
batch_op.add_column(sa.Column("user_locked_by_user_id", sa.String(length=36), nullable=True))
|
||||
batch_op.create_foreign_key(
|
||||
"fk_campaign_versions_user_locked_by_user_id_users",
|
||||
"users",
|
||||
"access_users",
|
||||
["user_locked_by_user_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
|
||||
@@ -62,25 +62,25 @@ def upgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
tables = set(inspector.get_table_names())
|
||||
user_columns = {column["name"] for column in inspector.get_columns("users")}
|
||||
user_columns = {column["name"] for column in inspector.get_columns("access_users")}
|
||||
# Base.metadata.create_all() from a newer application can create brand-new
|
||||
# tables while leaving existing tables unaltered. Repair that known drift by
|
||||
# removing only empty, unreferenced administration tables before applying the
|
||||
# real migration. A non-empty table is never guessed at or discarded.
|
||||
if "account_id" not in user_columns and "system_role_assignments" in tables:
|
||||
count = bind.execute(sa.text("SELECT COUNT(*) FROM system_role_assignments")).scalar_one()
|
||||
if "account_id" not in user_columns and "access_system_role_assignments" in tables:
|
||||
count = bind.execute(sa.text("SELECT COUNT(*) FROM access_system_role_assignments")).scalar_one()
|
||||
if count:
|
||||
raise RuntimeError("Cannot reconcile non-empty create_all system_role_assignments table")
|
||||
op.drop_table("system_role_assignments")
|
||||
tables.remove("system_role_assignments")
|
||||
if "account_id" not in user_columns and "accounts" in tables:
|
||||
count = bind.execute(sa.text("SELECT COUNT(*) FROM accounts")).scalar_one()
|
||||
op.drop_table("access_system_role_assignments")
|
||||
tables.remove("access_system_role_assignments")
|
||||
if "account_id" not in user_columns and "access_accounts" in tables:
|
||||
count = bind.execute(sa.text("SELECT COUNT(*) FROM access_accounts")).scalar_one()
|
||||
if count:
|
||||
raise RuntimeError("Cannot reconcile non-empty create_all accounts table")
|
||||
op.drop_table("accounts")
|
||||
op.drop_table("access_accounts")
|
||||
|
||||
op.create_table(
|
||||
"accounts",
|
||||
"access_accounts",
|
||||
sa.Column("id", sa.String(length=36), nullable=False),
|
||||
sa.Column("email", sa.String(length=320), nullable=False),
|
||||
sa.Column("normalized_email", sa.String(length=320), nullable=False),
|
||||
@@ -95,29 +95,29 @@ def upgrade() -> None:
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("normalized_email", name="uq_accounts_normalized_email"),
|
||||
)
|
||||
op.create_index(op.f("ix_accounts_normalized_email"), "accounts", ["normalized_email"])
|
||||
op.create_index(op.f("ix_access_accounts_normalized_email"), "access_accounts", ["normalized_email"])
|
||||
|
||||
with op.batch_alter_table("tenants") as batch_op:
|
||||
with op.batch_alter_table("tenancy_tenants") as batch_op:
|
||||
batch_op.add_column(sa.Column("description", sa.Text(), nullable=True))
|
||||
batch_op.add_column(sa.Column("default_locale", sa.String(length=20), nullable=False, server_default="en"))
|
||||
batch_op.add_column(sa.Column("settings", sa.JSON(), nullable=False, server_default="{}"))
|
||||
|
||||
with op.batch_alter_table("groups") as batch_op:
|
||||
with op.batch_alter_table("access_groups") as batch_op:
|
||||
batch_op.add_column(sa.Column("description", sa.Text(), nullable=True))
|
||||
batch_op.add_column(sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.true()))
|
||||
|
||||
with op.batch_alter_table("roles") as batch_op:
|
||||
with op.batch_alter_table("access_roles") as batch_op:
|
||||
batch_op.add_column(sa.Column("description", sa.Text(), nullable=True))
|
||||
batch_op.add_column(sa.Column("is_builtin", sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||
batch_op.add_column(sa.Column("is_assignable", sa.Boolean(), nullable=False, server_default=sa.true()))
|
||||
|
||||
with op.batch_alter_table("users") as batch_op:
|
||||
with op.batch_alter_table("access_users") as batch_op:
|
||||
batch_op.add_column(sa.Column("account_id", sa.String(length=36), nullable=True))
|
||||
with op.batch_alter_table("auth_sessions") as batch_op:
|
||||
with op.batch_alter_table("access_auth_sessions") as batch_op:
|
||||
batch_op.add_column(sa.Column("account_id", sa.String(length=36), nullable=True))
|
||||
|
||||
users = bind.execute(sa.text(
|
||||
"SELECT id, tenant_id, email, display_name, is_active, is_tenant_admin, auth_provider, password_hash, last_login_at, created_at, updated_at FROM users ORDER BY created_at, id"
|
||||
"SELECT id, tenant_id, email, display_name, is_active, is_tenant_admin, auth_provider, password_hash, last_login_at, created_at, updated_at FROM access_users ORDER BY created_at, id"
|
||||
)).mappings().all()
|
||||
|
||||
accounts_by_email: dict[str, str] = {}
|
||||
@@ -155,7 +155,7 @@ def upgrade() -> None:
|
||||
first_system_owner_account_id = account_id
|
||||
|
||||
accounts_table = sa.table(
|
||||
"accounts",
|
||||
"access_accounts",
|
||||
sa.column("id", sa.String), sa.column("email", sa.String), sa.column("normalized_email", sa.String),
|
||||
sa.column("display_name", sa.String), sa.column("is_active", sa.Boolean), sa.column("auth_provider", sa.String),
|
||||
sa.column("password_hash", sa.String), sa.column("password_reset_required", sa.Boolean),
|
||||
@@ -166,54 +166,54 @@ def upgrade() -> None:
|
||||
bind.execute(accounts_table.insert(), list(account_rows.values()))
|
||||
for row in users:
|
||||
bind.execute(
|
||||
sa.text("UPDATE users SET account_id = :account_id WHERE id = :user_id"),
|
||||
sa.text("UPDATE access_users SET account_id = :account_id WHERE id = :user_id"),
|
||||
{"account_id": accounts_by_email[_normalize_email(row["email"])], "user_id": row["id"]},
|
||||
)
|
||||
bind.execute(sa.text(
|
||||
"UPDATE auth_sessions SET account_id = (SELECT users.account_id FROM users WHERE users.id = auth_sessions.user_id)"
|
||||
"UPDATE access_auth_sessions SET account_id = (SELECT access_users.account_id FROM access_users WHERE access_users.id = access_auth_sessions.user_id)"
|
||||
))
|
||||
|
||||
with op.batch_alter_table("users") as batch_op:
|
||||
with op.batch_alter_table("access_users") as batch_op:
|
||||
batch_op.alter_column("account_id", existing_type=sa.String(length=36), nullable=False)
|
||||
batch_op.create_foreign_key("fk_users_account_id_accounts", "accounts", ["account_id"], ["id"], ondelete="CASCADE")
|
||||
batch_op.create_foreign_key("fk_users_account_id_accounts", "access_accounts", ["account_id"], ["id"], ondelete="CASCADE")
|
||||
batch_op.create_unique_constraint("uq_users_tenant_account", ["tenant_id", "account_id"])
|
||||
op.create_index(op.f("ix_users_account_id"), "users", ["account_id"])
|
||||
op.create_index(op.f("ix_access_users_account_id"), "access_users", ["account_id"])
|
||||
|
||||
with op.batch_alter_table("auth_sessions") as batch_op:
|
||||
with op.batch_alter_table("access_auth_sessions") as batch_op:
|
||||
batch_op.alter_column("account_id", existing_type=sa.String(length=36), nullable=False)
|
||||
batch_op.create_foreign_key("fk_auth_sessions_account_id_accounts", "accounts", ["account_id"], ["id"], ondelete="CASCADE")
|
||||
op.create_index(op.f("ix_auth_sessions_account_id"), "auth_sessions", ["account_id"])
|
||||
batch_op.create_foreign_key("fk_auth_sessions_account_id_accounts", "access_accounts", ["account_id"], ["id"], ondelete="CASCADE")
|
||||
op.create_index(op.f("ix_access_auth_sessions_account_id"), "access_auth_sessions", ["account_id"])
|
||||
|
||||
op.create_table(
|
||||
"system_role_assignments",
|
||||
"access_system_role_assignments",
|
||||
sa.Column("id", sa.String(length=36), nullable=False),
|
||||
sa.Column("account_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("role_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["account_id"], ["accounts.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["role_id"], ["roles.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["account_id"], ["access_accounts.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["role_id"], ["access_roles.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("account_id", "role_id", name="uq_system_role_assignments"),
|
||||
)
|
||||
op.create_index(op.f("ix_system_role_assignments_account_id"), "system_role_assignments", ["account_id"])
|
||||
op.create_index(op.f("ix_system_role_assignments_role_id"), "system_role_assignments", ["role_id"])
|
||||
op.create_index(op.f("ix_access_system_role_assignments_account_id"), "access_system_role_assignments", ["account_id"])
|
||||
op.create_index(op.f("ix_access_system_role_assignments_role_id"), "access_system_role_assignments", ["role_id"])
|
||||
|
||||
roles_table = sa.table(
|
||||
"roles",
|
||||
"access_roles",
|
||||
sa.column("id", sa.String), sa.column("tenant_id", sa.String), sa.column("slug", sa.String),
|
||||
sa.column("name", sa.String), sa.column("description", sa.Text), sa.column("permissions", sa.JSON),
|
||||
sa.column("is_builtin", sa.Boolean), sa.column("is_assignable", sa.Boolean),
|
||||
sa.column("created_at", sa.DateTime(timezone=True)), sa.column("updated_at", sa.DateTime(timezone=True)),
|
||||
)
|
||||
now = _now()
|
||||
tenant_ids = [row[0] for row in bind.execute(sa.text("SELECT id FROM tenants")).all()]
|
||||
tenant_ids = [row[0] for row in bind.execute(sa.text("SELECT id FROM tenancy_tenants")).all()]
|
||||
definitions = _role_definitions()
|
||||
tenant_role_ids: dict[tuple[str, str], str] = {}
|
||||
for tenant_id in tenant_ids:
|
||||
for slug, definition in definitions.items():
|
||||
existing = bind.execute(
|
||||
sa.text("SELECT id FROM roles WHERE tenant_id = :tenant_id AND slug = :slug"),
|
||||
sa.text("SELECT id FROM access_roles WHERE tenant_id = :tenant_id AND slug = :slug"),
|
||||
{"tenant_id": tenant_id, "slug": slug},
|
||||
).scalar_one_or_none()
|
||||
if existing:
|
||||
@@ -253,7 +253,7 @@ def upgrade() -> None:
|
||||
|
||||
op.create_index(
|
||||
"uq_roles_system_slug",
|
||||
"roles",
|
||||
"access_roles",
|
||||
["slug"],
|
||||
unique=True,
|
||||
sqlite_where=sa.text("tenant_id IS NULL"),
|
||||
@@ -267,11 +267,11 @@ def upgrade() -> None:
|
||||
continue
|
||||
owner_role_id = tenant_role_ids[(row["tenant_id"], "owner")]
|
||||
exists = bind.execute(sa.text(
|
||||
"SELECT 1 FROM user_role_assignments WHERE tenant_id = :tenant_id AND user_id = :user_id AND role_id = :role_id"
|
||||
"SELECT 1 FROM access_user_role_assignments WHERE tenant_id = :tenant_id AND user_id = :user_id AND role_id = :role_id"
|
||||
), {"tenant_id": row["tenant_id"], "user_id": row["id"], "role_id": owner_role_id}).first()
|
||||
if not exists:
|
||||
bind.execute(sa.text(
|
||||
"INSERT INTO user_role_assignments (id, tenant_id, user_id, role_id, created_at, updated_at) VALUES (:id, :tenant_id, :user_id, :role_id, :created_at, :updated_at)"
|
||||
"INSERT INTO access_user_role_assignments (id, tenant_id, user_id, role_id, created_at, updated_at) VALUES (:id, :tenant_id, :user_id, :role_id, :created_at, :updated_at)"
|
||||
), {"id": str(uuid.uuid4()), "tenant_id": row["tenant_id"], "user_id": row["id"], "role_id": owner_role_id, "created_at": now, "updated_at": now})
|
||||
|
||||
# Bootstrap rule for existing installations: the earliest active legacy
|
||||
@@ -284,40 +284,40 @@ def upgrade() -> None:
|
||||
), None)
|
||||
if first_system_owner_account_id:
|
||||
bind.execute(sa.text(
|
||||
"INSERT INTO system_role_assignments (id, account_id, role_id, created_at, updated_at) VALUES (:id, :account_id, :role_id, :created_at, :updated_at)"
|
||||
"INSERT INTO access_system_role_assignments (id, account_id, role_id, created_at, updated_at) VALUES (:id, :account_id, :role_id, :created_at, :updated_at)"
|
||||
), {"id": str(uuid.uuid4()), "account_id": first_system_owner_account_id, "role_id": system_owner_role_id, "created_at": now, "updated_at": now})
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index("uq_roles_system_slug", table_name="roles")
|
||||
op.drop_index(op.f("ix_system_role_assignments_role_id"), table_name="system_role_assignments")
|
||||
op.drop_index(op.f("ix_system_role_assignments_account_id"), table_name="system_role_assignments")
|
||||
op.drop_table("system_role_assignments")
|
||||
op.drop_index("uq_roles_system_slug", table_name="access_roles")
|
||||
op.drop_index(op.f("ix_access_system_role_assignments_role_id"), table_name="access_system_role_assignments")
|
||||
op.drop_index(op.f("ix_access_system_role_assignments_account_id"), table_name="access_system_role_assignments")
|
||||
op.drop_table("access_system_role_assignments")
|
||||
|
||||
op.drop_index(op.f("ix_auth_sessions_account_id"), table_name="auth_sessions")
|
||||
with op.batch_alter_table("auth_sessions") as batch_op:
|
||||
op.drop_index(op.f("ix_access_auth_sessions_account_id"), table_name="access_auth_sessions")
|
||||
with op.batch_alter_table("access_auth_sessions") as batch_op:
|
||||
batch_op.drop_constraint("fk_auth_sessions_account_id_accounts", type_="foreignkey")
|
||||
batch_op.drop_column("account_id")
|
||||
|
||||
op.drop_index(op.f("ix_users_account_id"), table_name="users")
|
||||
with op.batch_alter_table("users") as batch_op:
|
||||
op.drop_index(op.f("ix_access_users_account_id"), table_name="access_users")
|
||||
with op.batch_alter_table("access_users") as batch_op:
|
||||
batch_op.drop_constraint("uq_users_tenant_account", type_="unique")
|
||||
batch_op.drop_constraint("fk_users_account_id_accounts", type_="foreignkey")
|
||||
batch_op.drop_column("account_id")
|
||||
|
||||
with op.batch_alter_table("roles") as batch_op:
|
||||
with op.batch_alter_table("access_roles") as batch_op:
|
||||
batch_op.drop_column("is_assignable")
|
||||
batch_op.drop_column("is_builtin")
|
||||
batch_op.drop_column("description")
|
||||
|
||||
with op.batch_alter_table("groups") as batch_op:
|
||||
with op.batch_alter_table("access_groups") as batch_op:
|
||||
batch_op.drop_column("is_active")
|
||||
batch_op.drop_column("description")
|
||||
|
||||
with op.batch_alter_table("tenants") as batch_op:
|
||||
with op.batch_alter_table("tenancy_tenants") as batch_op:
|
||||
batch_op.drop_column("settings")
|
||||
batch_op.drop_column("default_locale")
|
||||
batch_op.drop_column("description")
|
||||
|
||||
op.drop_index(op.f("ix_accounts_normalized_email"), table_name="accounts")
|
||||
op.drop_table("accounts")
|
||||
op.drop_index(op.f("ix_access_accounts_normalized_email"), table_name="access_accounts")
|
||||
op.drop_table("access_accounts")
|
||||
|
||||
@@ -28,7 +28,7 @@ def upgrade() -> None:
|
||||
tables = set(inspector.get_table_names())
|
||||
|
||||
# Reconcile only the empty create_all shape for the newly introduced tables.
|
||||
for table_name in ("governance_template_assignments", "governance_templates", "system_settings"):
|
||||
for table_name in ("admin_governance_template_assignments", "admin_governance_templates", "core_system_settings"):
|
||||
if table_name in tables:
|
||||
count = bind.execute(sa.text(f"SELECT COUNT(*) FROM {table_name}")).scalar_one()
|
||||
if count:
|
||||
@@ -36,7 +36,7 @@ def upgrade() -> None:
|
||||
op.drop_table(table_name)
|
||||
|
||||
op.create_table(
|
||||
"system_settings",
|
||||
"core_system_settings",
|
||||
sa.Column("id", sa.String(length=36), nullable=False),
|
||||
sa.Column("default_locale", sa.String(length=20), nullable=False, server_default="en"),
|
||||
sa.Column("allow_tenant_custom_groups", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
@@ -50,16 +50,23 @@ def upgrade() -> None:
|
||||
now = _now()
|
||||
bind.execute(sa.text(
|
||||
"""
|
||||
INSERT INTO system_settings
|
||||
INSERT INTO core_system_settings
|
||||
(id, default_locale, allow_tenant_custom_groups, allow_tenant_custom_roles,
|
||||
allow_tenant_api_keys, settings, created_at, updated_at)
|
||||
VALUES
|
||||
('global', 'en', 1, 1, 1, '{}', :created_at, :updated_at)
|
||||
('global', 'en', :allow_tenant_custom_groups, :allow_tenant_custom_roles,
|
||||
:allow_tenant_api_keys, '{}', :created_at, :updated_at)
|
||||
"""
|
||||
), {"created_at": now, "updated_at": now})
|
||||
), {
|
||||
"allow_tenant_custom_groups": True,
|
||||
"allow_tenant_custom_roles": True,
|
||||
"allow_tenant_api_keys": True,
|
||||
"created_at": now,
|
||||
"updated_at": now,
|
||||
})
|
||||
|
||||
op.create_table(
|
||||
"governance_templates",
|
||||
"admin_governance_templates",
|
||||
sa.Column("id", sa.String(length=36), nullable=False),
|
||||
sa.Column("kind", sa.String(length=20), nullable=False),
|
||||
sa.Column("slug", sa.String(length=100), nullable=False),
|
||||
@@ -72,51 +79,51 @@ def upgrade() -> None:
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("kind", "slug", name="uq_governance_templates_kind_slug"),
|
||||
)
|
||||
op.create_index(op.f("ix_governance_templates_kind"), "governance_templates", ["kind"])
|
||||
op.create_index(op.f("ix_admin_governance_templates_kind"), "admin_governance_templates", ["kind"])
|
||||
|
||||
op.create_table(
|
||||
"governance_template_assignments",
|
||||
"admin_governance_template_assignments",
|
||||
sa.Column("id", sa.String(length=36), nullable=False),
|
||||
sa.Column("template_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("tenant_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("mode", sa.String(length=20), nullable=False, server_default="available"),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["template_id"], ["governance_templates.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["template_id"], ["admin_governance_templates.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("template_id", "tenant_id", name="uq_governance_template_tenant"),
|
||||
)
|
||||
op.create_index(op.f("ix_governance_template_assignments_template_id"), "governance_template_assignments", ["template_id"])
|
||||
op.create_index(op.f("ix_governance_template_assignments_tenant_id"), "governance_template_assignments", ["tenant_id"])
|
||||
op.create_index(op.f("ix_admin_governance_template_assignments_template_id"), "admin_governance_template_assignments", ["template_id"])
|
||||
op.create_index(op.f("ix_admin_governance_template_assignments_tenant_id"), "admin_governance_template_assignments", ["tenant_id"])
|
||||
|
||||
with op.batch_alter_table("tenants") as batch_op:
|
||||
with op.batch_alter_table("tenancy_tenants") as batch_op:
|
||||
batch_op.add_column(sa.Column("allow_custom_groups", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("allow_custom_roles", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("allow_api_keys", sa.Boolean(), nullable=True))
|
||||
|
||||
with op.batch_alter_table("groups") as batch_op:
|
||||
with op.batch_alter_table("access_groups") as batch_op:
|
||||
batch_op.add_column(sa.Column("system_template_id", sa.String(length=36), nullable=True))
|
||||
batch_op.add_column(sa.Column("system_required", sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||
batch_op.create_foreign_key(
|
||||
"fk_groups_system_template_id_governance_templates",
|
||||
"governance_templates", ["system_template_id"], ["id"], ondelete="SET NULL",
|
||||
"admin_governance_templates", ["system_template_id"], ["id"], ondelete="SET NULL",
|
||||
)
|
||||
op.create_index(op.f("ix_groups_system_template_id"), "groups", ["system_template_id"])
|
||||
op.create_index(op.f("ix_access_groups_system_template_id"), "access_groups", ["system_template_id"])
|
||||
|
||||
with op.batch_alter_table("roles") as batch_op:
|
||||
with op.batch_alter_table("access_roles") as batch_op:
|
||||
batch_op.add_column(sa.Column("system_template_id", sa.String(length=36), nullable=True))
|
||||
batch_op.add_column(sa.Column("system_required", sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||
batch_op.create_foreign_key(
|
||||
"fk_roles_system_template_id_governance_templates",
|
||||
"governance_templates", ["system_template_id"], ["id"], ondelete="SET NULL",
|
||||
"admin_governance_templates", ["system_template_id"], ["id"], ondelete="SET NULL",
|
||||
)
|
||||
op.create_index(op.f("ix_roles_system_template_id"), "roles", ["system_template_id"])
|
||||
op.create_index(op.f("ix_access_roles_system_template_id"), "access_roles", ["system_template_id"])
|
||||
|
||||
# Existing system owners use system:* and need no data change. Extend the
|
||||
# read-only built-in auditor role to the newly introduced read scopes.
|
||||
auditor = bind.execute(sa.text(
|
||||
"SELECT id, permissions FROM roles WHERE tenant_id IS NULL AND slug = 'system_auditor'"
|
||||
"SELECT id, permissions FROM access_roles WHERE tenant_id IS NULL AND slug = 'system_auditor'"
|
||||
)).mappings().first()
|
||||
if auditor:
|
||||
raw_permissions = auditor["permissions"] or []
|
||||
@@ -125,7 +132,7 @@ def upgrade() -> None:
|
||||
if scope not in permissions:
|
||||
permissions.append(scope)
|
||||
roles_table = sa.table(
|
||||
"roles",
|
||||
"access_roles",
|
||||
sa.column("id", sa.String),
|
||||
sa.column("permissions", sa.JSON),
|
||||
sa.column("updated_at", sa.DateTime(timezone=True)),
|
||||
@@ -138,26 +145,26 @@ def upgrade() -> None:
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index(op.f("ix_roles_system_template_id"), table_name="roles")
|
||||
with op.batch_alter_table("roles") as batch_op:
|
||||
op.drop_index(op.f("ix_access_roles_system_template_id"), table_name="access_roles")
|
||||
with op.batch_alter_table("access_roles") as batch_op:
|
||||
batch_op.drop_constraint("fk_roles_system_template_id_governance_templates", type_="foreignkey")
|
||||
batch_op.drop_column("system_required")
|
||||
batch_op.drop_column("system_template_id")
|
||||
|
||||
op.drop_index(op.f("ix_groups_system_template_id"), table_name="groups")
|
||||
with op.batch_alter_table("groups") as batch_op:
|
||||
op.drop_index(op.f("ix_access_groups_system_template_id"), table_name="access_groups")
|
||||
with op.batch_alter_table("access_groups") as batch_op:
|
||||
batch_op.drop_constraint("fk_groups_system_template_id_governance_templates", type_="foreignkey")
|
||||
batch_op.drop_column("system_required")
|
||||
batch_op.drop_column("system_template_id")
|
||||
|
||||
with op.batch_alter_table("tenants") as batch_op:
|
||||
with op.batch_alter_table("tenancy_tenants") as batch_op:
|
||||
batch_op.drop_column("allow_api_keys")
|
||||
batch_op.drop_column("allow_custom_roles")
|
||||
batch_op.drop_column("allow_custom_groups")
|
||||
|
||||
op.drop_index(op.f("ix_governance_template_assignments_tenant_id"), table_name="governance_template_assignments")
|
||||
op.drop_index(op.f("ix_governance_template_assignments_template_id"), table_name="governance_template_assignments")
|
||||
op.drop_table("governance_template_assignments")
|
||||
op.drop_index(op.f("ix_governance_templates_kind"), table_name="governance_templates")
|
||||
op.drop_table("governance_templates")
|
||||
op.drop_table("system_settings")
|
||||
op.drop_index(op.f("ix_admin_governance_template_assignments_tenant_id"), table_name="admin_governance_template_assignments")
|
||||
op.drop_index(op.f("ix_admin_governance_template_assignments_template_id"), table_name="admin_governance_template_assignments")
|
||||
op.drop_table("admin_governance_template_assignments")
|
||||
op.drop_index(op.f("ix_admin_governance_templates_kind"), table_name="admin_governance_templates")
|
||||
op.drop_table("admin_governance_templates")
|
||||
op.drop_table("core_system_settings")
|
||||
|
||||
@@ -148,9 +148,9 @@ def upgrade() -> None:
|
||||
sa.Column("revoked_at", sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["campaign_id"], ["campaigns.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["created_by_user_id"], ["users.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["created_by_user_id"], ["access_users.id"], ondelete="SET NULL"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("campaign_id", "target_type", "target_id", name="uq_campaign_share_target"),
|
||||
)
|
||||
@@ -160,32 +160,32 @@ def upgrade() -> None:
|
||||
op.create_index("ix_campaign_shares_target_id", "campaign_shares", ["target_id"])
|
||||
op.create_index("ix_campaign_shares_created_by_user_id", "campaign_shares", ["created_by_user_id"])
|
||||
op.create_index("ix_campaign_shares_revoked_at", "campaign_shares", ["revoked_at"])
|
||||
if "roles" not in tables:
|
||||
if "access_roles" not in tables:
|
||||
return
|
||||
|
||||
rows = bind.execute(sa.text("SELECT id, permissions FROM roles")).mappings().all()
|
||||
rows = bind.execute(sa.text("SELECT id, permissions FROM access_roles")).mappings().all()
|
||||
for row in rows:
|
||||
bind.execute(
|
||||
sa.text("UPDATE roles SET permissions = :permissions WHERE id = :id"),
|
||||
sa.text("UPDATE access_roles SET permissions = :permissions WHERE id = :id"),
|
||||
{"id": row["id"], "permissions": _json(_expand_legacy(_decode(row["permissions"])))},
|
||||
)
|
||||
|
||||
for slug, permissions in TENANT_ROLE_PERMISSIONS.items():
|
||||
bind.execute(
|
||||
sa.text("UPDATE roles SET permissions = :permissions WHERE tenant_id IS NOT NULL AND slug = :slug AND is_builtin = TRUE"),
|
||||
sa.text("UPDATE access_roles SET permissions = :permissions WHERE tenant_id IS NOT NULL AND slug = :slug AND is_builtin = TRUE"),
|
||||
{"slug": slug, "permissions": _json(permissions)},
|
||||
)
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
for slug, permissions in SYSTEM_ROLE_PERMISSIONS.items():
|
||||
existing = bind.execute(
|
||||
sa.text("SELECT id FROM roles WHERE tenant_id IS NULL AND slug = :slug"), {"slug": slug}
|
||||
sa.text("SELECT id FROM access_roles WHERE tenant_id IS NULL AND slug = :slug"), {"slug": slug}
|
||||
).first()
|
||||
is_protected = slug == "system_owner"
|
||||
if existing:
|
||||
bind.execute(
|
||||
sa.text(
|
||||
"UPDATE roles SET permissions = :permissions, is_builtin = :is_builtin, is_assignable = TRUE "
|
||||
"UPDATE access_roles SET permissions = :permissions, is_builtin = :is_builtin, is_assignable = TRUE "
|
||||
"WHERE tenant_id IS NULL AND slug = :slug"
|
||||
),
|
||||
{"slug": slug, "permissions": _json(permissions), "is_builtin": is_protected},
|
||||
@@ -199,7 +199,7 @@ def upgrade() -> None:
|
||||
name, description = names[slug]
|
||||
bind.execute(
|
||||
sa.text(
|
||||
"INSERT INTO roles (id, tenant_id, slug, name, description, permissions, is_builtin, is_assignable, "
|
||||
"INSERT INTO access_roles (id, tenant_id, slug, name, description, permissions, is_builtin, is_assignable, "
|
||||
"system_template_id, system_required, created_at, updated_at) "
|
||||
"VALUES (:id, NULL, :slug, :name, :description, :permissions, :is_builtin, TRUE, NULL, FALSE, :created_at, :updated_at)"
|
||||
),
|
||||
|
||||
@@ -18,7 +18,7 @@ depends_on = None
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('tenants',
|
||||
op.create_table('tenancy_tenants',
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('slug', sa.String(length=100), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
@@ -27,7 +27,7 @@ def upgrade() -> None:
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_tenants'))
|
||||
)
|
||||
op.create_index(op.f('ix_tenants_slug'), 'tenants', ['slug'], unique=True)
|
||||
op.create_index(op.f('ix_tenancy_tenants_slug'), 'tenancy_tenants', ['slug'], unique=True)
|
||||
op.create_table('attachment_blobs',
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('tenant_id', sa.String(length=36), nullable=False),
|
||||
@@ -38,25 +38,25 @@ def upgrade() -> None:
|
||||
sa.Column('storage_key', sa.String(length=1000), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], name=op.f('fk_attachment_blobs_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenancy_tenants.id'], name=op.f('fk_attachment_blobs_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_attachment_blobs')),
|
||||
sa.UniqueConstraint('tenant_id', 'sha256', name='uq_attachment_blobs_tenant_sha256')
|
||||
)
|
||||
op.create_index(op.f('ix_attachment_blobs_sha256'), 'attachment_blobs', ['sha256'], unique=False)
|
||||
op.create_index(op.f('ix_attachment_blobs_tenant_id'), 'attachment_blobs', ['tenant_id'], unique=False)
|
||||
op.create_table('groups',
|
||||
op.create_table('access_groups',
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('tenant_id', sa.String(length=36), nullable=False),
|
||||
sa.Column('slug', sa.String(length=100), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], name=op.f('fk_groups_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenancy_tenants.id'], name=op.f('fk_groups_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_groups')),
|
||||
sa.UniqueConstraint('tenant_id', 'slug', name='uq_groups_tenant_slug')
|
||||
)
|
||||
op.create_index(op.f('ix_groups_tenant_id'), 'groups', ['tenant_id'], unique=False)
|
||||
op.create_table('roles',
|
||||
op.create_index(op.f('ix_access_groups_tenant_id'), 'access_groups', ['tenant_id'], unique=False)
|
||||
op.create_table('access_roles',
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('tenant_id', sa.String(length=36), nullable=True),
|
||||
sa.Column('slug', sa.String(length=100), nullable=False),
|
||||
@@ -64,12 +64,12 @@ def upgrade() -> None:
|
||||
sa.Column('permissions', sa.JSON(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], name=op.f('fk_roles_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenancy_tenants.id'], name=op.f('fk_roles_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_roles')),
|
||||
sa.UniqueConstraint('tenant_id', 'slug', name='uq_roles_tenant_slug')
|
||||
)
|
||||
op.create_index(op.f('ix_roles_tenant_id'), 'roles', ['tenant_id'], unique=False)
|
||||
op.create_table('users',
|
||||
op.create_index(op.f('ix_access_roles_tenant_id'), 'access_roles', ['tenant_id'], unique=False)
|
||||
op.create_table('access_users',
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('tenant_id', sa.String(length=36), nullable=False),
|
||||
sa.Column('email', sa.String(length=320), nullable=False),
|
||||
@@ -78,13 +78,13 @@ def upgrade() -> None:
|
||||
sa.Column('is_tenant_admin', sa.Boolean(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], name=op.f('fk_users_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenancy_tenants.id'], name=op.f('fk_users_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_users')),
|
||||
sa.UniqueConstraint('tenant_id', 'email', name='uq_users_tenant_email')
|
||||
)
|
||||
op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=False)
|
||||
op.create_index(op.f('ix_users_tenant_id'), 'users', ['tenant_id'], unique=False)
|
||||
op.create_table('api_keys',
|
||||
op.create_index(op.f('ix_access_users_email'), 'access_users', ['email'], unique=False)
|
||||
op.create_index(op.f('ix_access_users_tenant_id'), 'access_users', ['tenant_id'], unique=False)
|
||||
op.create_table('access_api_keys',
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('tenant_id', sa.String(length=36), nullable=False),
|
||||
sa.Column('user_id', sa.String(length=36), nullable=False),
|
||||
@@ -97,13 +97,13 @@ def upgrade() -> None:
|
||||
sa.Column('revoked_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], name=op.f('fk_api_keys_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], name=op.f('fk_api_keys_user_id_users'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenancy_tenants.id'], name=op.f('fk_api_keys_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['access_users.id'], name=op.f('fk_api_keys_user_id_users'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_api_keys'))
|
||||
)
|
||||
op.create_index(op.f('ix_api_keys_prefix'), 'api_keys', ['prefix'], unique=False)
|
||||
op.create_index(op.f('ix_api_keys_tenant_id'), 'api_keys', ['tenant_id'], unique=False)
|
||||
op.create_index(op.f('ix_api_keys_user_id'), 'api_keys', ['user_id'], unique=False)
|
||||
op.create_index(op.f('ix_access_api_keys_prefix'), 'access_api_keys', ['prefix'], unique=False)
|
||||
op.create_index(op.f('ix_access_api_keys_tenant_id'), 'access_api_keys', ['tenant_id'], unique=False)
|
||||
op.create_index(op.f('ix_access_api_keys_user_id'), 'access_api_keys', ['user_id'], unique=False)
|
||||
op.create_table('campaigns',
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('tenant_id', sa.String(length=36), nullable=False),
|
||||
@@ -115,8 +115,8 @@ def upgrade() -> None:
|
||||
sa.Column('current_version_id', sa.String(length=36), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(['created_by_user_id'], ['users.id'], name=op.f('fk_campaigns_created_by_user_id_users'), ondelete='SET NULL'),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], name=op.f('fk_campaigns_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['created_by_user_id'], ['access_users.id'], name=op.f('fk_campaigns_created_by_user_id_users'), ondelete='SET NULL'),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenancy_tenants.id'], name=op.f('fk_campaigns_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_campaigns')),
|
||||
sa.UniqueConstraint('tenant_id', 'external_id', name='uq_campaigns_tenant_external_id')
|
||||
)
|
||||
@@ -138,8 +138,8 @@ def upgrade() -> None:
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(['blob_id'], ['attachment_blobs.id'], name=op.f('fk_attachment_instances_blob_id_attachment_blobs'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['campaign_id'], ['campaigns.id'], name=op.f('fk_attachment_instances_campaign_id_campaigns'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['owner_user_id'], ['users.id'], name=op.f('fk_attachment_instances_owner_user_id_users'), ondelete='SET NULL'),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], name=op.f('fk_attachment_instances_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['owner_user_id'], ['access_users.id'], name=op.f('fk_attachment_instances_owner_user_id_users'), ondelete='SET NULL'),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenancy_tenants.id'], name=op.f('fk_attachment_instances_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_attachment_instances'))
|
||||
)
|
||||
op.create_index(op.f('ix_attachment_instances_blob_id'), 'attachment_instances', ['blob_id'], unique=False)
|
||||
@@ -157,9 +157,9 @@ def upgrade() -> None:
|
||||
sa.Column('details', sa.JSON(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(['api_key_id'], ['api_keys.id'], name=op.f('fk_audit_log_api_key_id_api_keys'), ondelete='SET NULL'),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], name=op.f('fk_audit_log_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], name=op.f('fk_audit_log_user_id_users'), ondelete='SET NULL'),
|
||||
sa.ForeignKeyConstraint(['api_key_id'], ['access_api_keys.id'], name=op.f('fk_audit_log_api_key_id_api_keys'), ondelete='SET NULL'),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenancy_tenants.id'], name=op.f('fk_audit_log_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['access_users.id'], name=op.f('fk_audit_log_user_id_users'), ondelete='SET NULL'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_audit_log'))
|
||||
)
|
||||
op.create_index(op.f('ix_audit_log_action'), 'audit_log', ['action'], unique=False)
|
||||
@@ -213,7 +213,7 @@ def upgrade() -> None:
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(['campaign_id'], ['campaigns.id'], name=op.f('fk_campaign_jobs_campaign_id_campaigns'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['campaign_version_id'], ['campaign_versions.id'], name=op.f('fk_campaign_jobs_campaign_version_id_campaign_versions'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], name=op.f('fk_campaign_jobs_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenancy_tenants.id'], name=op.f('fk_campaign_jobs_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_campaign_jobs')),
|
||||
sa.UniqueConstraint('campaign_version_id', 'entry_index', name='uq_campaign_jobs_version_entry')
|
||||
)
|
||||
@@ -243,7 +243,7 @@ def upgrade() -> None:
|
||||
sa.ForeignKeyConstraint(['campaign_id'], ['campaigns.id'], name=op.f('fk_campaign_issues_campaign_id_campaigns'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['campaign_version_id'], ['campaign_versions.id'], name=op.f('fk_campaign_issues_campaign_version_id_campaign_versions'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['job_id'], ['campaign_jobs.id'], name=op.f('fk_campaign_issues_job_id_campaign_jobs'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], name=op.f('fk_campaign_issues_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['tenancy_tenants.id'], name=op.f('fk_campaign_issues_tenant_id_tenants'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_campaign_issues'))
|
||||
)
|
||||
op.create_index(op.f('ix_campaign_issues_campaign_id'), 'campaign_issues', ['campaign_id'], unique=False)
|
||||
@@ -327,20 +327,20 @@ def downgrade() -> None:
|
||||
op.drop_index(op.f('ix_campaigns_external_id'), table_name='campaigns')
|
||||
op.drop_index(op.f('ix_campaigns_created_by_user_id'), table_name='campaigns')
|
||||
op.drop_table('campaigns')
|
||||
op.drop_index(op.f('ix_api_keys_user_id'), table_name='api_keys')
|
||||
op.drop_index(op.f('ix_api_keys_tenant_id'), table_name='api_keys')
|
||||
op.drop_index(op.f('ix_api_keys_prefix'), table_name='api_keys')
|
||||
op.drop_table('api_keys')
|
||||
op.drop_index(op.f('ix_users_tenant_id'), table_name='users')
|
||||
op.drop_index(op.f('ix_users_email'), table_name='users')
|
||||
op.drop_table('users')
|
||||
op.drop_index(op.f('ix_roles_tenant_id'), table_name='roles')
|
||||
op.drop_table('roles')
|
||||
op.drop_index(op.f('ix_groups_tenant_id'), table_name='groups')
|
||||
op.drop_table('groups')
|
||||
op.drop_index(op.f('ix_access_api_keys_user_id'), table_name='access_api_keys')
|
||||
op.drop_index(op.f('ix_access_api_keys_tenant_id'), table_name='access_api_keys')
|
||||
op.drop_index(op.f('ix_access_api_keys_prefix'), table_name='access_api_keys')
|
||||
op.drop_table('access_api_keys')
|
||||
op.drop_index(op.f('ix_access_users_tenant_id'), table_name='access_users')
|
||||
op.drop_index(op.f('ix_access_users_email'), table_name='access_users')
|
||||
op.drop_table('access_users')
|
||||
op.drop_index(op.f('ix_access_roles_tenant_id'), table_name='access_roles')
|
||||
op.drop_table('access_roles')
|
||||
op.drop_index(op.f('ix_access_groups_tenant_id'), table_name='access_groups')
|
||||
op.drop_table('access_groups')
|
||||
op.drop_index(op.f('ix_attachment_blobs_tenant_id'), table_name='attachment_blobs')
|
||||
op.drop_index(op.f('ix_attachment_blobs_sha256'), table_name='attachment_blobs')
|
||||
op.drop_table('attachment_blobs')
|
||||
op.drop_index(op.f('ix_tenants_slug'), table_name='tenants')
|
||||
op.drop_table('tenants')
|
||||
op.drop_index(op.f('ix_tenancy_tenants_slug'), table_name='tenancy_tenants')
|
||||
op.drop_table('tenancy_tenants')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
@@ -19,10 +19,10 @@ def upgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
tables = set(inspector.get_table_names())
|
||||
if "auth_sessions" in tables:
|
||||
columns = {column["name"] for column in inspector.get_columns("auth_sessions")}
|
||||
if "access_auth_sessions" in tables:
|
||||
columns = {column["name"] for column in inspector.get_columns("access_auth_sessions")}
|
||||
if "csrf_token_hash" not in columns:
|
||||
op.add_column("auth_sessions", sa.Column("csrf_token_hash", sa.String(length=128), nullable=True))
|
||||
op.add_column("access_auth_sessions", sa.Column("csrf_token_hash", sa.String(length=128), nullable=True))
|
||||
if "mail_server_profiles" not in tables:
|
||||
op.create_table(
|
||||
"mail_server_profiles",
|
||||
@@ -40,9 +40,9 @@ def upgrade() -> None:
|
||||
sa.Column("updated_by_user_id", sa.String(length=36), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["created_by_user_id"], ["users.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["updated_by_user_id"], ["users.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["created_by_user_id"], ["access_users.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["updated_by_user_id"], ["access_users.id"], ondelete="SET NULL"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("tenant_id", "slug", name="uq_mail_server_profiles_tenant_slug"),
|
||||
)
|
||||
@@ -67,7 +67,7 @@ def downgrade() -> None:
|
||||
except Exception:
|
||||
pass
|
||||
op.drop_table("mail_server_profiles")
|
||||
if "auth_sessions" in inspector.get_table_names():
|
||||
columns = {column["name"] for column in inspector.get_columns("auth_sessions")}
|
||||
if "access_auth_sessions" in inspector.get_table_names():
|
||||
columns = {column["name"] for column in inspector.get_columns("access_auth_sessions")}
|
||||
if "csrf_token_hash" in columns:
|
||||
op.drop_column("auth_sessions", "csrf_token_hash")
|
||||
op.drop_column("access_auth_sessions", "csrf_token_hash")
|
||||
|
||||
@@ -31,7 +31,7 @@ def upgrade() -> None:
|
||||
inspector = sa.inspect(bind)
|
||||
tables = set(inspector.get_table_names())
|
||||
|
||||
for table_name in ("users", "groups", "campaigns"):
|
||||
for table_name in ("access_users", "access_groups", "campaigns"):
|
||||
if table_name not in tables:
|
||||
continue
|
||||
columns = _columns(inspector, table_name)
|
||||
@@ -83,6 +83,6 @@ def downgrade() -> None:
|
||||
batch.drop_column("scope_type")
|
||||
batch.alter_column("tenant_id", existing_type=sa.String(length=36), nullable=False)
|
||||
|
||||
for table_name in ("campaigns", "groups", "users"):
|
||||
for table_name in ("campaigns", "access_groups", "access_users"):
|
||||
if table_name in tables and "mail_profile_policy" in _columns(inspector, table_name):
|
||||
op.drop_column(table_name, "mail_profile_policy")
|
||||
|
||||
@@ -25,7 +25,7 @@ def upgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
tables = set(inspector.get_table_names())
|
||||
for table_name in ("users", "groups", "campaigns"):
|
||||
for table_name in ("access_users", "access_groups", "campaigns"):
|
||||
if table_name not in tables:
|
||||
continue
|
||||
if "settings" not in _columns(inspector, table_name):
|
||||
@@ -36,6 +36,6 @@ def downgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
tables = set(inspector.get_table_names())
|
||||
for table_name in ("campaigns", "groups", "users"):
|
||||
for table_name in ("campaigns", "access_groups", "access_users"):
|
||||
if table_name in tables and "settings" in _columns(inspector, table_name):
|
||||
op.drop_column(table_name, "settings")
|
||||
|
||||
Reference in New Issue
Block a user