feat(admin): 嵌入代码双形态 + shared_widget_domain 平台键

- 后端 GET /api/platform/settings/widget-domain:读平台设置键
  shared_widget_domain(settings_store 内存快照,PUT settings 后热刷新
  即时可见);platform_user 两种角色可读(超管配置/租户管理员生成嵌入
  代码——与 settings CRUD 的 require_super 不同,非密钥无敏感面);
  空串/非字符串视同未配置 → null
- config/center 嵌入卡双形态(spec 2026-10-19 §5):域名形态 +
  共享域名形态(/t/<slug>/static/widget.js,路径前缀即租户标识);
  可用性规则(两料齐双形态/无域名仅共享/共享未配仅域名/都无引导文案)
  与生效态回退;data-site 两形态通用
- normalizeSharedDomain 主机名字符集校验(拒引号/尖括号/空格——防
  配置侧把恶意字符注入生成的 script src 串)+ buildEmbedCode 纯函数,
  vitest 6 例钉位
- 平台设置页新增「共享域名」组(shared_widget_domain 推荐键,表驱动
  渲染自动生效);settings API 模块加 getWidgetDomain

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-19 19:24:56 +08:00
parent f938118fee
commit 5813fc91c7
8 changed files with 268 additions and 24 deletions
+17 -2
View File
@@ -29,7 +29,9 @@
ai.*/smtp.* 对齐 admin 平台设置页)即时生效,无需重启。
鉴权矩阵(spec §3):config 系 = require_tenant(slug)(租户全开——本租户
管理员可自助改配置);settings = require_super。409 门经 platform_user 连锁。
管理员可自助改配置);settings = require_super(例外:GET /settings/widget-domain
= platform_user 两种角色可读——嵌入代码生成器的原料,spec 2026-10-19 §5)。
409 门经 platform_user 连锁。
密钥掩码哨兵(Phase 3 Task 1):push.api_key / ai.api_key /
email.smtp.password 及平台设置的密钥名值,所有携带 config/value 的响应
@@ -53,7 +55,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.engine.services import boundary_filter
from app.engine.services.email_service import send_test_email
from app.engine.wall_schema import WallConfigError, compile_wall
from app.platform.auth import UserContext, require_super
from app.platform.auth import UserContext, platform_user, require_super
from app.platform.config_validation import (
CONFIG_GROUPS,
ValidationContext,
@@ -743,3 +745,16 @@ async def put_setting(request: Request, user=Depends(require_super)):
await session.commit()
await settings_store.refresh(engine)
return {"key": key, "value": _mask_setting_item(key, value)}
@settings_router.get("/widget-domain")
async def get_widget_domain(user: platform_user):
"""共享挂件域名读取(spec 2026-10-19 §5)——配置中心嵌入代码生成器
消费的平台设置键 shared_widget_domain。两种平台角色可读:超管在平台
设置页配置、租户管理员生成 /t/<slug>/ 形态嵌入代码都要用(与 settings
CRUD 的 require_super 不同——本值非密钥、无敏感面,公开到登录管理员
层即可)。读 settings_store 内存快照(lifespan 装载 / PUT settings 后
refresh 热刷新,Phase 4 Task 5A 同链路);空串/None/非字符串值视同
未配置 → None(前端生成侧还有域名合法字符校验兜底)。"""
raw = settings_store.get("shared_widget_domain")
return {"widget_domain": raw if isinstance(raw, str) and raw else None}