146 lines
9.1 KiB
Python
146 lines
9.1 KiB
Python
# -*- 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 name;none 时为空
|
||
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
|