Merge branch 'develop' of http://8.153.151.51:8888/team_group/model-develop into develop
This commit is contained in:
@@ -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."""
|
||||
+170
-70
@@ -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')
|
||||
Reference in New Issue
Block a user