migration: establish exact preserved app baseline

This commit is contained in:
leefer
2026-07-30 23:51:48 +08:00
parent 41329943c4
commit 4083dceba3
399 changed files with 129967 additions and 23 deletions
+9
View File
@@ -0,0 +1,9 @@
from .container import ApplicationContainer, build_application_container
from .settings import RuntimeSettings, load_runtime_settings
__all__ = [
"ApplicationContainer",
"RuntimeSettings",
"build_application_container",
"load_runtime_settings",
]
+60
View File
@@ -0,0 +1,60 @@
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
from collections.abc import Callable
from backend.data import DataGateway, build_data_gateway
from backend.database.repositories import RepositoryBundle, build_repository_bundle
from backend.features.alerts import AlertService
from backend.features.review import TradeJournalService
from backend.features.screener import StrategyTrackingService
from backend.jobs import InProcessJobRunner, JobRegistry, SQLiteJobRunRepository
from chart_data_provider import MarketChartClient
from database import ReviewDatabase
from ifind_client import IfindHttpClient
from mentor_agent import MentorSkillRegistry
from realtime_aggregator import WebRealtimeAggregator
from screener import ScreenerEngine
@dataclass(frozen=True)
class ApplicationContainer:
database: ReviewDatabase
repositories: RepositoryBundle
data_gateway: DataGateway
ifind: IfindHttpClient
screener: ScreenerEngine
strategy_tracking: StrategyTrackingService
alert_service: AlertService
trade_journal: TradeJournalService
mentor_skills: MentorSkillRegistry
realtime_aggregator: WebRealtimeAggregator
chart_data: MarketChartClient
jobs: InProcessJobRunner
def build_application_container(
database: ReviewDatabase,
credentials: dict[str, object],
mentor_skills_dir: Path,
private_mentor_skills_dir: Path,
tushare_token_supplier: Callable[[], str] | None = None,
) -> ApplicationContainer:
data_gateway = build_data_gateway(credentials, tushare_token_supplier)
repositories = build_repository_bundle(database)
jobs = InProcessJobRunner(JobRegistry.load(), SQLiteJobRunRepository(database))
return ApplicationContainer(
database=database,
repositories=repositories,
data_gateway=data_gateway,
ifind=data_gateway.ifind,
screener=ScreenerEngine(database),
strategy_tracking=StrategyTrackingService(repositories.strategy_tracking),
alert_service=AlertService(repositories.alerts),
trade_journal=TradeJournalService(repositories.trades),
mentor_skills=MentorSkillRegistry(mentor_skills_dir, private_mentor_skills_dir),
realtime_aggregator=data_gateway.realtime_observer,
chart_data=data_gateway.chart_data,
jobs=jobs,
)
+55
View File
@@ -0,0 +1,55 @@
from __future__ import annotations
import os
from dataclasses import dataclass
from typing import Mapping
from app_config import load_local_env, save_local_env
from security import SecretVault
def environment_credentials(environment: Mapping[str, str]) -> dict[str, str]:
return {
"tushare_token": str(environment.get("TUSHARE_TOKEN") or "").strip(),
"ifind_refresh_token": str(environment.get("IFIND_REFRESH_TOKEN") or "").strip(),
"ifind_access_token": str(environment.get("IFIND_ACCESS_TOKEN") or "").strip(),
"platform_llm_primary_api_key": str(
environment.get("LLM_PRIMARY_API_KEY") or environment.get("LLM_API_KEY") or ""
).strip(),
"platform_llm_primary_base_url": str(
environment.get("LLM_PRIMARY_BASE_URL")
or environment.get("LLM_BASE_URL")
or "https://api.openai.com/v1"
).strip(),
"platform_llm_primary_model": str(
environment.get("LLM_PRIMARY_MODEL") or environment.get("LLM_MODEL") or ""
).strip(),
"platform_llm_fallback_api_key": str(
environment.get("LLM_FALLBACK_API_KEY") or ""
).strip(),
"platform_llm_fallback_base_url": str(
environment.get("LLM_FALLBACK_BASE_URL") or ""
).strip(),
"platform_llm_fallback_model": str(
environment.get("LLM_FALLBACK_MODEL") or ""
).strip(),
}
@dataclass(frozen=True)
class RuntimeSettings:
encryption_key: str
initial_credentials: dict[str, str]
def load_runtime_settings() -> RuntimeSettings:
load_local_env()
encryption_key = os.environ.get("APP_ENCRYPTION_KEY", "").strip()
if not encryption_key:
encryption_key = SecretVault.generate_key()
save_local_env({"APP_ENCRYPTION_KEY": encryption_key})
os.environ["APP_ENCRYPTION_KEY"] = encryption_key
return RuntimeSettings(
encryption_key=encryption_key,
initial_credentials=environment_credentials(os.environ),
)