# 单域名多租户(路径前缀路由)+ 上线收口项 实施计划 > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 按 spec(docs/superpowers/specs/2026-10-19-shared-domain-routing-design.md)实现 `/t//` 路径前缀路由(与 Host 模式共存)、静态面前缀自推导、嵌入代码双形态,并收口登录限流 XFF 末段与 limiter 周期清扫。 **Architecture:** 纯 ASGI 中间件层加二级解析(路径前缀优先于 Host,剥离后下游路由零改动);widget.js/chat.html 从自身地址推导基础路径(Host 模式空串零变化);registry 增 slug 解析公开方法;登录限流 IP 取 XFF 末段(反代追加语义,不可伪造);lifespan 注册 300s 限流器清扫任务。 **Tech Stack:** FastAPI/Starlette 纯 ASGI、原生 JS(widget/chat 静态面,无构建)、Vue3+TS admin、pytest(PYTHONUTF8=1)/vitest。 **执行环境注意(Windows):** - 仓库根 `D:\refactor\all-kefu`;后端测试 `PYTHONUTF8=1 .venv/Scripts/python -m pytest tests/test_xxx.py -q`(全量 `PYTHONUTF8=1 .venv/Scripts/python -m pytest -q`,基线 **632+2skip**) - 前端:`cd admin && pnpm test`(基线 **182**);`pnpm build`;dts 行尾纪律照旧(`git diff --ignore-cr-at-eol` 为空才 checkout 还原) - 分支:`feature/shared-domain`(从 main 切出) - 本地路径模式验证技巧:路径前缀优先于 Host——`curl http://127.0.0.1:8018/t/jiancai/`(Host 是 IP 未知也没关系,路径解析兜住) - 单进程铁律、日志/内存上限铁律(本计划 Task 7 为其进程内面)不变 **拼接安全性四防线(用户 2026-10-19 要求,实现与测试双重钉住):** 1. **后端 slug 只查表不拼输出**——`TENANT_SLUG_RE` 白名单(`[a-z0-9-]`)后仅作 dict lookup key;404/503 响应为静态常量串,任何用户输入(slug/路径段)都不进响应体 → 无服务端注入/反射面; 2. **前端 base 值白名单推导**——chat.html 引导脚本的 base 来自 `location.pathname.match(/^\/t\/[a-z0-9-]+\/$/)`(match 命中即字符集白名单),widget.js 的 basePath 来自自身 script.src 定位切分;两者**只做 URL 属性赋值**(`l.href`/`s.src`/`img.src`/fetch/WebSocket),**绝不进任何 innerHTML/字符串拼 HTML**; 3. **既有 innerHTML 纪律不破**——widget.js 现有插值全走 `esc()` 五字符转义(已核:panel/invite/toggle 三处);本计划新增代码**零 innerHTML**(静态测试断言钉住); 4. **嵌入代码生成侧域名校验**——`normalizeSharedDomain` 与域名形态生成前做主机名字符集校验(`^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$`,拒绝引号/尖括号/空格/协议),非法返回 null/空串不产出 `") # 引导块(首个内联 script) assert "innerHTML" not in boot def test_chatjs_端点全部走_BASE_PATH(): src = read("static/js/chat.js") assert '__BASE_PATH__' in src # 裸绝对端点不允许(fetch("/api/... 或 + "/ws/ 直接拼接形态) assert 'fetch("/api/' not in src ``` - [ ] **Step 2: RED** → **Step 3: 实现**——chat.html `` 内 charset/title 之后、原 link/script 位置替换为: ```html ``` (body 末尾原 ` * shared 形态 = * 生成前域名须过同款主机名校验(tenantSlug 由创建侧 SLUG_RE 保证字符集); * 不可用(缺域名/共享域名/校验不过)返回 '' */ export type EmbedForm = 'domain' | 'shared' export function buildEmbedCode(form: EmbedForm, opts: { domain: string; sharedDomain: string | null; tenantSlug: string; site: string | null }): string ``` vitest:normalize 各形态 + **恶意字符样例**(`kefu.com" onload="x`、`