LoveChengke
|
24e8dd902b
|
feat: 完善学生生命周期管理与权限范围并发布 1.4.2
|
2026-10-23 22:55:21 +08:00 |
|
LoveChengke
|
6347d1e5aa
|
feat: 完善课表档案、课件归档、提醒与客户端更新并发布 1.4.1
- 重写课表档案工作区并合并时间表、课表与完整档案导入导出
- 修复课前/上下课提醒遮罩与通知收回冲突
- 增加课件文件夹分类、教师归档路径和客户端本机归档
- 打通客户端下载/安装/完成状态上报并在管理端展示更新过程
- 修复侧栏折叠裁切、课时多选与上传控件样式
- 统一版本到 1.4.1
v1.4.1
|
2026-10-18 17:39:54 +08:00 |
|
LoveChengke
|
2612a912ef
|
chore(release): 重打包并提交 1.4.0 交付物
v1.4.0
|
2026-10-18 13:46:09 +08:00 |
|
LoveChengke
|
c7f215a09c
|
feat(courseware,docs): 完善课件传输与岛编辑并统一全仓文档
- 增加课件上传、班级可见范围、设备补收与传输进度
- 整合自定义灵动岛编辑器和相关客户端交互
- 同步三端契约、数据库迁移、部署配置与隔离回归
- 按源码统一文档与官网说明,移除过时任务书和审查记录
- 保持产品版本为 1.4.0
|
2026-10-18 11:53:37 +08:00 |
|
LoveChengke
|
536379036a
|
feat(ui): 补齐课程提醒进度并内嵌编辑器与重构后台导航
|
2026-10-17 21:21:28 +08:00 |
|
LoveChengke
|
3379eb975c
|
feat(island): 对齐 ClassIsland 课表提醒并恢复开合动画
|
2026-10-17 16:43:36 +08:00 |
|
LoveChengke
|
e84e43544a
|
fix(island): 合并常驻与自定义岛并统一经典通知和即时课表同步
|
2026-10-17 15:56:44 +08:00 |
|
LoveChengke
|
05848570d1
|
fix(island): 重写常驻岛自适应界面与 ClassIsland 动画
|
2026-10-17 15:12:02 +08:00 |
|
LoveChengke
|
7f7d3925b2
|
feat(island): 增加常驻内容并沿用 ClassIsland 尺寸与避让设置
|
2026-10-17 14:29:25 +08:00 |
|
LoveChengke
|
c83b03d8e6
|
fix(island): 将组件与提醒合入岛体并重写布局设置
|
2026-10-17 13:45:26 +08:00 |
|
LoveChengke
|
ead66f0b93
|
test(docs): 补齐隔离验收与功能实施记录
|
2026-10-17 13:04:22 +08:00 |
|
LoveChengke
|
57069a9a75
|
fix(grades,term): 封闭实时成绩泄露并完善并发升级审计
|
2026-10-17 12:55:40 +08:00 |
|
LoveChengke
|
b990701cde
|
feat(client): 实现可编辑岛组件方案与连接更新状态反馈
|
2026-10-17 12:55:39 +08:00 |
|
LoveChengke
|
35b252249d
|
feat(web): 增加日课表编辑与学期设备管理界面
|
2026-10-17 09:38:29 +08:00 |
|
LoveChengke
|
4dc824c283
|
feat(server): 统一课表解析并完善权限范围与年度升级
|
2026-10-17 09:34:23 +08:00 |
|
LoveChengke
|
66ef878a71
|
feat(shared,prisma): 建立日课表与岛组件契约及数据范围模型
|
2026-10-17 09:33:32 +08:00 |
|
LoveChengke
|
c0ebf88138
|
chore(release): 1.3.3 发布并退役旧联动
v1.3.3
|
2026-10-17 03:06:46 +08:00 |
|
LoveChengke
|
b7cdc7c9ea
|
fix(clients,client,web): 远控指令的两个修复 —— 执行失败不再谎报成功 + 「上次指令」让"点了没反应"可见(1.3.2)
背景:用户报「下发锁屏等等指令不生效」。先把服务端那条链路单独验了一遍
(模拟客户端:登记 → 下发 → 心跳带回 → 设备列表可见 → 回执 → 清除),**整条是通的**;
真正的问题在两处。
客户端:执行失败会谎报成功
- `spawn` 找不到可执行文件 / 没权限时**不抛异常**,而是在下一个 tick 发一个异步 `error` 事件 ——
只包 `try/catch` 抓不到,代码一路走到 `return { ok: true }`,回执写着「已下达系统命令」,
Web 端看起来一切正常而机器纹丝不动
- 现在等一个 200ms 的窗口确认没有立刻报错,失败如实回执(`client-agent.ts` 的 `executeCommand`)
Web 端:点了没生效,界面上什么都不说
- 一条命令被**取消 / 过期**之后就从 `pendingCommand` 里消失,页面留白 ——
而最常见的两种"没反应"恰恰是「本机没人确认(客户端 60 秒自动按取消)」与「下达时机器离线」
- `GET /clients/devices` 增加 `lastCommand`(最近一条,不论状态);
「客户端」页的设备表多一列「上次指令」,由 `commandStateText()` 翻成一句人话
(已执行 / 本机用户取消 / 已过期 —— 下达时机器不在线 / 待执行)
- 下发成功的提示补上「60 秒内没人点确认就按取消处理」
文档
- AGENTS.md §5 第 69 条记下这两条,并写明排查同类反馈的顺序:**先看那台机器的客户端版本是否 ≥ 1.3.0**
(远控命令的处理 `client-agent.ts` 是 1.3.0 才有的,旧客户端连心跳都不发)
- docs/reference.md 4.15 的表格补上「二次确认」的准确口径与新增的「上次指令」列
验收:`lint` / `typecheck` / `check:icons` / `docs:check` 全绿;
后端端到端 **247/247**、联动链路 **44/44**、插件静态契约 **76/76**、
Web 真实点击 **34/34**、客户端冒烟 **90/90**;
远控链路另用模拟客户端跑通,并确认新增的「上次指令」在待执行与已回执两种状态下都显示正确。
v1.3.2
|
2026-10-11 17:00:11 +08:00 |
|
LoveChengke
|
227117ccb3
|
fix(integrations,grades,clients,web,client,plugin): 课表同步改为按需(修「课表被反复删改」)+ 客户端页合并 + 成绩排名(1.3.1)
线上问题的根因
- ClassIsland 插件默认每 15 秒上报一次,每次都带全量课表,上报完还顺手把课表镜像回教室
(本机 `UploadSchedule` / `MirrorSchedule` 两个开关默认都是开)。于是教室的课表每 15 秒被重写、
服务端也每 15 秒 merge + 清理一次幽灵行 —— 老师看到的就是"课表被反复删改"。
- 现在两个方向都只由 Web 端的一次性待办触发,插件侧那两个开关整体删除;
定时上报只报**运行状态**(当前上什么课),不带课表。
课表同步(#3)
- 新增 `IntegrationDevice.requestMirrorSchedule` + `POST /integrations/devices/:id/mirror-schedule`
(「下发课表」),与既有的 `request-schedule`(「获取课表」)对称;拉取成功即清标记
- 插件 `ClassPlanWriter.Apply(..., force: true)`:停用同一天同一单双周相位下冲突的旧课表
—— **只停用不删除**,内容原样留在档案里,老师随时能重新启用
- 插件设置页删掉「自动上报课表」「镜像回 ClassIsland」两个开关,改为一句说明
「客户端」页(#4)
- 把「ClassIsland 联动」与「客户端设备」合成一页 `ClientHubView.vue`:一个班一张卡片,
两条链路状态放一起,勾选可批量下发课表 / 更新,点卡片进详情做逐台操作
- 「下发提醒」也搬了过来;入口权限用 `classisland:notify`(默认 ADMIN + TEACHER),
页内按钮再各自按 device:manage / remote:control / client:update-push 显示或禁用
- 两个旧页面 `meta.hidden: true` **保留路由**(安装包缓存管理、联动步骤说明仍在),
从详情抽屉里给链接 —— 不删功能
成绩(#5/#6/#7)
- 新增 `Exam`(班级 + 考试名 + `countInTotal`)与迁移;`GET/PUT /api/grades/exams`、
`GET /api/grades/ranking`(两条都**不挂权限键**,班级端的有效权限是空集,边界在 service 里)
- 排名口径写在服务端:**同一学生同一科目只取最高分**(补录 / 重考会多行,不去重等于"总分多一科")、
并列同名次(1、2、2、4)
- 「成绩录入」页加「考试口径」开关(按班级 + 考试名记住);客户端「成绩」页新增总分榜 + 单科榜
- `updateStudentGradeQuery` 改成只有教师 / 管理员能改 —— 原先班级端自己也能改
- 客户端的成绩查询开关改为只读(只显示服务端下发的状态)
其他(#2/#5/#8/#9)
- 全校学期开始日期改成 `AppSetting` 的 `term.startDate`,管理员在「学期周次」页直接改、立刻重算周次
(原先这一级只能来自 `.env`)
- 大模型助手发紧急通知补上「上课时段 3 秒全屏警告」:判据抽成 `composables/useClassInSession.ts`,
与手动发布页共用
- 客户端去掉「关怀模式」「自动更新」两个开关(教室机器是统一投放的终端)
- 修掉「选中设置二级菜单后收起分组、高亮块仍留在原地」:底片只在路由 / 滚动 / 缩放时重测,
而收起分组动不了 `.aside` 的盒子 —— 补上"子项被 overflow:hidden 裁掉"的判据 + 观察分组 `<ul>` 高度
- `src/types/express.d.ts` 里 `req.device` 改成引用 `DeviceContext`:原先手抄了一份结构,
往接口加字段时两边就漂了,而报错会落在 `getDevice()` 的 `return device;` 上,看不出真正原因
验收:`lint` / `typecheck` / `check:icons` / `docs:check` 全绿;
后端端到端 **247/247**、联动链路 **44/44**、插件静态契约 **76/76**、
Web 真实点击 **34/34**(含新增的「客户端」页用例)、客户端冒烟 **90/90**。
v1.3.1
|
2026-10-11 16:21:38 +08:00 |
|
LoveChengke
|
3236d3ad83
|
fix(deploy): install.sh --version 1.3.0 下载 404 —— 拼地址时 tag 少了 v 前缀
Release 的 tag 是 `v1.3.0`,而 `--version` 的用法一向不带 v(用法提示与文档都写 `--version 1.1.2`),
GitHub 的 `/releases/download/<tag>/` 又是**按 tag 精确匹配**的(`releases/tags/1.3.0` 直接 404),
于是 install.sh 直接把 `VERSION` 拿去拼出来的地址必然 404:
[classhelper] 下载:.../releases/download/1.3.0/classhelper-server-linux-x64-1.3.0.tar.gz
curl: (22) The requested URL returned error: 404
同一个功能上,`classhelper` 的 `resolve_download()` 一直是 `tag="v${want#v}"`,install.sh 漏了这句 ——
所以 `classhelper upgrade --version 1.3.0` 正常、`bash install.sh --version 1.3.0` 却 404,
两处只在 `deploy/` 内部对不齐。现在两边都归一化。
`VERSION` 本身**保持不带 v**:`tag` 是另一个局部变量,装完写进安装目录的 VERSION 文件用的仍是不带 v
的裸版本号,别把它一起改了。
实测(把 install.sh 里的 `resolve_package_url()` 原样抽出来单独跑,不手抄):
| VERSION 传参 | 修复前 | 修复后 |
| ------------- | --------------------------------- | ------------------------------------ |
| `1.3.0` | `.../download/1.3.0/...` → 404 | `.../download/v1.3.0/...` |
| `v1.3.0` | `.../download/v1.3.0/...` | `.../download/v1.3.0/...` |
| 留空(latest) | `.../download/v1.3.0/...` | `.../download/v1.3.0/...` |
`bash -n` 四个脚本(install.sh / classhelper / verify-linux.sh / install-linux.sh)全过。
本机 curl 到 github.com 超时(见 §7 第 1 条),所以**真正下载要走服务器实测**。
AGENTS.md §5 第 59 条补 ⑦ 记下这条约定。
|
2026-10-11 14:28:07 +08:00 |
|
LoveChengke
|
0b9a321bc9
|
docs(release,website,readme): 1.3.0 的更新日志、官网功能块与 README 清单,并把「每次发版四件套」写进 AGENTS.md
- `releases/ReleaseNotes-1.3.0.md`:1.3.0 的 Release 正文草稿(一句话定位 / 七个分节 /
⚠️ 升级须知 / 验收 / 产物表)。验收表只写本轮真跑过的静态门禁,运行时验收没跑就直说没跑
- `website/index.html`:老师与管理员加 #权限 / #教室机器 / #大模型助手 / #批量发布 四块,
教室机器与学生机加 #自愈 一块(11 → 16 块,实测渲染正常)
- `README.md`:功能清单补上权限组、客户端设备、大模型助手、批量发布、系统设置、关怀模式、
崩溃自愈与离线补弹,以及「客户端更新不依赖外网」
- `AGENTS.md` §4.4 新增「每次发版必须一起交付的四样东西」(更新日志 / Linux 包 / 文档 / 官网与 README),
并记下「CI 按 tag 的 ref 检出源码」这个坑 —— 发完版再改构建脚本时**光推 master 不够**,
必须把 tag 挪到修复后的提交;第 ⑦ 步的示例也改成用 `--notes-file`
- `docs/dev/release.md`:同一份四件套清单(面向人)+ 第 7 步改用 `--notes-file` + tag 那个坑
`pnpm docs:check` 0 warning 0 error。
|
2026-10-11 14:23:29 +08:00 |
|
LoveChengke
|
966c8f9b5d
|
fix(dist): Linux 包打不出 tar.gz —— 交付目录不再由 staging 顺带创建
1.3.0 把服务端组装中间物从 `releases/server/<版本>/linux-x64/classhelper-server-linux-x64/`
挪进 `.cache/server-staging/` 之后,`releases/server/<版本>/linux-x64/` 就再没有谁创建它了 ——
`tar -czf <那里>/classhelper-server-linux-x64-<版本>.tar.gz` 直接以
`tar (child): ...tar.gz: Cannot open: No such file or directory` 退出码 2 失败。
Windows 侧不受影响(`buildInstaller()` 里有 `mkdirSync(INSTALLER_DIR)`),
所以本地 `pnpm dist:server` 永远踩不到;`release-linux-server.yml` 在 v1.3.0 的 tag 上一跑就红
(run 38109984623 挂在「构建 Linux 安装包」)。补救是幂等的:目录已存在就什么也不做。
`packLinuxTarball()` 的 tar 调用与 1.2.0 逐字节相同,而 1.2.0 的 CI(run 37458412997)是绿的
—— 唯一的差异就是那句隐式创建没了。
v1.3.0
|
2026-10-11 14:17:37 +08:00 |
|
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 |
|
LoveChengke
|
e51879667a
|
feat(website): 官网首页首屏下面直接接一段下载区(版本 + 装什么 + 卡片)
- 首屏下面新增 `#download` 一整段,与 /download.html 是同一套类名、同一个 `assets/download.js`:
访客不跳页就能下;完整的下载页仍然保留(顶栏「下载」指着它);
- `download.js` 改成两个页面都能跑:首屏那颗主按钮只有下载页有,所以它不再是必需元素 ——
之前首页没有它,整个脚本会静默 return,那一段什么也不渲染;
- **兜底版本号改成"读页面自己显示的那一份"**:下载页读 `<meta name="ch-version">`,
首页读顶栏那个版本徽标(`.brand-ver`)。两个都是 `pnpm version:bump` 的既有落点,
所以不必为首页再添一个手写版本号(落点数仍是 14);
- 顺带:首页那条 Linux 安装命令现在也会跟着选中版本钉上 `--version`(原先不带);
- 修一处"字看着被切断":两个分段控件是**横滚**的,而滚动条被藏了起来
(`scrollbar-width: none`),装不下时末尾那个版本号就是硬切一刀、还没有任何提示。
现在溢出时右边缘淡出,滚到最右自动撤销;主按钮下面那行 mono 文件名也从 0.68rem 提到
0.75rem(它原本是全站最小的字,再小就看着像被裁掉一截)。
|
2026-10-06 19:56:51 +08:00 |
|
LoveChengke
|
9d5ac7adf9
|
feat(shared,server,web,client,plugin)!: 角色与权限重构 + 学期周次 + 课表方向反转(1.2.0)
学生、任课关系、周次口径三处长期靠约定维持的事情,改为由数据模型保证。
学生不是账号
- 拆出独立 Student 表,没有 passwordHash / role / 任何登录字段
- 「学生不能登录」由表结构保证,不再靠「登录前判 role 再 403」的代码分支
- 三张个人数据表外键 userId → studentId;删掉 Enrollment 与 ClassTeacher
- 学生一律称「学号」、教师一律称「工号」,「学生账号」这个概念消失
任课关系唯一来源 = Course(班级 + 科目 + 教师)
- 班主任 = Class.teacherId,科任 = Course.teacherId,同一人两处指向同一账号、不重复建号
- 作业与成绩按科目再收一道:挂科目只由该科任课老师改,没挂科目归班主任 / 管理员
- 新增「任课教师分配」与「班级教师批量导入」(带「使用工号匹配」开关)
成绩与名单
- 成绩等级:百分制 / 等级制 A·B·C·D / 自定义,支持单个与批量改等级
- 按学号查成绩明细(教师端 + 班级端 + 设备令牌),受按班开关控制
- 调班 / 转出(单个与批量)+ 可查历史;学号不变、历史作业成绩留原归属
- 毕业归档页(按年度记录毕业班级 / 毕业生 / 转出学生 / 该届通知与作业)
- 学年升级(未毕业不归档、数据沿用);班级称呼改为「2026级1班」
ClassHelper 班级端在线状态
- Web 端显示 班级 / 设备名 / 设备号 / 在线离线 / 最后在线 / 最后心跳
- lastHeartbeatAt 驱动 60 秒在线窗口,与只表示「带状态上报」的 lastSeenAt 分开
学期周次
- 新增 TermWeek(classId 空串 = 全校默认),管理员可用日期范围选择器逐周配置
- 一键按开学日期生成整学期;联网拉取调休建议(只给建议、不改数据,无外网失败属正常)
- 改周次后随课表下发给教室的 ClassHelper 班级端
课表方向反转
- 主方向改为「服务端 → 教室」自动下发;教室不再自动回传
- 取教室课表改为手动动作(点击前弹警告),插件按待办标记在下一次心跳补报
改名
- 班级小助手 → ClassHelper:界面、窗口标题、托盘、灵动岛、EXE、安装包、
安装目录、systemd 描述、插件清单与设置页、官网、文档站、README
BREAKING CHANGE: 产品名与 EXE 名变更(班级小助手.exe → ClassHelper.exe),
安装目录随之改变,不支持覆盖安装,需先卸载旧版。
服务端首次启动会自动应用 6 个数据库迁移(学生行按 id 原样搬迁,历史数据不丢),
升级前请先备份。
v1.2.0
|
2026-10-06 19:41:18 +08:00 |
|
LoveChengke
|
17f55864bf
|
feat(website): 下载页改成「客户端 / 服务端 / 插件」切换,卡片照 ClassIsland 官网那排重做
进来默认停在**客户端**(原先那颗主按钮指着服务端),服务端与插件各是一个可切换的分组。
- 新增「装什么」分段控件(客户端 / 服务端 / 插件):客户端一组一张卡,服务端一组两张
(Windows + Linux),插件一组一张。它与版本选择器共用同一套指示器实现(beUI Tabs 的滑移;
位置用 offsetLeft / offsetWidth 量,所以版本多了横滚之后也不会错位);
- 卡片改成 ClassIsland 官网那一页的样子:平台图标 + 名称 + 系统要求 + 主按钮 + 变体下拉。
第一个文件是主按钮,其余进 `<details>` 下拉(点别处或 Esc 收起);插件那张只有一个文件,
就不摆一个点不开的箭头;
- 主按钮下面留一行 mono 文件名与体积 —— 自托管场景里"点下去拿到哪个文件"是要看得见的;
- 首屏那颗主按钮跟着「版本 + 分组」走:默认是这一版客户端的安装版,切分组就跟着换;
- Linux 那个企鹅重画了一版:窄头宽肚 + 白脸 + 橙嘴橙脚(卡片底色恒为纸白,可以用实色)。
原先那版头身糊成一团,44px 下认不出来;
- 卡片整排**左对齐**(跟着这一节的标题走):参考图那一页整体居中,但本站在每一带都是
左对齐的节奏,孤零零一张卡居中会跟标题对不上。窄屏下卡片单列、分裂按钮撑满卡片宽。
`<meta name="ch-version">` 那一行的版本号没有跟着工作区里的 1.2.0 一起提交:仓库里另外
13 个落点还是 1.1.2,单独把它改成 1.2.0 会让 `pnpm version:check` 在 HEAD 上直接失败。
等整批版本号一起提交时它会自然跟上(它是 `scripts/version.mjs` 的落点之一)。
|
2026-10-06 19:35:59 +08:00 |
|
LoveChengke
|
0ea6bd887f
|
feat(website,docs,build): 官网加一个下载页(挑版本装机),文档站的照片换成与 README 同一份
下载页(`website/download.html` + `assets/download.js`)
- 官网首页的「立即下载」、顶栏与页脚的「取安装包」全部指向新页 `/download.html`。
那一页是「先挑一个版本,再挑要装的那一份」:一个版本分段控件 + 四张卡
(班级端 / 服务端 Windows / 服务端 Linux / ClassIsland 插件),每张卡列出这一版里
真实存在的文件、体积与直链;
- 版本与文件清单**不在站点里存副本**,每次打开都由 `assets/download.js` 当场读 GitHub
Releases —— 文件名、体积、链接照抄发布页,不靠命名约定去猜;
- 读不到时(离线,或匿名接口 60 次/小时按出口 IP 被限流)退回页面上那份
`<meta name="ch-version">`,按发布命名约定拼直链,并在页面上**明说这是拼出来的**;
- 老版本缺哪个包就显示「这一版没有发布…」(`v0.1.0-fix` 只有插件包,实测正确);
Linux 那一行安装命令跟着选中版本钉上 `--version`(tag 不是 `x.y.z` 时不带);
- 动态建出来的卡片与文件行派发 `ch:content`,由 `main.js` 补挂按压反馈
—— 这些元素是拉到清单之后才出现的,启动时还看不到它们。
文档站的照片(`docs/images/`)
- 页面里的图一律从 `docs/screenshots/**` 换成 `docs/images/**`:后者就是 README 与官网
吃的那一份(字节相同),前者是 9-26 的冒烟留档,比 README 落后一个版本;
- 展示图与回归留档这两套图的分工写进 `docs/dev/testing.md`「截图留档」,含重新采集命令
(冒烟会把「岛外留白对照图」落在同一个目录,所以要先落临时目录再挑);
- 顺带修 README 手机截图那张标错的图注(`mobile-dashboard.jpg` 是仪表盘,不是抽屉菜单);
- `docs/reference.md` 验收表里那张 ClassIsland「真机实测截图」不再嵌图:它是连着桌面、
任务栏和另一个窗口整屏截下来的,当文档配图不能用。断言与用例名都保留,另外注明了抓图位置。
版本落点
- `scripts/version.mjs` 增加 `website/download.html` 的兜底版本号,落点 13 → 14 处
(`AGENTS.md` §4.4 与 `docs/dev/release.md` 同步);
- README「开始使用」表里那三个文件名改成**发布页上真实的名字**
(`-x64-client-setup` / `-x64-client-portable` / `-x64-server-setup`)—— 原来写的是构建
产物的名字;「上传前把产物改成 ASCII 名」这一步也一并写进发布流程,
它是官网下载页离线兜底与 README 表格的共同依据。
|
2026-10-06 18:37:42 +08:00 |
|
LoveChengke
|
ded10118f5
|
feat(website,docs,build): 首屏改成大标题 + 整行渐变填色,官网与文档站合并发布到 GitHub Pages
官网(`website/`)
- 首屏标题放大一档(`--fs-display` 上限 3.6rem → 4rem),第二行改成**整行渐变填色**,
照 ClassIsland 官网那种「大标题 + 中间一段渐变 + 一行副标 + 一排徽章 + 两个按钮」的写法;
- 标题下补一排 mono 芯片(版本 / Windows / Linux·Docker / 触摸屏),主按钮换成
「立即下载」+「GitHub 仓库」;
- 顶部导航与页脚补「文档站」入口;页脚原本指向 GitHub blob 的那三个链接改成指向文档站
—— 那几个文件已经移到 `docs/management/` 下,原链接是 404。
> ⚠ 渐变那一行**不参与逐字揭示**:`main.js` 的 `setupHeadlineReveal` 会跳过 `.reveal-grad`。
> 拆成 per-char 的 `inline-block` 之后每个字各自建一层合成,祖先的 `background-clip: text`
> 就穿不过去,渐变会整行失效 —— 所以它按整行入场(透明度 + 轻微上浮)。
站点发布(`.github/workflows/pages.yml`,取代 `docs.yml`)
- **一个仓库在 GitHub Pages 上只有一个站点**,所以官网与文档站拼成一份产物:
官网在根、文档站在 `/docs/` 下,组装逻辑在 `scripts/build-pages.mjs`;
- 站点根 404 由 `website/404.html` 提供(含 `{{BASE}}` 占位符,由脚本与 workflow 按仓库名注入);
- 官网目录里的 `serve.mjs` 与 `tools/` 是开发用的,不发布;
- `website/serve.mjs` 把 `../docs/_site` 挂在 `/docs/` 下,让本地预览与线上同形 ——
否则页脚那几个「文档站」链接在本地全是 404;
- 文档站模板顶栏补一个「官网」入口(`{{_rel}}../`)。
版本迭代
- `scripts/version.mjs` 补上**官网上手写的三处版本号**(顶栏徽标 / 首屏芯片 / 页脚)。
官网是纯静态站、没有构建步骤,没有哪段代码会去读 `package.json`,漏改的后果是
首页一直挂着旧版本号 —— 而那是别人看到的第一眼。落点从 10 处变成 13 处,
`AGENTS.md` §4.4 与 `docs/dev/release.md` 同步。
|
2026-10-06 16:44:23 +08:00 |
|
LoveChengke
|
19c2aa07b2
|
feat(docs,scripts,build): 用 docfx 搭文档站并发布到 GitHub Pages,版本号收口成脚本迭代
文档站(`docs/` 就是 docfx 项目,发布在 https://lovechengke.github.io/ClassHelper/):
- 四章 + 常见问题 + 完整参考,共 44 页:快速上手(8 页)/ 应用帮助(10 页)/
管理运维(6 页)/ 开发文档(11 页)。信息架构照 ClassIsland 文档站来:
顶栏分章、左侧章节导航、右侧本页目录、上一页 / 下一页、站内搜索、深色主题跟随系统;
- 自定义模板覆盖(`templates/classhelper/`):把 docfx modern 模板那套英文 UI 文案
(In this article / Improve this doc / Next…)换成中文,补 GitHub 入口与版本徽标,
并加一层只调排版与配色的 `public/main.css`(不动 DOM 结构);
- 站点顶栏的版本徽标取自根 `package.json`(`docs/build.mjs` 用 `--metadata` 注入),
不在文档里写第二份 —— 否则一定漂;
- 404 页单独生成:GitHub Pages 的项目页挂在 `/<仓库名>/` 子路径下,而浏览器是按**被请求的路径**
解析页面里的相对链接的,所以 `<base>` 必须钉到站点根,由 `build.mjs`(本地 `./`)与
workflow(CI 传 `/<仓库名>/`)分别注入;
- 构建前清空 `_site`:docfx 只覆盖不清理,删掉或改名的页面会以旧文件留在目录里,
而 Pages 上传的是整个目录。
版本迭代(**每一次 releases,不是每一次提交**):
- 新增 `scripts/version.mjs`。这个仓库里同一个版本号散在 **10 个文件**里(5 份 `package.json`、
`deploy/package.runtime.json`、两个 compose 的镜像 tag、插件 `manifest.yml`、以及插件 C# 里的
`PluginVersion` 常量),手工改必然漏一处,而漏掉的那处**不会报错**,只在某个不常走的分支上
表现为"版本号对不上"。现在收口成两条命令:`pnpm version:check`(逐处比对,不一致 exit 1)
与 `pnpm version:bump patch|minor|major`(一次改写全部 10 处,任何一处匹配不到或匹配到多处
就直接失败退出,不做尽力而为的部分改写);
- `AGENTS.md` 新增 §4.4「版本迭代(每一次 releases,不是每一次提交)」:写清版本号落在哪 10 处、
完整发布命令序列(体检 → 验收 → `dist:all` → 哈希清单 → 推 master → `gh release create` →
Linux 包由 CI 合并哈希行),以及"已发布的版本号不要复用"的理由。
其它:
- `.github/workflows/docs.yml`:推 master(`docs/**` / `package.json` / workflow 自身有变)即构建并
部署到 Pages;构建带 `--check`,把 docfx 的警告当错误 —— 最常见的警告是"某个 `.md` 没被任何
`toc.yml` 引用",那种页面有内容但没有侧边栏,属于必须修掉的疏漏;
- docs 目录重组后的引用同步:`production` / `mysql` / `linux-deploy` / `winisland-design-tokens`
移到新位置后,AGENTS.md、`deploy/`、`scripts/`、客户端源码注释里的路径引用一并更新;
- 文档站只发布 `docs/` 这一棵树,页面里指向树外的相对链接(`../AGENTS.md` 等)改成 GitHub 绝对 URL,
否则上线全是 404;
- 图标派生清单补上文档站模板里的 `logo.png` 与 `favicon.png`(`pnpm icons` 一并生成,
仍然只有一个图形源 `build/classhelper.{png,ico}`);
- `docs/_site` 进 `.gitignore` / `.prettierignore` 与 ESLint ignores;
- 根脚本补 `version:check` / `version:bump` / `docs:build` / `docs:serve` / `docs:check`。
|
2026-10-06 16:27:44 +08:00 |
|
LoveChengke
|
5b002a4f0b
|
docs(readme,docs,website): README 精简为展示页,说明移入 docs/reference.md,重采实机截图
README 从 1465 行的「产品说明书 + 验收对照表」改成门面页:居中头部 + 徽章 + 功能清单 +
实机截图 + 怎么装,细节外链到文档站与 docs/reference.md。原全文整份移入
docs/reference.md(不是删除),路径引用按新位置修正。
一并带上:
- docs 目录重组:production / mysql / linux-deploy → management/,winisland-design-tokens
→ dev/(引用同步)
- 新增 docs/images/:README 的展示图,全部是脚本把程序真的跑起来截的,不是示意图
- website/assets/shots/ 同步重采(旧图里是冒烟测试残条,如「联动页也不该下发」)
- capture-shots.mjs 支持 CAPTURE_OUT(逗号分隔多目录),一次采集同时喂官网与 README
- 修:种子作业没写 assignDate —— seed 直接走 prisma.create,绕过了 service 里
`assignDate ?? 今天` 的兜底,落成 schema 默认值空串,按天查看与日期高亮因此全是空的
- 修:客户端留档截图硬编码了作业日期,种子一重播就截成空看板
- AGENTS.md 同步:README 定位改为门面页,权威口径指向 docs/reference.md
|
2026-10-06 16:26:41 +08:00 |
|
LoveChengke
|
580f5bed16
|
fix(dist): 交叉构建 Linux 包时 tar 的盘符冒号坑(转正斜杠 + --force-local)
在 Windows 上跑 pnpm dist:server:linux 时,packLinuxTarball 传给 tar 的归档路径带盘符,GNU tar 会把 F: 当成主机名去解析远程归档,报 resolve failed 并以退出码 2 失败。现象很误导:包已经整套铺好,只差最后一步打 tar。反斜杠还会被当转义符。
改成路径统一转正斜杠并加 --force-local。该选项在 Linux 上无害(本地路径本来就该走本地分支),因此无条件加上、不做平台分支 —— Linux 上路径没有盘符,这个坑在 CI 与真机上永远复现不了,只在交叉构建时现形。已记入 AGENTS.md 第 5 节第 59 条与 README 的交付验收表。
v1.1.2
|
2026-10-06 15:52:05 +08:00 |
|
LoveChengke
|
ce9bed73f4
|
feat(shared,web-admin,desktop-client,build): beUI 动效层与两端深色主题,全项目图标改用设计导出的新图标
动效层:把 beUI 的动效语言移植到两端,令牌放在 packages/shared/src/motion.ts(纯常量,后端也引用得到)。弹簧用数值积分生成 CSS linear(),纯 CSS 即可驱动,不必每组件一个 rAF 循环;两端各一份 composables/motion.ts 负责 DOM 部分,在 app.mount() 之前注入 :root。含 reduced-motion 全局降级与 hover 能力探测;灵动岛只移植单调曲线。
深色主题:Web 管理端补齐,机制与桌面客户端一致;语义色令牌只覆盖颜色,不动圆角与动效令牌。默认跟随系统,用户手动点过之后以用户为准。
图标:build/classhelper.{png,ico} 成为全项目唯一图形源,pnpm icons 从它派生各端图标与界面内品牌标。版本号统一到 1.1.2。
|
2026-10-06 15:32:01 +08:00 |
|
LoveChengke
|
82c85b59c1
|
feat(dist,deploy,website): 交付产物统一归集到 releases/<组件>/<版本>,补齐官网与 Linux 验收
三个打包脚本原先各写各的输出目录,现在一律直接落在仓库根 releases/<组件>/<版本>/,分层固定为安装包/免安装/构建中间;dist-win 用 --config.directories.output 覆盖 electron-builder 的目录后再整理,显式传入时不覆盖。插件侧顺手修掉一个既有 bug:原先 rmSync 整个组件目录,重打一次就把上一版的留档抹掉,现在只清当前版本目录。verify-packaged 的 --exe 默认值跟着改到新落点。
deploy/verify-linux.sh 修掉管道在 pipefail 下的 SIGPIPE 误判(命中被算成失败);CI workflow 的出包与上传路径同步;electron-builder.yml 显式指定安装包图标。
website/ 是产品官网(纯静态、无构建步骤,不进 pnpm workspace),含 beUI 动效 token 与实机留档图;客户端冒烟相应加了采集留档图用的外观开关。
|
2026-10-06 15:31:38 +08:00 |
|
LoveChengke
|
7b1ae29774
|
feat(desktop-client,build): 换用设计导出的新图标(客户端 / 服务端安装程序 / 托盘)
build/classhelper.{ico,png} 是新的图标来源(设计导出、手工放入),不再走 `pnpm icons` 那条
SVG 渲染流水线(那条只会重写 icon.*,两套并存容易搞混):
- packages/desktop-client/build/classhelper.{ico,png}:客户端安装包与应用图标;
- packages/desktop-client/public/tray.png:托盘图标(由 classhelper.png 覆盖);
- 服务端安装程序与客户端的"接线"分别在 scripts/dist-server.mjs 的 @ICON@ 与
packages/desktop-client/electron-builder.yml 的 win.icon —— 这两个文件当前另有未提交改动
(另一处工作在进行),那两行会随那批改动一起入库。
说明:Web 管理端(PWA)的 favicon / icon-192 / icon-512 未动,仍是原图标。
|
2026-10-05 21:13:52 +08:00 |
|
LoveChengke
|
9e52cfad4d
|
fix(deploy): 改密前把密码规则说清楚,并让"不合规"提示与输入提示走同一个终端
实机反馈:`classhelper password admin` 连问三次"新密码:"却不说明为什么——用户得失败一两次
才知道规则(而且 read -s 不回显,屏幕上连星号都没有,更容易以为卡住了)。
- 开问之前先打印规则(≥8 位、大小写/数字/符号至少 3 类、不能与用户名相同、别用
admin/123456 这类开头,并给一个合规示例),并说明"输入不回显是正常的";
- 提示语带上次数:`新密码(第 1/3 次):`,三次都没过时明确告知密码未改动,
并给出非交互改密的完整命令;
- 新增 warn_user():这类"正对着交互输入"的提示直接写 /dev/tty(与提示语同一个设备),
不再走 stdout/stderr 分流,避免提示与它对应的警告错位或看不见;没有终端时回退 stderr。
用 `{ …; } 2>/dev/null` 这个形状而不是 `… >/dev/tty 2>/dev/null`,否则 /dev/tty 打不开时
bash 自己那句 "No such device or address" 会漏在用户眼前(重定向从左往右处理,压不住)。
v1.1.1
|
2026-10-05 21:01:10 +08:00 |
|
LoveChengke
|
79d73c5889
|
chore(release): 版本号统一到 1.1.1(服务端 CSP 白屏修复)
对齐 1.1.0 的做法把版本号统一:5 份 package.json、deploy/package.runtime.json、
两份 .env.example 的 APP_VERSION、两个 docker-compose 的镜像标签、ClassIsland 插件的
manifest.yml 与 BridgeService.PluginVersion(verify:classisland-plugin 有 manifest↔代码
一致性断言,已跑 68/68)、README 首页版本、以及文档与脚本里的安装包名示例,共 28 处。
刻意不动:docs/linux-deploy.md 排障条目里"1.1.0 的服务端在 HTTP 直连访问时会下发 CSP
upgrade-insecure-requests…"——那句说的是**出问题的那个版本**,改成 1.1.1 反而是错的。
|
2026-10-05 20:05:39 +08:00 |
|
LoveChengke
|
48e0c89394
|
fix(server): 纯 HTTP 部署下 CSP 的 upgrade-insecure-requests 会让管理端白屏
helmet 的**默认** CSP 自带 upgrade-insecure-requests,它让浏览器把页面**子资源**(JS/CSS)
请求全部改写成 https。而安装器的默认形态是 http://IP:端口 直连(没有 TLS),改写后的请求
必然连接失败 —— 表现是「HTML 外壳加载出来了(标签页标题正常)、脚本没加载(页面一片空白)」。
2026-10-05 用一行安装脚本在 Ubuntu 24.04 上装完后实测踩到。
改法:securityHeaders() 按请求挑策略 —— req.secure(直连 TLS,或反代下由
x-forwarded-proto 决定)走 buildHelmet(true),否则 buildHelmet(false):
- 明文请求不再下发 upgrade-insecure-requests(置 null 去掉 helmet 的默认指令);
- 明文请求也不再下发 HSTS(按规范浏览器本就忽略,没必要发);
- HTTPS 请求的行为与改动前完全一致,安全性没有被削弱。
本地实测(另起一份服务在 4100、TRUST_PROXY=1,避免扰动开发机上已有的 4000 实例):
明文请求的 CSP 里 upgrade-insecure-requests 出现 0 次、带 X-Forwarded-Proto: https 的
请求里出现 1 次;healthz 与首页均 200;tsc 与 ESLint 均无问题。
|
2026-10-05 20:02:30 +08:00 |
|
LoveChengke
|
5d2fa2d6b4
|
fix(deploy): 修两处安装器 bug——下载地址多了一段 /releases、菜单选 4(只体检)不生效
Ubuntu 24.04 实机跑出来的两个问题(用户贴的输出各暴露一个):
1. **下载地址变成 .../releases/releases/download/...**(必然 404):GITHUB_RELEASES_PAGE 本身
已经以 /releases 结尾,拼接时又写了一遍。install.sh 与 classhelper 各有一份,都改用新增的
GITHUB_REPO_URL(不含 /releases);GITHUB_RELEASES_PAGE 只留给"发布页"这类展示用途。
2. **菜单里选 4)只体检,却直接开始安装**:CHECK_ONLY 的分流写在菜单**之前**,菜单里再把它置 1
已经没人看。现在把 root 检查与 tee 都挪到菜单之后,菜单选 4 与 --check 走同一条路
—— 顺带让非 root 用户也能看到菜单并选 4,不必非得记住 --check。
|
2026-10-05 19:52:57 +08:00 |
|
LoveChengke
|
2be8d30376
|
fix(deploy): 交互提示写不进终端导致静默退出——fd 3 必须用 <> 打开,不能只读
Ubuntu 24.04 实机定位:菜单打印完就退回 shell、退出码 1、没有任何提示(用户三次复现)。
根因是 `exec 3</dev/tty` 以**只读**方式打开 fd 3,而 read_prompt / read_secret 要往它**写**提示语:
$ exec 3<file; printf x >&3
bash: printf: write error: Bad file descriptor # 退出码 1
在 set -e 下这一下就把脚本打死;错误信息还因为走 tee 管道、脚本退出时进程替换的子进程来不及
flush 而被吞掉 —— 于是表现成"菜单打完就默默消失"。
- install.sh 的 open_prompt_fd、classhelper 的 open_tty 都改成 `exec 3<>/dev/tty`(读写打开);
- 提示语写失败改为显式降级(该项用默认值 + 打印一条提示),不再让整条命令消失;
- classhelper 里是同一份实现,不修的话 Linux 上 `classhelper password` 会同样毫无提示地退出。
本地复现验证过三种情形:只读 fd 写 → Bad file descriptor / 退出码 1;`<>` 写 → 成功;
`if printf … >&3; then` 里的失败不会被 set -e 打死(新加的护栏确实兜住了)。
|
2026-10-05 19:50:24 +08:00 |
|
LoveChengke
|
f6edfa5ea1
|
fix(deploy): 修正一行安装的写法——bash -c 会吞掉参数,改用「先落盘再执行」
实测(Ubuntu 24.04):`bash -c "$(curl …)" --check` 并没有进体检分支,而是走进了交互式安装。
原因是 `bash -c '代码' 第一个参数` 里那个参数会被赋给 **$0** 而不是 $1,脚本根本收不到它 ——
我之前在 install.sh 头部、内嵌 --help 与 README 里推荐的"末尾追加 --version / --yes / --check"
全都因此无效(用户按文档加了参数,实际一个都没生效)。
改成先落盘再执行:
curl -fsSL -o /tmp/classhelper-install.sh <url> && sudo bash /tmp/classhelper-install.sh
参数直接接在最后(`sudo bash /tmp/classhelper-install.sh --check`)。这个形式顺带避免了
"下载中断却执行到半个脚本"的风险,stdin 也仍是终端。
同步位置:install.sh 头部注释、内嵌 usage(远端模式 --help)、README、docs/linux-deploy.md;
另在脚本里写明这个 $0 坑,免得以后又改回去。
|
2026-10-05 19:46:31 +08:00 |
|
LoveChengke
|
985e602012
|
fix(deploy): 修一行安装"菜单打印完就静默退出"——read_prompt 的返回值被 set -e 当成致命错误,且报错被 tee 吞掉
Ubuntu 24.04 实机跑一行安装时的现象:菜单打印出来后**没有任何提示**就退回 shell,
用户敲的 1 被 shell 当成命令(1: command not found)。两个原因叠在一起:
- read_prompt 里写的是 `open_prompt_fd || return 1`,而调用处(`read_prompt "…" choice`)是独立命令,
在 `set -e` 下返回非 0 会当场终止整个脚本;改成永远返回 0(读不到终端就把变量留空、由调用方用默认值)。
- `exec > >(tee -a 安装日志)` 之后所有输出都走进程替换,而脚本退出时那个子进程可能来不及把最后几行
写出去 —— 于是**致命错误被彻底吞掉**,只剩"默默退出"。现在脚本开头先把真正的 stderr 存到 fd 9,
die / on_error 绕开 tee 直接写 fd 9(终端立刻可见)并直接追加日志文件。
随之而来的两处改进:
- 交互式安装读不到终端时**明确报错**并给出三条出路(--yes / 存成文件再执行 / --check),
不再默默按默认值装下去 —— 那会让用户以为自己在选,其实一个都没问到;
- 开始安装前打印最终生效的选择(形态|目录|端口|数据库|Node 来源):非交互或问答被跳过时,
这一行是唯一能看出"到底按什么装的"地方。
"请用 root" 的提示也按本地/远端两种执行方式分别给出可照抄的命令。
|
2026-10-05 19:44:15 +08:00 |
|
LoveChengke
|
d820926491
|
feat(deploy): install.sh 支持一行远程安装(从 GitHub 取脚本与安装包,并自动校验哈希)
用户侧只要一行:
sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/LoveChengke/ClassHelper/master/deploy/install.sh)"
为让它成立改了四处(原来只支持"本地有脚本 + 本地有安装包"):
- usage():读不到脚本文件时(bash -c "$(curl …)" / curl|bash)打印内置参数表,
不再去 awk 一个不存在的文件;
- SCRIPT_PATH / SCRIPT_DIR:容忍非文件执行;与脚本同目录的模板本来也由安装包提供;
- 交互问答统一走 /dev/tty(新增 read_prompt / can_prompt):原来判断 [ ! -t 0 ] 会让
curl|bash 下的所有提问被**静默跳过、直接按默认值装完**;并且刻意不退回 stdin ——
管道模式里 stdin 就是脚本本身,从 stdin 读会把脚本的下一行当成用户输入吃掉;
- fetch_package:没给 --sha256 时自动取发布页上同名的 .sha256 来校验。一行安装的用户
不会自己去核哈希,不补这一步"校验"就只是把哈希记进日志;取不到时明确告警,而不是
假装校验过了。
文档:README 增加一行安装入口;docs/linux-deploy.md 的开头同样补上(该文件另有一处
改动不属于本次提交,故未纳入)。
|
2026-10-05 19:27:20 +08:00 |
|
LoveChengke
|
f1edb42665
|
ci(workflow): Linux 包一并上传 .sha256,避免发布后哈希对不上
首次跑通后发现:workflow 只上传了 tar.gz 与 SHA256SUMS,而那份独立发布的 .sha256 还是上一版的哈希 —— 拿它校验会 mismatch(classhelper upgrade 用的正是这个文件,不是 SHA256SUMS)。另补一道兜底:文件缺失时现场生成。
|
2026-10-05 18:19:02 +08:00 |
|
LoveChengke
|
67e3961e55
|
ci(workflow): 修 Linux 出包 —— 补 Prisma Client 生成,SHA256SUMS 不再排序打乱
首次在 tag push 上跑就挂了(run 37294579347,失败在"构建 Linux 安装包")。两处都是我的问题:
1. 漏了 `pnpm --filter @classhelper/server db:generate`。`packages/server/src/generated/prisma`
是 gitignored 的,CI 全新检出来没有它,于是 Prisma 类型解析不到、回调参数全被推成 any,
`tsc` 报出一大片 TS7006 / TS2339 —— 看着像代码写错了,其实只是缺生成物。
本机因为早就生成过,永远踩不到这个坑;deploy/Dockerfile 的构建阶段本来就有这一步,
workflow 里漏抄了(同一条注释在那边写着)。
2. 更新 SHA256SUMS 时用了 `sort -u -k2` 去重,会把那份人读的清单(表头 + 体积 + 说明行)打散。
改成只替换/追加本包那一行,其余原样保留。
|
2026-10-05 18:10:29 +08:00 |
|
LoveChengke
|
9a38f86cd3
|
feat(server,web,desktop,shared,deploy,docs): Linux 一键安装与 classhelper 运维命令、更新检查、图表去 echarts,版本统一 1.1.0
Linux 交付链(新增)
- deploy/install.sh:一键安装器(CLASSHELPER 大字 TUI、native/docker 双形态、向导选安装目录/
端口/数据库/初始管理员密码);识别 apt/yum/dnf 只补缺的依赖、建独立系统用户、下载内置 Node
运行时(glibc < 2.28 的 CentOS 7 自动改用 unofficial-builds 的 glibc-217 构建)、幂等覆盖安装
(保留 config.env 与 data/,不重置密钥)、全程写 /var/log/classhelper/install.log;
`--check` 只体检(不需要 root)、`--yes` 无人值守、`--package` 支持内网离线分发。
- deploy/classhelper:运维命令 —— status / doctor / password / backup(list,restore,schedule) /
upgrade / rollback / start|stop|restart|reload / logs / config / port / key rotate / uninstall /
completion / help。特权操作一律校验 EUID,关键动作写 /var/log/classhelper/audit.log。
- deploy/tools/admin-cli.mjs:离线账号工具(改密、列账号、VACUUM INTO 一致性快照、哈希回滚),
直连数据库 —— 服务停机也能救急,不需要先有一个能登录的账号。
- deploy/{classhelper.service,profile.d,logrotate.d,systemd}/ 模板 + Dockerfile.linux-package。
- scripts/dist-server.mjs 增加 --platform linux(根脚本 `pnpm dist:server:linux`),产物
classhelper-server-linux-x64-<版本>.tar.gz + 同名 .sha256;新增 GitHub Actions workflow 在
ubuntu runner 上构建并挂 Release(@libsql/linux-* 这类平台相关依赖只能在 Linux 上装)。
- docs/linux-deploy.md:安装 / 运维命令 / 升级回滚 / 备份恢复 / 排障 / 安全清单 /
Ubuntu 22.04 与 Rocky 9 的验收清单。deploy/install-linux.sh 改为转发壳,不留两份实现。
三处必须知道的取舍(已写进 AGENTS.md §5 第 59 条)
- 配置真身在 /etc/classhelper/config.env,安装目录里的 .env 是指向它的软链:Web 端
「数据库管理 → 一键切换」改写的是 <安装目录>/.env,而 systemd 注入的是同一份配置 ——
两处不是同一个文件时,切换会「看起来成功、重启后又连回旧库」。classhelper doctor 有专门断言。
- 升级/回滚替换程序文件用「改名 + 就位」而不是 rm+cp:升级时正在运行的就是 bin/classhelper
自己,直接删掉同名文件会让 bash 读到半个脚本。
- 交叉构建(在非 Linux 机器上打 Linux 包)会在包内写 .cross-built 标记、doctor 据此提示:
已核对 @libsql/linux-x64-gnu 与 -musl 就位且无 win32 残留,但 prisma CLI 的原生 schema engine
是宿主平台的,Web 端「数据库一键切换」那一步存疑;正式产物一律走 CI。
更新检查(此前只在工作区,本次一并提交)
- 服务端 modules/update:由服务端转发 GitHub Releases(Web 端 CSP 不允许直连),离线/限流/超时
一律收敛成 ok:false + 一句人话,失败结果同样进缓存;?force=1 只对管理员生效(避免刷爆匿名限流)。
- 版本比较放 @classhelper/shared(compareVersions / isNewerVersion),三端共用;服务端版本号
改由 lib/version.ts 读 package.json,不再在 app.ts 里写字面量。
- Web 端顶栏「检查更新」弹窗、客户端「关于」页的检查更新与诊断信息。
Web 端 / 客户端
- 成绩图表改用自包含的轻量组件(ScoreBarChart / ScoreLineChart),不再全量引入 echarts
(原先 echarts 占桌面端渲染产物 1.12MB、Web 端 1.13MB);柱状图是纯 HTML/CSS,折线图是
viewBox SVG,均无 resize 监听。
- Element Plus 图标改为按需注册(两端各一份 icons.ts),新增 scripts/check-icons.mjs 静态门禁
并挂进两端 build —— 漏注册的后果是界面静默渲染空白,只在控制台一条 warning。
版本号统一 1.1.0
- 5 份 package.json、deploy/package.runtime.json、.env.example 的 APP_VERSION、
docker-compose 与两个 Dockerfile 的镜像标签、Linux 安装包名示例、README 首页版本;
- ClassIsland 插件 manifest.yml 与 BridgeService.PluginVersion 同步(verify:classisland-plugin
有 manifest ↔ code 的一致性断言);
- 刻意保持不变的是**历史性表述与举例**:docs/mysql.md 的「自 v1.0.0 起内置 MySQL 适配器」、
shared/utils.ts 讲版本比较用的 1.0.0 / 1.0.0-fix、README 验收表里的实测记录。
v1.1.0
|
2026-10-05 16:39:38 +08:00 |
|
LoveChengke
|
aad22df4fd
|
docs: 重写 README —— 按 1.0.0 现状重排为「产品说明书 + 验收对照表」
结构重排:组成部分 → 交付产物与部署形态 → 快速开始 → 功能说明(Web 端 / 客户端 / 灵动岛 / 作业 /
叫人 / 上课时段 / 导入 / ClassIsland 联动 / 数据库管理)→ 技术参考(目录结构 / 技术栈 / REST API /
WebSocket / RBAC / 模块化 / 数据库与迁移)→ 验收 → 打包与交付 → 常见问题 → 后续增强。
1594 行 → 1236 行,去掉的是与 AGENTS.md 重复的踩坑叙述与「按阶段的交付清单」文件流水(后者改由目录结构
与 git 历史承载)。
修正过期内容:
- 迁移数 9 → 10(补回 add_schedule_source),迁移清单同步
- verify:e2e 173 项 → 186 项;verify:web 30/31、verify:desktop 99/102、插件静态契约 68/68 按最近一次实测重写
- 删掉两条与现状相反的验收行(「客户端设置页提供修改密码」),改为「客户端有意不提供改密入口」,
与 AGENTS.md §5 第 31 条、冒烟断言「改密入口存在=false」一致
- 服务端安装包体积口径统一为 ~71MB(切库依赖内置后);插件版本 1.0.0.0;正文标注当前版本 1.0.0
新增与加强:
- 补 1.0.0 的内容:数据库管理(仅管理员)、新手引导(Web 端 el-tour + 客户端六步向导)、
设置拆 6 个子路由、深色模式、客户端主窗口与 ClassIsland 对齐(1242x582)、
课表 source 字段与「幽灵行清理」口径
- 6.3「已知的时序 / 环境相关用例」:写明 verify:desktop 那 3 项在「当前正在上课」时必然失败的原因,
并给出各验证脚本覆盖凭据的方式(ELECTRON_SMOKE_* / UI_SMOKE_* 支持环境变量,verify:e2e 不支持)
验证(2026-10-02 本机):verify:e2e 186/186;verify:web 30/31(1 项为既有脆弱用例);
verify:desktop 99/102(3 项时序相关);verify:classisland-plugin 68/68;typecheck 与 lint 通过。
v1.0.0
|
2026-10-02 11:24:31 +08:00 |
|
LoveChengke
|
572295c297
|
feat(server,web,desktop,shared,deploy,plugin): 数据库管理模块 + 客户端深色模式/设置拆子路由/首启引导,版本统一 1.0.0
- 服务端新增 database 模块(/api/database/*,仅 ADMIN):全库 JSON 快照(备份 / 恢复 / 导入导出 / 定时备份
共用同一格式,14 张表按拓扑序导出、恢复时关外键检查)、连接测试与目标库校验、一键 SQLite ⇄ MySQL 切换
(备份 → 导出 → 测试连接 → 校验空库 → 改 schema → 子进程 generate / tsc / db push → 写数据,全部成功才改写
.env,中途失败回滚 schema);XML 快照与 SQLite 数据文件均可下载;打包形态随包带上 prisma CLI 与 typescript
(prisma-client 生成物是 .ts,需再编进 dist/generated,安装包因之变大)
- Web 管理端新增「数据库管理」页(仅管理员),并加首次登录新手引导(el-tour,可跳过、可随时重看,版本号写
localStorage,改版后老用户会再收到一次)
- 客户端设置改版:侧边栏改为分组折叠菜单,设置拆成 6 个子路由
(/settings/{general,appearance,island,reminder,account,about}),新增深色模式(主题持久化到主进程配置,
主窗口底色跟主题走,避免"先白后黑"闪一下),新增「关于」页(应用信息 / 诊断信息)
- 客户端新增首启六步引导(OnboardingWelcome,状态存主进程配置 onboardingDone)
- 客户端主窗口初始尺寸与 ClassIsland 对齐(1242x582 / minHeight 540 / 居中)
- 版本号统一提升到 1.0.0:5 个 package.json、部署配置与镜像标签(.env.example / docker-compose / Dockerfile)、
服务端 /health 与首页、插件 manifest.yml 与 BridgeService.PluginVersion、文档里的安装包名
- 文档与验收同步:AGENTS.md 补第 37~51 条约定(深色主题实现边界、设置子路由、数据库管理三件事、侧栏与窗口
尺寸、引导在自动化里的判据等),README 补「数据库管理 / 新手引导 / 设置改版」三节;verify:e2e 增补 13 条
数据库管理用例,客户端冒烟与 Web UI 冒烟跟进子路由、深色主题、侧栏与引导
验证:pnpm lint 0 error;pnpm typecheck 全过;verify:e2e 186/186;verify:classisland-plugin 68/68;
build:desktop 通过(含产物凭据门禁);verify:desktop 99/102(3 项为上课时段下的"真实通知链路"用例,
客户端按产品规则暂存普通通知所致,见下);插件 dotnet 编译 0 错误。
注:verify:desktop 的「真实通知链路 / 标记已读」三项要求当前不在上课时段——本次运行时种子课表恰有
「周五 10:55-11:40 生物」正在进行,客户端正确地暂存了普通通知(AGENTS.md 第 18 条),属时间相关用例,
与本次改动无关。
|
2026-10-02 11:18:47 +08:00 |
|
LoveChengke
|
391e2c1591
|
docs(desktop,island): 修正被写成既成事实的"收回握手"机制描述
AGENTS.md 第 34 条把一个从未落地的设计当成了已实现的规则,还配上了"踩过的坑"与
"两条配套铁律",读起来像历史教训。实际核对:`island:closing`、`island:closing-done`、
`sendClosingDone`、`noteClosingDone`、`closingTimer`、`finishClosing` 六个符号在全部
源码中零命中,在 git 全部历史里也从未出现过。
真实实现是主进程单向的:handleAction() → dismissActive() / clearPendingForClose()
→ hide() → fadeOut(),状态同一帧写成 hidden,不等渲染进程任何回执。渲染侧只有本地
弹簧形变(spring.ts),是纯视觉动画,不参与淡出时机。
另,早先那层自研的"卡片缩回胶囊再淡出"快照动画(closing / closingSnapshot)确实存在过,
因在触摸屏上会露出旧帧/重影,已按用户要求整体删除 —— IslandApp.vue 顶部注释有记载。
改动:重写 AGENTS.md 第 34 条为真实机制,并注明该自研动画已移除、别再照着不存在的
握手机制改代码;README 三处同样表述一并修正;smoke.ts 里那句"渲染进程收敛上报"的
过时注释改为描述真实的淡出轮询。
v0.1.0-fix
|
2026-10-02 00:32:48 +08:00 |
|
LoveChengke
|
20fcca0388
|
feat(server,web,shared,desktop): 上传超限正确报错 + 两个登录入口都限流 + 作业按天深链,文档与验证脚本同步
服务端加固:
- express.json 上限 2mb → 12mb:表格导入把文件以 base64 放进 JSON(约 4/3 倍),
8MB 的声明上限原先到 1.5MB 就被拦掉;超限现在由 error 中间件映射成
413 IMPORT_TOO_LARGE(中文提示"请拆分后分批导入"),不再落进 500 兜底分支抛英文原文;
- loginRateLimiter 同时挂 /auth/login 与 /auth/class-login(班级密码默认 123456,
班级登录曾是唯一没有暴力破解防护的入口),两个路径共享同一计数器;
- CORS_ORIGIN=* 时启动告警(不拦截启动,避免挡住教室机 file:// 的合法部署);
- 删除已无用的 DEFAULT_STUDENT_PASSWORD;班级 termWeeks 上限 40 → 30,
与 MAX_TERM_WEEK / weekNumberSchema 对齐(原先对外承诺了系统兑现不了的学期长度)。
Web 端:
- 作业页 ?date= 深链在同路由再推 query 时也生效(watch route.query.date);
- 表格导入对话框适配 8MB 上限与 413 提示。
冒烟与文档:
- 新增 scripts/lib/smoke-class.mjs:验证用临时班级,避免探针通知弹到用户自己的客户端与灵动岛;
- README 与 AGENTS.md 同步本轮全部行为变更(含 schedule source、学生账号清理、凭据门禁)。
|
2026-10-02 00:25:21 +08:00 |
|
LoveChengke
|
3880eee08e
|
feat(desktop,island,shared): 灵动岛支持多条通知竖排列表 + 触摸模式 + 收回动画,产物加凭据门禁
灵动岛列表:多条通知时展开为竖排列表,默认显示 3 条,点「展开更多」铺到屏幕容纳
上限,放不下的收进「更多请前往应用内操作」。卡片高度由主进程与渲染进程各算一次
同一个数 —— 度量表 ISLAND_LIST_METRICS 与排序权重 PRIORITY_RANK 放在 shared,
窗口包围盒随之长高(不够高会把底部按钮裁掉)。
触摸模式:触摸屏上手指既不产生 mousemove、也不移动系统光标,原先两条命中链路都会
让窗口永远停在穿透态、手指点不开岛。现按 navigator.maxTouchPoints 上报后,主进程把
窗口贴合岛体并始终接收输入;非触摸屏机器完全不走这条路。
收回动画:main 先发 island:closing,渲染进程形变收敛后回 closing-done 才淡出,
不再靠猜时长(rAF 被节流时会把卡片缩到一半窗口就没了)。
安全门禁:新增 scripts/check-bundle-secrets.mjs 并接进 build:desktop ——
dist 产物会被打进 app.asar 发到每台学生机、且可解包,冒烟凭据一律改由环境变量注入,
产物里出现种子口令即构建失败。
|
2026-10-02 00:25:10 +08:00 |
|