Commit Graph

103 Commits

Author SHA1 Message Date
ryan 30bbe965bf fix(task): execute dispatched jobs and persist run records
Asynq func handlers now go through ProcessTask so admin execution
rows leave pending. The in-process worker resolves admin type
identifiers and writes the same w_task_executions table. Remove
the no-op admin system_cleanup that shadowed the upload handler.
2026-09-02 16:11:45 +08:00
ryan 33f28ad671 fix: sql 2026-09-02 15:37:06 +08:00
ryan c22ca408d4 merge: merge branch 'feat/cordis-router-raw-routes' into main 2026-08-30 17:57:38 +08:00
ryan 374289bfda fix(api): align upload permissions and mount robots and swagger routes 2026-08-30 17:53:42 +08:00
ryan 7c5c196ede feat(response): add AbortNotFoundIfMissing and AbortBadRequestOnError
Lift the handler helpers that map a non-nil error to Abort* so plugins do not each reimplement record-not-found vs bad-request branching.
2026-08-30 17:45:06 +08:00
ryan 7af6fee5d5 fix(core): apply earlier pending plugins before later ones
Rescan the Use() list after each Load so a consumer registered before its provider still runs before later consumers that became ready in the same pass.
2026-08-30 16:58:20 +08:00
ryan 6553ac7782 fix(system): expose only GET /api/healthz
Remove /healthz and /api/health so the process advertises a single probe at /api/healthz with {status: ok}.
2026-08-30 16:41:07 +08:00
ryan 12b4c3e54c fix(cap): keep only /api/v1/cap routes
Drop the unversioned /api/cap aliases so Challenge and Redeem exist only under /api/v1/cap.
2026-08-30 16:39:40 +08:00
ryan 56c650c5b5 docs(swagger): restore gold @Router comments on platform APIs 2026-08-30 14:20:21 +08:00
ryan a617457a3c feat(core): add WithMigrationBaseline hook before goose Up 2026-08-30 11:41:00 +08:00
ryan 4ce7110e23 feat(platform): add GET /api/health and GET /api/v1/user/self 2026-08-30 11:32:03 +08:00
ryan 9a5c2fa643 feat(message_gateway): expose PushRegistry contract 2026-08-30 11:23:01 +08:00
ryan 177d771acf feat(upload): mount existing my/update/download routes on user API 2026-08-30 11:17:11 +08:00
ryan 6f25618e83 fix(config): decode *bool so trailing-slash redirect binds from yaml/env 2026-08-30 11:10:36 +08:00
ryan b4c4b0a27e feat(http): make trailing-slash redirect configurable 2026-08-30 11:07:22 +08:00
ryan b8ad06f49f feat(system): allow PublicConfigProvider to replace public config payload 2026-08-30 11:03:17 +08:00
ryan 254533c013 feat(cap): expose CaptchaService and unversioned /api/cap routes 2026-08-30 10:53:43 +08:00
ryan be79eb4eb7 feat(core): add HandleRaw and BasePath for trailing-slash routes 2026-08-30 10:44:14 +08:00
ryan f3d85d51fb fix(pkg/cache/disk): LRU 节点类型断言失败时降级而非 panic
items 与 evictList 的不变量一旦被破坏,读、写、删除与淘汰路径上的裸类型断言
会直接崩掉进程。改为带 ok 检查:Get 退化为缓存未命中,Set 报告污染条目,
deleteUnlocked 跳过容量回退,evict 移除坏节点后继续。

新增 cache_corruption_test.go 锁住该行为:去掉守卫后用例会以
「interface conversion: interface {} is string, not *disk.cacheItem」失败,
加上守卫后 4 个用例全通过。

验证:go build 通过;go test ./pkg/cache/disk/ 全绿(含原有 5 个用例);
golangci-lint 0 issues;check_cordis_architecture.sh 0 violations。
2026-08-30 01:00:02 +08:00
ryan 8ff017b5e8 feat(pkg/util): 补齐版本比较、网络与格式化通用助手
下游 OpenFlare 的边缘守护进程与发布流程需要这些与业务无关的纯函数,
按上游/下游归属规约回流到平台层,避免下游在上游目录里长期携带本地文件:

- version / version_compare:CompareVersions、ParseVersionInfo(版本区间比较)
- network:GetIP、IsPrivateIPv4
- format / value / string / slice:Bytes2Size、Seconds2Time、Interface2String、
  TrimStringFields、UniqueAndCleanStringSlice 与 IdentifiableTimeRecord

