Files
OJ2/packages/contract/src/roles.ts
T
xuyue d25b5dbe6d
Deploy / deploy (push) Canceled after 0s
update
2026-09-27 19:36:28 +08:00

79 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 角色与题目权限的字面量。**全仓唯一的定义处。**
*
* 这些字符串是**落库的值** —— Django choices 的遗产,带空格、带大小写,`user` 表
* 一千多行存的就是它们,前端也按这些值判断菜单。所以只能新增、不能改写已有的。
*
* 收到这里之前:`auth/middleware.ts` 和 `routes/helpers.ts` 各抄了一份
* ADMIN_ROLES / TEACHER_ROLES,`["Regular User", "Student Admin"]` 这个学生口径
* 在四个文件里各写一遍,另有二十多处散落的 `=== "Super Admin"`。拼错一个字母
* TypeScript 一个字都不会说,只会在运行时静默放行或静默拒绝。
*
* **这个文件不能 import zod。** 前端入口要用这里的 USER_TYPE / toAdminType,
* 一 import 就把整个 zod 运行时(~100KB)拖进首屏包。对应的 zod schema 在 admin.ts。
*/
export const ADMIN_TYPES = [
"Regular User",
"Student Admin",
"Teacher Admin",
"Super Admin",
] as const
export type AdminType = (typeof ADMIN_TYPES)[number]
export const PROBLEM_PERMISSIONS = ["None", "Own", "All"] as const
export type ProblemPermission = (typeof PROBLEM_PERMISSIONS)[number]
/**
* 三个分组一律是**白名单**,对齐旧后端 `account/models.py:65-73` 的显式列举写法。
*
* 不要改成黑名单(`!== "Regular User"`):当前四种角色下两者等价,但将来新增任何角色
* (助教、家长……)都会**默认拿到管理员权限**。白名单则默认拒绝,加角色的人必须
* 回到这里才能放行,而这正是应该被逼着想一遍的地方。
*/
/** 能进后台的三种 */
export const ADMIN_ROLES: readonly AdminType[] = ["Student Admin", "Teacher Admin", "Super Admin"]
/** 老师及以上 */
export const TEACHER_ROLES: readonly AdminType[] = ["Teacher Admin", "Super Admin"]
/**
* 学生口径:排行榜、班级榜、比赛榜、自学统计都只算这两种,教师和超管不入榜。
* 注意 Student Admin 算学生 —— 他要参赛、要上榜,只是多了个后台入口。
*/
export const STUDENT_ROLES: readonly AdminType[] = ["Regular User", "Student Admin"]
/**
* 把库里读出来的裸字符串收成联合类型。**认不出来的一律降成最低权限**,
* 不抛错:脏数据不该让人登不上,但更不该让人凭一个拼错的角色名拿到权限。
*/
export function toAdminType(value: string): AdminType {
return (ADMIN_TYPES as readonly string[]).includes(value) ? (value as AdminType) : "Regular User"
}
export function toProblemPermission(value: string): ProblemPermission {
return (PROBLEM_PERMISSIONS as readonly string[]).includes(value)
? (value as ProblemPermission)
: "None"
}
/**
* 按名字取值的写法(`USER_TYPE.SUPER_ADMIN`),给前端用 —— 它到处是这种比较,
* 换成数组反而更难读。和上面 ADMIN_TYPES 是同一批字符串,
* `satisfies` 保证两边不会各自漂走。
*
* 原先 `apps/web/src/utils/constants.ts` 里另抄了一份,那是这四个字符串的第三份副本。
*/
export const USER_TYPE = {
REGULAR_USER: "Regular User",
STUDENT_ADMIN: "Student Admin",
TEACHER_ADMIN: "Teacher Admin",
SUPER_ADMIN: "Super Admin",
} as const satisfies Record<string, AdminType>
export const PROBLEM_PERMISSION = {
NONE: "None",
OWN: "Own",
ALL: "All",
} as const satisfies Record<string, ProblemPermission>