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
+32
View File
@@ -424,6 +424,38 @@ def test_platform_settings_crud(super_client):
assert super_client.get(SETTINGS).status_code == 401
def test_widget_domain_read_matrix(super_client):
"""GET settings/widget-domain(spec 2026-10-19 §5):嵌入代码生成器的
原料——两种角色可读(超管配置/租户管理员生成);未认证 401;未配置
null;PUT settings 后经 settings_store.refresh 热刷新即时可见。"""
url = f"{SETTINGS}/widget-domain"
# 未认证 401
assert super_client.get(url).status_code == 401
# 超管读:未配置 → None
r = super_client.get(url, headers=_auth(super_client))
assert r.status_code == 200
assert r.json() == {"widget_domain": None}
# 配置后即时可见(PUT settings 触发内存快照热刷新)
r = super_client.put(SETTINGS, headers=_auth(super_client),
json={"key": "shared_widget_domain",
"value": "kefu.example.com"})
assert r.status_code == 200
r = super_client.get(url, headers=_auth(super_client))
assert r.json() == {"widget_domain": "kefu.example.com"}
# 租户管理员同可读(与 settings CRUD 的 require_super 不同——嵌入卡原料)
_create(super_client, slug="wd1")
token = _tenant_admin_token(super_client, "wd1-admin", "wd1-pass-1")
r = super_client.get(url, headers=_bearer(token))
assert r.status_code == 200
assert r.json() == {"widget_domain": "kefu.example.com"}
# 空串值视同未配置(settings_store 空槽语义)
assert super_client.put(
SETTINGS, headers=_auth(super_client),
json={"key": "shared_widget_domain", "value": ""}).status_code == 200
assert super_client.get(
url, headers=_auth(super_client)).json() == {"widget_domain": None}
# ---- 版本列表形态 -----------------------------------------------------------------