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>
This commit is contained in:
@@ -1,4 +1,32 @@
|
|||||||
import { randomBytes } from "node:crypto"
|
import { randomBytes } from "node:crypto"
|
||||||
|
import { readFileSync } from "node:fs"
|
||||||
|
import { resolve } from "node:path"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bun 只自动加载「当前工作目录」下的 .env。而本应用的启动方式(`bun run --filter '@oj2/api' dev`)
|
||||||
|
* 会把 cwd 切到 apps/api/,于是仓库根的 .env 读不到 —— 而 .env.example 恰恰教人写在根目录。
|
||||||
|
* 这里显式补读仓库根的 .env,让文档指引真正生效,且不管从哪个目录启动都一致。
|
||||||
|
*
|
||||||
|
* 只填充尚未设置的键:真实环境变量与 cwd 下的 .env 优先级更高,不被覆盖。
|
||||||
|
*/
|
||||||
|
function loadRepoRootEnv() {
|
||||||
|
try {
|
||||||
|
const text = readFileSync(resolve(import.meta.dir, "../../../.env"), "utf8")
|
||||||
|
for (const line of text.split("\n")) {
|
||||||
|
const trimmed = line.trim()
|
||||||
|
if (!trimmed || trimmed.startsWith("#")) continue
|
||||||
|
const eq = trimmed.indexOf("=")
|
||||||
|
if (eq <= 0) continue
|
||||||
|
const key = trimmed.slice(0, eq).trim()
|
||||||
|
if (process.env[key] !== undefined) continue
|
||||||
|
process.env[key] = trimmed.slice(eq + 1).trim().replace(/^["']|["']$/g, "")
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// 根目录没有 .env 是正常情况(例如生产用真实环境变量注入),静默跳过
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadRepoRootEnv()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判题机 token。对齐旧后端 `options/options.py:93`:
|
* 判题机 token。对齐旧后端 `options/options.py:93`:
|
||||||
|
|||||||
@@ -209,7 +209,9 @@ async function submissionDetail(id: string, user: AuthUser) {
|
|||||||
shared: row.submission.shared,
|
shared: row.submission.shared,
|
||||||
statisticInfo: objectValue(row.submission.statisticInfo),
|
statisticInfo: objectValue(row.submission.statisticInfo),
|
||||||
ip: full ? row.submission.ip : null,
|
ip: full ? row.submission.ip : null,
|
||||||
contestId: row.submission.contestId,
|
// contest 也在旧后端的排除名单里(exclude 的三个字段是 info / contest / ip),
|
||||||
|
// 首轮修复只处理了 info 与 ip,这里补齐。
|
||||||
|
contestId: full ? row.submission.contestId : null,
|
||||||
problemId: row.submission.problemId,
|
problemId: row.submission.problemId,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
canUnshare: canViewSubmission(user, row.submission, row.problem, row.contest, false),
|
canUnshare: canViewSubmission(user, row.submission, row.problem, row.contest, false),
|
||||||
|
|||||||
Reference in New Issue
Block a user