openlearn-next 0.7.6 → 0.7.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -150
- package/dist/assets/{AcademicYearFilter-DUUGpDKt.js → AcademicYearFilter-Dm3no4Nb.js} +1 -1
- package/dist/assets/ActivityWorkspaceWidget-BXS1IamH.js +1 -0
- package/dist/assets/{AdminDirectoryView-DL9zZi-_.js → AdminDirectoryView-479SfYyD.js} +2 -2
- package/dist/assets/{AssessmentCenter-B2JzXBGe.js → AssessmentCenter-CJ-pchOQ.js} +1 -1
- package/dist/assets/{AssignmentFilePreview-Bk751Blz.js → AssignmentFilePreview-wjnrKjsp.js} +1 -1
- package/dist/assets/{BatchPickerModal-9ekXDUUj.js → BatchPickerModal-JvoD-1pF.js} +1 -1
- package/dist/assets/{ClassesView-PCt8I3MU.js → ClassesView-CB8q2kW0.js} +12 -12
- package/dist/assets/ClassroomCountdownWidget-BqzBpAvi.js +2 -0
- package/dist/assets/{CloudDriveModal-Mp59sr-C.js → CloudDriveModal-DHxOnL4m.js} +1 -1
- package/dist/assets/ComputerLabView-BEDe7WiU.js +1 -0
- package/dist/assets/CourseManagement-BjxQC6sq.js +1 -0
- package/dist/assets/{CourseWizardModal-B7-e8otR.js → CourseWizardModal-D3UPFtT_.js} +2 -2
- package/dist/assets/{CoursewareHubPanel-DLjH-UzF.js → CoursewareHubPanel-CqJqcZGs.js} +1 -1
- package/dist/assets/{Dashboard-D3LXLQNC.js → Dashboard-C3BaEHIG.js} +2 -2
- package/dist/assets/{ExportWeightModal-vd7XgaJ0.js → ExportWeightModal-iF0pVevG.js} +1 -1
- package/dist/assets/{HelpTour-BLuiS8rr.js → HelpTour-6ChRqWnB.js} +1 -1
- package/dist/assets/{HelpView-C4BHoNqR.js → HelpView-D3iYhzBz.js} +2 -2
- package/dist/assets/ImportLessonsModal-BOwj7ezj.js +1 -0
- package/dist/assets/{ImportModal-H5s5MyZY.js → ImportModal-DAHGdI9h.js} +1 -1
- package/dist/assets/{InteractiveCoursewareViewer-D9GzG28E.js → InteractiveCoursewareViewer-DX9xnk6j.js} +1 -1
- package/dist/assets/InteractiveWhiteboard-41mi9keH.js +65 -0
- package/dist/assets/LazyCourseware-DKyUHXaE.js +2 -0
- package/dist/assets/LazyWhiteboard-fXS2P44c.js +2 -0
- package/dist/assets/LessonEditorView-Dx-hOKtt.js +1 -0
- package/dist/assets/LiveClassroomView-m0hKdcVK.js +61 -0
- package/dist/assets/NotificationDetailModal-C2tg_IoJ.js +1 -0
- package/dist/assets/PluginView-BPRUpCjV.js +77 -0
- package/dist/assets/{ProcessLogsModal-_BwkmJVw.js → ProcessLogsModal-CULHzXp7.js} +1 -1
- package/dist/assets/{QuizGeneratorModal-DiItok6z.js → QuizGeneratorModal-CYxngpW9.js} +1 -1
- package/dist/assets/StudentAssignmentEvalPanel-Dfe4IOs5.js +1 -0
- package/dist/assets/{StudentAssignmentView-UK_7_MF3.js → StudentAssignmentView-IfRo3yJk.js} +1 -1
- package/dist/assets/StudentLessonView-Dw2uxBhq.js +1 -0
- package/dist/assets/StudentView-DVYUSrVU.js +2 -0
- package/dist/assets/{SystemResourceLibraryModal-dEdNKbk2.js → SystemResourceLibraryModal-BhbpfIBt.js} +1 -1
- package/dist/assets/TeacherView-CZjIejet.js +2 -0
- package/dist/assets/{TimetableView-AkReskNG.js → TimetableView-CupOIfEy.js} +1 -1
- package/dist/assets/{UTIF-4E-wdh3X.js → UTIF-BtQhea94.js} +1 -1
- package/dist/assets/index-BDFyco9S.css +1 -0
- package/dist/assets/index-BiHQqzdm.js +131 -0
- package/dist/assets/{index.es-WXdI0L-F.js → index.es-CijVATXs.js} +1 -1
- package/dist/assets/{pluginParsers-k90WUDrU.js → pluginParsers-C9W6GBVx.js} +1 -1
- package/dist/assets/{student-workspace-BDw1i4Vl.js → student-workspace-CsbA8Cs4.js} +1 -1
- package/dist/assets/{vendor-charts-SrpBsvM3.js → vendor-charts-skggaP9D.js} +1 -1
- package/dist/assets/{vendor-icons-hc6MjYFv.js → vendor-icons-DaYNfRAc.js} +2 -2
- package/dist/assets/{vendor-konva-Dne0Z-WC.js → vendor-konva-BOuJWFuE.js} +1 -1
- package/dist/assets/{vendor-pdf-By0fofA0.js → vendor-pdf-DVWGghIL.js} +3 -3
- package/dist/assets/{vendor-pptx-EjNUtdws.js → vendor-pptx-BqU7v5zS.js} +5 -5
- package/dist/assets/{vendor-react-Kv_JATwr.js → vendor-react-DHoBZpn2.js} +2 -2
- package/dist/assets/{vendor-utils-aM7aYvPP.js → vendor-utils-BxOuk3id.js} +1 -1
- package/dist/env-validation.cjs +7 -1
- package/dist/index.html +6 -6
- package/dist/server.cjs +343 -37
- package/migrations/044_students_gender.sql +8 -0
- package/migrations/README.md +1 -0
- package/migrations/schema-golden.json +3 -2
- package/package.json +2 -2
- package/dist/assets/ActivityWorkspaceWidget-0DUqiqFK.js +0 -1
- package/dist/assets/ClassroomCountdownWidget-HxelcHUp.js +0 -2
- package/dist/assets/ComputerLabView-FStILvFC.js +0 -1
- package/dist/assets/CourseManagement-BQw2idgZ.js +0 -1
- package/dist/assets/ImportLessonsModal-Y6uIIemO.js +0 -1
- package/dist/assets/InteractiveWhiteboard-DswIV-ES.js +0 -65
- package/dist/assets/LazyCourseware-BEoTTg5h.js +0 -2
- package/dist/assets/LazyWhiteboard-CMBBxnFX.js +0 -2
- package/dist/assets/LessonEditorView-BgJtG8Ks.js +0 -1
- package/dist/assets/LiveClassroomView-Czwt5WW-.js +0 -61
- package/dist/assets/NotificationDetailModal-D42vEs3S.js +0 -1
- package/dist/assets/PluginView-BcOWIjLu.js +0 -77
- package/dist/assets/StudentAssignmentEvalPanel-DHoZc3g0.js +0 -1
- package/dist/assets/StudentLessonView-C11Qkdi_.js +0 -1
- package/dist/assets/StudentView-j0hx3IOi.js +0 -2
- package/dist/assets/TeacherView-COZw2tFl.js +0 -2
- package/dist/assets/index-DN3E-Fmw.js +0 -131
- package/dist/assets/index-DPky2leG.css +0 -1
package/README.md
CHANGED
|
@@ -1,96 +1,64 @@
|
|
|
1
1
|
# OpenLearnV2 — Educational OS
|
|
2
2
|
|
|
3
|
-
> 插件驱动的下一代在线教育操作系统(LMS
|
|
3
|
+
> 插件驱动的下一代在线教育操作系统(LMS),融合微内核架构、三档沙箱隔离插件生态、零信任课件容器与原生 AI 伴随教学。
|
|
4
4
|
|
|
5
5
|
[](package.json)
|
|
6
6
|
[](package.json)
|
|
7
|
-
[](package.json)
|
|
8
|
+
[](https://github.com/aymwoo/OpenLearn-Next-V2/actions/workflows/ci.yml)
|
|
9
9
|
|
|
10
|
-
📚
|
|
10
|
+
📚 **官方文档**:[在线文档库 (ReadTheDocs)](https://openlearn-next-v2.readthedocs.io/zh-cn/latest/) · [快速上手](https://openlearn-next-v2.readthedocs.io/zh-cn/latest/getting-started/quickstart.html) · [插件开发教程](https://openlearn-next-v2.readthedocs.io/zh-cn/latest/tutorials/plugin-development-tutorial.html) · [部署运维手册](https://openlearn-next-v2.readthedocs.io/zh-cn/latest/deployment/production-guide.html)
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
## 🌟 核心特性
|
|
15
15
|
|
|
16
|
-
- 🧩
|
|
17
|
-
-
|
|
18
|
-
- `EventBus`
|
|
19
|
-
- 🛡️ **三档隔离插件沙箱 (Plugin Host & Sandboxing)**
|
|
20
|
-
- 支持 **Inline 原生模式**、**Worker 线程沙箱** 与 **子进程沙箱**(`process`)。第三方 ZIP 插件被强制使用 `process` 模式;隔离模式下经 RPC 代理访问内核服务,并具备看门狗自动拉起(Watchdog)与 5 分钟崩溃 4 次熔断保护(Circuit Breaker)。
|
|
21
|
-
- 严谨的 7 态插件生命周期管理(`INSTALLED` ➔ `ACTIVATING` ➔ `ACTIVE` ➔ `DEACTIVATING` ➔ `INACTIVE` ➔ `ERROR` ➔ `UNINSTALLED`),卸载与停用自动触发资源与权限彻底回收。
|
|
22
|
-
- 插件专属 REST API 安全网关:命名空间路由隔离、滑动窗口限流、SSE 并发连接配额与响应头清洗。
|
|
23
|
-
- 📦 **零信任课件沙箱与透明 Bridge SDK**
|
|
24
|
-
- HTML5 课件与小程序在严格 `iframe`(`sandbox="allow-scripts allow-forms allow-downloads"`,严格禁止 `allow-same-origin`)中运行。
|
|
25
|
-
- 独创 **Bridge SDK** 透明代理跨域通讯,内置 `ResultWatcher` 强信号监听器,实现无需题目作者适配即可自动识别「结算页/通关页」并抓取分数自动回写。
|
|
26
|
-
- 📡 **课堂实时协同与学生端多维遥测**
|
|
27
|
-
- 在线感知(Presence Engine):实时捕获学生专注度(Focused/Unfocused)、离屏状态与班级专注率。
|
|
28
|
-
- 多人协同(Collaboration Engine):支持分组协作、共享对象锁(ObjectLock)与白板事件插槽(WhiteboardEventSlot)。
|
|
29
|
-
- **学生端极简低干扰异常遥测**:端侧左下角极简感叹号图标+红色角标,降低课堂认知负荷;异常秒级上报至教师端大屏并落盘审计日志。
|
|
30
|
-
- **班级上课临时密码与双轨登录**:课前就绪一键设定与动态时效管控,学生可凭班级临时口令或个人密码快速入课;预留第三方插件扩展插槽与标准获取接口。
|
|
31
|
-
- 🤖 **原生 AI 伴随教学引擎**
|
|
32
|
-
- 兼容 OpenAI 协议(DeepSeek、Qwen、Ollama、OpenAI 等任意兼容提供商),密钥多重加密存储。
|
|
33
|
-
- 赋能教师工作台:一键智能生成结构化教案、自动出题与随堂小测、学情诊断与个性化学习规划。
|
|
16
|
+
- 🧩 **微内核与事件驱动底座 (Educational OS Microkernel)**
|
|
17
|
+
- 基于 CQRS `CommandBus`、强类型 DI 依赖注入与 SQLite 关系型存储(WAL 高并发模式)。
|
|
18
|
+
- 内核级 `EventBus` 支持精准事件订阅与审计追溯,搭载 Schema Golden 结构守卫,杜绝数据库版本漂移。
|
|
34
19
|
|
|
35
|
-
|
|
20
|
+
- 🛡️ **三档隔离插件生态 (Extensible Plugin Architecture)**
|
|
21
|
+
- 支持 **Inline 原生模式**、**Worker 线程** 与 **独立子进程 (Process)** 三档隔离沙箱,具备崩溃看门狗自动重启与熔断保护。
|
|
22
|
+
- 支持 ESM ZIP 热插拔分发、独立 REST API 命名空间路由隔离、出网权限白名单(Net Grants)与精细化资源配额。
|
|
23
|
+
- 声明式 UI 扩展槽位:画板图元、管理端侧栏、教学工具箱、课件类型等均可由插件任意扩展。
|
|
36
24
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
│ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────────────┐ │
|
|
54
|
-
│ │ Command Bus │ │ Event Bus │ │ DI Container (Tokens)│ │
|
|
55
|
-
│ └────────┬────────┘ └────────┬────────┘ └──────────┬───────────┘ │
|
|
56
|
-
│ │ │ │ │
|
|
57
|
-
│ ┌────────▼─────────────────────▼───────────────────────▼───────────┐ │
|
|
58
|
-
│ │ SQLite Database (WAL Mode / Migration / Audit Log Events) │ │
|
|
59
|
-
│ └──────────────────────────────────────────────────────────────────┘ │
|
|
60
|
-
└────────────────────────────────────┬───────────────────────────────────┘
|
|
61
|
-
│
|
|
62
|
-
┌────────────────────────────────────▼───────────────────────────────────┐
|
|
63
|
-
│ Plugin Runtime & Sandbox Subsystem │
|
|
64
|
-
│ ┌───────────────────────────────┐ ┌──────────────────────────────┐ │
|
|
65
|
-
│ │ Inline Mode Runtime │ │ Worker Process Sandbox │ │
|
|
66
|
-
│ │ (Direct In-Memory Execution) │ │ (RPC / Watchdog / Breaker) │ │
|
|
67
|
-
│ └───────────────────────────────┘ └──────────────────────────────┘ │
|
|
68
|
-
│ ┌───────────────────────────────┐ ┌──────────────────────────────┐ │
|
|
69
|
-
│ │ Dynamic ESM ZIP Loader │ │ Capability Guard (RBAC/ABAC) │ │
|
|
70
|
-
│ └───────────────────────────────┘ └──────────────────────────────┘ │
|
|
71
|
-
└────────────────────────────────────────────────────────────────────────┘
|
|
72
|
-
```
|
|
25
|
+
- 📦 **零信任课件容器与透明 Bridge SDK**
|
|
26
|
+
- HTML5 / 网页应用课件运行于极严沙箱容器(无 `allow-same-origin`),彻底阻断越权窥探。
|
|
27
|
+
- 独创 **Bridge SDK** 跨域通讯代理,内置 `ResultWatcher` 自动感知通关与结算信号,无需课件改造即可自动回写成绩。
|
|
28
|
+
|
|
29
|
+
- 📡 **课堂实时协同与低干扰多维遥测**
|
|
30
|
+
- **实时在线与专注度感知**:毫秒级捕获学生离屏、切台与活跃状态,呈现全班专注度热力图。
|
|
31
|
+
- **低干扰健康遥测**:学生端极简角标异常上报(不中断正常教学),故障秒级推向教师端并持久化审计。
|
|
32
|
+
- **多端实时互动白板**:基于 Konva 的高帧率协同画板,支持区域对象锁、随堂测验卡片投送与生生互评。
|
|
33
|
+
|
|
34
|
+
- 🤖 **原生 AI 伴随教学引擎**
|
|
35
|
+
- 兼容 OpenAI 开放标准(DeepSeek、Qwen、Ollama、OpenAI 等任意兼容提供商),密钥多重加密落盘。
|
|
36
|
+
- 深度赋能备课与授课:一键生成结构化互动教案、随堂出题与测评建议、学情智能诊断与个性化干预。
|
|
37
|
+
|
|
38
|
+
- 🧰 **开箱即用与生产级可靠运维**
|
|
39
|
+
- 内置命令行诊断看护:`npx openlearn-next doctor --fix` 一键健康体检、依赖自愈与在线冷备/快照还原。
|
|
40
|
+
- 严格的主机安全基线:密码强制安全初始化、CSP 防注入、LTI 1.3 标准对接兼容。
|
|
73
41
|
|
|
74
42
|
---
|
|
75
43
|
|
|
76
|
-
## 快速开始
|
|
44
|
+
## 🚀 快速开始
|
|
77
45
|
|
|
78
|
-
###
|
|
46
|
+
### 方式 A:一键试用(无需克隆代码)
|
|
79
47
|
|
|
80
48
|
```bash
|
|
81
|
-
#
|
|
49
|
+
# 启动最新发布版体验
|
|
82
50
|
npx openlearn-next@latest
|
|
83
51
|
|
|
84
|
-
# 自定义端口 / 启动后自动唤起浏览器 (-o) /
|
|
52
|
+
# 自定义端口 / 启动后自动唤起浏览器 (-o) / 演示模式 (--demo)
|
|
85
53
|
npx openlearn-next@latest -p 3000 -o
|
|
86
54
|
npx openlearn-next@latest --demo -o
|
|
87
55
|
|
|
88
|
-
#
|
|
56
|
+
# 运维诊断自愈与在线冷备
|
|
89
57
|
npx openlearn-next@latest doctor --fix
|
|
90
58
|
npx openlearn-next@latest backup
|
|
91
59
|
```
|
|
92
60
|
|
|
93
|
-
###
|
|
61
|
+
### 方式 B:全局安装运行
|
|
94
62
|
|
|
95
63
|
```bash
|
|
96
64
|
npm install -g openlearn-next # 安装
|
|
@@ -98,116 +66,64 @@ openlearn-next # 启动
|
|
|
98
66
|
npm update -g openlearn-next # 更新
|
|
99
67
|
```
|
|
100
68
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
- 若设置了 `OPENLEARN_ADMIN_PASSWORD` / `OPENLEARN_TEACHER_PASSWORD`,使用该口令。口令若仍等于用户名,登录后必须立刻修改,期间除改密、登出和会话查询外,其余 `/api` 请求(含 GET)返回 403。
|
|
104
|
-
- 未设置时随机生成 20 位口令,明文只打印一次,并写入 `storage/bootstrap-credentials.txt`(权限 600)。请在首次登录后修改并删除该文件。
|
|
105
|
-
- 已有数据库若口令仍是 `admin` / `teacher`,可以登录,但同样被强制改密拦住,直到 `POST /api/auth/change-password` 成功。不要在生产环境设置 `PLAYWRIGHT_TEST`(该变量会保留测试用默认口令并跳过改密门禁)。
|
|
106
|
-
|
|
107
|
-
### 本地开发
|
|
69
|
+
### 方式 C:源码本地开发
|
|
108
70
|
|
|
109
71
|
```bash
|
|
110
|
-
#
|
|
72
|
+
# 1. 克隆仓库并安装依赖(建议 Node 22 + pnpm)
|
|
73
|
+
git clone https://github.com/aymwoo/OpenLearn-Next-V2.git
|
|
74
|
+
cd openlearnv2
|
|
111
75
|
pnpm install
|
|
112
76
|
|
|
113
|
-
#
|
|
77
|
+
# 2. 启动开发服务器(支持 Vite HMR,默认端口 9000)
|
|
114
78
|
pnpm dev
|
|
115
79
|
|
|
116
|
-
#
|
|
80
|
+
# 3. 浏览器打开
|
|
117
81
|
open http://localhost:9000
|
|
118
82
|
```
|
|
119
83
|
|
|
120
|
-
>
|
|
121
|
-
|
|
122
|
-
---
|
|
123
|
-
|
|
124
|
-
## 生产部署
|
|
125
|
-
|
|
126
|
-
```bash
|
|
127
|
-
chmod +x deploy.sh
|
|
128
|
-
./deploy.sh
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
脚本自动完成:依赖校验 ➔ 生产构建 ➔ 生成 Nginx 配置 ➔ PM2 进程守护 ➔ 生成 API Key 加密密钥。
|
|
132
|
-
详细部署方案可参阅 [生产部署指南](https://openlearn-next-v2.readthedocs.io/zh-cn/latest/deployment/production-guide.html)。
|
|
84
|
+
> 🔐 **初始凭据安全说明**:
|
|
85
|
+
> 平台内置种子管理员账号 `admin` 与教师账号 `teacher`。首次启动若未预设环境变量口令,系统将在 `storage/bootstrap-credentials.txt` 生成随机一次性口令。登录后系统将强制引导修改密码。
|
|
133
86
|
|
|
134
87
|
---
|
|
135
88
|
|
|
136
|
-
##
|
|
89
|
+
## 🛠️ 常用命令
|
|
137
90
|
|
|
138
|
-
| 命令 | 说明
|
|
139
|
-
| :----------------- |
|
|
140
|
-
| `pnpm dev` |
|
|
141
|
-
| `pnpm build` |
|
|
142
|
-
| `pnpm start` |
|
|
143
|
-
| `pnpm test` | 执行 Vitest
|
|
144
|
-
| `pnpm lint` | TypeScript 静态类型检查(`tsc --noEmit`)
|
|
145
|
-
| `pnpm lint:
|
|
146
|
-
| `pnpm format` | Prettier
|
|
147
|
-
| `pnpm db:backup` | 数据库安全在线冷备 |
|
|
148
|
-
| `pnpm db:reset` | 重置本地测试数据库环境 |
|
|
91
|
+
| 命令 | 说明 |
|
|
92
|
+
| :----------------- | :------------------------------------------------ |
|
|
93
|
+
| `pnpm dev` | 启动开发服务(Express + Vite 前端热更新) |
|
|
94
|
+
| `pnpm build` | 全量生产构建(Vite 构建 ➔ 插件打包 ➔ 服务端打包) |
|
|
95
|
+
| `pnpm start` | 运行生产打包产物(`dist/server.cjs`) |
|
|
96
|
+
| `pnpm test` | 执行 Vitest 自动化单元与集成测试套件 |
|
|
97
|
+
| `pnpm lint` | TypeScript 静态类型检查(`tsc --noEmit`) |
|
|
98
|
+
| `pnpm lint:strict` | 严格模式类型检查 |
|
|
99
|
+
| `pnpm format` | Prettier 代码自动格式化 |
|
|
149
100
|
|
|
150
101
|
---
|
|
151
102
|
|
|
152
|
-
##
|
|
103
|
+
## 🚢 生产部署
|
|
153
104
|
|
|
154
|
-
|
|
155
|
-
| :------------------ | :--: | :------------------------------------: | :------------------------------------------------------------------------ |
|
|
156
|
-
| `ENCRYPTION_KEY` | ✅ | — | 64 位 Hex 字符串,用于 AI Provider API Key 加密(`deploy.sh` 会自动生成) |
|
|
157
|
-
| `PORT` | — | `9000` | HTTP 服务监听端口 |
|
|
158
|
-
| `OPENLEARN_DB_PATH` | — | `./packages/core/db/educational_os.db` | SQLite 数据库文件路径(npx 运行时默认为 `~/openlearn-next/data.db`) |
|
|
159
|
-
| `ALLOWED_ORIGINS` | — | `*` | CORS 跨域白名单(逗号分隔) |
|
|
160
|
-
| `LOG_LEVEL` | — | `info` | 系统日志输出级别(`debug` / `info` / `warn` / `error`) |
|
|
105
|
+
推荐通过项目自带的自动化部署脚本完成生产部署:
|
|
161
106
|
|
|
162
|
-
|
|
107
|
+
```bash
|
|
108
|
+
chmod +x deploy.sh
|
|
109
|
+
./deploy.sh
|
|
110
|
+
```
|
|
163
111
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
根据平台近期代码与架构深度审计结果(相关一致性由 [架构文档漂移审计](docs/developer-guide/docs-drift-audit.md) 在 CI 中持续看守),OpenLearn 制定了分阶段的演进路线:
|
|
167
|
-
|
|
168
|
-
### 🎯 阶段一:稳固与安全收敛 (v0.3.21 - 当前阶段)
|
|
169
|
-
|
|
170
|
-
- [x] **P0 严重安全缺陷彻底闭环**:
|
|
171
|
-
- [x] 为 `/api/plugins/execute-command` 挂载全局有效会话鉴权(`requireAuth()`),消除未授权命令执行面
|
|
172
|
-
- [x] 插件更新检查器(`version-fetcher.ts`)消除 Shell 命令注入,采用安全参数化执行(`execFileSync`)与仓库白名单正则校验
|
|
173
|
-
- [x] 插件数据库建表(`ensureTable`)在 Inline 与 Worker 双模式下全面引入 SQL 标识符与 DDL 结构校验,防御 SQL 注入
|
|
174
|
-
- [x] 规范插件停用与卸载生命周期,非 ACTIVE 状态下无条件回收宿主资源(`resourceTracker.disposeAll`)与能力注销(`capService.revokeAll`)
|
|
175
|
-
- [x] **端侧异常健康遥测与审计体系**:
|
|
176
|
-
- [x] 实现学生端 React 运行时、未捕获 Promise、网络请求故障的自动捕获与多通道秒级上报(WebSocket 优先 + HTTP 兜底)
|
|
177
|
-
- [x] 教师端互动课堂大屏与学生卡片集成红色脉冲告警、异常堆栈诊断与 Markdown 报告一键导出
|
|
178
|
-
- [x] 服务端事件总线统一写入 SQLite `events` 审计日志,内置学生 ID 防冒用验证与上报频率限制
|
|
179
|
-
- [x] 学生端 UI 实施低干扰改造(左下角微型图标+数字角标),保障课堂学习专注度
|
|
180
|
-
- [ ] **SDK 契约一致性治理**:重新构建打包 `@openlearn/plugin-sdk`,补齐 `STORAGE_TOKEN` 等缺失导出的 Token 与类型定义
|
|
181
|
-
- [ ] **自动化测试体系清理**:解耦对未提交外部目录的物理路径硬依赖,使 215 个套件的全量测试达到 100% 绿灯
|
|
182
|
-
|
|
183
|
-
### 🚀 阶段二:K12 课堂交互与智能伴随深化 (v0.4.0 - 中期演进)
|
|
184
|
-
|
|
185
|
-
- [ ] **课件状态双向镜像与远程协助**:
|
|
186
|
-
- 教师端可按需拉取单个学生的课件作答实时快照(DOM/Canvas 镜像)
|
|
187
|
-
- 支持教师端一键向指定学生或全班推送课件交互指令(如强制锁屏、重置题目、跳转指定页)
|
|
188
|
-
- [ ] **弱网自适应与离线断点续传**:
|
|
189
|
-
- 学生端在网络波动或断网状态下自动切换至离线缓存模式,答题数据暂存 IndexedDB
|
|
190
|
-
- 网络恢复后触发幂等对账上报,保障农村/边远地区学校课堂的无缝教学体验
|
|
191
|
-
- [ ] **多学生白板分组协同微内核完善**:
|
|
192
|
-
- 生产化 `CollaborationEngineKernel` 中的区域对象锁(ObjectLock),避免多人同时绘制冲突
|
|
193
|
-
- 支持多白板画板并行动态分发与学生分组讨论室一键合并展示
|
|
194
|
-
- [ ] **AI 伴随助教深度赋能**:
|
|
195
|
-
- 聚合全班随堂测验实时答题热力图与学生端异常/离屏遥测,自动向教师推送「教学干预建议」
|
|
196
|
-
- 针对薄弱学生自动生成分层递进练习题并直接推送到学生互动端
|
|
197
|
-
- [ ] **插件 Worker 沙箱默认化**:全面推广独立 Worker 进程沙箱,实现插件与宿主主线程的硬隔离
|
|
112
|
+
脚本将自动执行:环境依赖预检 ➔ 生产构建 ➔ Nginx 反向代理配置 ➔ PM2 进程守护与加密密钥初始化。
|
|
113
|
+
更多部署方式(Docker Compose、多实例集群与环境变量全集)请查阅 [生产部署指南](https://openlearn-next-v2.readthedocs.io/zh-cn/latest/deployment/production-guide.html) 与 [系统配置手册](https://openlearn-next-v2.readthedocs.io/zh-cn/latest/configuration/system-configuration.html)。
|
|
198
114
|
|
|
199
115
|
---
|
|
200
116
|
|
|
201
|
-
## 📖
|
|
202
|
-
|
|
203
|
-
- 📚 [官方完整文档](https://openlearn-next-v2.readthedocs.io/zh-cn/latest/)
|
|
117
|
+
## 📖 更多文档
|
|
204
118
|
|
|
205
|
-
-
|
|
206
|
-
-
|
|
207
|
-
-
|
|
119
|
+
- 📚 [完整官方文档库](https://openlearn-next-v2.readthedocs.io/zh-cn/latest/)
|
|
120
|
+
- ⚙️ [系统环境变量配置规范](docs/configuration/system-configuration.md)
|
|
121
|
+
- 🔌 [插件系统与 SDK 规范](docs/tutorials/plugin-development-tutorial.md)
|
|
122
|
+
- 🤝 [贡献指南与开发规范 (AGENTS.md)](AGENTS.md)
|
|
123
|
+
- 📝 [版本更新日志 (CHANGELOG.md)](CHANGELOG.md)
|
|
208
124
|
|
|
209
125
|
---
|
|
210
126
|
|
|
211
127
|
## 📄 开源许可证
|
|
212
128
|
|
|
213
|
-
本项目基于 MIT License
|
|
129
|
+
本项目基于 [MIT License](package.json) 开源发布。
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as r}from"./vendor-react-
|
|
1
|
+
import{j as r}from"./vendor-react-DHoBZpn2.js";function c({value:a,options:i,onChange:t,lang:o="zh",includeAll:n=!0,className:s=""}){return r.jsxs("select",{value:a,onChange:e=>t(e.target.value),className:`text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all font-semibold ${s}`,"aria-label":o==="zh"?"学年筛选":"Academic year filter",children:[n&&r.jsx("option",{value:"all",children:o==="zh"?"全部学年":"All academic years"}),i.map(e=>r.jsx("option",{value:e,children:e},e))]})}export{c as A};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{r as o,j as e}from"./vendor-react-DHoBZpn2.js";import{e as D}from"./index-BiHQqzdm.js";import{a4 as Q,X as q,o as H,n as J,B as V,h as X,Q as _,D as G,U as K,b1 as Y,b0 as Z,b2 as ee,e as te,P as se}from"./vendor-icons-DaYNfRAc.js";import"./vendor-utils-BxOuk3id.js";import"./vendor-pdf-DVWGghIL.js";import"./vendor-motion-BFAPOs-b.js";import"./vendor-charts-skggaP9D.js";async function ie(n="all"){const a=await fetch(`/api/activities?role=${encodeURIComponent(n)}`,{headers:{Accept:"application/json"}});if(!a.ok)throw new Error(`Failed to load activities (${a.status})`);return(await a.json()).activities??[]}async function k(){const n=await fetch("/api/activities/running",{headers:{Accept:"application/json"}});if(!n.ok)throw new Error(`Failed to load running activities (${n.status})`);return(await n.json()).activities??[]}async function ne(n){const a=await fetch(`/api/activities/${encodeURIComponent(n)}/finish`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!a.ok){const s=await a.json().catch(()=>({}));throw new Error(s.error||`Failed to finish activity (${a.status})`)}}async function ae(n){const a=await fetch(`/api/activities/${encodeURIComponent(n)}/pause`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!a.ok){const s=await a.json().catch(()=>({}));throw new Error(s.error||`Failed to pause activity (${a.status})`)}}async function re(n){const a=await fetch(`/api/activities/${encodeURIComponent(n)}/resume`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!a.ok){const s=await a.json().catch(()=>({}));throw new Error(s.error||`Failed to resume activity (${a.status})`)}}async function oe(n,a,s){const l=await fetch(`/api/activities/${encodeURIComponent(n)}/start`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({payload:a??{},actorId:s})}),c=await l.json();if(!l.ok||c.ok===!1)throw new Error(c.error||`Failed to start activity (${l.status})`);return c}const x={padding:"12px",color:"#e2e8f0",fontSize:"13px"},ce={HelpCircle:te,Vote:ee,BarChart3:Z,MessagesSquare:Y,Users:K,FileText:G,Trophy:_,CheckCircle2:X,BookOpen:V};function le({name:n,className:a}){const s=n&&ce[n]||se;return e.jsx(s,{size:18,className:a})}function de(n,a){if(!n)return a==="zh"?"进行中":"In progress";const s=Math.max(0,Math.floor((Date.now()-n)/1e3)),l=Math.floor(s/60),c=s%60;return a==="zh"?`${l} 分 ${c} 秒`:`${l}m ${c}s`}const ge=({role:n,actorId:a,lang:s="en",classroomId:l,mode:c="launcher"})=>{const[p,E]=o.useState([]),[A,b]=o.useState(!0),[j,$]=o.useState(null),[v,m]=o.useState({}),[h,w]=o.useState([]),[T,R]=o.useState(!0),[P,I]=o.useState(null),[y,u]=o.useState(null),[f,z]=o.useState(!1),[S,C]=o.useState(null),B=D(i=>i.setTeacherTab),F=o.useMemo(()=>{const i=new Map;for(const t of p){const r=t.category;i.has(r)||i.set(r,[]),i.get(r).push(t)}return Array.from(i.entries())},[p]);o.useEffect(()=>{if(c!=="launcher")return;let i=!1;return b(!0),ie(n).then(t=>{i||E(t)}).catch(t=>{i||$(t instanceof Error?t.message:String(t))}).finally(()=>{i||b(!1)}),()=>{i=!0}},[n,c]),o.useEffect(()=>{if(c!=="status")return;let i=!1;const t=async()=>{try{const d=await k();i||w(d)}catch(d){i||I(d instanceof Error?d.message:String(d))}finally{i||R(!1)}};t();const r=setInterval(t,5e3);return()=>{i=!0,clearInterval(r)}},[c]);const g=async i=>{if(y){z(!0),C(null);try{await i(y),w(await k())}catch(t){C(t instanceof Error?t.message:String(t))}finally{z(!1)}}},W=()=>g(ne),M=()=>g(ae),O=()=>g(re),L=()=>{u(null),B("live_class")};if(c==="status"){if(T)return e.jsx("div",{className:"bg-white border border-slate-200/80 rounded-2xl p-5 shadow-sm h-24 animate-pulse"});if(P||h.length===0)return null;const i=h.find(t=>t.id===y)||null;return e.jsxs("div",{className:"bg-white border border-slate-200/80 rounded-2xl p-5 shadow-sm",children:[e.jsxs("div",{className:"flex items-center justify-between mb-3",children:[e.jsxs("div",{className:"flex items-center gap-2 text-slate-800 font-extrabold",children:[e.jsx("span",{children:"🧩"}),e.jsx("span",{children:s==="zh"?"进行中的活动":"Activities in Progress"})]}),e.jsx("span",{className:"text-xs font-bold bg-indigo-50 text-indigo-700 px-2 py-0.5 rounded-full",children:h.length})]}),e.jsx("div",{className:"flex flex-col gap-2",children:h.map(t=>{const r=t.state==="running";return e.jsxs("button",{onClick:()=>u(t.id),className:"w-full flex items-center gap-3 text-left bg-slate-50 hover:bg-slate-100 border border-slate-200/70 rounded-xl p-3 transition-colors",children:[e.jsx(le,{name:t.icon,className:"text-indigo-600 shrink-0"}),e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsx("div",{className:"font-bold text-slate-800 truncate",children:t.name}),t.category&&e.jsx("div",{className:"text-xs text-slate-500 truncate",children:t.category})]}),e.jsxs("div",{className:"flex flex-col items-end gap-1 shrink-0",children:[e.jsx("span",{className:`text-xs font-bold px-2 py-0.5 rounded-full ${r?"bg-emerald-100 text-emerald-700":"bg-amber-100 text-amber-700"}`,children:r?s==="zh"?"进行中":"Running":s==="zh"?"已暂停":"Paused"}),e.jsxs("span",{className:"text-xs text-slate-400 flex items-center gap-1",children:[e.jsx(Q,{size:10})," ",de(t.startedAt,s)]})]})]},t.id)})}),i&&e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/30 p-4",onClick:()=>u(null),children:e.jsxs("div",{className:"bg-white rounded-2xl shadow-xl w-full max-w-sm p-5",onClick:t=>t.stopPropagation(),children:[e.jsxs("div",{className:"flex items-center justify-between mb-3",children:[e.jsx("h3",{className:"font-bold text-slate-800",children:i.name}),e.jsx("button",{onClick:()=>u(null),className:"text-slate-400 hover:text-slate-600","aria-label":"close",children:e.jsx(q,{size:18})})]}),e.jsxs("div",{className:"text-sm text-slate-500 mb-1",children:[s==="zh"?"状态":"Status",":"," ",i.state==="running"?s==="zh"?"进行中":"Running":s==="zh"?"已暂停":"Paused"]}),e.jsxs("div",{className:"text-sm text-slate-500 mb-4",children:[s==="zh"?"开始时间":"Started",":"," ",i.startedAt?new Date(i.startedAt).toLocaleString():"-"]}),S&&e.jsx("div",{className:"text-xs text-rose-600 mb-2",children:S}),e.jsxs("div",{className:"flex gap-2",children:[i.state==="running"?e.jsxs("button",{disabled:f,onClick:M,className:"flex-1 flex items-center justify-center gap-1 bg-amber-500 hover:bg-amber-600 disabled:opacity-60 text-white rounded-lg py-2 text-sm font-bold",children:[e.jsx(H,{size:14})," ",s==="zh"?"暂停":"Pause"]}):e.jsxs("button",{disabled:f,onClick:O,className:"flex-1 flex items-center justify-center gap-1 bg-indigo-500 hover:bg-indigo-600 disabled:opacity-60 text-white rounded-lg py-2 text-sm font-bold",children:[e.jsx(J,{size:14})," ",s==="zh"?"恢复":"Resume"]}),e.jsx("button",{disabled:f,onClick:W,className:"flex-1 flex items-center justify-center gap-1 bg-rose-600 hover:bg-rose-700 disabled:opacity-60 text-white rounded-lg py-2 text-sm font-bold",children:f?s==="zh"?"处理中…":"Working…":s==="zh"?"结束":"End"})]}),e.jsx("button",{onClick:L,className:"w-full mt-2 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded-lg py-2 text-sm font-bold",children:s==="zh"?"进入课堂":"Enter Classroom"})]})})]})}const U=async i=>{m(t=>({...t,[i.id]:s==="zh"?"启动中…":"Starting…"}));try{const t=await oe(i.id,{classroomId:l,role:n},a);m(r=>({...r,[i.id]:t.dispatched?s==="zh"?"已启动 ✓":"Started ✓":s==="zh"?"已通知 ✓":"Notified ✓"}))}catch(t){m(r=>({...r,[i.id]:t instanceof Error?t.message:String(t)}))}};if(A)return e.jsx("div",{style:x,children:s==="zh"?"加载活动中…":"Loading activities…"});if(j)return e.jsxs("div",{style:x,children:[e.jsxs("div",{style:{fontWeight:600,marginBottom:6},children:["🧩 ",s==="zh"?"活动中心":"Activity Center"]}),e.jsx("div",{style:{opacity:.7},children:j})]});const N=n==="teacher"?s==="zh"?"活动中心(教师)":"Activity Center":s==="zh"?"活动(学生)":"Activities";return p.length===0?e.jsxs("div",{style:x,children:[e.jsxs("div",{style:{fontWeight:600,marginBottom:6},children:["🧩 ",N]}),e.jsx("div",{style:{opacity:.7},children:s==="zh"?"暂无可用活动。":"No activities available."})]}):e.jsxs("div",{style:x,children:[e.jsxs("div",{style:{fontWeight:600,marginBottom:8},children:["🧩 ",N]}),F.map(([i,t])=>e.jsxs("div",{style:{marginBottom:10},children:[e.jsx("div",{style:{opacity:.6,fontSize:12,textTransform:"uppercase",marginBottom:4},children:i}),e.jsx("div",{style:{display:"flex",flexWrap:"wrap",gap:8},children:t.map(r=>e.jsxs("div",{style:{border:"1px solid #334155",borderRadius:8,padding:"8px 10px",minWidth:150,background:"#0f172a"},children:[e.jsxs("div",{style:{fontWeight:600},children:[r.icon?`${r.icon} `:"▫️ ",r.name]}),r.description&&e.jsx("div",{style:{opacity:.7,fontSize:12,margin:"2px 0 6px"},children:r.description}),e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6},children:[e.jsx("button",{onClick:()=>U(r),style:{cursor:"pointer",background:n==="teacher"?"#2563eb":"#0ea5e9",color:"#fff",border:"none",borderRadius:6,padding:"4px 10px",fontSize:12},children:n==="teacher"?s==="zh"?"启动":"Start":s==="zh"?"参与":"Open"}),v[r.id]&&e.jsx("span",{style:{fontSize:12,opacity:.85},children:v[r.id]})]}),e.jsx("div",{style:{opacity:.5,fontSize:12,marginTop:4},children:r.provider==="official"?"official":r.provider})]},r.id))})]},i))]})};export{ge as ActivityWorkspaceWidget};
|