添加查看调度运行结果
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
"""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."""
|
||||
@@ -0,0 +1,158 @@
|
||||
"""ensure the self-hosted demo login remains available
|
||||
|
||||
Revision ID: e5f6a7b8c9d0
|
||||
Revises: d4e5f6a7b8c9
|
||||
Create Date: 2026-08-05 15:31:00
|
||||
"""
|
||||
|
||||
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: str = "e5f6a7b8c9d0"
|
||||
down_revision: str | Sequence[str] | None = "d4e5f6a7b8c9"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
DISABLED_PASSWORD = "demo-login-disabled"
|
||||
SEEDED_USERS = (
|
||||
(
|
||||
"0000000000RF6FG1SDBXG59S13",
|
||||
"admin-zhang",
|
||||
"张三",
|
||||
"0000000000000000000000000A",
|
||||
),
|
||||
(
|
||||
"0000000000H2QYCGPCWQM1JSGS",
|
||||
"admin-li",
|
||||
"李四",
|
||||
"0000000000000000000000000A",
|
||||
),
|
||||
(
|
||||
"0000000000RWG40ESZPGJT629J",
|
||||
"dev-wang",
|
||||
"王五",
|
||||
"0000000000000000000000000B",
|
||||
),
|
||||
(
|
||||
"00000000004CQV7WASJA6N6FW4",
|
||||
"dev-zhao",
|
||||
"赵六",
|
||||
"0000000000000000000000000B",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _create_users_table() -> None:
|
||||
op.create_table(
|
||||
"users",
|
||||
sa.Column("user_id", mysql.CHAR(length=26), nullable=False),
|
||||
sa.Column("username", sa.String(length=64), nullable=False),
|
||||
sa.Column("display_name", sa.String(length=100), nullable=False),
|
||||
sa.Column("password_hash", sa.String(length=255), nullable=False),
|
||||
sa.Column(
|
||||
"status",
|
||||
sa.String(length=16),
|
||||
server_default=sa.text("'active'"),
|
||||
nullable=False,
|
||||
comment="active/disabled/locked",
|
||||
),
|
||||
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("email", sa.String(length=255), nullable=True),
|
||||
sa.Column("platform_role_id", mysql.CHAR(length=26), nullable=True),
|
||||
sa.Column("avatar_uri", sa.String(length=1000), nullable=True),
|
||||
sa.Column("last_login_at", mysql.DATETIME(fsp=3), 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("user_id"),
|
||||
comment="平台用户",
|
||||
)
|
||||
op.create_index("fk_users_platform_role", "users", ["platform_role_id"])
|
||||
op.create_index("idx_users_status", "users", ["status"])
|
||||
op.create_index("uk_users_email", "users", ["email"], unique=True)
|
||||
op.create_index("uk_users_username", "users", ["username"], unique=True)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
connection = op.get_bind()
|
||||
if not sa.inspect(connection).has_table("users"):
|
||||
_create_users_table()
|
||||
|
||||
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),
|
||||
)
|
||||
existing = {
|
||||
row.username: row.password_hash
|
||||
for row in connection.execute(
|
||||
sa.select(users.c.username, users.c.password_hash).where(
|
||||
users.c.username.in_([user[1] for user in SEEDED_USERS])
|
||||
)
|
||||
)
|
||||
}
|
||||
password_hash = hash_password(
|
||||
os.environ.get("INITIAL_ADMIN_PASSWORD", "admin12345")
|
||||
)
|
||||
|
||||
for user_id, username, display_name, role_id in SEEDED_USERS:
|
||||
if username not in existing:
|
||||
connection.execute(
|
||||
users.insert().values(
|
||||
user_id=user_id,
|
||||
username=username,
|
||||
display_name=display_name,
|
||||
password_hash=password_hash,
|
||||
status="active",
|
||||
email=f"{username}@model-platform.local",
|
||||
platform_role_id=role_id,
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
||||
if existing[username] in {None, "", DISABLED_PASSWORD}:
|
||||
connection.execute(
|
||||
users.update()
|
||||
.where(users.c.username == username)
|
||||
.values(password_hash=password_hash)
|
||||
)
|
||||
|
||||
connection.execute(
|
||||
users.update()
|
||||
.where(users.c.username == "admin-zhang")
|
||||
.values(status="active")
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Do not remove or disable accounts that may contain user data."""
|
||||
Reference in New Issue
Block a user