Putong OJ 的 2025 年度回顾
January 16, 2026
在刚刚过去的 2025 年,随着 Putong OJ 功能的持续迭代与优化,越来越多的课程选择在此平台布置作业、开展实验教学。过去一年平台新增用户超过 1,400 人,接收提交代码近 19 万份,均为历史新高。
我于 2024 年因 Putong OJ 的安全相关事宜混入维护团队,实际上直到 2025 年才真正有时间和精力接手并持续维护这一项目,目前仍是唯一的活跃维护者(前端、后端、测试及运维一体机)。
搬个新家
曾经 Putong OJ 部署在一台 Ubuntu 14 服务器上。时过境迁,即使是扩展安全维护(ESM)也已在 2024 年结束,因此我决定干脆就趁机申请换一台新服务器,顺带现代化重构一下整个架构。
Ubuntu 24 至少可以再用 10 年。
Brand new Code Judger
原有的 Judger 是基于 Acdream 版本的魔改,它依赖 ptrace 和 seccomp 限制系统调用来实现代码沙箱运行。然而这种方式存在诸多问题和限制,例如无法有效控制内存使用。并且随着时间推移,维护成本也越来越高,更新 C / C++ 编译器版本意味着要重新不断调整 seccomp 规则以适应新的系统调用。
更重要的是,对于 C / C++ 之外的语言,通过白名单限制系统调用几乎不可行。不同语言的 Runtime 往往涉及各种复杂且难以预测的系统调用,为每种语言单独维护一套 seccomp 规则需要巨大的工作量。因此原有的 Judger 实际上并未对 Java 语言进行安全限制,也很难扩展支持其他语言。
在 GitHub 上经过一番搜罗后,我决定采用 criyle/go-judge 作为新评测系统的基础。它基于 Linux 的 cgroup 和 namespace 机制实现资源限制与隔离,能够对内存、CPU 时间等多种资源进行精细控制。我花费了大约三周时间完成了全新评测系统的开发,并于 2025 年 3 月随服务器迁移一并上线。
得益于新评测器架构的良好扩展性,我还进一步增加了对 Python 3 和 PyPy 的支持,并实现了 Special Judge 题和 Interaction 题的评测功能。在此过程中参考了 QDUOJ 和 Vijos 等项目的经验与恶意代码样本,尽可能提升了评测系统的安全性。
Deploy in Containers
过去 Putong OJ 的部署方式较为传统:MongoDB 和 Redis 运行在 Docker 容器中,而主程序则通过 PM2 直接管理在宿主机上,代码更新通过从 GitHub 拉取实现。随着迭代频率的提高,这种方式显然不能满足需求(特别是由于众所周知的原因,从 GitHub 直接拉取代码变得很不稳定)。
因此我直接将新架构设计为多容器部署。主程序、评测器、Nginx 反代、MongoDB 和 Redis 均运行在独立的容器中,通过 Docker Compose 进行编排,提供很可观的可维护性和可扩展性。
由于目前用户量和访问量尚不算大,所有服务均运行在同一台服务器上,也就没有考虑引入 Kubernetes 等编排工具。如果未来需要横向扩展,可能会考虑尝试 K3s 等轻量级方案。
小修小补
TypeScript & Monorepo
将代码库从 JavaScript 逐步迁移至 TypeScript,其带来的类型安全提升是显而易见的,我就不在这里赘述什么弱类型笑话了。
既然前后端都采用了 TypeScript,共享同一套类型定义便是很自然的事情。为此我将项目重构为 Monorepo 结构,并通过一个独立的 shared 包来存放前后端共用的类型定义与工具函数。
User AuthN & AuthZ
如果你感兴趣的话,曾经 Putong OJ 代码层面在用户认证与权限管理方面存在并且不限于下列问题:
- 管理员修改用户权限与用户修改自身资料共用同一接口,且缺乏权限校验,导致普通用户可构造请求提升自身为管理员权限。该漏洞记录于 CVE-2024-48920。
- 平台缺少用户的 Ban / Suspend 机制,且采用无状态 JWT 进行认证,致使服务端无法强制用户登出或撤销令牌。即便用户修改密码或账户被删除,已签发的令牌仍可被继续使用。
由于系统完全依赖 JWT 所携带的权限标识进行鉴权,上述两个问题叠加可以打出以下「组合拳」:攻击者可先将自身账户权限提升至管理员,重新登录获取管理员权限的 JWT 并本地保存,随后将权限降级甚至删除账户,之后仍可凭借之前保存的 JWT 以管理员身份访问系统。
以上问题已在我接手后得到修复和完善,同时增加了一定的审计能力。当然计划上我还是想再重构一下会话管理模块,从而实现更灵活的用户会话控制。
此外在 2025 年 9 月,平台接入了学校统一身份认证系统,支持通过校园账号登录;同年年底我注意到 Codeforces 开放了 OAuth 授权接口,便速速实现了与其的账户关联功能,支持 Rating 同步并在用户资料中展示。目前已有近 200 名用户使用了 Putong OJ 的 OAuth 功能。
至于你要问我现在平台上还有没有安全漏洞?那我可就不清楚了,只能说如果有的话我可以帮你去让 GitHub 申请个 CVE 编号下来。
Kick out View UI Plus
Putong OJ 的前端最初基于 View UI Plus(原 iView)构建,这个组件库本身并不差,我也用它开发了许多功能。然而随着时间的推移,一些问题逐渐显现:
- 社区维护进展缓慢,许多依赖版本已经过时;
- 开源版本不支持暗色主题,官方暗色模式需要付费购买,而我也不愿手动维护一套自定义主题;
- 表格组件在设计上较为陈旧,在使用中有着大量需求的表格组件,实际上都是手搓 HTML 实现。
更换 UI 组件库的想法由来已久,但选择哪个替代方案呢?我对着 UI Lib Picker 翻了好久:
- Ant Design:刻意不考虑,即便从设计的角度上它或许很优秀,但是在工程方面,参考其 Ho Ho Ho! 圣诞节彩蛋以及账号被盗导致仓库被转移等往日种种;
- Element Plus:感觉千篇一律的设计风格让人审美疲劳;
- Vuetify:我曾经使用过,现在感觉它的 Material Design 风格正在逐渐失去美感。
最后选择了 PrimeVue + TailwindCSS 作为新的前端技术栈,PrimeVue 组件丰富、设计现代化,主要是在主观上更符合我的审美,而 TailwindCSS 则提供了极高的样式定制灵活性。在系统重构的过程中,我逐步将部分页面从 View UI Plus 迁移到了 PrimeVue,目前仍在继续这个过程。
由于 TailwindCSS 4 对浏览器的兼容性主要针对 2023 年及之后发布的浏览器版本,因此这意味着正式与一些古老的浏览器告别了。
Other Improvements
随着平台在教学场景中的应用日益广泛,已不适合给每位老师分配全站管理员权限。因此在 2025 年全新编写了课程系统,能够允许教师在自己的课程空间内添加题目、创建比赛,采用更精细的权限管理。
用 Vditor 替换了原有富文本编辑器,主要是冲着 Markdown 语法而去的。但是由于 Vditor 功能繁多且体积较大,我也仍在寻找更轻量的替代方案。
原有的讨论区功能相当简陋,并且也无法满足对于 UGC(User-Generated Content,用户生成内容)的管理需求,于是我对其进行了彻底重构,更重要的是将讨论功能与题目和比赛进行了集成,每道题目和每场比赛都拥有独立的讨论区,便于形成围绕具体学习内容的讨论氛围。
引入了 WebSocket 服务提供实时状态更新,用户提交代码后无需手动刷新即可看到评测结果,同时也可以用于向全体用户广播系统通知。
办场比赛
2025 年底,计算机系部分课程的期末机考暨学校程序设计个人赛在 Putong OJ 平台成功举办。比赛共有近 300 名学生参与,累计提交代码近 6,000 份。
本场比赛共有 14 道题目,其中包含 3 道 Special Judge 题和 1 道 Interaction 题,由于不同题目的测试点数量和评测方式各异,沙箱每分钟处理的任务数在峰值时段超过了 500 次。整个赛事期间,系统表现稳定流畅,未出现评测排队、服务宕机或严重延迟。
在赛事准备阶段老师还曾特意嘱咐,这是第一次在 Putong OJ 上举办如此多人的比赛,需注意系统稳定性与响应速度。比赛期间我看了一眼服务器 Metrics,CPU 使用率基本保持在 10% 以下,我寻思系统即使应对 3,000 人同时在线的场景也游刃有余。下图纵轴仅显示至 50%,而非 100%。
然后呢?
都看到这里了,不来 acm309/PutongOJ 点个 Star 吗?构想中还有很多东西要写,来年便知后事如何。
This article is authored by luoingly and licensed under CC BY-NC 4.0
Permalink: https://luoy.ing/posts/putong-oj-2025-annual-review/




