信息学竞赛程序回收系统:选手在浏览器中提交源代码,管理员管理用户与比赛,可与 LemonServer 联动评测。
版本 0.1.8 · Node.js ≥ 22.2,无第三方依赖。
从 Releases 下载(已内置 Node.js,无需另外安装):
- Windows:
CodeHub-<版本>-windows-x64.zip,解压后双击start.bat,数据在同目录的data中。 - Linux:
CodeHub-<版本>-linux-x64.tar.gz,解压后运行./codehub,数据在同目录的data中。 - Ubuntu / Debian:
sudo apt install ./codehub_<版本>_amd64.deb,然后sudo systemctl enable --now codehub(数据在/var/lib/codehub);也可以在终端运行codehub(数据在~/.local/share/codehub)。
默认端口 8088,浏览器打开 http://<本机 IP>:8088/。
node server.js # 默认 http://0.0.0.0:8088/,数据在 ./data
node server.js --port 80 --data D:\CodeHubData
node server.js --reset-admin # 管理员密码重置为 123456Windows 下也可以双击 start.bat。
首次启动会创建管理员账号 admin / 123456,登录后请在右上角菜单中修改密码。
选手
- 登录(上传了考生手册时,需勾选“已阅读考生手册”)。
- 比赛列表:只显示当前的比赛(从“提前进入”时间到比赛结束),且只显示自己所在分组可参加的比赛。
- 比赛页:考试信息与倒计时、下载考生手册 / 试题包、按计划公布的试题包密码、每道题一个卡片(绿色 = 已提交,显示提交时间、语言、代码长度)。题目名称与密码同一时间显示,无密码时也适用;此前显示“题目待公布”。试题包和密码分别提示“未设置”或“待下发 / 待公布”。
- 提交代码(C++):可粘贴或从文件读取,比赛期间可无限次提交,以最后一次为准;查看已提交的代码,并可一键复制(不带行号)。
- 提前结束比赛:比赛进行中可在比赛页点“提前结束比赛”,之后不能再提交,但到比赛结束前仍可进入比赛页、下载文件、查看已提交的代码。
- 修改密码:右上角菜单(至少 6 位,其他设备上的登录会退出)。
管理员
- 用户:批量导入(
用户名,密码[,真实姓名[,分组;分组]],真实姓名为空则同用户名;已存在的用户可跳过或覆盖)、编辑姓名 / 密码 / 分组、批量加入或移出分组、删除、导出 CSV。 - 分组:新建、重命名、删除。
- 比赛:比赛名(即文件夹名)、标题、开始与结束时间(必填)、提前进入时间(默认 30 分钟)、题目列表、可参加的分组(不选则所有用户)、down.zip 与解压密码及各自公开时间(可提前准备)、是否启用子文件夹。
- 提交概况:每名选手每道题的代码长度;关联 LemonServer 后还显示是否已评测、得分与总分,并提供评测结果链接。比赛中与赛后看到的一样,随时可查看代码、下载全部代码(zip)。“提前结束”一列显示选手结束比赛的时间,可撤销(选手误点时恢复提交)。
- 结果表格:点击表头按账号、姓名、总分或单题分数排序,未评测项置后;单击格子选中/取消,Ctrl/Shift 多选,双击或点“查看代码”打开提交。
- 设置:站点名称、代码长度上限、存储位置、考生手册、LemonServer 接入、管理员密码。
- 浅色 / 深色 / 跟随系统。
选手和管理员的代码查看窗口都提供“复制代码”。局域网 HTTP 页面也会尝试兼容复制;浏览器阻止复制时会提示手动选中代码复制,不会关闭窗口或更改提交。
<存储位置>/<比赛名>/<选手用户名>/<题目名>.cpp # 不启用子文件夹
<存储位置>/<比赛名>/<选手用户名>/<题目名>/<题目名>.cpp # 启用子文件夹
与 LemonServer(LemonLime)的 source/ 结构一致。存储位置默认为 <数据目录>/submissions,修改后已有代码会一并移动。已有提交的比赛不能再切换子文件夹设置。
其余数据(用户、比赛、提交记录)保存在 <数据目录>/codehub.json,试题包与考生手册在 <数据目录>/files/。
编辑比赛时可提前选择 down.zip、填写解压密码并设置公开时间,点击保存后生效。时间均填写“开赛前多少分钟”,0 表示开赛时,范围为 0–10080 的整数,且必须满足:
提前进入 ≥ down.zip 公开 ≥ 密码公布
例如:30 分钟 ≥ 20 分钟 ≥ 10 分钟
未上传或未填写时显示“未设置”;已经准备、但未到时间时显示“待下发 / 待公布”和计划时间,到时自动更新。未到时不能通过直接下载链接绕过限制,密码也不会提前返回到选手页面。
题目名称(含文件名)按密码公布时间统一展示,与是否填写密码无关;未到时间时选手详情不返回题目列表,也不能直接查看题目代码,管理员仍可查看。该规则控制网站与接口展示,不会改写提前下发的压缩包内容。
旧比赛未设置这两个时间时,仍按“提前进入时可下载、开赛时公布密码”处理,题目名称也在开赛时显示。比赛结束后不能修改时间设置;管理员不受选手公开时间限制,可提前检查文件和密码。
API:比赛设置提供 downZipReleaseMinutes 和 passwordReleaseMinutes。选手详情返回 downZipState / passwordState(unset、pending、available)及计划时间 downZipReleaseAt / passwordReleaseAt;hasDownZip 表示已上传,downZip 表示当前可下载,password 仅在到时后返回。problemsReleased 和 problemsReleaseAt 表示题目名称是否已公布及公布时间;未公布时 problems 为空列表。
- 在 LemonServer 网页“设置 → 令牌与 API Key”中创建一个 API Key。
- CodeHub“设置 → LemonServer”中填写 API 地址(如
http://127.0.0.1:8080)与 API Key,点“测试连接”。网页地址只用于结果链接,浏览器访问地址不同时再填。 - 编辑比赛:
- “从 LemonServer 导入”:一键导入该比赛的试题作为题目,并可同时导入 down.zip;或者选择“关联比赛”/“新建同名”。比赛可以先设置好,之后再关联。
- 评测方式:
- 赛后统一:比赛结束后评测;
- 每次提交:每有新提交就执行一次任务;
- 定时:每 x 分钟执行一次,从 max(比赛开始时间, 关联时间) 起算。
关联后:
- 选手的每次提交都会上传到 LemonServer 比赛的
source/中(失败会自动重试)。LemonServer 收到新代码后会把对应格子标为未评测,所以重新提交的代码在下一次任务中一定会重新评测。 - 一次“任务”= 上传未同步的代码 → 在 LemonServer 的评测队列中加入“评测未评测的代码”(开始时先刷新选手)→ 等待它完成。该比赛上一次的评测还没完成(包括在 LemonServer 上手动发起的)时,本次跳过;LemonServer 正在评测其他比赛时,本次排队等待。需要 LemonServer ≥ 0.1.6。
- 比赛结束后,等最后一次任务结束,再执行一次任务评测未评测的代码。
- 比赛时间只在 CodeHub 中设置(LemonServer 没有比赛时间,成绩与评测详情任何时候都能看到):它决定选手何时能进入和提交、定时评测从何时算起、何时执行赛后评测。
在“编辑比赛”中修改题目名(文件名)并保存,会同步改名所有已提交的对应 .cpp 文件,以及启用的同名子文件夹;提交内容、时间和次数保持不变。仅修改显示名称或调整题目顺序不会移动文件。目标文件已存在、原文件缺失或正在同步/评测时会拒绝改名,不覆盖已有代码;普通写入失败会回滚本次改名。
关联 LemonServer 时,改名后的代码会按新名字重新同步;请确保 LemonServer 的源程序名也已对应修改。不会改写代码里的 freopen 等字符串。
API:PUT /api/admin/contests/{id} 的 problems 项可带 previousName 指定原题目,例如 {"name":"sum","previousName":"aplusb","title":"求和"};新添加的题目可用 previousName: null。不带此字段时,仅在一进一出的对应关系唯一时推断改名,多题改名请显式提供原名。
node tests/smoke.mjs # 只测 CodeHub
node --test tests/problem-rename.mjs # 改名、冲突、回滚及同步
node --test tests/material-release.mjs # 试题包/密码公开时间与权限
node tests/login-layout.mjs # 浏览器界面回归(需要 Chromium / CHROME_BIN)
node tests/smoke.mjs http://127.0.0.1:18080 D:\tmp\contests # 连同 LemonServer(新配置、令牌 123456、需要 g++)server.js:启动参数。src/app.js:HTTP 服务、会话、静态文件;src/routes-user.js:选手接口;src/routes-admin.js:管理接口。src/lemon.js:LemonServer 客户端与联动调度(上传、评测任务)。src/contest-edit.js:比赛/题目改名,提交记录迁移和失败回滚。src/contest-materials.js:试题包/密码的公开时间与选手可见状态。src/store.js:JSON 数据文件;src/zip.js:打包下载。public/:网页前端(原生 JS,无需构建)。packaging/:发布包(node packaging/package.mjs windows|linux,packaging/linux/build-deb.sh);推送v*标签后由 GitHub Actions 构建并发布。