Files
account-manager/scripts/db/schema.py
chendelian 91057fa3b7
All checks were successful
技能自动化发布 / release (push) Successful in 7s
Release v1.0.56: Credential & Browser Profile Manager
2026-05-05 18:51:00 +08:00

146 lines
9.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""
Database schema v2 — Credential & Browser Profile Manager.
Time stored as INTEGER Unix seconds (UTC). DDL comments are preserved in
sqlite_master, visible in DBeaver / Navicat.
Schema version management:
PRAGMA user_version = 2 (set in connection.py after migration).
"""
import textwrap
# ---------------------------------------------------------------------------
# platforms — Registry of all platforms (logistics, LLM, content, etc.)
# ---------------------------------------------------------------------------
PLATFORMS_TABLE_SQL = textwrap.dedent("""\
CREATE TABLE IF NOT EXISTS platforms (
platform_key TEXT PRIMARY KEY, -- 平台唯一键,例如 maersk / deepseek / sohu
display_name TEXT NOT NULL, -- 展示名称,例如 Maersk / DeepSeek / 搜狐号
domain TEXT NOT NULL, -- 业务域logistics / llm / content / ecommerce / generic
provider_code TEXT, -- 供应商代码,物流 profile 可复用;例如 maersk、cma_cgm
default_url TEXT, -- 默认入口 URL
aliases_json TEXT NOT NULL DEFAULT '[]',-- 别名 JSON 数组,中英文别名
capabilities_json TEXT NOT NULL DEFAULT '{}', -- 能力声明,例如 {"web":true,"api_key":true,"rpa":true}
enabled INTEGER NOT NULL DEFAULT 1,-- 是否启用 (1=yes 0=no)
created_at INTEGER NOT NULL, -- 记录创建时间Unix 秒 UTC
updated_at INTEGER NOT NULL -- 记录最后更新时间Unix 秒 UTC
);
""")
PLATFORMS_INDEXES_SQL = textwrap.dedent("""\
CREATE INDEX IF NOT EXISTS idx_platforms_domain ON platforms(domain);
CREATE INDEX IF NOT EXISTS idx_platforms_provider_code ON platforms(provider_code);
""")
# ---------------------------------------------------------------------------
# accounts — An identity that can be used (web account, RPA profile, API account)
# ---------------------------------------------------------------------------
ACCOUNTS_TABLE_SQL = textwrap.dedent("""\
CREATE TABLE IF NOT EXISTS accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT, -- 账号主键(自增)
platform_key TEXT NOT NULL, -- 对应 platforms.platform_key
account_label TEXT NOT NULL, -- 人可读名称,例如 Maersk simulator booking
login_id TEXT, -- 登录标识:邮箱、用户名、手机号、客户代码等
login_id_type TEXT NOT NULL DEFAULT 'unknown', -- email / username / phone / customer_code / api_account / unknown
tenant_id TEXT, -- 租户/客户/项目标识,可空
environment TEXT NOT NULL DEFAULT 'production',-- simulator / staging / production / test
role TEXT NOT NULL DEFAULT 'default', -- booking / tracking / admin / sales / api / default
provider_code TEXT, -- 冗余供应商码,便于业务 skill 查询
profile_dir TEXT, -- Playwright 持久化浏览器用户数据目录web/rpa 账号使用
url TEXT, -- 账号默认入口 URL为空时用 platforms.default_url
status TEXT NOT NULL DEFAULT 'active', -- active / disabled / needs_review
session_status TEXT NOT NULL DEFAULT 'unknown', -- unknown / likely_valid / needs_login / expired
last_used_at INTEGER, -- 最近被业务 skill pick/使用时间
last_login_check_at INTEGER, -- 最近一次登录状态检查时间,可空
last_error_code TEXT, -- 最近一次错误代码
last_error_message TEXT, -- 最近一次错误消息
extra_json TEXT NOT NULL DEFAULT '{}', -- 扩展字段 JSON
created_at INTEGER NOT NULL, -- 记录创建时间Unix 秒 UTC
updated_at INTEGER NOT NULL -- 记录最后更新时间Unix 秒 UTC
);
""")
ACCOUNTS_INDEXES_SQL = textwrap.dedent("""\
CREATE INDEX IF NOT EXISTS idx_accounts_platform ON accounts(platform_key);
CREATE INDEX IF NOT EXISTS idx_accounts_pick_web ON accounts(platform_key, environment, tenant_id, role, status, updated_at);
CREATE INDEX IF NOT EXISTS idx_accounts_provider ON accounts(provider_code);
CREATE INDEX IF NOT EXISTS idx_accounts_status ON accounts(status);
""")
# Note: Unique constraint (platform_key, login_id, tenant_id, environment, role)
# is intentionally NOT added here because SQLite allows multiple NULL login_id.
# Application-level checks are used when login_id is provided.
# ---------------------------------------------------------------------------
# credentials — Secret-type credentials (API Key, Token, Password, etc.)
# Raw secret is NEVER stored in SQLite. Only metadata, secret_ref, secret_mask.
# ---------------------------------------------------------------------------
CREDENTIALS_TABLE_SQL = textwrap.dedent("""\
CREATE TABLE IF NOT EXISTS credentials (
id INTEGER PRIMARY KEY AUTOINCREMENT, -- 凭据主键(自增)
account_id INTEGER, -- 可空;关联 accounts.id有些 API Key 可直接挂平台,也可挂具体账号
platform_key TEXT NOT NULL, -- 平台唯一键
credential_label TEXT NOT NULL, -- 人可读名称,例如 DeepSeek main API key
credential_type TEXT NOT NULL, -- api_key / password / bearer_token / oauth_client / refresh_token / browser_profile / note
secret_storage TEXT NOT NULL, -- none / env / windows_credential
secret_ref TEXT, -- env 时为环境变量名windows_credential 时为 target namenone 时为空
secret_mask TEXT, -- 打码展示,例如 sk-****abcd不能反推出原文
expires_at INTEGER, -- 过期时间Unix 秒 UTC可空
status TEXT NOT NULL DEFAULT 'active', -- active / disabled / expired / needs_rotation
last_used_at INTEGER, -- 最近使用时间
extra_json TEXT NOT NULL DEFAULT '{}', -- 扩展字段 JSON
created_at INTEGER NOT NULL, -- 记录创建时间Unix 秒 UTC
updated_at INTEGER NOT NULL -- 记录最后更新时间Unix 秒 UTC
);
""")
CREDENTIALS_INDEXES_SQL = textwrap.dedent("""\
CREATE INDEX IF NOT EXISTS idx_credentials_platform_type ON credentials(platform_key, credential_type);
CREATE INDEX IF NOT EXISTS idx_credentials_account ON credentials(account_id);
CREATE INDEX IF NOT EXISTS idx_credentials_status ON credentials(status);
""")
# ---------------------------------------------------------------------------
# account_leases — RPA / profile concurrency locks
# Prevents multiple tasks from opening the same profile_dir simultaneously.
# ---------------------------------------------------------------------------
ACCOUNT_LEASES_TABLE_SQL = textwrap.dedent("""\
CREATE TABLE IF NOT EXISTS account_leases (
id INTEGER PRIMARY KEY AUTOINCREMENT, -- 租约主键(自增)
account_id INTEGER NOT NULL, -- 关联 accounts.id
lease_token TEXT NOT NULL UNIQUE, -- 随机 token用于 release / heartbeat
holder TEXT NOT NULL, -- 持有者,例如 monitor-competitor-rates 或 run id
purpose TEXT, -- 用途,例如 maersk_sim_rpa / booking_collect
expires_at INTEGER NOT NULL, -- 租约过期时间Unix 秒 UTC
heartbeat_at INTEGER, -- 最近一次心跳时间
released_at INTEGER, -- 主动释放时间
status TEXT NOT NULL DEFAULT 'active', -- active / released / expired
created_at INTEGER NOT NULL, -- 记录创建时间Unix 秒 UTC
updated_at INTEGER NOT NULL -- 记录最后更新时间Unix 秒 UTC
);
""")
ACCOUNT_LEASES_INDEXES_SQL = textwrap.dedent("""\
CREATE INDEX IF NOT EXISTS idx_leases_account_status ON account_leases(account_id, status);
CREATE INDEX IF NOT EXISTS idx_leases_token ON account_leases(lease_token);
CREATE INDEX IF NOT EXISTS idx_leases_expires ON account_leases(expires_at);
""")
# ---------------------------------------------------------------------------
# Full DDL assembly
# ---------------------------------------------------------------------------
ALL_DDL = (
PLATFORMS_TABLE_SQL
+ PLATFORMS_INDEXES_SQL
+ ACCOUNTS_TABLE_SQL
+ ACCOUNTS_INDEXES_SQL
+ CREDENTIALS_TABLE_SQL
+ CREDENTIALS_INDEXES_SQL
+ ACCOUNT_LEASES_TABLE_SQL
+ ACCOUNT_LEASES_INDEXES_SQL
)
SCHEMA_VERSION = 2