docs: OJ2 后端重写设计文档
将 Django 后端重写为 Bun + TypeScript 的设计方案,含前后端 monorepo 结构、技术选型与分阶段路线。 两处高风险技术假设已实测验证,spike 代码见 docs/spikes/: - Django pbkdf2 密码哈希可在 Bun 侧验证,存量密码无需重置 - tree-sitter 可用 WASM 在服务端运行,且比现状更易部署 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
48
docs/spikes/ast-spike.ts
Normal file
48
docs/spikes/ast-spike.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Language, Parser } from "web-tree-sitter"
|
||||
|
||||
// 复刻 ast_checker 的 C_MAPPING 片段
|
||||
const C_MAPPING: Record<string, string> = {
|
||||
for_loop: "for_statement",
|
||||
while_loop: "while_statement",
|
||||
function_definition: "function_definition",
|
||||
include: "preproc_include",
|
||||
}
|
||||
|
||||
await Parser.init()
|
||||
|
||||
const parser = new Parser()
|
||||
const cLang = await Language.load("./node_modules/tree-sitter-c/tree-sitter-c.wasm")
|
||||
parser.setLanguage(cLang)
|
||||
|
||||
const code = `#include <stdio.h>
|
||||
int main() {
|
||||
int sum = 0;
|
||||
for (int i = 1; i <= 100; i++) {
|
||||
sum += i;
|
||||
}
|
||||
printf("%d\\n", sum);
|
||||
return 0;
|
||||
}`
|
||||
|
||||
const t0 = performance.now()
|
||||
const tree = parser.parse(code)!
|
||||
const parseMs = performance.now() - t0
|
||||
|
||||
// 数各类节点出现次数(等价于 ast_checker 的 engine.check 遍历)
|
||||
const counts: Record<string, number> = {}
|
||||
const walk = (n: any) => {
|
||||
counts[n.type] = (counts[n.type] ?? 0) + 1
|
||||
for (let i = 0; i < n.childCount; i++) walk(n.child(i))
|
||||
}
|
||||
walk(tree.rootNode)
|
||||
|
||||
console.log("解析耗时:", parseMs.toFixed(2), "ms")
|
||||
for (const [label, tsType] of Object.entries(C_MAPPING)) {
|
||||
console.log(` 规则 ${label.padEnd(20)} -> ${tsType.padEnd(20)} 命中 ${counts[tsType] ?? 0}`)
|
||||
}
|
||||
|
||||
// Python grammar 也验一下
|
||||
const pyParser = new Parser()
|
||||
pyParser.setLanguage(await Language.load("./node_modules/tree-sitter-python/tree-sitter-python.wasm"))
|
||||
const pyTree = pyParser.parse("for i in range(10):\n print(i)")!
|
||||
console.log("\nPython 根节点:", pyTree.rootNode.type, "| 首个子节点:", pyTree.rootNode.child(0)?.type)
|
||||
26
docs/spikes/pbkdf2-spike.ts
Normal file
26
docs/spikes/pbkdf2-spike.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { pbkdf2Sync, timingSafeEqual } from "node:crypto"
|
||||
|
||||
// 验证 Django pbkdf2_sha256$<iterations>$<salt>$<b64hash>
|
||||
function verifyDjangoPassword(raw: string, encoded: string): boolean {
|
||||
const [algo, iterStr, salt, hash] = encoded.split("$")
|
||||
if (algo !== "pbkdf2_sha256") return false
|
||||
const expected = Buffer.from(hash, "base64")
|
||||
const actual = pbkdf2Sync(raw, salt, Number(iterStr), expected.length, "sha256")
|
||||
return expected.length === actual.length && timingSafeEqual(expected, actual)
|
||||
}
|
||||
|
||||
const encoded = "pbkdf2_sha256$1200000$JIVbwvl1TpoWNHUitEA0iJ$5pOkPVGvtZbPGHZ1DnYbhpbtywLdsnEKtzm66IBABIU="
|
||||
|
||||
const t0 = performance.now()
|
||||
const ok = verifyDjangoPassword("student123", encoded)
|
||||
const cost = performance.now() - t0
|
||||
|
||||
console.log("正确密码 :", ok)
|
||||
console.log("错误密码 :", verifyDjangoPassword("wrongpass", encoded))
|
||||
console.log("单次耗时 :", cost.toFixed(0), "ms (1200000 轮迭代)")
|
||||
|
||||
// 透明升级路径:验通后改存 argon2id
|
||||
const upgraded = await Bun.password.hash("student123", { algorithm: "argon2id" })
|
||||
const t1 = performance.now()
|
||||
const ok2 = await Bun.password.verify("student123", upgraded)
|
||||
console.log("argon2id :", ok2, "耗时", (performance.now() - t1).toFixed(0), "ms")
|
||||
Reference in New Issue
Block a user