Files
LoveChengke 817af02357 feat(permissions,clients,ai,system,server,web,client,shared)!: 权限组 + 客户端设备远控与自动更新 + 大模型助手 + 系统诊断(1.3.0)
功能级准入改为「权限键」,`requireRole` 退成默认模板。

权限组
- 权限目录(39 键:键 / 中文名 / 分类 / 默认角色)收口到 shared,路由挂 requirePermission / requireAnyPermission
- 有效权限每请求实时算,改完分组立刻生效:ADMIN 全集且 permission:manage、system:manage 不可剥夺;
  CLASS_DEVICE 空集(不受分组影响);有分组 = 并集(因此也能用来**收回**权限);无分组 = 角色默认模板
- 新增「权限组」页:组增删改、成员分配、生效权限预览
- 班级维度的科目规则(assertCanManageSubjectContent)故意不可被权限组覆盖

客户端设备(ClassHelper 桌面端)
- 设备身份是 deviceKey(首次启动生成的 UUID,saveConfig 拒绝改写、退出登录也保留)
- 心跳走「DB 记状态 + 客户端定时拉」,不引入新的长连接:30 秒一次,在线窗口 90 秒
- 远控命令 shutdown / restart / logoff / lock:服务端有效期 10 分钟,客户端弹默认选中「取消」的对话框、
  60 秒未确认按取消处理,执行后必须回执(重复回执幂等),同一台机器同一条命令只弹一次
- 新增「客户端设备」页:版本一览、一键推送更新、下发远控

客户端安装包由服务端下载并缓存(教室机常常没有外网,浏览器也受 CSP 限制)
- POST /api/update/client/fetch 由服务端下载到 data/client-releases/<版本>/ 并登记 sha256 + size
- 自动安装三条护栏:没有 sha256 就拒收、必须等到非上课且主窗口隐藏满 5 分钟、冒烟模式永不安装

大模型助手:只解析、不落库
- POST /api/ai/interpret 返回**待确认**的结构化动作(建作业 / 发通知 / 叫人),
  真正落库仍调既有端点 —— 权限断言、上课时段紧急通知保护、ClassIsland 推送全部自动复用
- 模型给的 params 用与手动入口**同一份** zod 再校验(模型输出不可信)
- API Key 走 AES-256-GCM(密钥由 JWT_SECRET 经 scrypt 派生)加密存 AppSetting,只进不出
- provider=mock 是离线确定性解析,自动化验收一律用它;每次解析写一条 AiAudit(不含密钥)

系统与运维
- 开发者模式(服务端内存缓存开关,热路径不查库;开启后 500 才附 error.stack)、运行诊断(绝不回传密钥)
- 开机自启动运行期可改:Windows 改 HKCU\...\Run、Linux systemctl enable/disable,
  **非安装形态一律返回 supported:false**,不往开发机的注册表里写自启项
- 服务端侧栏折叠只用 CSS 收拢(不重建 DOM),关怀模式只放大字号与控件、不动任何几何令牌

客户端
- 崩溃自愈:渲染进程挂掉 / 超 10 秒无响应先静默重建主窗口(10 分钟最多 3 次),
  未捕获异常只在「没在上课、没人看着」时才重启进程;冒烟模式绝不重启(会杀掉验证实例)
- 上线补弹离线期间收到的消息只做一次:起点只在「可达 → 不可达」的边界写,恢复时取最近 5 条按时间升序推

发布内容批量
- POST /notifications/bulk、POST /homeworks/bulk:**单班失败不影响整批**,逐班返回结果
- 作业的科目名跨班**按名匹配**已有课程,找不到就整班记失败,绝不自动建课
  (自动建课会把创建者写成任课老师,等于越权改任教关系)
- 通知与作业同口径按「所属日期」归档,新增 /notifications/days 与 /notifications/creators

BREAKING CHANGE: 能不能做某件事不再看角色名,而是看权限键(GET /api/permissions/me)。
未分组的账号按角色默认模板生效,行为与 1.2.0 一致;分组之后生效集合只由组的并集决定。
服务端首次启动会自动应用 1 个数据库迁移(权限组 3 表 / AppSetting / 客户端设备·命令·安装包 /
AiAudit / Notification.assignDate / User.careMode,历史通知按本地日期回填),升级前请先备份。
2026-10-11 11:49:32 +08:00

