feat(HEL-382): 搭建 datahub 底座和盘后正式数据链路

新增独立 xiaobai-datahub 服务(SQLite WAL、Tushare 盘后发布、/v1 契约和管理后台),不改现站页面与数据链路。

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
总工
2026-09-02 12:05:26 +08:00
co-authored by Cursor multica-agent
parent c2ebc0ab91
commit 3498dd7a4b
52 changed files with 4259 additions and 0 deletions
+54
View File
@@ -0,0 +1,54 @@
from __future__ import annotations
from pathlib import Path
from datahub.adapters.tushare import TushareAdapter
from datahub.admin_api import AdminAPI
from datahub.auth import AuthService
from datahub.crypto import SecretVault
from datahub.db import HubDB
from datahub.governance.circuit import CircuitBreaker
from datahub.governance.lkg import LastKnownGood
from datahub.governance.ratelimit import TokenBucket
from datahub.pipeline import Pipeline
from datahub.scheduler import Scheduler
from datahub.serving import V1API
from datahub.settings import Settings, load_settings
class Hub:
def __init__(self, settings: Settings, adapter: TushareAdapter | None = None) -> None:
if not settings.encryption_key:
raise SystemExit("DATAHUB_ENCRYPTION_KEY 未配置")
self.settings = settings
self.db = HubDB(settings.db_path)
self.vault = SecretVault(settings.encryption_key)
self.auth = AuthService(self.db, self.vault, settings.api_token, settings.admin_password)
token = settings.tushare_token or self.auth.load_credential("tushare_token")
if settings.tushare_token:
self.auth.store_credential("tushare_token", settings.tushare_token)
token = settings.tushare_token
self.adapter = adapter or TushareAdapter(token)
self.pipeline = Pipeline(
self.db,
self.adapter,
settings,
bucket=TokenBucket(settings.tushare_rate_per_minute),
breaker=CircuitBreaker(),
)
self.lkg = LastKnownGood(self.db)
self.scheduler = Scheduler(self.db, self.pipeline)
self.api = V1API(self.db, self.pipeline, settings)
self.admin = AdminAPI(self.db, self.pipeline, self.scheduler, self.auth)
self.static_dir = Path(__file__).resolve().parents[1] / "admin"
def start(self) -> None:
if self.settings.scheduler_enabled:
self.scheduler.start()
def stop(self) -> None:
self.scheduler.stop()
def build_hub(settings: Settings | None = None) -> Hub:
return Hub(settings or load_settings())