fix: 提交列表和删除提交的慢查询,素材上传、登出和几处前端加载问题
性能(6 万条提交、18 万条消息的测试库上测的) - 提交列表 join task_task 时用 +s.task_id,不再从任务表出发经 task_id 单列索引回表读 html/css/js 大行,改走 slim 覆盖索引:翻页 260-700ms → 4-18ms - 删除提交时先查出 id 再按等值删关联行:prompt_message.submission_id 几乎全是 NULL, 写成 IN (子查询) 时统计信息会让 SQLite 扫整张表 bug - 提交列表排序加 rowid 作次级键,同分的提交翻页不再重复或遗漏 - 素材文件名只允许字母数字和 - _ .(否则 422),覆盖同名文件改成写临时文件再 rename; 前端删除时对名字做 URL 编码 - 刷新 token 只是网络失败时不再强制登出;退出请求失败时界面也显示为已退出 - 历史面板加载中收到的刷新不再丢掉;成绩册只用最后一次请求的结果,打开时不再加载两次 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
40f3063b91
commit
03878f6cc2
8 files changed
+124
-41
No files matched your search
@@ -1,5 +1,10 @@
|
||||
import { beforeEach, describe, expect, test } from "bun:test"
|
||||
import { get, all } from "../src/db"
|
||||
import { app } from "../src/app"
|
||||
import { config } from "../src/config"
|
||||
import { mkdtemp, readFile, readdir, rm } from "node:fs/promises"
|
||||
import { tmpdir } from "node:os"
|
||||
import { join } from "node:path"
|
||||
import { client, freshDb, makeSubmission, makeTask, makeUser, uuid } from "./helpers"
|
||||
|
||||
beforeEach(freshDb)
|
||||
@@ -373,3 +378,40 @@ describe("自由创作", () => {
|
||||
expect((await me("POST", "/api/submission/", { task_id: c.task_id })).json.detail).toBe("自由创作不需要提交")
|
||||
})
|
||||
})
|
||||
|
||||
describe("任务素材", () => {
|
||||
test("同名上传原地覆盖,文件名不合法 422", async () => {
|
||||
const media = await mkdtemp(join(tmpdir(), "media-"))
|
||||
const saved = config.mediaRoot
|
||||
config.mediaRoot = media
|
||||
try {
|
||||
makeUser("adm", "admin")
|
||||
makeTask("挑战", "challenge", 3)
|
||||
const adm = await client("adm")
|
||||
const upload = (name: string, body: string) => {
|
||||
const form = new FormData()
|
||||
form.append("name", name)
|
||||
form.append("file", new File([body], "x"))
|
||||
return app.request("/api/assets/challenge/3", {
|
||||
method: "POST",
|
||||
headers: { authorization: `Bearer ${adm.tokens.access_token}` },
|
||||
body: form,
|
||||
})
|
||||
}
|
||||
expect((await upload("a.css", "v1")).status).toBe(200)
|
||||
const res = await upload("a.css", "v2")
|
||||
expect(((await res.json()) as any).url).toBe("/media/tasks/challenge/3/a.css")
|
||||
const dir = join(media, "tasks/challenge/3")
|
||||
expect(await readdir(dir)).toEqual(["a.css"])
|
||||
expect(await readFile(join(dir, "a.css"), "utf8")).toBe("v2")
|
||||
for (const bad of ["a b.png", "../x.png", "a/b.png", "x#.png"]) {
|
||||
expect((await upload(bad, "x")).status).toBe(422)
|
||||
}
|
||||
expect((await adm("DELETE", "/api/assets/challenge/3/a.css")).status).toBe(200)
|
||||
expect(await readdir(dir)).toEqual([])
|
||||
} finally {
|
||||
config.mediaRoot = saved
|
||||
await rm(media, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user