diff --git a/apps/api/src/routes/content.ts b/apps/api/src/routes/content.ts index 00c17b8..904105f 100644 --- a/apps/api/src/routes/content.ts +++ b/apps/api/src/routes/content.ts @@ -8,7 +8,7 @@ import { reactionKeySchema, reactionStateSchema, setReactionRequestSchema, - submissionDetailSchema, + embeddedSubmissionSchema, tutorialSchema, tutorialSummarySchema, } from "@oj2/contract" @@ -73,32 +73,33 @@ contentRoutes.get("/messages", requireAuth, async (c) => { const offset = queryInteger(c.req.query("offset"), 0, { min: 0 }) const [totalRows, rows] = await Promise.all([ db.select({ value: count() }).from(schema.message).where(eq(schema.message.recipientId, user.id)), - db.select({ message: schema.message, sender: schema.user, realName: schema.userProfile.realName, submission: schema.submission }) + db.select({ message: schema.message, sender: schema.user, realName: schema.userProfile.realName, submission: schema.submission, displayId: schema.problem.displayId }) .from(schema.message).innerJoin(schema.user, eq(schema.message.senderId, schema.user.id)) .leftJoin(schema.userProfile, eq(schema.userProfile.userId, schema.user.id)) .innerJoin(schema.submission, eq(schema.message.submissionId, schema.submission.id)) + .innerJoin(schema.problem, eq(schema.submission.problemId, schema.problem.id)) .where(eq(schema.message.recipientId, user.id)).orderBy(desc(schema.message.createTime)).limit(limit).offset(offset), ]) return success(c, messageListSchema.parse({ - results: rows.map(({ message, sender, realName, submission }) => messageSchema.parse({ + results: rows.map(({ message, sender, realName, submission, displayId }) => messageSchema.parse({ id: message.id, sender: sampleUser(sender, realName), createTime: message.createTime, message: message.message, - submission: submissionDetailSchema.parse({ + submission: embeddedSubmissionSchema.parse({ id: submission.id, createTime: submission.createTime, userId: submission.userId, username: submission.username, code: submission.code, result: submission.result, - info: {}, + // info / ip / contestId 三个字段不在 embeddedSubmissionSchema 里,故不传 —— + // 对齐旧后端 SubmissionSafeModelSerializer 的 exclude,这三个键不出现在响应中 language: submission.language, shared: submission.shared, statisticInfo: objectValue(submission.statisticInfo), - ip: null, - contestId: submission.contestId, - problemId: submission.problemId, + // 展示用题号而非数字主键,站内信页面拿它拼 /problem/<题号> + problem: displayId, showLink: true, canUnshare: false, }), diff --git a/packages/contract/src/content.ts b/packages/contract/src/content.ts index ee4a4be..9b666b4 100644 --- a/packages/contract/src/content.ts +++ b/packages/contract/src/content.ts @@ -1,7 +1,7 @@ import { z } from "zod" import { paginatedSchema, sampleUserSchema } from "./common" -import { submissionDetailSchema } from "./submission" +import { embeddedSubmissionSchema } from "./submission" export const announcementSchema = z.object({ id: z.number().int(), @@ -21,7 +21,7 @@ export const messageSchema = z.object({ sender: sampleUserSchema, createTime: z.string(), message: z.string(), - submission: submissionDetailSchema, + submission: embeddedSubmissionSchema, }) export const messageListSchema = paginatedSchema(messageSchema) diff --git a/packages/contract/src/submission.ts b/packages/contract/src/submission.ts index 710f76c..0ed8ff2 100644 --- a/packages/contract/src/submission.ts +++ b/packages/contract/src/submission.ts @@ -46,6 +46,21 @@ export const submissionDetailSchema = z.object({ canUnshare: z.boolean(), }) +/** + * 内嵌在别处(目前只有站内信)的提交对象。对齐旧后端的 + * `SubmissionSafeModelSerializer(exclude=("info", "contest", "ip"))` —— + * 这三个键**根本不出现**,而不是出现但值为空。 + * + * 独立成一个 schema 而不是复用 submissionDetailSchema 传空值:形状一致了, + * 将来有人「顺手」把空值改成真值就不会变成泄露,因为这里压根没有这三个字段。 + */ +export const embeddedSubmissionSchema = submissionDetailSchema + .omit({ info: true, ip: true, contestId: true, problemId: true }) + // 旧 SubmissionSafeModelSerializer 里 problem 是 + // `SlugRelatedField(slug_field="_id")`,即**展示用题号**而非数字主键。 + // 站内信页面拿它拼 `/problem/<题号>` 链接,给数字 id 会拼出打不开的地址。 + .extend({ problem: z.string() }) + export const submissionUpdateSchema = z.object({ type: z.literal("submission_update"), submission_id: z.string(),