7.1 KiB
站点运行手册(W9-A 站点中心后端)
规格:docs/design/site-center.md。实现:apps/server/src/modules/sources-identity/service/account.ts(凭证、刷新登录、用户数据、健康检测、删除、定时任务)、routes/sources.ts、runtime.ts(凭证注入、站点默认、代理);账密登录 packages/adapters/src/sites/login/nexusphp.ts;迁移 0031_site_center。
端点
| 端点 | scope | 说明 |
|---|---|---|
GET /sites?include=accounts |
read | 列表;带 include=accounts 时每项附 accounts[](完整 SiteAccount)和 accountStatus(多账号取最差,无启用账号为 unconfigured)。已删除站点不出现 |
POST /sites / PATCH /sites/{id} |
admin | 登记 / 修改;新增 proxy。rateLimit 为空、snapshotIntervalSec 为 null 时继承站点默认 |
DELETE /sites/{id} |
admin | 见"删除" |
PUT /sites/{id}/credentials |
admin | 按 kind 判别:password{username,password,totpSecret?,saveCookieOnly?,userAgent?} / cookie{cookie,userAgent?} / api_key{apiKey} / passkey{passkey},都带 label(缺省 default)。旧格式 {authKind, credential, username?} 仍接受。明文进 secret 表,响应只有 secretRef |
POST /sites/{id}/refresh-login |
admin | body {accountId?, wait?}。缺省异步入队 refreshSiteAccount 返回 {taskId, result:null}(幂等键按 10 s 取整);wait:true 同步执行(≤30 s)返回 {taskId:null, result: SiteLoginResult} |
POST /sites/{id}/health-check |
admin | 同步(≤20 s,走站点限流);返回 SiteHealthResult,写 site.last_health、last_health_at、health_status |
GET /sites/{id}/user-data?accountId&days=30 |
read | site_user_data 按天历史(1–400 天) |
DELETE /site-accounts/{id} |
admin | 删账号及其全部 secret |
POST/PATCH /site-accounts 的凭证部分已标弃用,新客户端只用 PUT /sites/{id}/credentials。该端点从 auth-settings 模块迁到 sources-identity。
账号状态(site_account.status)
| 值 | 界面 | 进入条件 |
|---|---|---|
unknown |
未验证 | 新建或更新凭证 |
active |
正常 | 刷新登录 / 校验成功 |
expired |
已失效 | Cookie 校验失败;账密登录后仍未检测到登录态;快照 / Feed 遇 auth_expired |
invalid |
已失效 | 密码错、账号封禁 / 未确认、API Key 无效 |
challenge |
需验证 | 验证码、二次验证(无 TOTP)、Cloudflare 挑战 |
网络失败只写 last_error(reason:"network"),状态不变。站点没有定义、Passkey 账号、不支持账密的站点(馒头等)返回 reason:"unsupported",状态不变。
失败时开待处理事项:kind source_unreachable,dedupeKey = site-login:<accountId>,subject site_account。选项 use_cookie(input 带 siteId / accountId,前端深链到凭证对话框)、retry(仅 challenge;选中后入队重新登录)、dismiss。更新凭证或登录成功时关闭该事项和 site-auth:<siteId>。
刷新登录流程
- 账密(仅
umt.kind = nexusphp且定义有umt.login):GET 登录页 → 页面有imagecode.php判验证码 → 取 takelogin 表单隐藏字段,填用户名 / 密码 /selectDefaults/extraFormData,有 TOTP 且表单有two_step_code/otp等字段时填当前 6 位码 → POST(不跟随 3xx)→ GETindex.php,loginSuccess(缺省a[href*="logout"])命中即成功。Cookie 存session_secret_ref(purposesite_cookie),之后适配器取 cookie 时优先用它。saveCookieOnly为真时,成功后凭证类型改为 cookie,密码与 TOTP secret 删除。 - Cookie / API Key:用
refreshSiteUserData取用户数据作为校验(不做 schema 回退,避免对站点连发 20 次请求);站点没有解析器时退回适配器healthCheck()。 - 成功后写
user_data、user_data_at,并 upsert 当日site_user_data(同日取最新)。账密登录成功但用户数据没取到时仍算登录成功,message说明原因。 - 快照 / Feed 遇登录失效(
onAuthError)时,若站点有账密账号,入队一次自动重登(同账号 10 分钟一个幂等槽)。
用户数据字段
SiteAccount.userData:schema, userId, username, userLevel, vip, joinAt, upload, download, ratio, bonus, seeding, seedingSize, leeching, leechingSize, messageUnread。ratio 为 null 表示站点没给且下载量为 0;bonus 为 0 时记 null(不显示);vip 由定义 umt.profile.vip_css(首页命中)或 umt.vipLevels(等级名包含匹配)判定,都没配置为 null。做种分页上限取 settings.sites.userDataMaxSeedingPages。
健康检测
healthCheck() 结果映射:ok → healthy,auth_* → failing,其余(限流、不可达、维护、解析失败)→ degraded;已关闭站点保持 disabled。loggedIn:ok 为 true,auth_* 为 false,其余 null。首页已登录时顺手更新 active 账号的 last_login_at。
删除
DELETE /sites/{id} 在一个事务里:资源 availability 改为 site_disabled(资源和下载任务都保留,source_release.site_id 外键为 restrict)、Feed 停用并解除账号关联、关闭该站待处理、删全部账号(site_user_data 随账号级联删除)、站点行软删除(deleted_at,enabled=false,key 改为 <key>-deleted-<时间>,同一定义可以重新登记)。事务提交后删除账号的全部 secret。
定时任务
| key | 名称 | 默认 | 行为 |
|---|---|---|---|
sites.user_data |
站点数据刷新 | 每 6 小时,一个周期后首跑 | 启用站点里 status=active 的非 passkey 账号逐个入队 refreshSiteAccount{mode:"userData"};会话失效的账密账号自动重登一次;只读对账模式跳过 |
sites.health_check |
站点健康检测 | 每小时,一个周期后首跑 | 启用站点(非 generic_rss)逐个同步检测;可在定时任务页关闭 |
注:定时任务 key 只允许小写(注册表校验),所以规格里的 sites.userData / sites.healthCheck 落为下划线形式。
站点默认(settings.sites)
| 字段 | 缺省 | 说明 |
|---|---|---|
defaultRateLimit |
{requestsPerMinute: 20, concurrency: 1} |
站点 rate_limit 为空时使用;不会比定义里的 umt.rateLimit 更激进(取两者较慢的间隔、较小的并发) |
defaultSnapshotIntervalSec |
3600 | 站点开了定时快照但未设周期时使用 |
userAgent |
固定桌面 Chrome UA | 账号未设 UA 时用于登录与用户数据 |
proxyUrl |
null | 站点 proxy=true 时,适配器、登录、用户数据请求都经此代理(Deno.createHttpClient) |
userDataMaxSeedingPages |
10 | 用户数据做种分页上限 |
运行时每 30 s 最多重读一次设置;设置变化会让站点适配器按新签名重建。
待核验
- takelogin 表单字段、二次验证字段名、错误文本全部只对
@umt/testkitmock 站点验证过,未连接真实站点。 - 失败文本归类是关键词匹配(
classifyLoginError),真实站点的措辞可能落到unknown(状态记为 expired,原文截 200 字写进last_error)。 - Cloudflare 站点只识别并转待处理,不做浏览器登录。
- 馒头用户数据:
MTorrent解析器的做种列表首页按 GET 请求,真实 API 可能要求 POST。