chore(repo): initialize team collaboration repository
CI / Python 3.12 (push) Waiting to run
CI / Python 3.9 (push) Waiting to run

This commit is contained in:
2026-07-27 20:40:12 +08:00
commit c91a64fddb
109 changed files with 21121 additions and 0 deletions
@@ -0,0 +1,107 @@
"""配置。
产品名只出现在这里和 apps/console/lib/brand.ts —— 改名成本 = 改两个常量。
商标检索未完成前,任何地方都不要再硬编码产品名。
"""
from __future__ import annotations
import os
from dataclasses import dataclass, field
from pathlib import Path
from typing import Optional
APP_NAME = "FlashOps"
APP_TAGLINE = "存储实验室无人值守执行层"
# 仓库根目录:.../flashops
REPO_ROOT = Path(__file__).resolve().parents[3]
def _env(key: str, default: str) -> str:
return os.environ.get(f"FLASHOPS_{key}", default)
def _env_int(key: str, default: int) -> int:
return int(os.environ.get(f"FLASHOPS_{key}", str(default)))
def _env_float(key: str, default: float) -> float:
return float(os.environ.get(f"FLASHOPS_{key}", str(default)))
@dataclass(frozen=True)
class TimingPolicy:
"""时间策略。
全部集中在这里,且引擎不直接读它 —— 引擎从 Snapshot 拿数值。
这样"30 秒心跳超时"这类规则可以在测试里用 0.03 秒验证。
"""
heartbeat_interval_s: float = 5.0
heartbeat_degraded_after_s: float = 15.0
heartbeat_lost_after_s: float = 30.0
lease_ttl_s: float = 60.0
lease_poll_timeout_s: float = 20.0
step_default_timeout_s: float = 600.0
recovery_settle_s: float = 20.0 # 恢复动作后等待主机回来的时间
ac_cycle_off_s: float = 10.0 # AC 断电后的安全间隔(方案 §7.2)
max_recovery_per_loop: int = 3
max_recovery_total: int = 20
@dataclass(frozen=True)
class Settings:
app_name: str = APP_NAME
env: str = field(default_factory=lambda: _env("ENV", "development"))
# 开发态:SQLite。生产:postgresql+asyncpg://...
database_url: str = field(
default_factory=lambda: _env(
"DATABASE_URL", "sqlite+aiosqlite:///" + str(REPO_ROOT / "var" / "flashops.db")
)
)
# 开发态:本地目录。生产:s3://minio:9000/flashops
object_store_url: str = field(
default_factory=lambda: _env("OBJECT_STORE_URL", str(REPO_ROOT / "var" / "objects"))
)
# 开发态:进程内 asyncio。生产:redis://... / nats://...
bus_url: Optional[str] = field(default_factory=lambda: os.environ.get("FLASHOPS_BUS_URL"))
# Agent 首次注册口令。开发态未配置时允许本机演示;生产态缺失时拒绝注册。
agent_enrollment_token: Optional[str] = field(
default_factory=lambda: os.environ.get("FLASHOPS_AGENT_ENROLLMENT_TOKEN")
)
api_host: str = field(default_factory=lambda: _env("API_HOST", "0.0.0.0"))
api_port: int = field(default_factory=lambda: _env_int("API_PORT", 8000))
cors_origins: str = field(default_factory=lambda: _env("CORS_ORIGINS", "http://localhost:3000"))
# 引擎主循环的 tick 间隔。demo/测试里调小以加速。
engine_tick_s: float = field(default_factory=lambda: _env_float("ENGINE_TICK_S", 0.5))
# 演示/仿真加速倍率:1.0 = 真实速度
time_scale: float = field(default_factory=lambda: _env_float("TIME_SCALE", 1.0))
timing: TimingPolicy = field(default_factory=TimingPolicy)
@property
def is_sqlite(self) -> bool:
return self.database_url.startswith("sqlite")
@property
def object_store_path(self) -> Path:
return Path(self.object_store_url)
_settings: Optional[Settings] = None
def get_settings() -> Settings:
global _settings
if _settings is None:
_settings = Settings()
return _settings
def reset_settings() -> None:
"""测试用:让下一次 get_settings() 重新读环境变量。"""
global _settings
_settings = None