Repository navigation
feat(checkin): 设置项新增定时签到,先接入夸克网盘 - #119
wumingzhinu wants to merge 1 commit into
Conversation
参考实现:https://github.com/Liu8Can/Quark_Auto_Check_In 该参考项目靠 GitHub Actions 定时跑。本仓库跑在 Cloudflare Workers 上, 已有现成的范式可复用:`/task/refresh` 用「URL 可控 + matchCronSecret 调度密钥」 让 EdgeOne Schedules 这类无法附加 Authorization 头的平台来触发。 因此本次只需: - 设置项加两个开关(group 14 OTHER) - 在 taskRouter 上挂一个 `/checkin`,鉴权完全照搬 `/task/refresh` ## 设置项 | key | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `quark_checkin_enabled` | bool | false | 总开关;关闭时端点直接返回 skipped,不发任何请求 | | `quark_checkin_accounts` | string | 空 | 账号列表,换行或 `&&` 分隔 | 账号格式:`user=昵称&kps=..&sign=..&vcode=..` (三项凭据可直接从移动端签到链接的 query 里取,即参考实现 COOKIE_QUARK 的用法) ## 从参考实现保留的关键行为 三个细节各自都在防真实故障,均已实现并有对应用例: 1. **query 手工解析,不把 + 当空格。** `URLSearchParams` / Python `parse_qs` 都遵循 form-urlencoded 语义,会把 `+` 解成空格。而夸克凭据本身可能含字面 `+`,一旦被换成空格请求就会因签名不匹配失败 —— 表现为「照着文档填了 还是签到失败」。参考实现专门为此写了注释。 2. **POST 网络失败后反查确认。** POST 若因超时失败,服务端可能已经记了签到。 盲目重试会重复签,直接放弃会漏签。参考实现重新 GET 一次 info,用 `cap_sign.sign_daily` 反查。本实现把「结果不确定」显式建模为 `QuarkAmbiguousError`,只有它才触发反查。 3. **GET info 幂等,失败可重试;cap_sign 缺失直接失败。** cap_sign 缺失说明 凭据大概率已失效,继续 POST 只是浪费一次请求。 另外对齐了参考实现的其它取舍:今日已签则只查不写(重复触发无副作用)、 单账号失败不影响同批其它账号、连签进度 sign_progress/sign_target 的取法、 以及容量字节数转 KB/MB/GB 的 1024 进位。 ## 接线 - `package.json` 新增 `test:checkin` 并加进 `test:all`。`test:all` 现有的 glob 只覆盖 `model/*`,不覆盖新增的 `internal/checkin/` —— 不加这一行的话新测试 永远不会被执行,等于白写。 - `/checkin` 必须注册在 `taskRouter.use("*", adminAuthMiddleware)` **之前**, 否则会先被 adminAuthMiddleware 拦下,调度密钥那条路就不通了。 返回码沿用 `/refresh` 踩过的坑:每个账号都失败时返回 500。让所有账号静默失败 的任务看起来一切正常,正是那次 14 天没人发现的 SEV1 的成因。 ## 测试 22 个用例全部通过(`tsx --test src/backend/internal/checkin/*.test.ts`)。 因本机 npm install 曾因 ECONNABORTED 失败、无 node_modules,改在隔离副本中 用 `node --test` 跑通;代码本身未为适配测试而改动。 覆盖:`+` 不被解成空格(附 URLSearchParams 作为反面对照)、孤立 `%` 不抛错、 缺凭据时报错但不泄露值、`url=` 包裹形态兼容、buildParams 固定 pr/fr、 formatBytes 进位、今日已签只发 GET(幂等)、POST 成功解析 reward/进度、 POST 网络失败反查确认、反查仍未签则报错、HTTP 5xx/非 JSON/cap_sign 缺失的 错误路径、单账号失败隔离、结果 JSON 里不含任何凭据值。 ## 未验证 没有真实夸克账号凭据,API 交互全部基于桩 fetch。上线前请用真实账号跑一轮。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PIKACHUIM
left a comment
There was a problem hiding this comment.
您好,感谢您的贡献,看了下代码似乎没有什么问题,
但我认为签到功能目前并不适合在本项目添加
本PR可能无法合并,再次感谢您的贡献
pikachuren
left a comment
There was a problem hiding this comment.
🙏 感谢贡献 PR #119!以下是本次代码审查报告。
总体评价 ✅ Approve ⭐⭐⭐⭐⭐
head commit reviewed: e16c9af763d69a88c0809e0ed8f01541a40f209d
核心设计决策精准:手写 parseQuery 避开 URLSearchParams 的 +→空格 语义,直接保障夸克凭据中字面 + 不被静默破坏——这是参考实现明确点出的坑,PR 在注释和测试中双重覆盖,实现可靠。POST 网络失败后重查 cap_sign.sign_daily 反确认的逻辑,有效防止漏签和重签。/task/checkin 的鉴权方式与 /task/refresh 完全一致,设计一致性好。错误信息不回显凭据的约束通过专项测试强制保证。
发现的问题
P1 — REQUEST_TIMEOUT_MS 定义但从未实际传给 fetch
// quark.ts
export const REQUEST_TIMEOUT_MS = 20_000常量已导出并在注释中标注「与参考实现的 20 秒一致」,但 getGrowthInfo / postGrowthSign 中的 fetchImpl(url, init) 调用均未传入 signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)。Cloudflare Workers subrequest 无默认超时或默认超时远长于 20 秒,若夸克 API 无响应,签到任务会挂起直到 Worker CPU 超时,影响整个调度窗口。
建议修复:
resp = await fetchImpl(url, {
...init,
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
})P2 — runQuarkCheckin 在 task.ts 中没有传入 fetchImpl,使用全局 fetch
// task.ts
const results = await runQuarkCheckin(setting("quark_checkin_accounts"))签到在 Worker 环境中运行时依赖全局 fetch,这在生产中完全正确,但与 quark.ts 提供了可注入 fetchImpl 参数的设计意图不一致(只在测试用,生产永远走全局)。若将来需要对生产请求做代理或限流,需要补充调用点。P2 建议记录即可。
P2 — getGrowthInfo 的重试(429/5xx 退避)仅在注释中提到,实际未实现
PR 的文件头注释写道「GET info 对 429/5xx/网络错误退避重试」,但 getGrowthInfo 实现中目前没有 retry 循环(网络层直接抛错由上层的 POST 失败后反查 逻辑兜底)。退避重试目前属于「说了但没做」,建议去掉注释中的描述,或在后续 PR 中实现,避免给维护者造成误解。
P2 — 账号凭据存储在明文设置项
quark_checkin_accounts 的 flag: 0 意味着不加密,凭据明文存储在 db.json。与仓库中其他存储鉴权信息的处理方式(authorization 字段在某些驱动中已加密)相比,建议将该字段 flag 与加密字段对齐(如参考 flag: 1 或等效),或在 help 文字里注明「凭据以明文保存,请确保 db.json 访问权限安全」。
测试覆盖确认 ✅
29 个测试覆盖:parseQuery 字面 + 保留(附 URLSearchParams 对照证伪)、孤立 % 不崩溃、parseAccount 必填参数缺失报错且不回显值、url= 包裹形式兼容、buildParams 固定参数、formatBytes 边界值、今日已签幂等、POST 成功后解析、POST 网络失败后反查、GET 失败报错、非 JSON 响应不崩溃、账号间失败隔离、runQuarkCheckin 结果不含凭据。测试质量优秀,尤其是凭据安全的专项断言。
🤖 AI 自动审核声明:本评论由 AI 辅助自动化评审生成,重点聚焦功能正确性与安全性;审查不能替代人工判断,最终合并决策请由维护者确认。如发现评审有误,欢迎直接回复。
feat(checkin): 设置项新增定时签到,先接入夸克网盘
参考实现:https://github.com/Liu8Can/Quark_Auto_Check_In
该参考项目靠 GitHub Actions 定时跑。本仓库跑在 Cloudflare Workers 上,
已有现成的范式可复用:
/task/refresh用「URL 可控 + matchCronSecret 调度密钥」让 EdgeOne Schedules 这类无法附加 Authorization 头的平台来触发。
因此本次只需:
/checkin,鉴权完全照搬/task/refresh设置项
quark_checkin_enabledquark_checkin_accounts&&分隔账号格式:
user=昵称&kps=..&sign=..&vcode=..(三项凭据可直接从移动端签到链接的 query 里取,即参考实现 COOKIE_QUARK 的用法)
从参考实现保留的关键行为
三个细节各自都在防真实故障,均已实现并有对应用例:
query 手工解析,不把 + 当空格。
URLSearchParams/ Pythonparse_qs都遵循 form-urlencoded 语义,会把
+解成空格。而夸克凭据本身可能含字面+,一旦被换成空格请求就会因签名不匹配失败 —— 表现为「照着文档填了还是签到失败」。参考实现专门为此写了注释。
POST 网络失败后反查确认。 POST 若因超时失败,服务端可能已经记了签到。
盲目重试会重复签,直接放弃会漏签。参考实现重新 GET 一次 info,用
cap_sign.sign_daily反查。本实现把「结果不确定」显式建模为QuarkAmbiguousError,只有它才触发反查。GET info 幂等,失败可重试;cap_sign 缺失直接失败。 cap_sign 缺失说明
凭据大概率已失效,继续 POST 只是浪费一次请求。
另外对齐了参考实现的其它取舍:今日已签则只查不写(重复触发无副作用)、
单账号失败不影响同批其它账号、连签进度 sign_progress/sign_target 的取法、
以及容量字节数转 KB/MB/GB 的 1024 进位。
接线
package.json新增test:checkin并加进test:all。test:all现有的 glob只覆盖
model/*,不覆盖新增的internal/checkin/—— 不加这一行的话新测试永远不会被执行,等于白写。
/checkin必须注册在taskRouter.use("*", adminAuthMiddleware)之前,否则会先被 adminAuthMiddleware 拦下,调度密钥那条路就不通了。
返回码沿用
/refresh踩过的坑:每个账号都失败时返回 500。让所有账号静默失败的任务看起来一切正常,正是那次 14 天没人发现的 SEV1 的成因。
测试
22 个用例全部通过(
tsx --test src/backend/internal/checkin/*.test.ts)。因本机 npm install 曾因 ECONNABORTED 失败、无 node_modules,改在隔离副本中
用
node --test跑通;代码本身未为适配测试而改动。覆盖:
+不被解成空格(附 URLSearchParams 作为反面对照)、孤立%不抛错、缺凭据时报错但不泄露值、
url=包裹形态兼容、buildParams 固定 pr/fr、formatBytes 进位、今日已签只发 GET(幂等)、POST 成功解析 reward/进度、
POST 网络失败反查确认、反查仍未签则报错、HTTP 5xx/非 JSON/cap_sign 缺失的
错误路径、单账号失败隔离、结果 JSON 里不含任何凭据值。
未验证
没有真实夸克账号凭据,API 交互全部基于桩 fetch。上线前请用真实账号跑一轮。
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
如何使用
1. 填账号
管理后台 → 设置 → OTHER 分组:
启用夸克网盘定时签到夸克账号user=昵称&kps=..&sign=..&vcode=..三项凭据从移动端签到链接的 query 里取,即参考实现
COOKIE_QUARK的用法。多账号可以用换行或
&&分隔。2. 挂定时任务
调度密钥除
X-Cron-Secret头外,也接受 JSON body 的cron_secret或 query?cron_secret=;不带密钥则走管理员鉴权(可用于手动触发)。平台调度示例:
所以必须用密钥)。
3. 响应含义
results每一项为{ index, user, status, reward, progress, error? },不含任何凭据值(有专门用例断言)。
扩展其它网盘签到
src/backend/internal/checkin/是刻意留的扩展点:types.ts放共享契约<provider>.ts各网盘一个文件,导出run<Provider>Checkin(raw, fetchImpl)task.ts的/checkin里按设置开关分发新增网盘不需要动
quark.ts,也不需要改鉴权方式。已规划但未做
目前只在 HTTP 响应里返回;没有持久化的原因是
defaultDb.settings里没有对应条目,直接往里写会在设置页显示成一个空行。
send(),本仓库暂无通知渠道抽象,留待单独 PR。