验证:go build 通过;go test ./... exit 0(48 包 ok);
check_cordis_architecture.sh 0 violations;golangci-lint 0 issues;gofmt 干净。
2026-08-30 00:36:13 +08:00
ryan bad6fa785d refactor(core): Handle 与 HandleRaw 共用 addRoute
消除注册逻辑重复,并修正 HandleRaw 里 append(g.registry.middlewares, ...)
复用底层数组的隐患:中间件快照统一在 addRoute 内构造为新切片。

验证:go build 通过;go test ./core/... 全绿;golangci-lint ./core/... 0 issues。
2026-08-30 00:26:01 +08:00
ryan cb339ab0dc feat(core): RouterExtension 增加 HandleRaw 与 BasePath
Handle 经 cleanPath 归一化会剥掉尾部斜杠,插件无法同时声明 /resource 与
/resource/ 两条路由;部署关闭 gin 的 RedirectTrailingSlash 时,缺失的那条
直接 404。下游 OpenFlare 有 20 个历史列表接口依赖该行为。

- HandleRaw:与组前缀拼接但保留尾部斜杠,分配独立路由 ID;
- BasePath:返回组的绝对前缀(根注册表为空串);
- 作用域包装器为 HandleRaw 同样登记 OnDispose 反注册。

验证:go build 通过;go test ./... exit 0(48 包 ok);
check_cordis_architecture.sh 0 violations;gofmt 干净。
2026-08-30 00:22:56 +08:00
ryan d7c851bc47 autoresearch iter 35: BUGFIX a failed whitelist read is no longer cached as an admin decision 2026-08-29 19:29:25 +08:00
ryan b22f8633ba autoresearch iter 34: BUGFIX an unreadable SMTP config no longer looks like an unconfigured mailer 2026-08-29 19:19:17 +08:00
ryan 99fca9ee09 autoresearch iter 33: BUGFIX storage migration no longer migrates from a config it could not read 2026-08-29 19:12:58 +08:00
ryan f7a86d3608 autoresearch iter 32: PERF whitelist parses patterns once, 14 allocs/op to 1 2026-08-29 19:03:27 +08:00
ryan 9ea0e2bdff autoresearch iter 30: enforce user lookup column allow-list instead of trusting a comment 2026-08-29 18:49:37 +08:00
ryan 2ff0cb87c9 autoresearch iter 29: CORDIS contracts DTO must not carry a table name 2026-08-29 18:45:17 +08:00
ryan 50370971ec Revert "autoresearch iter 28: CORDIS gate contracts must not carry table names"
This reverts commit 2cb8d9a892.
2026-08-29 18:42:31 +08:00
ryan 2cb8d9a892 autoresearch iter 28: CORDIS gate contracts must not carry table names 2026-08-29 18:40:04 +08:00
ryan 31f3af61c5 autoresearch iter 27: drop one dead contextcheck suppression, document the other 2026-08-29 18:32:54 +08:00
ryan ea97b64407 fix(task): restore task metadata contract and type fields in task types api 2026-08-29 12:22:51 +08:00
ryan 49f9d1076f fix(admin): move w_task_executions and w_schedules migrations to admin plugin
- Include w_schedules and w_task_executions DDL in admin initial migrations for both SQLite and PostgreSQL dialects
- Remove driver-specific migration registrations from driver_asynq_worker and driver_asynq_cron
- Fix missing table error when running in Zero-Redis standalone mode without Asynq
- Update white paper table ownership mapping and ensure test cleanup
2026-08-29 11:58:56 +08:00
ryan e568b96388 fix(auth): add missing masked_token column to w_access_tokens table in migrations 2026-08-29 11:57:04 +08:00
ryan 7786f416f8 perf(auth): eliminate redundant password queries during user info retrieval 2026-08-29 11:54:46 +08:00
ryan 64fe1658d4 fix(user): clear need_change_password and invalidate cache on password change 2026-08-29 11:52:41 +08:00
ryan d3d7c783a9 fix(auth): synchronize need_change_password across login, user-info and repositories 2026-08-29 11:49:14 +08:00
ryan 107251891f fix(user): restore plaintext default password checking and warning mechanism 2026-08-29 11:46:39 +08:00
ryan 86c750077f fix(user): seed default administrator account in initial migration 2026-08-29 11:42:43 +08:00
ryan e0f2309520 feat(router): add whitelist mechanism for http driver and auth plugin
- implement route whitelist registration and wildcard matching in RouterExtension
- add cookie store session fallback when Redis is disabled in driver_http
- actively register public auth endpoints to whitelist in auth plugin
- update user handlers to persist session and clear cookie on logout
- document router whitelist mechanism in AGENTS.md and new-api skill
2026-08-29 11:39:13 +08:00
Ryan 53ae3007d0 fix(cordis): fail-closed auth guards for user/message_gateway/admin (#1)
* autoresearch iter 23: fail-closed auth guarding for user/message_gateway

Both plugins resolve contracts.AuthService in Apply to build their route
middleware, but declared only DBService in Inject(). The kernel gates a
plugin's Apply solely on declared deps, and cmd/app.go registers user
before auth, so user mounted first, core.Inject failed, and loginMW
silently degraded to a pass-through closure — leaving /api/v1/user
change-password, profile and access-tokens unguarded. message_gateway
was saved only by its later list position.

Declare AuthService in Inject() for both, and pin the property with a
reconcile-level test that mirrors production registration order and
asserts the real auth middleware reaches the route table.

* autoresearch iter 24: make auth middleware fallbacks fail closed

user, message_gateway and admin each fell back to a c.Next() closure when
contracts.AuthService could not be resolved, so a route would be served as
if authenticated. For admin this is reachable at runtime: OnDispose calls
service.ResetServices(), which nils the global the per-request guard reads,
so requests still in flight during dispose bypass authorization entirely.

Add ginutil.AuthUnavailable() and bind every fallback to it, with a test
that drives each plugin's registered guard without an auth service present
and asserts the request is aborted rather than passed through.

* chore(autoresearch): log iter 23 (fail-open auth ordering, proven)

* autoresearch iter 24 follow-up: let staticcheck infer the auth guard type

* docs(autoresearch): log iters 24-25 and lessons 9-11 (declared-dep bug class, gate discipline)
2026-08-29 11:21:04 +08:00
ryan b624de9620 feat(cmd): log actual plugin migration version instead of up-to-date 2026-08-29 11:11:51 +08:00
ryan 4d65e57f9a merge: feat(core): implement cordis configuration extension and migrate all plugins 2026-08-29 10:54:28 +08:00
ryan ed8491addf feat(core): implement cordis configuration extension and migrate all plugins 2026-08-29 10:53:53 +08:00
ryan 8bd59511a8 refactor(config): make legacy loader reentrant and add engine parity test
load(configPath, testMode) 用私有 viper 实例替代包级全局,使同一输入可反复
求值;对拍测试以入库的 config.example.yaml 为必备基准(本地 config.yaml
存在时加测),在四类 env 场景下逐 key 比对新引擎与旧装载器,并以变异检验
确认其能发现漂移。
2026-08-29 10:15:32 +08:00
ryan 696809899e feat(infra): add viper backed configuration source adapter
实现 core.ConfigSource:按 CONFIG_PATH 或向上查找定位 config.yaml,缺文件
降级为纯环境变量来源,坏文件返回错误而非 log.Fatalf,并把 key 命中与"设为
零值"区分开来。viper 依赖被隔离在此包,内核保持零具体运行时依赖。
2026-08-29 10:06:46 +08:00
ryan 4acb529b85 feat(core): add config resolution barrier and plugin gating to App
App 新增 WithConfigSource / WithConfigDecl / Prepare / ShutdownTimeout /
SetShutdownTimeout;Use 收集门禁插件的提前声明,调和循环内求值门禁并跳过
被关闭的插件,使组合根无需再跨插件读配置选实现。未注入配置源的 App 保持
原行为,被门禁但无配置源则 fail fast 点名原因。
2026-08-29 10:03:56 +08:00
ryan ad8384182c autoresearch iter 22: delete lint suppressions that suppress nothing
24 of the 96 nolint directives were dead: they covered findings that no
longer exist. A stale suppression is not inert — it silently claims any
future finding for that linter in that scope, so a real problem raised
there would vanish without anyone noticing. Explanatory prose was kept as
ordinary comments.

Two directives proved load-bearing under the project gate even though
nolintlint reported them unused, and removing them exposed verified
contextcheck false positives: App.Run does forward a sigCtx derived from
the caller's context to Start, and the migration lock renewal must keep
its own deadline because the task context may already be canceled. Both
were restored, narrowed to the live linter, and given the reason the
originals lacked.
2026-08-29 09:54:33 +08:00
ryan afaa8f79eb feat(core): add skipped fiber state for configuration gates
Fiber 新增 SKIPPED 态与 Skip/Skipped 方法:门禁为假的插件在 Apply 之前
即被排除并释放其作用域 Context,为互斥实现(cache 与 cache_memory 等)
同时挂载由内核择一激活铺路。
2026-08-29 09:53:10 +08:00
ryan 39a81f7723 feat(core): mount the configuration extension point on the kernel Context
Context 新增 Config() 访问器,注册表随 Fork 共享(配置声明是进程级事实),
并在 types.go 导出配置别名与 ConfigGatedPlugin 可选接口,为插件门禁做准备。
2026-08-29 09:47:59 +08:00