mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): add bounded legacy panel cron adapter
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
# QingLong 3.0 Architecture RFC
|
||||
|
||||
- D-427/ADR-0529(源码候选,等待双架构阶段实物):开始把现有 2.x 面板从“不能零改直连”推进为受控页面复用,而不是把旧 Express Service 或数据库写 authority 搬回 3.0。第一切片只在既有 `@qinglong/local-api/panel-compatibility` 子域开放认证后的 `GET /api/crons`:正式 Bearer credential、`task.read` Policy、durable audit、credential reconfirm 后,按 Edge 64/Standalone 256 行上限读取 `qinglong/cron@v1` Trigger 前缀,并以 Trigger 固定的 `taskId/revision/contentDigest` 读取 pinned Task revision。每个 Trigger 投影成一条旧 Cron 包络,字符串 `triggerId` 保持稳定身份,schedule/timezone/misfire 来自规范化 Trigger;command 只返回 `ql3:<kind>:<taskId>@<revision>` 描述符,不暴露 argv、环境、Secret、label、mutation 或 digest。缺失 pinned Task、identity 漂移、未知 Trigger schema、异常页和超预算均整体失败关闭。首切片只接受旧页面初始空 search/filter 与有界 page/size;搜索、排序、View query 和全部写操作明确拒绝。它不新增 package、dependency、migration、连接、listener、timer 或 watcher;headless 与 Cluster 路径不变,也不提供 2.x login/JWT、静态面板、WebSocket、Cron View、Subscription、Script、Env 或 Run/Log 兼容。本地真实 SQLite/credential/Policy/HTTP 集成、Local API 83/83、18-package clean build/test 和 Edge/Standalone Application API 资源门已通过;仍须完成双架构 artifact 门,并继续实现 capability shell 与 Run/Log read,才能把改造后的现有页面称为阶段性可用。
|
||||
|
||||
- D-426c3/ADR-0528(源码候选,等待 exact 双架构阶段实物):在 D-426c2 的三阶段回滚链上保留 `apply-rollback`,新增显式 `apply-plan` 与 `complete`。完成型 CI 使用完整 2.x schema、空 Apps/Auths、无未知插件表的独立 fixture;外部 completion review 对 blocked 事实直接拒绝,对 Legacy/Target Run History 分别授权 `retain_both/retain_target`,对 Secret/Config 只授权 `manual_external`。`apply-plan` 消费互相独立的 Automation/review decision,依次完成 Automation apply/verify、双侧终态 Run History preservation/verify、Secret/Config plan/verify 和 decision prepare,停在 `secret_config_decision_required`;`complete` 再消费独立 Secret/Config decision,完成 decision/apply/verify,并以 completion v3 同时绑定三类 adapter,要求 `reconciliation_completed`、`adapterCount=3`。Run History 必须位于 Automation apply 与会推进 head 的 Secret/Config plan 之间。completion 后 target/Legacy 仍 stopped,两个 restart 都是 `not_authorized`。针对 readiness 要求 Apps/Auths 必须存在而旧诊断又无条件阻塞 identity 域的矛盾,仅把“Legacy 已知 identity 表全部为空”收窄为 `informational/catalog_evidence`;任一身份行、未知表、目标 identity 或异常仍 fail-closed。Trial Kit/verification/auditor 升为 `@v11/@v9/@v8`、manifest schemaVersion 12,Local milestone 升为 `@v7`/schemaVersion 7,并新增 required `legacyUpgradeReconciliationCompletion=passed`;CI 仍独立保留 Automation apply→rollback 实证,completion 不覆盖 recovery 证据。本切片不增加 package、production dependency、daemon/listener/timer/watcher/连接或稳态资源,默认低配 headless 与 Cluster authority 边界不变。
|
||||
|
||||
- D-426c2/ADR-0527(exact headless 双架构阶段实物已交付):downloadable Trial Kit 新增三阶段 canonical `reconciliation-rehearsal.sh`。`prepare` 从 D-426c1 stopped capture 建立 bounded plan/diagnostics/strong-auth review prepare 后停在 `operator_decision_required`;`review` 只消费 owner-private 外部 NDJSON,完成 authorization commit/verify、application plan 与 Automation plan 后停在 `automation_decision_required`;`apply-rollback` 再消费独立外部 Automation row NDJSON,只应用一个无冲突 Automation 行、验证正式 Task/Trigger 投影并显式回滚,终态固定 `reconciliation_automation_rolled_back`。交付脚本永不生成 decision;CI fixture 不进入 bundle,Legacy Run History 固定 `manual_external`。decision 父目录必须 current-UID `0700`、恰好一个 canonical file、与所有 authority roots 不重叠,并整体只读挂载;60 秒 authorization 不得晚于 strong principal,authentication database 必须在异步 confirm 完成后才关闭。target SQLite 必须位于 deployment root 下且避开 reconciliation sibling roots;apply/rollback 额外只读挂载 exact Legacy root,运行在 128 MiB/0.5 CPU/32 PID、无网络、只读 rootfs、drop-all/no-new-privileges 的短生命周期 Operator 中。带时间 command 和成功 result 支持中断后的 exact replay,不得静默重写。Trial Kit/verification/auditor 升为 `@v10/@v8/@v7`、manifest schemaVersion 11,milestone 升为 `@v6`/schemaVersion 6,并增加 required `legacyUpgradeReconciliationAutomationRollback=passed` 与双架构 reconciliation script digest。最终本地 arm64 exact bundle 使用 Application `sha256:eec404d24b5c101871e000caac902d3866e5fe3f0e6dcef31366cb526ef32f80`、无源码覆盖 Operator `sha256:10f75f12d185e5796dcc4a230b6684c074bd9a6e6156ee1110fff1c2d8dd3390`,offline audit 返回 `compatible=true`;全新 2.x fixture 贯通 readiness→stage→cutover→capture→437 条外部 review decision→1 条 Automation decision→apply/verify→rollback/verify,实际采用 1 Task/1 Trigger 后恢复应用前快照,未尝试 Secret/Config、Run History、completion 或任一重启。Docker Desktop 的只读 bind mount UID 瞬时漂移仅通过同一 inspect 一次有界重试收敛,持续错误仍 fail-closed,不放宽 Linux owner proof。首次普通 CI run `33525269537` 在 x64 暴露既有兼容测试的 module-scope `TaskLimit` 异步 SQLite 初始化与临时目录清理竞态,未以重跑掩盖;隔离测试副作用的提交 `c8d9eed95d402aae642e81e60fce336670ac06a0` 后,普通主 CI [run 33526720941](https://github.com/whyour/qinglong/actions/runs/33526720941) 为 41 success/3 expected skip/0 fail,Kubernetes [run 33526721040](https://github.com/whyour/qinglong/actions/runs/33526721040) 成功,显式 Local headless [run 33528370769](https://github.com/whyour/qinglong/actions/runs/33528370769) 为 42 success/2 scope skip/0 fail。该 run 交付 amd64/arm64/milestone artifact `9809046864`/`9809000920`/`9809293769`,大小 `226266116`/`221665796`/`6734` bytes,GitHub digest 为 `sha256:bffabf76c9d7b599c5ac65dd4ff7aa8f65af6a3167a68afb497a9c8967f34c36`、`sha256:8b74e5e9e1437962185fb87e7b0208bf157d063af28dfb1676c59b9785b937b8`、`sha256:a69de90a07bc36a397b08bb567739a001ac2f830fed73860255db7de175b3527`,保留至 2026-10-01;milestone v6 下载后 auditor 返回 `compatible=true`,并绑定双架构 reconciliation script digest。该切片不新增 package、production dependency、daemon/listener/timer/watcher/连接或稳态资源;低配路由设备默认 headless 不变,Cluster 不复用 Local SQLite/POSIX/Docker authority,2.x 老面板仍需独立 API/认证/领域 adapter,不能零改直连。
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# ADR-0529:有界只读 Local 旧面板 Cron Adapter
|
||||
|
||||
- 状态:Proposed(源码候选,尚未进入双架构阶段实物)
|
||||
- 日期:2026-09-02
|
||||
- 关联 RFC:QL-RFC-0001 D-427、D-423、D-424、D-426c3
|
||||
|
||||
## 背景
|
||||
|
||||
QingLong 2.x 面板以 `/api/crons`、数值型 Cron 行和 `{code,data}` 包络读取定时任务;QingLong 3.0 Local API 则以 Project-scoped Task、immutable Trigger revision 和 `/api/v3` 为权威。当前 Console Alpha 已经可以操作 Task、Trigger、Run、日志和 Secret,但它携带的是小型离线 Console,不包含 33 MiB 的 2.x 静态面板,也不能把 2.x JWT、明文 Env、整数 ID 或旧 Service 直接当作 3.0 authority。
|
||||
|
||||
直接让旧面板写入 3.0 SQLite 会绕过 Project Policy、credential reconfirm、durable audit、revision/content digest fence,并重新耦合已经分离的 Task 与 Trigger。另一方面,要求所有部署者立即迁移到新 Console 会阻断现有页面的渐进复用。因此需要一个显式、窄面、可逐步扩展的 Adapter,而不是恢复完整旧后端。
|
||||
|
||||
## 决策
|
||||
|
||||
第一切片在既有 `@qinglong/local-api` 内新增 `panel-compatibility/` 子域,不新增 workspace package、依赖、数据库表、连接、listener、timer、watcher 或后台进程。
|
||||
|
||||
### HTTP 与权限边界
|
||||
|
||||
- 仅新增 `GET /api/crons`;没有 POST、PUT、DELETE、login、session、WebSocket 或静态面板分发。
|
||||
- 请求仍走正式 Local API Bearer credential、`task.read` Project Policy、durable security audit 和 credential reconfirm;审计 operation 固定为 `panel.cron.list`。
|
||||
- 第一切片固定投影 `default` Project。多 Project 选择必须在后续 capability/session 设计中显式增加,不能从未受信 Header、Cookie 或查询参数猜测。
|
||||
- 默认 headless Application 不包含 `@qinglong/local-api`,因此没有新增端口或稳态开销。Cluster 不复用本 Adapter,后续使用独立 Cluster Panel Gateway。
|
||||
|
||||
### 有界查询
|
||||
|
||||
- 接受旧页面初始读取所需的 `page`、`size`、空 `searchValue`、空 `filters={}` 和 Axios cache-buster `t`。
|
||||
- 非空搜索、排序、View query 或其他字段暂时返回 `400 invalid_panel_cron_list_query`,不能静默忽略并给出错误结果。
|
||||
- `size` 最大 64;`page * size` 在 Edge 最大 64、Standalone 最大 256。Adapter 用同一上限向 Trigger source 做一次有界 keyset 前缀读取,再截取所需页。
|
||||
- `total` 是当前已观察前缀加一个 `truncated` 继续标记;它足以让旧分页逐页推进,但不执行无界 COUNT 或全表扫描。
|
||||
|
||||
### 领域映射
|
||||
|
||||
- 每个 `qinglong/cron@v1` Trigger 投影为一条旧面板 Cron 行,稳定 `id` 使用 `triggerId` 字符串,不构造有碰撞风险的伪数值 ID。
|
||||
- Adapter 按 Trigger 固定的 `taskId + taskRevision + taskContentDigest` 读取 pinned Task revision,并复算 Trigger/Task record 与 cron semantic;缺失、漂移、未知 Trigger schema 或异常页整体返回 503。
|
||||
- `schedule`、timezone、misfire policy 来自已规范化 Trigger;名称、启停状态来自 pinned Task 与 Trigger 的合成结果。
|
||||
- `command` 只返回 `ql3:<kind>:<taskId>@<revision>` 描述符,不返回 Task spec、argv、环境、Secret、label、mutation ID 或 content digest。
|
||||
- 返回行附带只读 `ql3` identity,明确 Project、Task/Trigger revision 与 `readOnly=true`;旧页面当前未知字段会忽略它,后续改造版面板可据此关闭写按钮。
|
||||
|
||||
## 明确不做
|
||||
|
||||
本 ADR 不声明现有 2.x 面板可以零修改登录或完整运行。尤其不允许:
|
||||
|
||||
- 把 `ql3c_` credential 放入 2.x 登录密码字段或长期存入 Local Storage;
|
||||
- 复用 2.x Auths/Users/JWT 作为 3.0 Identity/Policy;
|
||||
- 猜测 `/api/crons` 写操作、整数 ID、Cron View、Subscription 或 Script 文件语义;
|
||||
- 回显 Secret/Env 明文,或将 Task 与 Trigger 的独立 revision 压回一个可直接覆盖的旧对象;
|
||||
- 为兼容页面扩大 loopback、CSP、response byte、并发或低配资源预算。
|
||||
|
||||
## 验证与后续门禁
|
||||
|
||||
源码候选必须通过:
|
||||
|
||||
1. Adapter 单测:分页、禁用合成、pinned identity、未知 schema、预算和 storage failure;
|
||||
2. HTTP 契约:编码的 `{}` 查询、正式 operation 解析、拒绝非空搜索且不进入 Admission;
|
||||
3. Admission 契约:authenticate → `task.read` → audit → confirm → route;
|
||||
4. 真实 SQLite 集成:正式 credential、Policy、Task/Trigger revision 和 durable audit,且响应不出现真实 argv;
|
||||
5. Local API 全包、18-package build/test、dependency/import、Console/Headless image 与双架构 artifact 门。
|
||||
|
||||
后续按 `health/system/user capability → Run/Log read → 显式写操作` 推进。只有改造版面板取消 Local Storage credential、按 capability 隐藏未实现页面,并完成真实浏览器 journey 后,才能声明“现有面板页面可复用”;完整 2.x 零改兼容不作为 3.0 目标。
|
||||
@@ -532,6 +532,7 @@
|
||||
| [ADR-0526](./ADR-0526-exact-post-write-reconciliation-capture.md) | Exact 写后 Reconciliation Capture | Accepted(同源 exact Console 双架构阶段实物已交付) |
|
||||
| [ADR-0527](./ADR-0527-reviewed-automation-reconciliation-application-and-rollback.md) | 受审核 Automation Reconciliation 应用与显式回滚 | Accepted(exact headless 双架构阶段实物已交付) |
|
||||
| [ADR-0528](./ADR-0528-cross-domain-reconciliation-completion-rehearsal.md) | 跨域 Reconciliation Completion 演练 | Accepted(D-426c3 源码候选;双架构阶段实物待 gate) |
|
||||
| [ADR-0529](./ADR-0529-bounded-read-only-local-panel-cron-adapter.md) | 有界只读 Local 旧面板 Cron Adapter | Proposed(D-427 源码候选;双架构阶段实物待 gate) |
|
||||
|
||||
## 规则
|
||||
|
||||
|
||||
Reference in New Issue
Block a user