From 95625d6711a7a9ae509993009dfd51b3a7ce107a Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Sat, 8 Aug 2026 06:28:44 -0600 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=8F=A6=E5=A4=96=E4=B8=A4?= =?UTF-8?q?=E5=A4=84=E5=AE=88=E5=8D=AB=E4=B9=9F=E6=94=B6=E5=88=B0=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E8=A1=8C=E4=B8=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 走查题单进度时撞到 `/problem-sets/:id/user-progress` 对学生 403 —— 那是**正确**的 (它是给教师看全班进度的),但守卫又写在 handler 体内。评审的 M3 只列了 3 处, 按同一模式扫全仓,还有 2 处: content.ts POST /messages isSuperAdmin problemset.ts GET /problem-sets/:id/user-progress isTeacherOrAbove 改用 requireSuperAdmin / requireTeacher,理由同 M3:守卫要能从注册行上看出来, 不然下一个人加同类端点容易漏掉那个 if。 实测档位没变:学生两条都 403,教师看全班进度 200。 路由遮蔽检查仍然干净。 Co-Authored-By: Claude Opus 5 --- apps/api/src/routes/content.ts | 7 +++---- apps/api/src/routes/problemset.ts | 8 +++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/apps/api/src/routes/content.ts b/apps/api/src/routes/content.ts index 904105f..5a23a00 100644 --- a/apps/api/src/routes/content.ts +++ b/apps/api/src/routes/content.ts @@ -15,11 +15,11 @@ import { import { and, asc, count, desc, eq, inArray } from "drizzle-orm" import { Hono } from "hono" -import { requireAuth, type AppEnv } from "../auth/middleware" +import { requireAuth, requireSuperAdmin, type AppEnv } from "../auth/middleware" import { db, schema } from "../db" import { failure, success } from "../http" import { JudgeStatus } from "../judge/status" -import { isSuperAdmin, objectValue, queryInteger, sampleUser } from "./helpers" +import { objectValue, queryInteger, sampleUser } from "./helpers" export const contentRoutes = new Hono() @@ -108,9 +108,8 @@ contentRoutes.get("/messages", requireAuth, async (c) => { })) }) -contentRoutes.post("/messages", requireAuth, async (c) => { +contentRoutes.post("/messages", requireSuperAdmin, async (c) => { const user = c.get("user")! - if (!isSuperAdmin(user)) return failure(c, 403, "permission-denied", "Permission denied") const parsed = createMessageRequestSchema.safeParse(await c.req.json().catch(() => null)) if (!parsed.success) return failure(c, 400, "invalid-request", "Invalid message payload") if (parsed.data.recipientId === user.id) return failure(c, 400, "invalid-recipient", "Can not send a message to yourself") diff --git a/apps/api/src/routes/problemset.ts b/apps/api/src/routes/problemset.ts index 86da9fd..e16e218 100644 --- a/apps/api/src/routes/problemset.ts +++ b/apps/api/src/routes/problemset.ts @@ -27,13 +27,13 @@ import { } from "drizzle-orm" import { Hono } from "hono" -import { optionalAuth, requireAuth, type AppEnv } from "../auth/middleware" +import { optionalAuth, requireAuth, requireTeacher, type AppEnv } from "../auth/middleware" import { db, schema } from "../db" import { publishAchievementNotification } from "../events" import { failure, success } from "../http" import { JudgeStatus } from "../judge/status" import { updateAchievementsForProblemSet } from "../services/achievements" -import { isTeacherOrAbove, objectValue, queryInteger, sampleUser } from "./helpers" +import { objectValue, queryInteger, sampleUser } from "./helpers" export const problemsetRoutes = new Hono() @@ -362,9 +362,7 @@ problemsetRoutes.get("/problem-sets/:id/badges", async (c) => { return success(c, badges.map((badge) => badgeData(badge))) }) -problemsetRoutes.get("/problem-sets/:id/user-progress", requireAuth, async (c) => { - const user = c.get("user")! - if (!isTeacherOrAbove(user)) return failure(c, 403, "permission-denied", "Permission denied") +problemsetRoutes.get("/problem-sets/:id/user-progress", requireTeacher, async (c) => { const id = queryInteger(c.req.param("id"), 0, { min: 1 }) const [problemSet] = await db.select({ id: schema.problemset.id }).from(schema.problemset).where(and( eq(schema.problemset.id, id), eq(schema.problemset.visible, true), ne(schema.problemset.status, "draft"),