feat: add A-card operations frontend and backend foundation

This commit is contained in:
郑龙捷
2026-09-02 09:54:03 +08:00
parent 9badd3597f
commit ad71259ba5
106 changed files with 12461 additions and 1796 deletions
+55
View File
@@ -26,6 +26,7 @@ from venv import logger
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from pydantic import Field, field_validator, model_validator
from pydantic_settings import BaseSettings, NoDecode, SettingsConfigDict
from sqlalchemy.engine import make_url
# ── AES 解密核心函数 ────────────────────────────────────────────────────────
@@ -63,6 +64,43 @@ class Settings(BaseSettings):
),
description="SQLAlchemy async URI for the platform MySQL.",
)
operations_database_url: str | None = Field(
default=None,
description=(
"SQLAlchemy async URI for the isolated model_operations database. "
"When omitted it reuses DATABASE_URL credentials and switches only "
"the schema name to model_operations."
),
)
platform_read_database_url: str | None = Field(
default=None,
description="Read-only model_platform connection used by operations APIs.",
)
deploy_database_url: str | None = Field(
default=None,
description="Read-only model_deploy connection used by operations APIs.",
)
# ── operations Redis accelerator ─────────────────────────────
redis_url: str | None = Field(
default=None,
description=(
"Optional Redis URL for operations query cache and event Streams. "
"MySQL remains the source of truth when Redis is unavailable."
),
)
redis_socket_connect_timeout_seconds: float = Field(default=2.0, gt=0, le=30)
redis_socket_timeout_seconds: float = Field(default=2.0, gt=0, le=30)
operations_cache_ttl_seconds: int = Field(default=300, ge=10, le=86400)
operations_redis_prefix: str = Field(default="model-platform:operations")
operations_event_stream: str = Field(
default="model-platform:operations:events"
)
operations_event_stream_maxlen: int = Field(default=10000, ge=100)
operations_data_mode: str = Field(
default="database",
description="Operations data source: database or mock.",
)
# ── JWT ───────────────────────────────────────────────────────
jwt_secret: str = Field(
@@ -268,6 +306,23 @@ class Settings(BaseSettings):
# print(values[key])
return values
@model_validator(mode="after")
def _derive_operations_database_url(self) -> "Settings":
"""Default the operations database to the platform server credentials."""
if not self.operations_database_url:
platform_url = make_url(self.database_url)
self.operations_database_url = platform_url.set(
database="model_operations"
).render_as_string(hide_password=False)
if not self.platform_read_database_url:
self.platform_read_database_url = self.database_url
if not self.deploy_database_url:
platform_url = make_url(self.database_url)
self.deploy_database_url = platform_url.set(
database="model_deploy"
).render_as_string(hide_password=False)
return self
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",