Files
ClassHelper/packages/server/src/modules/update/update.schemas.ts
T
LoveChengke 66be3a63b1 feat(authz): 重写权限模板分配与班级授权边界
拆分能力、分配范围和业务关系,固定管理员能力并收紧组织级写入口。
补齐内容、课表、设备会话和实时通道授权,兼容旧库分配及历史快照。
重写权限分配预览,按实际可操作范围展示维护入口,合并学生入口并独立学年升级页。
增加授权攻击矩阵及静态门禁,更新隔离回归与开发契约。

验证:授权矩阵 23 段通过,Node 24 下 Web 63/63,类型检查、lint 与文档严格构建通过。
2026-10-31 09:40:23 +08:00

44 lines
1.6 KiB
TypeScript

import { z } from 'zod';
/**
* 安装包文件名/版本号只允许这些字符。
*
* 它们会被拼进磁盘路径(`data/client-releases/<版本>/<文件名>`),
* 所以必须挡住 `..`、`/`、`\` —— 否则一个 `version=../../` 就能写到仓库外面去。
*/
const SAFE_SEGMENT = /^[0-9A-Za-z._-]+$/;
export const clientReleaseVersionSchema = z
.string()
.trim()
.min(1, '请输入版本号')
.max(40, '版本号最多 40 个字符')
.refine((value) => SAFE_SEGMENT.test(value) && value !== '.' && value !== '..' && !value.includes('..'), {
message: '版本号只能包含字母、数字、点、下划线与短横线',
});
/** `POST /api/update/client/fetch`:让服务端替教室机器从 GitHub 拉一份安装包 */
export const fetchClientReleaseSchema = z.object({
version: clientReleaseVersionSchema,
});
/** `POST /api/update/client/releases`:登记一份已经放进磁盘的安装包(手工兜底与自动化用) */
export const registerClientReleaseSchema = z.object({
version: clientReleaseVersionSchema,
fileName: z
.string()
.trim()
.min(1)
.max(120)
.refine((value) => SAFE_SEGMENT.test(value) && value !== '.' && value !== '..' && !value.includes('..'), {
message: '文件名只能包含字母、数字、点、下划线与短横线',
})
.optional(),
notes: z.string().trim().max(500).optional(),
});
export const clientReleaseVersionParamSchema = z.object({ version: clientReleaseVersionSchema });
export type FetchClientReleaseInput = z.infer<typeof fetchClientReleaseSchema>;
export type RegisterClientReleaseInput = z.infer<typeof registerClientReleaseSchema>;