Commit Graph

109 Commits

Author SHA1 Message Date
f548aef9f0 fix(阶段3): 补齐 contestId 脱敏与仓库根 .env 加载
两处是首轮六条修复的收尾,均由验证过程暴露:

1. contestId 未脱敏 —— 旧后端 SubmissionSafeModelSerializer 排除的是
   info/contest/ip 三个字段,首轮只处理了 info 与 ip。

2. 根 .env 读不到 —— Bun 只自动加载 cwd 下的 .env,而启动方式
   (bun run --filter '@oj2/api' dev) 会把 cwd 切到 apps/api/,
   于是 .env.example 教人写在仓库根的 JUDGE_SERVER_TOKEN 静默失效,
   后端降级成随机 token,判题全部卡在 PENDING。config 改为显式补读
   根 .env,且只填充未设置的键(真实环境变量与 cwd 下的 .env 优先)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 02:12:31 -06:00
b7adf2993d fix(阶段3): 去掉判题机 token 的弱默认值
F5:token 校验实现本身是对的(用了 timingSafeEqual),问题是缺省值
"oj2-dev-token" 写死在仓库里 —— 写死在仓库里的 token 等于没有 token。

后端改为对齐旧后端 options/options.py:93 的 fail-safe:
env 缺失时生成随机值并在启动日志里告警,判题机连不上,但不会静默用弱默认值。
compose 改为 ${OJ2_JUDGE_TOKEN:?...},未设置直接报错退出。

本地开发怎么设写在 docker/compose.dev.yml 顶部与 .env.example 里:
两个变量名不同(判题机镜像认 TOKEN,后端认 JUDGE_SERVER_TOKEN)但值必须相同。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 01:59:27 -06:00
8237909d00 fix(阶段3): 提交可见性守卫补上匿名,详情脱敏,提交接口加限流
F3:`isRegularUser(user)` 对匿名用户(user 为 null)返回 false,
`submission_list_show_all` 关闭时守卫整体短路 —— 匿名能看到全部提交,
权限反而大于登录学生。实测开关关闭时匿名 total=23、登录学生 total=0。
而关闭这个开关的典型场景正是考试。改为 `!isAdminRole(user)`(非管理员即受限),
并删掉 isRegularUser(全仓只有这一处调用,留着就是下一个坑)。

F4:提交详情把 info(含每个测试点的 test_case 编号与 output_md5)与 ip
按「是不是自己的提交」下发。旧后端 submission/views/oj.py 把关的是角色:
is_admin_role() 决定用 SubmissionModelSerializer 还是
SubmissionSafeModelSerializer(exclude=("info", "contest", "ip"))。改为仅管理员可见。

F6:旧后端 SubmissionAPI.throttling 的 TokenBucket 没搬过来。
按旧参数重建(options/options.py:120 的 user 桶:capacity 20、fill_rate 0.03、
default_capacity 10,实际值仍以数据库 throttling 配置项为准),
落在 redis 上并改用 Lua 脚本做成原子操作 —— 旧实现自己注明「不是线程安全的」,
而限流要挡的正是并发突发。挂点位置与旧后端一致:比赛权限校验之后、取题目之前。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 01:59:19 -06:00
b4b61af6b0 fix(阶段3): 匿名不可读用户档案,真名改为默认不下发
F1:GET /profiles/:username 只挂了 optionalAuth、handler 内无登录判断,
匿名可读 email、adminType、className、lastLogin。用户名又能经 /rankings/users
公开枚举,等于可以无 cookie 批量收集全校学生的邮箱与最后登录时间。
handler 开头补上未登录即返回空,对齐旧后端 account/views/oj.py 的
UserProfileAPI.get 首行 `if not user.is_authenticated: return self.success()`。

F2:旧后端把「是否下发真名」做成 UsernameSerializer(need_real_name=False)
的默认关闭开关,全仓 11 处调用只有比赛榜单一处显式打开;新后端没搬这一层,
真名随用户对象无条件下发,13 个下发点里 8 个匿名可达。

这里补回同一层:helpers.ts 新增 sampleUser(),realName 默认不下发,
需要的地方显式传 { includeRealName: true }。12 个下发点改为走这个函数,
只有比赛榜单一处打开(对齐 contest/serializers.py:84 的 is_contest_admin)。
没有逐处删字段 —— 那样下次新增端点还会重犯。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 01:59:05 -06:00
8c00cdc947 feat(阶段3): oj 侧端点铺开(基线提交,未经评审)
由外部 agent (Codex) 在本会话额度中断期间完成。原样提交作为基线,
后续修复单独成 commit,便于区分与回退。

覆盖 oj 侧 65 个端点,新增 9 组路由(account/achievement/ai/classroom/
content/contest/flowchart/problemset/site)与对应 Zod 契约。

已核验:tsc --noEmit 退出码 0;API 可启动;/api/problems 返回真实数据;
judge 与 flowchart worker 均 ready。
未核验:权限边界与数据泄露,评审进行中。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 01:25:36 -06:00
ec274419c3 Build Phase 2 judge vertical slice 2026-08-06 22:42:39 -06:00
3c975e85ee feat(阶段1): Hono 应用与题目列表接口,读通本地真实数据 2026-08-06 21:14:58 -06:00
0f0a5cb65a fix(阶段1): 修正 problem 主键与展示编号映射 2026-08-06 21:11:44 -06:00
f635737453 feat(阶段1): drizzle schema 从本地库生成并剪掉 Django 框架表
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 20:45:51 -06:00