feat: platform.db 模型与超管引导(WAL/scrypt/幂等bootstrap)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-13 07:57:15 +08:00
parent d088264ca0
commit 2d62108c73
5 changed files with 520 additions and 6 deletions
+17 -4
View File
@@ -18,6 +18,7 @@ from app.engine.models import Base
# "先 import app.main 再 import chat_rest"的循环导入次序坑随全局单例一并
# 消失(routes 不再反向 import app.main),直接导入即可。
from app.main import app
from app.platform.db import make_platform_engine
from app.platform.registry import TenantRegistry
REPO_ROOT = Path(__file__).resolve().parents[1]
@@ -136,9 +137,20 @@ def build_app(seed_dir, data_dir):
延迟取 registry),工厂只负责把 Registry 放进 app.state——lifespan 见
已挂则沿用(测试注入缝),不再加载仓库 seed。调用方用完必须复位
app.state.registry = None(见 app_test_client 的 finally)。
Phase 2 Task 1 起同时预置 platform_engine(data_dir 下的临时平台库,
同一注入缝)——不预置则 lifespan 会在仓库根建出真实 platform.db;
schema+bootstrap 由 lifespan 统一跑(幂等),dispose 也在 lifespan
finally(与 registry.dispose_all 同处)。
"""
registry = TenantRegistry.load_from_dir(seed_dir, data_dir=data_dir)
app.state.registry = registry
# data_dir 可能尚不存在(租户库由 _ensure_ready 按需 mkdir,平台库由
# lifespan 立即连接——这里先建目录,否则 sqlite "unable to open database
# file")
Path(data_dir).mkdir(parents=True, exist_ok=True)
app.state.platform_engine = make_platform_engine(
Path(data_dir) / "platform.db")
return app
@@ -172,10 +184,10 @@ class TenantTestClient(TestClient):
@contextmanager
def app_test_client(app_instance, base_url, cleanup_root):
"""TestClient 包装:统一退出纪律——lifespan finally 已 dispose_all
(Windows 文件锁),再置空 app.state.registry(关残留窗:不让已 dispose
的测试 Registry 被后续无夹具的 lifespan 误用),最后清 tmp 目录
(删不掉告警留痕,不静默吞)。"""
"""TestClient 包装:统一退出纪律——lifespan finally 已 dispose_all +
platform_engine.dispose(Windows 文件锁),再置空 app.state.registry /
platform_engine(关残留窗:不让已 dispose 的测试对象被后续无夹具的
lifespan 误用),最后清 tmp 目录(删不掉告警留痕,不静默吞)。"""
try:
with TenantTestClient(
app_instance, base_url=base_url,
@@ -183,6 +195,7 @@ def app_test_client(app_instance, base_url, cleanup_root):
yield c
finally:
app_instance.state.registry = None
app_instance.state.platform_engine = None
_rmtree_warn(cleanup_root)