This commit is contained in:
Winnie
2026-08-05 17:46:05 +08:00
101 changed files with 8797 additions and 3323 deletions
@@ -1,57 +0,0 @@
"""enable password login for the seeded development users
Revision ID: 9a1b2c3d4e5f
Revises: b71c4f2a9d10
Create Date: 2026-08-03 16:00:00
"""
from collections.abc import Sequence
import os
from alembic import op
import sqlalchemy as sa
from common.auth.passwords import hash_password
revision: str = "9a1b2c3d4e5f"
down_revision: str | Sequence[str] | None = "b71c4f2a9d10"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
SEEDED_USER_IDS = (
"0000000000RF6FG1SDBXG59S13",
"0000000000H2QYCGPCWQM1JSGS",
"0000000000RWG40ESZPGJT629J",
"00000000004CQV7WASJA6N6FW4",
)
DISABLED_PASSWORD = "demo-login-disabled"
def upgrade() -> None:
password = os.environ.get("INITIAL_ADMIN_PASSWORD", "admin12345")
users = sa.table(
"users",
sa.column("user_id", sa.String),
sa.column("password_hash", sa.String),
)
op.execute(
users.update()
.where(users.c.user_id.in_(SEEDED_USER_IDS))
.where(users.c.password_hash == DISABLED_PASSWORD)
.values(password_hash=hash_password(password))
)
def downgrade() -> None:
users = sa.table(
"users",
sa.column("user_id", sa.String),
sa.column("password_hash", sa.String),
)
op.execute(
users.update()
.where(users.c.user_id.in_(SEEDED_USER_IDS))
.values(password_hash=DISABLED_PASSWORD)
)
@@ -1,33 +0,0 @@
"""add the RustFS trash object key
Revision ID: a2b3c4d5e6f7
Revises: 9a1b2c3d4e5f
Create Date: 2026-08-03 16:30:00
"""
from collections.abc import Sequence
from alembic import op
import sqlalchemy as sa
revision: str = "a2b3c4d5e6f7"
down_revision: str | Sequence[str] | None = "9a1b2c3d4e5f"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
op.add_column(
"storage_objects",
sa.Column(
"trash_key",
sa.String(length=1100),
nullable=True,
comment="Path inside the trash bucket where soft-deleted bytes are stored",
),
)
def downgrade() -> None:
op.drop_column("storage_objects", "trash_key")
@@ -1,168 +0,0 @@
"""seed the self-hosted demo users and workspaces
Revision ID: b71c4f2a9d10
Revises: 8d86e2f82860
Create Date: 2026-07-31 16:00:00
"""
from collections.abc import Sequence
from alembic import op
import sqlalchemy as sa
revision: str = "b71c4f2a9d10"
down_revision: str | Sequence[str] | None = "8d86e2f82860"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
ADMIN_ROLE_ID = "0000000000000000000000000A"
DEVELOPER_ROLE_ID = "0000000000000000000000000B"
USERS = (
("0000000000RF6FG1SDBXG59S13", "admin-zhang", "张三", ADMIN_ROLE_ID),
("0000000000H2QYCGPCWQM1JSGS", "admin-li", "李四", ADMIN_ROLE_ID),
("0000000000RWG40ESZPGJT629J", "dev-wang", "王五", DEVELOPER_ROLE_ID),
("00000000004CQV7WASJA6N6FW4", "dev-zhao", "赵六", DEVELOPER_ROLE_ID),
)
WORKSPACES = (
("00000000000BM630VT9ARVFZPC", "model-development", "模型开发 Workspace"),
("0000000000AE0NC0V5T424KK86", "risk-validation", "风险验证 Workspace"),
)
def upgrade() -> None:
roles = sa.table(
"roles",
sa.column("role_id", sa.String),
sa.column("role_code", sa.String),
sa.column("role_name", sa.String),
sa.column("role_scope", sa.String),
sa.column("is_builtin", sa.Integer),
sa.column("description", sa.String),
)
users = sa.table(
"users",
sa.column("user_id", sa.String),
sa.column("username", sa.String),
sa.column("display_name", sa.String),
sa.column("password_hash", sa.String),
sa.column("status", sa.String),
sa.column("email", sa.String),
sa.column("platform_role_id", sa.String),
)
workspaces = sa.table(
"workspaces",
sa.column("workspace_id", sa.String),
sa.column("workspace_code", sa.String),
sa.column("workspace_name", sa.String),
sa.column("active_root_uri", sa.String),
sa.column("status", sa.String),
sa.column("created_by", sa.String),
sa.column("description", sa.String),
)
members = sa.table(
"workspace_members",
sa.column("workspace_id", sa.String),
sa.column("user_id", sa.String),
sa.column("role_id", sa.String),
sa.column("member_status", sa.String),
)
op.bulk_insert(
roles,
[
{
"role_id": ADMIN_ROLE_ID,
"role_code": "admin",
"role_name": "管理员",
"role_scope": "workspace",
"is_builtin": 1,
"description": "Self-hosted workspace administrator",
},
{
"role_id": DEVELOPER_ROLE_ID,
"role_code": "developer",
"role_name": "开发人员",
"role_scope": "workspace",
"is_builtin": 1,
"description": "Self-hosted workspace developer",
},
],
)
op.bulk_insert(
users,
[
{
"user_id": user_id,
"username": username,
"display_name": display_name,
"password_hash": "demo-login-disabled",
"status": "active",
"email": f"{username}@model-platform.local",
"platform_role_id": role_id,
}
for user_id, username, display_name, role_id in USERS
],
)
op.bulk_insert(
workspaces,
[
{
"workspace_id": workspace_id,
"workspace_code": workspace_code,
"workspace_name": workspace_name,
"active_root_uri": f"s3://workspaces/{workspace_id}/",
"status": "active",
"created_by": USERS[0][0],
"description": "Self-hosted demo workspace",
}
for workspace_id, workspace_code, workspace_name in WORKSPACES
],
)
op.bulk_insert(
members,
[
{
"workspace_id": workspace_id,
"user_id": user_id,
"role_id": role_id,
"member_status": "active",
}
for workspace_id, _, _ in WORKSPACES
for user_id, _, _, role_id in USERS
],
)
def downgrade() -> None:
connection = op.get_bind()
workspace_ids = [workspace_id for workspace_id, _, _ in WORKSPACES]
user_ids = [user_id for user_id, _, _, _ in USERS]
connection.execute(
sa.text(
"DELETE FROM workspace_members "
"WHERE workspace_id IN :workspace_ids AND user_id IN :user_ids"
).bindparams(
sa.bindparam("workspace_ids", expanding=True),
sa.bindparam("user_ids", expanding=True),
),
{"workspace_ids": workspace_ids, "user_ids": user_ids},
)
connection.execute(
sa.text("DELETE FROM workspaces WHERE workspace_id IN :workspace_ids")
.bindparams(sa.bindparam("workspace_ids", expanding=True)),
{"workspace_ids": workspace_ids},
)
connection.execute(
sa.text("DELETE FROM users WHERE user_id IN :user_ids")
.bindparams(sa.bindparam("user_ids", expanding=True)),
{"user_ids": user_ids},
)
connection.execute(
sa.text("DELETE FROM roles WHERE role_id IN :role_ids")
.bindparams(sa.bindparam("role_ids", expanding=True)),
{"role_ids": [ADMIN_ROLE_ID, DEVELOPER_ROLE_ID]},
)
@@ -1,27 +0,0 @@
"""reconcile the legacy remote database revision
Revision ID: d4e5f6a7b8c9
Revises: a2b3c4d5e6f7
Create Date: 2026-08-05 15:30:00
The shared development database was stamped with this revision by an older
migration history. The corresponding file was lost while branches were
merged. Keeping the marker in the active chain lets Alembic safely continue
without rewriting the existing database or replaying the baseline migration.
"""
from collections.abc import Sequence
revision: str = "d4e5f6a7b8c9"
down_revision: str | Sequence[str] | None = "a2b3c4d5e6f7"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
"""Preserve the already-applied legacy revision marker."""
def downgrade() -> None:
"""The compatibility marker has no schema operation to reverse."""
@@ -1,46 +1,39 @@
"""initial baseline (20 active tables)
"""squashed baseline — full schema + seed data in one migration
Revision ID: 8d86e2f82860
Revises:
Create Date: 2026-07-31 13:17:52.047720
Single baseline migration combining the previous 5-step chain:
8d86e2f82860 initial baseline (20 tables)
b71c4f2a9d10 seed demo users / workspaces / roles / members
9a1b2c3d4e5f enable password login for seeded users
a2b3c4d5e6f7 add storage_objects.trash_key
c3d4e5f6a7b8 add upload_sessions object-metadata columns
The column additions from the later migrations are folded directly into
the CREATE TABLE statements, so this file is a from-scratch schema.
Revision ID: d4e5f6a7b8c9
Revises: (none)
Create Date: 2026-08-05
"""
from collections.abc import Sequence
import os
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
from common.auth.passwords import hash_password
# revision identifiers, used by Alembic.
revision: str = '8d86e2f82860'
revision: str = "d4e5f6a7b8c9"
down_revision: str | Sequence[str] | None = None
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('audit_logs',
sa.Column('audit_id', mysql.BIGINT(), nullable=False),
sa.Column('action_code', sa.String(length=128), nullable=False),
sa.Column('target_type', sa.String(length=64), nullable=False),
sa.Column('operation_status', sa.String(length=16), server_default=sa.text("'success'"), nullable=False),
sa.Column('created_at', mysql.DATETIME(fsp=3), server_default=sa.text('CURRENT_TIMESTAMP(3)'), nullable=False),
sa.Column('workspace_id', mysql.CHAR(length=26), nullable=True),
sa.Column('actor_user_id', mysql.CHAR(length=26), nullable=True),
sa.Column('target_id', sa.String(length=128), nullable=True),
sa.Column('client_ip', sa.String(length=45), nullable=True),
sa.Column('user_agent', sa.String(length=1000), nullable=True),
sa.Column('detail_json', sa.JSON(), nullable=True),
sa.Column('is_deleted', mysql.TINYINT(display_width=1), server_default=sa.text('0'), nullable=False),
sa.Column('deleted_at', mysql.DATETIME(fsp=3), nullable=True),
sa.PrimaryKeyConstraint('audit_id'),
comment='操作审计日志'
)
op.create_index('idx_audit_action_time', 'audit_logs', ['action_code', 'created_at'], unique=False)
op.create_index('idx_audit_actor_time', 'audit_logs', ['actor_user_id', 'created_at'], unique=False)
op.create_index('idx_audit_workspace_time', 'audit_logs', ['workspace_id', 'created_at'], unique=False)
"""Full schema from scratch (all 20 tables, current model state)."""
op.create_table('consumer_inbox',
sa.Column('consumer_name', sa.String(length=128), nullable=False),
sa.Column('event_id', mysql.CHAR(length=26), nullable=False),
@@ -287,7 +280,7 @@ def upgrade() -> None:
sa.Column('workspace_id', mysql.CHAR(length=26), nullable=False),
sa.Column('object_type', sa.String(length=16), nullable=False, comment='file/directory'),
sa.Column('usage_type', sa.String(length=32), nullable=False, comment='working_copy/public_script/data_resource/version_artifact/snapshot/run_log/run_result'),
sa.Column('storage_backend', sa.String(length=16), nullable=False, comment='rustfs'),
sa.Column('storage_backend', sa.String(length=16), nullable=False, comment='s3'),
sa.Column('storage_uri', sa.String(length=1500), nullable=False),
sa.Column('file_name', sa.String(length=255), nullable=False),
sa.Column('size_bytes', mysql.BIGINT(), server_default=sa.text('0'), nullable=False),
@@ -308,10 +301,11 @@ def upgrade() -> None:
sa.Column('mime_type', sa.String(length=255), nullable=True),
sa.Column('content_hash', mysql.CHAR(length=64), nullable=True, comment='SHA-256 hex'),
sa.Column('object_etag', sa.String(length=255), nullable=True),
sa.Column('trash_key', sa.String(length=1100), nullable=True, comment='Path inside the trash bucket where soft-deleted bytes are stored'),
sa.Column('is_deleted', mysql.TINYINT(display_width=1), server_default=sa.text('0'), nullable=False),
sa.Column('deleted_at', mysql.DATETIME(fsp=3), nullable=True),
sa.PrimaryKeyConstraint('storage_object_id'),
comment='Workspace 文件和 RustFS 对象的统一元数据'
comment='Workspace 文件和 S3 对象的统一元数据'
)
op.create_index('fk_storage_created_by', 'storage_objects', ['created_by'], unique=False)
op.create_index('idx_storage_content_hash', 'storage_objects', ['content_hash'], unique=False)
@@ -338,10 +332,15 @@ def upgrade() -> None:
sa.Column('content_type', sa.String(length=255), nullable=True),
sa.Column('storage_object_id', mysql.CHAR(length=26), nullable=True),
sa.Column('completed_at', mysql.DATETIME(fsp=3), nullable=True),
# Object-metadata columns added by c3d4e5f6a7b8 (server-proxied upload).
sa.Column('file_name', sa.String(length=255), nullable=False, server_default=''),
sa.Column('usage_type', sa.String(length=32), nullable=False, server_default='working_copy', comment='data_resource/version_artifact/snapshot/run_log/run_result/working_copy/public_script'),
sa.Column('visibility', sa.String(length=16), nullable=False, server_default='private', comment='private/workspace/public'),
sa.Column('is_immutable', mysql.TINYINT(display_width=1), nullable=False, server_default='0'),
sa.Column('is_deleted', mysql.TINYINT(display_width=1), server_default=sa.text('0'), nullable=False),
sa.Column('deleted_at', mysql.DATETIME(fsp=3), nullable=True),
sa.PrimaryKeyConstraint('upload_id'),
comment='RustFS 预签名上传会话;URL 本身不持久化'
comment='S3 上传会话;URL 本身不持久化'
)
op.create_index('fk_upload_sessions_storage_object', 'upload_sessions', ['storage_object_id'], unique=False)
op.create_index('fk_upload_sessions_user', 'upload_sessions', ['user_id'], unique=False)
@@ -375,7 +374,7 @@ def upgrade() -> None:
sa.Column('workspace_id', mysql.CHAR(length=26), nullable=False),
sa.Column('script_id', mysql.CHAR(length=26), nullable=False),
sa.Column('source_object_id', mysql.CHAR(length=26), nullable=False, comment='发布时的源对象'),
sa.Column('artifact_object_id', mysql.CHAR(length=26), nullable=False, comment='RustFS 不可变版本制品'),
sa.Column('artifact_object_id', mysql.CHAR(length=26), nullable=False, comment='S3 不可变版本制品'),
sa.Column('version_no', mysql.INTEGER(), nullable=False),
sa.Column('version_label', sa.String(length=32), nullable=False, comment='例如 v1.0'),
sa.Column('source_path', sa.String(length=1024), nullable=False, comment='发布时路径快照'),
@@ -412,29 +411,6 @@ def upgrade() -> None:
)
op.create_index('idx_workspace_members_role', 'workspace_members', ['role_id'], unique=False)
op.create_index('idx_workspace_members_user', 'workspace_members', ['user_id', 'member_status'], unique=False)
op.create_table('workspace_operations',
sa.Column('operation_id', mysql.CHAR(length=26), nullable=False),
sa.Column('workspace_id', mysql.CHAR(length=26), nullable=False),
sa.Column('operation_type', sa.String(length=24), nullable=False, comment='open/close/mount/unmount/start/stop/restart/recycle'),
sa.Column('operation_status', sa.String(length=24), server_default=sa.text("'pending'"), nullable=False, comment='pending/running/succeeded/failed/cancelled'),
sa.Column('state_version', mysql.INTEGER(), server_default=sa.text('0'), nullable=False, comment='乐观锁版本'),
sa.Column('requested_by', mysql.CHAR(length=26), nullable=False),
sa.Column('created_at', mysql.DATETIME(fsp=3), server_default=sa.text('CURRENT_TIMESTAMP(3)'), nullable=False),
sa.Column('runtime_id', mysql.CHAR(length=26), nullable=True),
sa.Column('request_id', sa.String(length=128), nullable=True),
sa.Column('started_at', mysql.DATETIME(fsp=3), nullable=True),
sa.Column('finished_at', mysql.DATETIME(fsp=3), nullable=True),
sa.Column('error_code', sa.String(length=64), nullable=True),
sa.Column('error_message', sa.Text(), nullable=True),
sa.Column('is_deleted', mysql.TINYINT(display_width=1), server_default=sa.text('0'), nullable=False),
sa.Column('deleted_at', mysql.DATETIME(fsp=3), nullable=True),
sa.PrimaryKeyConstraint('operation_id'),
comment='无状态 Backend 的 Workspace/Jupyter 异步操作记录'
)
op.create_index('fk_workspace_operations_user', 'workspace_operations', ['requested_by'], unique=False)
op.create_index('idx_workspace_operations_runtime', 'workspace_operations', ['runtime_id', 'created_at'], unique=False)
op.create_index('idx_workspace_operations_workspace', 'workspace_operations', ['workspace_id', 'operation_status', 'created_at'], unique=False)
op.create_index('uk_workspace_operations_request', 'workspace_operations', ['request_id'], unique=True)
op.create_table('workspaces',
sa.Column('workspace_id', mysql.CHAR(length=26), nullable=False),
sa.Column('workspace_code', sa.String(length=64), nullable=False),
@@ -447,8 +423,8 @@ def upgrade() -> None:
sa.Column('created_at', mysql.DATETIME(fsp=3), server_default=sa.text('CURRENT_TIMESTAMP(3)'), nullable=False),
sa.Column('updated_at', mysql.DATETIME(fsp=3), server_default=sa.text('CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)'), nullable=False),
sa.Column('description', sa.String(length=1000), nullable=True),
sa.Column('artifact_bucket', sa.String(length=128), nullable=True, comment='RustFS bucket'),
sa.Column('artifact_prefix', sa.String(length=512), nullable=True, comment='RustFS object key prefix'),
sa.Column('artifact_bucket', sa.String(length=128), nullable=True, comment='S3 bucket'),
sa.Column('artifact_prefix', sa.String(length=512), nullable=True, comment='S3 object key prefix'),
sa.Column('is_deleted', mysql.TINYINT(display_width=1), server_default=sa.text('0'), nullable=False),
sa.Column('deleted_at', mysql.DATETIME(fsp=3), nullable=True),
sa.PrimaryKeyConstraint('workspace_id'),
@@ -457,21 +433,150 @@ def upgrade() -> None:
op.create_index('fk_workspaces_created_by', 'workspaces', ['created_by'], unique=False)
op.create_index('idx_workspaces_status', 'workspaces', ['status'], unique=False)
op.create_index('uk_workspaces_code', 'workspaces', ['workspace_code'], unique=True)
# ### end Alembic commands ###
# ── seed data (from b71c4f2a9d10) ──────────────────────────────
ADMIN_ROLE_ID = "0000000000000000000000000A"
DEVELOPER_ROLE_ID = "0000000000000000000000000B"
USERS = (
("0000000000RF6FG1SDBXG59S13", "admin-zhang", "张三", ADMIN_ROLE_ID),
("0000000000H2QYCGPCWQM1JSGS", "admin-li", "李四", ADMIN_ROLE_ID),
("0000000000RWG40ESZPGJT629J", "dev-wang", "王五", DEVELOPER_ROLE_ID),
("00000000004CQV7WASJA6N6FW4", "dev-zhao", "赵六", DEVELOPER_ROLE_ID),
)
WORKSPACES = (
("00000000000BM630VT9ARVFZPC", "model-development", "模型开发 Workspace"),
("0000000000AE0NC0V5T424KK86", "risk-validation", "风险验证 Workspace"),
)
roles = sa.table(
"roles",
sa.column("role_id", sa.String),
sa.column("role_code", sa.String),
sa.column("role_name", sa.String),
sa.column("role_scope", sa.String),
sa.column("is_builtin", sa.Integer),
sa.column("description", sa.String),
)
users = sa.table(
"users",
sa.column("user_id", sa.String),
sa.column("username", sa.String),
sa.column("display_name", sa.String),
sa.column("password_hash", sa.String),
sa.column("status", sa.String),
sa.column("email", sa.String),
sa.column("platform_role_id", sa.String),
)
workspaces = sa.table(
"workspaces",
sa.column("workspace_id", sa.String),
sa.column("workspace_code", sa.String),
sa.column("workspace_name", sa.String),
sa.column("active_root_uri", sa.String),
sa.column("status", sa.String),
sa.column("created_by", sa.String),
sa.column("description", sa.String),
)
members = sa.table(
"workspace_members",
sa.column("workspace_id", sa.String),
sa.column("user_id", sa.String),
sa.column("role_id", sa.String),
sa.column("member_status", sa.String),
)
op.bulk_insert(
roles,
[
{
"role_id": ADMIN_ROLE_ID,
"role_code": "admin",
"role_name": "管理员",
"role_scope": "workspace",
"is_builtin": 1,
"description": "Self-hosted workspace administrator",
},
{
"role_id": DEVELOPER_ROLE_ID,
"role_code": "developer",
"role_name": "开发人员",
"role_scope": "workspace",
"is_builtin": 1,
"description": "Self-hosted workspace developer",
},
],
)
op.bulk_insert(
users,
[
{
"user_id": user_id,
"username": username,
"display_name": display_name,
"password_hash": "demo-login-disabled",
"status": "active",
"email": f"{username}@model-platform.local",
"platform_role_id": role_id,
}
for user_id, username, display_name, role_id in USERS
],
)
op.bulk_insert(
workspaces,
[
{
"workspace_id": workspace_id,
"workspace_code": workspace_code,
"workspace_name": workspace_name,
"active_root_uri": f"s3://workspaces/{workspace_id}/",
"status": "active",
"created_by": USERS[0][0],
"description": "Self-hosted demo workspace",
}
for workspace_id, workspace_code, workspace_name in WORKSPACES
],
)
op.bulk_insert(
members,
[
{
"workspace_id": workspace_id,
"user_id": user_id,
"role_id": role_id,
"member_status": "active",
}
for workspace_id, _, _ in WORKSPACES
for user_id, _, _, role_id in USERS
],
)
# ── enable demo password login (from 9a1b2c3d4e5f) ─────────────
password = os.environ.get("INITIAL_ADMIN_PASSWORD", "admin12345")
seeded_user_ids = (
"0000000000RF6FG1SDBXG59S13",
"0000000000H2QYCGPCWQM1JSGS",
"0000000000RWG40ESZPGJT629J",
"00000000004CQV7WASJA6N6FW4",
)
users_update = sa.table(
"users",
sa.column("user_id", sa.String),
sa.column("password_hash", sa.String),
)
op.execute(
users_update.update()
.where(users_update.c.user_id.in_(seeded_user_ids))
.where(users_update.c.password_hash == "demo-login-disabled")
.values(password_hash=hash_password(password))
)
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
"""Drop everything (reverse of upgrade)."""
op.drop_index('uk_workspaces_code', table_name='workspaces')
op.drop_index('idx_workspaces_status', table_name='workspaces')
op.drop_index('fk_workspaces_created_by', table_name='workspaces')
op.drop_table('workspaces')
op.drop_index('uk_workspace_operations_request', table_name='workspace_operations')
op.drop_index('idx_workspace_operations_workspace', table_name='workspace_operations')
op.drop_index('idx_workspace_operations_runtime', table_name='workspace_operations')
op.drop_index('fk_workspace_operations_user', table_name='workspace_operations')
op.drop_table('workspace_operations')
op.drop_index('idx_workspace_members_user', table_name='workspace_members')
op.drop_index('idx_workspace_members_role', table_name='workspace_members')
op.drop_table('workspace_members')
@@ -550,9 +655,4 @@ def downgrade() -> None:
op.drop_index('idx_data_resources_owner', table_name='data_resources')
op.drop_table('data_resources')
op.drop_index('idx_consumer_inbox_status', table_name='consumer_inbox')
op.drop_table('consumer_inbox')
op.drop_index('idx_audit_workspace_time', table_name='audit_logs')
op.drop_index('idx_audit_actor_time', table_name='audit_logs')
op.drop_index('idx_audit_action_time', table_name='audit_logs')
op.drop_table('audit_logs')
# ### end Alembic commands ###
op.drop_table('consumer_inbox')