diff --git a/README.md b/README.md index 408164c..bc04ef8 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ 当前版本为 **0.0.1**:沙箱已有受控隔离、C/C++ 编译及 native 执行;平台已实现真实身份、 不可变题目、工件与幂等提交创建。事务 outbox、持久化 Coordinator、普通 Worker 和受控 -helper 工厂已接通 C/C++ 编译一次、多测试点评测;前端业务接入仍在开发,不能将前端预览 -成绩当作实际结果。 +helper 工厂已接通 C/C++ 编译一次、多测试点评测;登录、题库、提交和个人成绩页面 +使用真实接口。训练、比赛、社区等扩展页面仍为开发预览。 ## 工程划分 @@ -71,7 +71,8 @@ make sandbox-probe # 只读观察 Linux 环境,不创建隔离资源 开发中的规划文档仅在本地维护,不包含在远端仓库中。 单机提交服务的配置、最小权限、停止恢复与独立 VM 验收要求见 [判题说明](judge/README.md#单机持久化协调与常驻-worker)和[部署说明](deploy/README.md#单机提交服务的启动与停止)。 -后续接入平台成绩查询与前端,再实现分布式恢复、并行优化和学习功能。 +当前范围为单机普通题、已有账号、固定 GNU C17/C++20 与精确字节比较;自助注册、 +自测、SPJ/交互题、Python/Rust 用户程序、分布式调度和学习能力仍待后续实现。 ## 许可证 diff --git a/backend/.env.example b/backend/.env.example index 4362e64..ad5efb2 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -1,7 +1,15 @@ KF_OJ_ENVIRONMENT=development KF_OJ_LOG_LEVEL=INFO -# Optional until database-backed use cases are implemented. +# Dedicated platform login inherits only kf_oj_platform; replace development credentials. KF_OJ_DATABASE_URL=postgresql+asyncpg://kf_oj:local-dev-only@127.0.0.1:5432/kf_oj KF_OJ_AMQP_URL=amqp://kf_oj:local-dev-only@127.0.0.1:5672/kf_oj -KF_OJ_BROWSER_ORIGINS='["http://localhost:5173","http://localhost:8000"]' +KF_OJ_BROWSER_ORIGINS='["http://localhost:5173","http://localhost:8000","http://127.0.0.1:5173","http://127.0.0.1:8000"]' KF_OJ_SESSION_LIFETIME_SECONDS=43200 +# Required for submissions: private absolute artifact root and trusted JSON admission policy. +# KF_OJ_ARTIFACT_ROOT=/srv/kf-platform/artifacts +KF_OJ_ARTIFACT_MAX_BYTES=536870912 +KF_OJ_ARTIFACT_MAX_FILES=4096 +# KF_OJ_JUDGE_ADMISSION= +# Cancellation requires both; token equals Coordinator platform_token, never a Worker token. +# KF_OJ_COORDINATOR_URL=http://127.0.0.1:8100 +# KF_OJ_COORDINATOR_TOKEN= diff --git a/backend/README.md b/backend/README.md index 1f75f74..d7b43b6 100644 --- a/backend/README.md +++ b/backend/README.md @@ -17,7 +17,7 @@ make worker-check API 提供存活、真实登录/退出/当前用户、公开题目与有效 Worker 租约的清单/工件读取。 配置数据库后在 lifespan 内组合模块,配置缺失时业务端口明确不可用,存活接口仍可用。 -Worker 目前仅支持 `--check`,默认拒绝进入尚未实现的任务循环。 +学习 Worker 目前仅支持 `--check`;Rust 判题 Worker 使用单独的常驻消费入口。 提交创建 API 在持久化源码后,同一事务写入初始评测和 outbox;按用户与幂等键 防止重复创建,待评测总量和个人待评测数有固定上限。 @@ -28,7 +28,18 @@ Worker 目前仅支持 `--check`,默认拒绝进入尚未实现的任务循环 队列最多 64 条、32 KiB,拒绝溢出,消息有效期 10 分钟;broker 不可用采用有界退避, 不阻塞提交事务。日志仅固定错误代码,不打印源码、答案或连接口令。 后台每次投递最多 12 秒,关闭连接最多 2 秒,SIGTERM 在当前有界操作结束后退出。 -账号/题目/工件命令及限制见对应模块 README,前端成绩读取与模型接入尚未实现。 +账号/题目/工件命令及限制见对应模块 README;模型能力尚未接入。 + +个人提交查询 `/api/v1/submissions` 使用 `(created_at,id)` 降序游标,最多100条; +`/{id}`、`/{id}/source` 仅允许提交本人读取,统一拒绝外来/不存在记录。 +成绩投影不包含租约、工件摘要、隐藏用例 ID/输入/答案或程序输出;编译诊断至多64KiB, +缺失计量保留 null,未完成报告不展示成绩。身份/提交/内部响应均为 no-store。 +`POST /{id}/cancel` 验证本人、Origin、会话和 CSRF,再调用 Coordinator 接受取消; +202仅表示已持久化意图,实际停止与资源回收由执行服务确认。 +配置 `KF_OJ_COORDINATOR_URL` 为数值 loopback HTTP origin, +`KF_OJ_COORDINATOR_TOKEN` 与私有 Coordinator 的 `platform_token` 一致; +缺少配置或通信失败返回503,不直接修改执行状态。新增运行依赖 `httpx` 仅负责这个 +3秒有界控制调用,禁用代理/重定向,不缓存或打印远端响应及内部凭据。 Python 虚拟环境位于 `backend/.venv`,可在 `backend/` 直接使用 `uv sync --locked` 和 `uv run --locked`。 数据库由 Alembic 唯一管理;业务模型在 `migrations/env.py` 中显式登记元数据。 diff --git a/backend/src/kf_oj/modules/submissions/README.md b/backend/src/kf_oj/modules/submissions/README.md index ed86055..aa7ba8e 100644 --- a/backend/src/kf_oj/modules/submissions/README.md +++ b/backend/src/kf_oj/modules/submissions/README.md @@ -3,7 +3,10 @@ 负责提交幂等请求、不可变源码引用、创建初始评测、历史查询与学生可见结果。 创建提交、评测和待投递事件必须处于同一事务。 协调器拥有领取后的执行状态与最终结果写入权;本模块不能绕过协议覆盖终态。 -`queries.py` 提供已验证主体的私有源码读取,其他账号不可读取。 +`queries.py` 提供已验证主体的提交历史、详情与私有源码读取,其他账号不可读取; +历史按创建时间/UUID降序游标翻页,一次至多100条。读取完整成绩时严格核对报告契约和 +评测绑定,仅投影允许公开的诊断/计量/测试点序号。取消通过注入的 Coordinator 控制 +端口接受意图,不更新执行状态;202与清理完成的终态不同。 `worker_access.py` 核对当前 evaluation / attempt / Worker / fencing、未撤销及未过期凭证、 固定 submission 与版本及任务清单摘要;只允许读取该清单声明的工件。 内部 API 使用 WorkerLease bearer 和 X-Worker-ID / X-Fencing-Version,不使用用户会话授权。 @@ -14,5 +17,9 @@ Python/Rust `manifest_bytes` 与共享字节样例保持一致;Coordinator 必须用同一序列化定义存快照。 读取凭证与客户端提交的身份分开:身份和归属从实际数据库绑定获得,不接受客户端自报 user ID。 -当前只实现读取与授权基础。提交创建/幂等/outbox、Coordinator 正式 claim、报告及用户成绩 -查询仍在后续模块阶段,不能把测试数据库 fixture 的租约当作正式领取或实际执行证据。 +`service.py` 按账号与幂等UUID绑定版本/语言/源码摘要,持久化源码后同一事务创建 +submission、queued evaluation 和 outbox;同键同内容复用既有提交,不同内容409。 +待评测固定总量64、个人8,取消清理中也占用容量;配置或容量不足明确拒绝。 +Coordinator 通过正式 claim 创建短期领取权,Worker 不写业务数据库。 +真实服务链与安全条件必须在独立 VM 验收;数据库 fixture 的租约或合成报告只能 +验证控制规则、授权和投影,不能作为真实程序执行/停止证明。 diff --git a/deploy/README.md b/deploy/README.md index 8b3f5c6..a91c9e5 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -59,6 +59,49 @@ Sandbox 同版本仅为独立 VM 测试数据生成的开发依赖,Engine 不 最后停止工厂和本地依赖。强制死亡后的旧 attempt 必须撤销并取得实际停止证明; `cleanup_failed` 不等于普通终态,先核对保留资源再由管理员恢复工厂/Worker。 +平台配置私有 `KF_OJ_ARTIFACT_ROOT`、有界字节/文件容量以及 +`KF_OJ_JUDGE_ADMISSION`(完整可信 JSON);其工具链、编译/运行上限、总体作业期限、 +并发/prefetch/retry 必须与 Coordinator/Worker 一致。单机使用并发1、prefetch1, +不能仅提高 Worker 数量绕过节点容量。源码最多256KiB,测试点最多256个; +stdout/stderr 各不超过 file_bytes,合计不超过 workdir_bytes。 + +迁移账号与平台账号分开配置,命令均在 `backend/` 运行: + +```sh +uv run --locked alembic upgrade head # 私有迁移账号,仅执行受控 schema 操作 +uv run --locked kf-accounts create local_user # 换成平台角色;终端读取密码并二次确认 +uv run --locked kf-problems /srv/private-import +uv run --locked uvicorn kf_oj.api.app:create_app --factory --host 127.0.0.1 --port 8000 \ + --no-proxy-headers --limit-concurrency 32 --backlog 64 +uv run --locked python -m kf_oj.infra.messaging.publisher +``` + +每个账号操作/导入目录都由管理员准备,不从浏览器接受私有路径。导入格式见 +[题目模块](../backend/src/kf_oj/modules/problems/README.md),不可变版本以 UUID +在题库/题面链接和提交请求中传递。更新题目需导入新版本,不覆盖已有提交的题目版本。 +临时验收使用合成数据和测试口令,实际业务数据与服务配置都保存在仓库外的私有目录。 + +取消另需 API 私有配置 `KF_OJ_COORDINATOR_URL=http://127.0.0.1:8100` 与 +`KF_OJ_COORDINATOR_TOKEN`(Coordinator 的 platform_token)。所有服务令牌私下生成, +不可互用或放进前端环境。Coordinator/Worker 私有 JSON 为0600、单链接、有界文件; +Root 镜像/配置/二进制由 root 持有,任务委派子树独立于监督器自身 cgroup。 +运行前实际读回整体节点、服务和任务 memory/swap/pids/CPU/文件/运行期限上限, +并通过真实隔离门控;配置解析、readiness、存活探测都不能替代执行验收。 + +前端从 `frontend/` 启动 `pnpm dev`,默认127.0.0.1:5173;平台明确允许相同浏览器 +Origin,`/api` 同源代理。生产需另行配置 HTTPS、安全 cookie、SPA 回退与 `/api` +反向代理,本仓库不自动部署。用户以受控账号登录,在题面选择支持语言、提交源码, +随后查看同一 submission UUID 的持久化状态。等待/取消中/系统失败都不等于成功成绩。 + +broker 暂停时 API 仍能持久化 queued/outbox;恢复后 publisher confirm 与 Coordinator +补偿重新投递。Worker/Coordinator 重启使用原私有配置、工件、日志与 DB,不删除 +attempt 或强改状态。旧租约不能覆盖新结果,有限基础设施重试最终收敛。 +容量满时返回429或503;先确认待执行/清理状态,恢复服务或由管理员扩充经过核对的 +存储容量,不删除仍被提交/题目引用的工件。`kf-artifacts --limit 100` 仅回收满足年龄 +要求的无引用工件和已标记删除项;它不会通过删除用户源码来腾出容量。 +恢复 Root 必须核对 journal/inode/实际排空;未知资源保留,管理员处理自己确认的 +故障来源后再重启。不要递归清空任务目录,也不要把 cleanup_failed 改成已成功。 + 这些入口和顺序不表示生产部署已完成。隔离/超限与用户程序仅在无 NIC、无宿主共享 挂载、无业务凭据且整体上限已读回的独立测试 VM 内验收。开发 Compose 仍只提供可信 数据库和 broker,不在宿主或业务服务器运行用户代码。workflow 如需启用由指定 diff --git a/frontend/README.md b/frontend/README.md index 3cc7779..74d8722 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,7 +1,8 @@ # Vue 前端工程 Vue 3 + TypeScript + Vite + Element Plus,使用 Vue Router 与 Pinia。 -当前页面是开发预览和实际 API 连接检查;没有模拟登录或模拟评测结果。 +登录、已发布题库、题面、提交与个人成绩页面使用真实 API。训练、比赛、社区等 +本轮以外页面仍为明确的开发预览,不提供这些尚未实现的业务能力。 ```bash pnpm install --frozen-lockfile @@ -24,4 +25,19 @@ pnpm build API 类型更新执行根目录 `make contracts`;前端通过 `pnpm check:api` 拒绝过期类型。 登录凭据、模型密钥和数据库连接不能进入 `VITE_` 环境变量。 -`pnpm test` 验证代码草稿在刷新、切到后台与页面离开时的保存行为,已纳入 `make check-frontend`。 +已有账号登录使用 HttpOnly/Strict cookie 与单独 CSRF cookie;401清除内存身份, +保护页面重新登录后继续。自助注册暂不开放。密码/会话不存入 localStorage。 +浏览器与 API 使用同一 origin 的 `/api`,明确配置 `KF_OJ_BROWSER_ORIGINS`; +生产必须使用 HTTPS、安全 cookie 与独立部署的反向代理。 + +编辑器保留每题/每语言本地草稿,新增 C17 并保留原 C++/Python/Rust 草稿; +仅题目声明支持的 C17/C++20 可提交。提交凭据按账号/版本/内容 SHA-256 绑定, +POST前先保存 UUID,双击共用请求,网络超时/刷新重试复用原键;内容变化产生新键。 +草稿和已保存凭据不因失败/退出而清除,本地存储不可用时拒绝发起无法稳定重试的提交。 + +详情和列表查询每2秒一次,每轮最多240次/10分钟;终态、错误或离页停止,隐藏页面 +取消当前查询,恢复可见时继续原预算。路由变化/会话变化后的旧响应不能覆盖当前视图。 +耗尽后可手动刷新;不会用预览成绩填补错误。取消受理与最终停止分别展示。 +运行指标仅来自完整持久化报告,null显示“—”;诊断和源码按纯文本显示。 +`pnpm test` 覆盖草稿、幂等重试、轮询期限/隐藏/终态/失效响应和401处理, +已纳入 `make check-frontend`;真实浏览器编译/判题另在受限独立 VM 验收。 diff --git a/judge/README.md b/judge/README.md index d9eeb69..ef63067 100644 --- a/judge/README.md +++ b/judge/README.md @@ -58,7 +58,7 @@ helper 不接管 root 或其他身份的输入工件;每任务保留相同的 Engine 仅在真实、无基础设施错误的 exit 0 后逐字节比较输出。非零退出/信号为 RuntimeError,CPU/墙钟、内存、输出限制分别映射 TLE/MLE/OLE,取消与 SystemError 单独表达;不可用计量保持 null。汇总在 Engine 内按输入顺序确定,基础设施错误或取消停止后续执行;空用例和重复 run ID 在执行前拒绝。`judge-once` 返回结构化 verdict、用例与实际计量。新增 `serde` 直接依赖用于本地作业/领域结果类型,Engine 不引入 sandbox 或 OS 库。 本地已提供 native 单用例、编译一次多测试点与平台消费/持久化协调入口; -用户成绩展示、交互题、SPJ 和生产部署仍未实现。`kf-worker --check` 只检查入口,不消费或执行任务。权限 helper 和镜像必须由部署方配置,Worker 不调用 sudo、不修改账号或权限。 +个人成绩通过平台归属查询展示;交互题、SPJ 和生产部署仍未实现。`kf-worker --check` 只检查入口,不消费或执行任务。权限 helper 和镜像必须由部署方配置,Worker 不调用 sudo、不修改账号或权限。 ## 编译一次、多测试点