Files
ai-kefu-admin/app/platform/tenant_config.py
T
浅念 6839cb94d2 fix: 终审修复——站点停用接线/prefix查重补创建reset/默认凭据警示+取舍补录
- I-1 站点停用全链接线: Site 数据类加 status 字段; DB 源 _load_one 与
  文件源 read_tenant 均只输出 active 站点(停用站点域名不进 registry
  索引 → 该域名 404;全停用=合法运营状态输出空 sites;文件源无 status
  键默认 active 兼容)。E2E: PATCH 站点 disabled → 其域名 404、复启用
  恢复;源级直测两源过滤。
- I-3 prefix 跨租户查重补两口: db.other_push_prefixes 共用助手
  (MAX(version) 逐租户当前前缀,排除自身);创建向导(出厂档案前缀被占
  → 400 提示改 blank/改占用方)与 reset push(重置回出厂撞车 → 400 提示
  走 PUT)接入;PUT 原有查重改用同一助手。rollback 维持豁免(历史不可变
  语义优先,取舍清单记档)。既有测试第二租户改 blank profile 避撞。
- I-4 默认凭据警示: lifespan 启动检测 ADMIN_PASSWORD 仍为 Settings 字段
  默认值(非硬编码第二份,防双源漂移)→ logger.warning 醒目提示;
  CLAUDE.md Phase 4 部署记档补 ADMIN_PASSWORD 生产要求。
- I-2/M-2 取舍补录: 密钥掩码未实现(GET config/settings 明文返回
  api_key/smtp 密码,Phase 3 前须定 __SET__ 哨兵方案);站点管理权限现为
  仅超管(Phase 3 对接时决策放开或改 spec);取舍清单第 2 条同步刷新
  (rollback 现为唯一豁免口)。

全量回归: 571 passed(基线 566 + 新增 5)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-13 12:40:21 +08:00

42 lines
1.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""租户与站点配置模型。
- TenantConfig.data = deep_merge(DEFAULT_CONFIG, snapshot):租户快照 ⊕ 出厂默认
- Site.brand 的合并基线是**租户级** data["brand"](而非出厂默认),
这样租户级 brand 自定义也能被各站点继承。
"""
from dataclasses import dataclass
from app.platform.defaults import DEFAULT_CONFIG, deep_merge
@dataclass(frozen=True)
class Site:
slug: str
name: str
domains: tuple[str, ...]
brand: dict # 站点门面覆盖层 ⊕ 租户默认 brand(已深合并)。
# 只读约定:构造后不得修改
status: str = "active" # 终审 I-1:站点停用接线——数据源只输出
# active 站点(停用站点的域名不进索引 → 404),
# 本字段记录该契约(当前恒为 "active")
@dataclass(frozen=True)
class TenantConfig:
slug: str
name: str
status: str # active | disabled
data: dict # deep_merge(DEFAULT_CONFIG, snapshot)。
# 只读约定:构造后不得修改
sites: tuple[Site, ...] = ()
@classmethod
def from_snapshot(cls, slug, name, snapshot, sites=(), status="active"):
merged = deep_merge(DEFAULT_CONFIG, snapshot)
resolved = tuple(
Site(s["slug"], s.get("name", s["slug"]), tuple(s.get("domains", ())),
deep_merge(merged["brand"], s.get("brand", {})),
s.get("status", "active"))
for s in sites)
return cls(slug=slug, name=name, status=status, data=merged, sites=resolved)