openlearn-next 0.3.14 → 0.3.16
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/dist/assets/{BatchPickerModal-rH9zJFSU.js → BatchPickerModal-DtFV99dt.js} +1 -1
- package/dist/assets/{ClassesView-BOyfXlqX.js → ClassesView-E_bmIY3c.js} +1 -1
- package/dist/assets/{CloudDriveModal-C6pHIsha.js → CloudDriveModal-CZ-KJmic.js} +1 -1
- package/dist/assets/{CourseManagement-CzZa4BJM.js → CourseManagement-DEKMw74G.js} +1 -1
- package/dist/assets/{CourseWizardModal-B48s3we0.js → CourseWizardModal-B8Q_8YKV.js} +1 -1
- package/dist/assets/{Dashboard-D5-gR_UU.js → Dashboard-B2PI302K.js} +1 -1
- package/dist/assets/{ExportWeightModal-BT09J92K.js → ExportWeightModal-0TpuWHQy.js} +1 -1
- package/dist/assets/{HelpView-C2HDL6h1.js → HelpView-DvrWFfuf.js} +1 -1
- package/dist/assets/{ImportLessonsModal-BD8m23QT.js → ImportLessonsModal-C43YkKNn.js} +1 -1
- package/dist/assets/{InteractiveCoursewareViewer-BNaBBfEh.js → InteractiveCoursewareViewer-BALndtKl.js} +1 -1
- package/dist/assets/{InteractiveWhiteboard-Dlz0tFgm.js → InteractiveWhiteboard-Dty2usZE.js} +1 -1
- package/dist/assets/{LazyCourseware-xQ4Ilq2m.js → LazyCourseware-BRb-N2Gw.js} +1 -1
- package/dist/assets/{LazyWhiteboard-D5OvfMCX.js → LazyWhiteboard-BPtDG81P.js} +1 -1
- package/dist/assets/{LessonEditorView-Dc6l0ndm.js → LessonEditorView-ChnEY-M1.js} +1 -1
- package/dist/assets/{LiveClassroomView-BdNngMqV.js → LiveClassroomView-no5n32dK.js} +1 -1
- package/dist/assets/{NotificationDetailModal-Bzh67Pak.js → NotificationDetailModal-BSWVptQ9.js} +1 -1
- package/dist/assets/{PluginView-2RhbJwJP.js → PluginView-DofVkA49.js} +1 -1
- package/dist/assets/{QuizGeneratorModal-C1pLEuZK.js → QuizGeneratorModal-DSbBS1jm.js} +1 -1
- package/dist/assets/{StudentAssignmentView-C0r7SKM0.js → StudentAssignmentView-Dnim6GN3.js} +1 -1
- package/dist/assets/{StudentLessonView-DwFq5k0g.js → StudentLessonView-Bvb00YZM.js} +1 -1
- package/dist/assets/{StudentPreviewModal-DIx5Bvqg.js → StudentPreviewModal-Cm2dp3tA.js} +1 -1
- package/dist/assets/{StudentView-Df_P6K4C.js → StudentView-Bnpufxoh.js} +1 -1
- package/dist/assets/{SystemResourceLibraryModal-pi7iemD-.js → SystemResourceLibraryModal-CeZ6MMQK.js} +1 -1
- package/dist/assets/{TeacherView-DQ9uPyp4.js → TeacherView-BNRrbsHE.js} +1 -1
- package/dist/assets/{index-XcxaTusB.js → index-Cdsp6u2F.js} +1 -1
- package/dist/assets/{index-CWmuWSYa.js → index-D7ZJLlkd.js} +3 -3
- package/dist/index.html +1 -1
- package/dist/server.cjs +94 -7
- package/package.json +17 -17
package/dist/index.html
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
</script>
|
|
18
|
-
<script type="module" crossorigin src="/assets/index-
|
|
18
|
+
<script type="module" crossorigin src="/assets/index-D7ZJLlkd.js"></script>
|
|
19
19
|
<link rel="stylesheet" crossorigin href="/assets/index-D7PQWdtD.css">
|
|
20
20
|
</head>
|
|
21
21
|
<body>
|
package/dist/server.cjs
CHANGED
|
@@ -351,7 +351,7 @@ var init_token = __esm({
|
|
|
351
351
|
});
|
|
352
352
|
|
|
353
353
|
// packages/core/di/interfaces.ts
|
|
354
|
-
var ICommandBusServiceToken4, IEventBusServiceToken3, IActionRegistryServiceToken4, ICapabilityServiceToken, IProcessServiceToken2, IStorageServiceToken, IAIServiceToken, IDatabaseToken4, IPluginHostToken, IPluginLifecycleManagerToken, IPluginDistributionManagerToken, IPluginRuntimeCompositionToken, IUnifiedExtensionRegistryToken, IPluginCapabilityGatewayToken, ICapabilityRegistryToken, ISemesterGradeServiceToken, IPointsDimensionRegistryToken, IPointsLedgerServiceToken, ILessonEngineServiceToken, IClassroomRuntimeServiceToken, IPresenceEngineServiceToken, ITeachingCollaborationServiceToken, ILearningAnalyticsServiceToken, IAICapabilityServiceToken, ICapabilityRuntimeServiceToken, ICapabilityGovernanceServiceToken, IPlatformServiceRegistryToken;
|
|
354
|
+
var ICommandBusServiceToken4, IEventBusServiceToken3, IActionRegistryServiceToken4, ICapabilityServiceToken, IProcessServiceToken2, IStorageServiceToken, IAIServiceToken, IDatabaseToken4, IPluginHostToken, IPluginLifecycleManagerToken, IPluginDistributionManagerToken, IPluginRuntimeCompositionToken, IUnifiedExtensionRegistryToken, IPluginCapabilityGatewayToken, ICapabilityRegistryToken, IAuthSessionBridgeToken, ISemesterGradeServiceToken, IPointsDimensionRegistryToken, IPointsLedgerServiceToken, ILessonEngineServiceToken, IClassroomRuntimeServiceToken, IPresenceEngineServiceToken, ITeachingCollaborationServiceToken, ILearningAnalyticsServiceToken, IAICapabilityServiceToken, ICapabilityRuntimeServiceToken, ICapabilityGovernanceServiceToken, IPlatformServiceRegistryToken;
|
|
355
355
|
var init_interfaces = __esm({
|
|
356
356
|
"packages/core/di/interfaces.ts"() {
|
|
357
357
|
init_token();
|
|
@@ -400,6 +400,10 @@ var init_interfaces = __esm({
|
|
|
400
400
|
ICapabilityRegistryToken = new Token(
|
|
401
401
|
"@openlearn/core:ICapabilityRegistry"
|
|
402
402
|
);
|
|
403
|
+
IAuthSessionBridgeToken = new Token(
|
|
404
|
+
"@openlearn/core:IAuthSessionBridgeService",
|
|
405
|
+
"1.0.0"
|
|
406
|
+
);
|
|
403
407
|
ISemesterGradeServiceToken = new Token(
|
|
404
408
|
"@openlearn/core:ISemesterGradeService"
|
|
405
409
|
);
|
|
@@ -8402,6 +8406,78 @@ var PointsLedgerService = class {
|
|
|
8402
8406
|
}
|
|
8403
8407
|
};
|
|
8404
8408
|
|
|
8409
|
+
// packages/core/di/auth-session-bridge-service.ts
|
|
8410
|
+
var import_node_crypto = __toESM(require("node:crypto"), 1);
|
|
8411
|
+
var AuthSessionBridgeService = class {
|
|
8412
|
+
constructor(db2) {
|
|
8413
|
+
this.db = db2;
|
|
8414
|
+
}
|
|
8415
|
+
async createSession(user) {
|
|
8416
|
+
if (!user || !user.userId || !user.role) {
|
|
8417
|
+
throw new Error("Invalid user payload: userId and role are required");
|
|
8418
|
+
}
|
|
8419
|
+
const now = Date.now();
|
|
8420
|
+
const maxAgeSeconds = 7 * 24 * 60 * 60;
|
|
8421
|
+
const expiresAt = now + maxAgeSeconds * 1e3;
|
|
8422
|
+
const sessionToken = "token_" + import_node_crypto.default.randomBytes(16).toString("hex");
|
|
8423
|
+
if (user.role === "student") {
|
|
8424
|
+
const existingStudent = this.db.prepare("SELECT id FROM students WHERE id = ?").get(user.userId);
|
|
8425
|
+
if (!existingStudent) {
|
|
8426
|
+
const studentNumber = user.username || `sso_${user.userId}`;
|
|
8427
|
+
const studentNumberCheck = this.db.prepare("SELECT id FROM students WHERE student_number = ?").get(studentNumber);
|
|
8428
|
+
const finalStudentNumber = studentNumberCheck ? `sso_${user.userId}_${Date.now()}` : studentNumber;
|
|
8429
|
+
this.db.prepare(`
|
|
8430
|
+
INSERT INTO students (id, student_number, name, email, created_at)
|
|
8431
|
+
VALUES (?, ?, ?, ?, ?)
|
|
8432
|
+
`).run(
|
|
8433
|
+
user.userId,
|
|
8434
|
+
finalStudentNumber,
|
|
8435
|
+
user.name || user.username || user.userId,
|
|
8436
|
+
user.email || null,
|
|
8437
|
+
now
|
|
8438
|
+
);
|
|
8439
|
+
}
|
|
8440
|
+
} else if (user.role === "teacher" || user.role === "administrator") {
|
|
8441
|
+
const existingUser = this.db.prepare("SELECT id FROM users WHERE id = ?").get(user.userId);
|
|
8442
|
+
if (!existingUser) {
|
|
8443
|
+
const username = user.username || `sso_${user.userId}`;
|
|
8444
|
+
const usernameCheck = this.db.prepare("SELECT id FROM users WHERE username = ?").get(username);
|
|
8445
|
+
const finalUsername = usernameCheck ? `sso_${user.userId}_${Date.now()}` : username;
|
|
8446
|
+
this.db.prepare(`
|
|
8447
|
+
INSERT INTO users (id, username, password_hash, role, name, created_at, status)
|
|
8448
|
+
VALUES (?, ?, ?, ?, ?, ?, 'active')
|
|
8449
|
+
`).run(
|
|
8450
|
+
user.userId,
|
|
8451
|
+
finalUsername,
|
|
8452
|
+
"",
|
|
8453
|
+
// SSO 用户无本地明文/哈希密码
|
|
8454
|
+
user.role,
|
|
8455
|
+
user.name || user.username || user.userId,
|
|
8456
|
+
now
|
|
8457
|
+
);
|
|
8458
|
+
}
|
|
8459
|
+
}
|
|
8460
|
+
const sessionData = {
|
|
8461
|
+
userId: user.userId,
|
|
8462
|
+
username: user.username || user.userId,
|
|
8463
|
+
role: user.role,
|
|
8464
|
+
subRole: user.role,
|
|
8465
|
+
name: user.name || user.username || user.userId,
|
|
8466
|
+
email: user.email || null,
|
|
8467
|
+
avatar: user.avatar ?? null,
|
|
8468
|
+
classId: user.classId ?? null
|
|
8469
|
+
};
|
|
8470
|
+
this.db.prepare(`
|
|
8471
|
+
INSERT INTO client_sessions (id, session_data, updated_at, expires_at)
|
|
8472
|
+
VALUES (?, ?, ?, ?)
|
|
8473
|
+
`).run(sessionToken, JSON.stringify(sessionData), now, expiresAt);
|
|
8474
|
+
return {
|
|
8475
|
+
token: sessionToken,
|
|
8476
|
+
maxAge: maxAgeSeconds
|
|
8477
|
+
};
|
|
8478
|
+
}
|
|
8479
|
+
};
|
|
8480
|
+
|
|
8405
8481
|
// packages/core/plugin-host/index.ts
|
|
8406
8482
|
var import_uuid13 = require("uuid");
|
|
8407
8483
|
var import_fs5 = __toESM(require("fs"), 1);
|
|
@@ -8988,7 +9064,7 @@ var HotReloadActivationError = class extends HotReloadError {
|
|
|
8988
9064
|
init_interfaces();
|
|
8989
9065
|
|
|
8990
9066
|
// packages/core/version.ts
|
|
8991
|
-
var PLATFORM_VERSION = "0.3.
|
|
9067
|
+
var PLATFORM_VERSION = "0.3.16";
|
|
8992
9068
|
var OPENLEARN_VERSION = PLATFORM_VERSION;
|
|
8993
9069
|
|
|
8994
9070
|
// packages/core/plugin-host/plugin-runtime-composition.ts
|
|
@@ -17975,6 +18051,7 @@ var Kernel = class {
|
|
|
17975
18051
|
this.serviceRegistry.register(ICapabilityRuntimeServiceToken, { getRuntimeKernel: async () => this.capabilityFrameworkRuntime });
|
|
17976
18052
|
this.serviceRegistry.register(ICapabilityGovernanceServiceToken, { getGovernanceKernel: async () => this.capabilityGovernance });
|
|
17977
18053
|
this.serviceRegistry.register(IPlatformServiceRegistryToken, { getServiceRegistryKernel: async () => this.platformServiceRegistryKernel });
|
|
18054
|
+
this.serviceRegistry.register(IAuthSessionBridgeToken, new AuthSessionBridgeService(this.db));
|
|
17978
18055
|
this.commandBus.setInterceptor(async (command) => {
|
|
17979
18056
|
const action = this.actionRegistry.getActionByCommandType(command.type);
|
|
17980
18057
|
if (action) {
|
|
@@ -25069,6 +25146,12 @@ data: ${JSON.stringify({ error: msg })}
|
|
|
25069
25146
|
5e3
|
|
25070
25147
|
);
|
|
25071
25148
|
const status = typeof response.status === "number" && response.status >= 100 && response.status <= 599 ? response.status : 200;
|
|
25149
|
+
if (response.sessionToken && typeof response.sessionToken === "string" && response.sessionToken.startsWith("token_")) {
|
|
25150
|
+
const isSecure = req.secure || req.headers["x-forwarded-proto"] === "https" || process.env.NODE_ENV === "production" && process.env.ENABLE_HTTPS === "true";
|
|
25151
|
+
const secureFlag = isSecure ? "; Secure" : "";
|
|
25152
|
+
const sameSite = isSecure ? "SameSite=None" : "SameSite=Lax";
|
|
25153
|
+
res.setHeader("Set-Cookie", `edu_os_token=${response.sessionToken}; Path=/; HttpOnly; ${sameSite}; Max-Age=604800${secureFlag}`);
|
|
25154
|
+
}
|
|
25072
25155
|
if (response.headers && typeof response.headers === "object") {
|
|
25073
25156
|
for (const [k, v] of Object.entries(response.headers)) {
|
|
25074
25157
|
const lowerKey = k.toLowerCase();
|
|
@@ -25077,10 +25160,10 @@ data: ${JSON.stringify({ error: msg })}
|
|
|
25077
25160
|
}
|
|
25078
25161
|
}
|
|
25079
25162
|
}
|
|
25080
|
-
if (res.getHeader("content-type") === void 0) {
|
|
25163
|
+
if (res.getHeader("content-type") === void 0 && response.body !== void 0 && response.body !== null) {
|
|
25081
25164
|
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
25082
25165
|
}
|
|
25083
|
-
res.status(status).send(response.body);
|
|
25166
|
+
res.status(status).send(response.body !== void 0 ? response.body : "");
|
|
25084
25167
|
} catch (err) {
|
|
25085
25168
|
if (err.name === "GatewayTimeoutError" || err.message?.includes("timed out")) {
|
|
25086
25169
|
res.status(504).json({ success: false, error: "Gateway Timeout: Plugin did not respond in time" });
|
|
@@ -25752,8 +25835,11 @@ async function startServer() {
|
|
|
25752
25835
|
kernelContainer.pluginHost.setExpressApp(app);
|
|
25753
25836
|
const PORT = parseInt(process.env.PORT || "9000", 10);
|
|
25754
25837
|
app.set("trust proxy", 1);
|
|
25755
|
-
const frameAllowedOrigins = process.env.ALLOWED_FRAME_ORIGINS ? process.env.ALLOWED_FRAME_ORIGINS.split(",").map((s) => s.trim()) : ["http://localhost:*", "http://127.0.0.1:*"];
|
|
25838
|
+
const frameAllowedOrigins = process.env.ALLOWED_FRAME_ORIGINS ? process.env.ALLOWED_FRAME_ORIGINS.split(",").map((s) => s.trim()).filter(Boolean) : ["http://localhost:*", "http://127.0.0.1:*"];
|
|
25839
|
+
const ltiAllowedOrigins = process.env.LTI_ALLOWED_LMS_ORIGINS ? process.env.LTI_ALLOWED_LMS_ORIGINS.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
25756
25840
|
app.use((0, import_helmet.default)({
|
|
25841
|
+
// SEC-LTI: 若配置了允许嵌入的 LMS 平台域名,禁用全局 X-Frame-Options,由 CSP frame-ancestors 严格精细化管控
|
|
25842
|
+
xFrameOptions: ltiAllowedOrigins.length > 0 ? false : { action: "sameorigin" },
|
|
25757
25843
|
contentSecurityPolicy: {
|
|
25758
25844
|
directives: {
|
|
25759
25845
|
defaultSrc: ["'self'"],
|
|
@@ -25765,6 +25851,7 @@ async function startServer() {
|
|
|
25765
25851
|
fontSrc: ["'self'", "data:", "https://fonts.gstatic.com"],
|
|
25766
25852
|
// 移除通配 http: 和 https:,限制 iframe 仅能加载本地、沙箱或受信任课件源
|
|
25767
25853
|
frameSrc: ["'self'", "blob:", "data:", ...frameAllowedOrigins],
|
|
25854
|
+
frameAncestors: ltiAllowedOrigins.length > 0 ? ["'self'", ...ltiAllowedOrigins] : ["'self'"],
|
|
25768
25855
|
objectSrc: ["'none'"],
|
|
25769
25856
|
baseUri: ["'self'"]
|
|
25770
25857
|
}
|
|
@@ -25772,10 +25859,10 @@ async function startServer() {
|
|
|
25772
25859
|
crossOriginOpenerPolicy: false,
|
|
25773
25860
|
crossOriginEmbedderPolicy: false,
|
|
25774
25861
|
crossOriginResourcePolicy: { policy: "cross-origin" },
|
|
25775
|
-
// 允许沙箱 iframe(opaque origin
|
|
25862
|
+
// 允许沙箱 iframe(opaque origin)加载静态资源
|
|
25776
25863
|
originAgentCluster: false,
|
|
25777
25864
|
strictTransportSecurity: false
|
|
25778
|
-
//
|
|
25865
|
+
// 针对 HTTP 部署,禁用 HSTS(否则浏览器缓存后强制 HTTPS,导致 ERR_CONNECTION_REFUSED)
|
|
25779
25866
|
}));
|
|
25780
25867
|
const loginLimiter = (0, import_express_rate_limit.default)({
|
|
25781
25868
|
windowMs: 60 * 1e3,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openlearn-next",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.16",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A plugin-driven Educational OS / LMS platform with AI agent teaching assistant",
|
|
6
6
|
"keywords": [
|
|
@@ -43,22 +43,8 @@
|
|
|
43
43
|
"node": ">=20.0.0",
|
|
44
44
|
"pnpm": ">=9.0.0"
|
|
45
45
|
},
|
|
46
|
-
"scripts": {
|
|
47
|
-
"dev": "npx tsx --no-cache server.ts",
|
|
48
|
-
"build": "vite build && node scripts/build-plugins.mjs && esbuild server.ts --bundle --platform=node --format=cjs --packages=external --sourcemap --outfile=dist/server.cjs",
|
|
49
|
-
"start": "node dist/server.cjs",
|
|
50
|
-
"preview": "vite preview",
|
|
51
|
-
"clean": "rm -rf dist server.js",
|
|
52
|
-
"lint": "tsc --noEmit",
|
|
53
|
-
"lint:eslint": "eslint . --ext .ts,.tsx --ignore-pattern dist --ignore-pattern node_modules",
|
|
54
|
-
"format": "prettier --write .",
|
|
55
|
-
"test": "vitest run",
|
|
56
|
-
"db:backup": "npx tsx scripts/backup-db.ts",
|
|
57
|
-
"db:reset": "rm -f packages/core/db/educational_os.db* && echo \"✓ Database deleted. Run pnpm dev to regenerate.\""
|
|
58
|
-
},
|
|
59
46
|
"dependencies": {
|
|
60
47
|
"@google/genai": "^2.4.0",
|
|
61
|
-
"@openlearn/plugin-sdk": "3.6.0",
|
|
62
48
|
"@types/reveal.js": "^5.2.2",
|
|
63
49
|
"bcryptjs": "^3.0.3",
|
|
64
50
|
"better-sqlite3": "^12.11.1",
|
|
@@ -91,7 +77,8 @@
|
|
|
91
77
|
"xlsx": "^0.18.5",
|
|
92
78
|
"xss": "^1.0.15",
|
|
93
79
|
"zod": "^4.4.3",
|
|
94
|
-
"zustand": "^5.0.14"
|
|
80
|
+
"zustand": "^5.0.14",
|
|
81
|
+
"@openlearn/plugin-sdk": "3.6.1"
|
|
95
82
|
},
|
|
96
83
|
"devDependencies": {
|
|
97
84
|
"@eslint/js": "^10.0.1",
|
|
@@ -121,5 +108,18 @@
|
|
|
121
108
|
"tsx": "^4.21.0",
|
|
122
109
|
"typescript": "~5.8.2",
|
|
123
110
|
"vitest": "^4.1.9"
|
|
111
|
+
},
|
|
112
|
+
"scripts": {
|
|
113
|
+
"dev": "npx tsx --no-cache server.ts",
|
|
114
|
+
"build": "vite build && node scripts/build-plugins.mjs && esbuild server.ts --bundle --platform=node --format=cjs --packages=external --sourcemap --outfile=dist/server.cjs",
|
|
115
|
+
"start": "node dist/server.cjs",
|
|
116
|
+
"preview": "vite preview",
|
|
117
|
+
"clean": "rm -rf dist server.js",
|
|
118
|
+
"lint": "tsc --noEmit",
|
|
119
|
+
"lint:eslint": "eslint . --ext .ts,.tsx --ignore-pattern dist --ignore-pattern node_modules",
|
|
120
|
+
"format": "prettier --write .",
|
|
121
|
+
"test": "vitest run",
|
|
122
|
+
"db:backup": "npx tsx scripts/backup-db.ts",
|
|
123
|
+
"db:reset": "rm -f packages/core/db/educational_os.db* && echo \"✓ Database deleted. Run pnpm dev to regenerate.\""
|
|
124
124
|
}
|
|
125
|
-
}
|
|
125
|
+
}
|