feat(sites): 站点请求失败留样(site_failure_sample,每站最近 5 份)

- 迁移 0077 新表 site_failure_sample:类型(parse_error / challenge)、请求类型、脱敏 URL、状态码、
  关键响应头、错误文案、命中规则、脱敏页面开头(≤ 64 KB);样本几十 KB,不放进每条请求都读的 site.config
- 适配器 onFailureSample 回调接到 runtime.siteAdapter,异步写入并删到最近 5 份,写失败只记日志
- GET /sites/{id}/failure-samples(不带正文)与 /failure-samples/{sampleId}(带正文)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-06 15:41:00 +08:00
1 parent 1779e1a0f2
commit 0088c1c9e7
10 files changed
+355

No files matched your search

+16
View File
@@ -86,6 +86,22 @@ export const sourcesApi = {
params: IdParam,
response: sources.SiteRequestStats,
}),
failureSamples: ep({
method: "get",
path: "/sites/{id}/failure-samples",
scope: "read",
summary: "站点请求失败留样:最近几次页面结构无法识别 / 人机验证时的脱敏页面(列表不带正文)",
params: IdParam,
response: z.object({ items: z.array(sources.SiteFailureSampleSummary) }),
}),
failureSample: ep({
method: "get",
path: "/sites/{id}/failure-samples/{sampleId}",
scope: "read",
summary: "一份失败留样(含脱敏后的页面正文)",
params: z.object({ id: Id, sampleId: Id }),
response: sources.SiteFailureSample,
}),
credentials: ep({
method: "put",
path: "/sites/{id}/credentials",