77 lines
4.5 KiB
Markdown
Raw Permalink 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.
---
title: 管理运维
description: 部署形态怎么选、备份与恢复、换数据库、升级与回滚,以及上线前要逐项确认的清单。
---
# 管理运维
这一章写给管服务器的人。如果你只是要用它,看[应用帮助](../app/index.md)就够了。
## 先选部署形态
| 形态 | 适合 | 产物 | 详细 |
| ------------------------ | ---------------------------------------- | ----------------------------------- | ----------------------------------- |
| **Windows 安装包** | 学校机房、教师办公电脑(单机即完整系统) | NSIS 安装程序,内置 Node 运行时 | [生产部署指南](production.md) |
| **Linux 原生 + systemd** | 学校已有 Linux 服务器 | 一键安装器 + `classhelper` 运维命令 | [Linux 部署与运维](linux-deploy.md) |
| **Docker** | 云服务器、跟别的服务共用一台机器 | `deploy/docker-compose*.yml` | [生产部署指南](production.md) |
| **手动部署** | 已有 Node 环境、需要自定义 | 仓库源码(`pnpm build` 后直接跑) | [生产部署指南](production.md) |
一句话的取舍:
- **只有一台 Windows 机器** → 安装包形态,装完就能用,卸载也不留东西。
- **有 Linux 服务器** → 一键安装器,之后所有维护都在 `classhelper` 这一条命令里。
- **要对外提供服务** → Docker 或 Linux + Nginx 反代 + HTTPS(公网部署**必须**有 TLS)。
## 数据库:默认 SQLite,可以换 MySQL
默认用内嵌的 SQLite,**单校规模完全够用**,而且备份就是复制一个文件。
多校区 / 高并发 / 需要多实例再换 MySQL:
- 换法见[切换到 MySQL](mysql.md)(Web 端一键切换 / 命令行手工切换两条路);
- 备份、导出导入与跨库迁移共用同一种 JSON 快照格式,**任意备份能恢复回任意一种受支持的库**;
- 见[数据库管理](database.md)。
> 主库**只支持 SQLite 与 MySQL**。Redis 是内存键值库,Prisma 不支持它建表 / 迁移,接口层直接拒绝。
## 升级
不同形态的升级方式不一样,共同点是:**升级数据不会丢,但升级前一定先备份**。
| 形态 | 升级方式 |
| ----------- | -------------------------------------------------------------- |
| Windows | 停服务 → 备份 `data` → 跑新版本安装程序(覆盖安装保留 `.env`) |
| Linux | `sudo classhelper upgrade`(自动下载、校验、快照、失败回滚) |
| Docker | `docker compose pull && docker compose up -d` |
| 手动 / 源码 | 替换程序目录,启动时自动补跑数据库迁移 |
完整流程、回滚与注意事项见[更新与升级](upgrade.md)。
## 上线前逐项确认
- [ ] 改掉 `admin` 与教师账号的默认密码;
- [ ] `JWT_SECRET` 是随机值(≥ 48 字节)并妥善保管 —— **换它会让所有人重新登录**;
- [ ] 公网访问走 HTTPS 反向代理(`deploy/nginx.conf` 可直接用);
- [ ] `CORS_ORIGIN` 收敛到具体域名,不要留 `*`;
- [ ] `TRUST_PROXY=1` 并确认限流按真实 IP 生效(`/api` 响应头有 `RateLimit`);
- [ ] 反向代理**放行 `/socket.io/` 的 WebSocket 升级**(否则实时推送静默失效);
- [ ] 数据库账号用最小权限,只授权业务库;
- [ ] 配好定时备份,并且**验证过能恢复**;
- [ ] 关注 `/readyz` 与日志里的 5xx。
## 监控与日志
| 端点 | 用途 |
| ------------- | ---------------------------------------- |
| `/healthz` | 存活探针:进程活着即 200 |
| `/readyz` | 就绪探针:数据库可连才 200,否则 503 |
| `/api/health` | 详情:环境、数据库、已挂载模块、运行时长 |
日志输出到 stdout/stderr;安装包形态由启停脚本重定向到 `logs/server.log`(UTF-8)。
访问日志含方法、路径、状态码与耗时,5xx 记 error、4xx 记 warn。
## 故障排查
常见现象与排查方向列在[生产部署指南](production.md)的最后一节,
以及[常见问题](../faq.md)。Linux 特有的问题(`systemctl` 启动即退出、`EACCES`、
SQLite `database is locked`)在 [Linux 部署与运维](linux-deploy.md) 的故障排查一节。