collabdocchat 1.2.13 → 2.0.0

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.
Files changed (62) hide show
  1. package/README.md +219 -218
  2. package/index.html +2 -0
  3. package/install-and-start.bat +5 -0
  4. package/install-and-start.sh +5 -0
  5. package/package.json +9 -2
  6. package/scripts/pre-publish-check.js +213 -0
  7. package/scripts/start-app.js +15 -15
  8. package/server/index.js +38 -6
  9. package/server/middleware/cache.js +115 -0
  10. package/server/middleware/errorHandler.js +209 -0
  11. package/server/models/Document.js +66 -59
  12. package/server/models/File.js +49 -43
  13. package/server/models/Group.js +6 -0
  14. package/server/models/KnowledgeBase.js +254 -0
  15. package/server/models/Message.js +43 -0
  16. package/server/models/Task.js +87 -55
  17. package/server/models/User.js +67 -60
  18. package/server/models/Workflow.js +249 -0
  19. package/server/routes/ai.js +327 -0
  20. package/server/routes/audit.js +245 -210
  21. package/server/routes/backup.js +108 -0
  22. package/server/routes/chunked-upload.js +343 -0
  23. package/server/routes/export.js +440 -0
  24. package/server/routes/files.js +294 -218
  25. package/server/routes/groups.js +182 -0
  26. package/server/routes/knowledge.js +509 -0
  27. package/server/routes/tasks.js +257 -110
  28. package/server/routes/workflows.js +380 -0
  29. package/server/utils/backup.js +439 -0
  30. package/server/utils/cache.js +223 -0
  31. package/server/utils/workflow-engine.js +479 -0
  32. package/server/websocket/enhanced.js +509 -0
  33. package/server/websocket/index.js +233 -1
  34. package/src/components/knowledge-modal.js +485 -0
  35. package/src/components/optimized-poll-detail.js +724 -0
  36. package/src/main.js +5 -0
  37. package/src/pages/admin-dashboard.js +2248 -44
  38. package/src/pages/optimized-backup-view.js +616 -0
  39. package/src/pages/optimized-knowledge-view.js +803 -0
  40. package/src/pages/optimized-task-detail.js +843 -0
  41. package/src/pages/optimized-workflow-view.js +806 -0
  42. package/src/pages/simplified-workflows.js +651 -0
  43. package/src/pages/user-dashboard.js +677 -58
  44. package/src/services/api.js +65 -1
  45. package/src/services/auth.js +1 -1
  46. package/src/services/websocket.js +124 -16
  47. package/src/styles/collaboration-modern.js +708 -0
  48. package/src/styles/enhancements.css +392 -0
  49. package/src/styles/main.css +620 -1420
  50. package/src/styles/responsive.css +1000 -0
  51. package/src/styles/sidebar-fix.css +60 -0
  52. package/src/utils/ai-assistant.js +1398 -0
  53. package/src/utils/chat-enhancements.js +509 -0
  54. package/src/utils/collaboration-enhancer.js +1151 -0
  55. package/src/utils/feature-integrator.js +1724 -0
  56. package/src/utils/onboarding-guide.js +734 -0
  57. package/src/utils/performance.js +394 -0
  58. package/src/utils/permission-manager.js +890 -0
  59. package/src/utils/responsive-handler.js +491 -0
  60. package/src/utils/theme-manager.js +811 -0
  61. package/src/utils/ui-enhancements-loader.js +329 -0
  62. package/USAGE.md +0 -298
@@ -33,6 +33,30 @@ export const setupWebSocket = (wss) => {
33
33
  case 'typing':
34
34
  handleTyping(ws, data, wss);
35
35
  break;
36
+ case 'recall_message':
37
+ await handleRecallMessage(ws, data, wss);
38
+ break;
39
+ case 'message_read':
40
+ await handleMessageRead(ws, data, wss);
41
+ break;
42
+ case 'user_online':
43
+ handleUserOnline(ws, data, wss);
44
+ break;
45
+ case 'whiteboard_draw':
46
+ handleWhiteboardDraw(ws, data, wss);
47
+ break;
48
+ case 'whiteboard_clear':
49
+ handleWhiteboardClear(ws, data, wss);
50
+ break;
51
+ case 'whiteboard_collaboration_toggle':
52
+ handleWhiteboardCollaborationToggle(ws, data, wss);
53
+ break;
54
+ case 'whiteboard_user_join':
55
+ handleWhiteboardUserJoin(ws, data, wss);
56
+ break;
57
+ case 'whiteboard_user_leave':
58
+ handleWhiteboardUserLeave(ws, data, wss);
59
+ break;
36
60
  default:
37
61
  console.log('未知消息类型:', data.type);
38
62
  }
@@ -204,6 +228,109 @@ const handleTyping = (ws, data, wss) => {
204
228
  broadcastToGroup(wss, ws.groupId, message, ws);
205
229
  };
206
230
 
231
+ const handleRecallMessage = async (ws, data, wss) => {
232
+ if (!ws.userId) {
233
+ ws.send(JSON.stringify({ type: 'error', message: '未认证' }));
234
+ return;
235
+ }
236
+
237
+ try {
238
+ const message = await Message.findById(data.messageId);
239
+
240
+ if (!message) {
241
+ ws.send(JSON.stringify({ type: 'error', message: '消息不存在' }));
242
+ return;
243
+ }
244
+
245
+ // 检查是否是消息发送者
246
+ if (message.sender.toString() !== ws.userId) {
247
+ ws.send(JSON.stringify({ type: 'error', message: '只能撤回自己的消息' }));
248
+ return;
249
+ }
250
+
251
+ // 检查是否在2分钟内
252
+ const timeDiff = Date.now() - new Date(message.timestamp).getTime();
253
+ const twoMinutes = 2 * 60 * 1000;
254
+
255
+ if (timeDiff > twoMinutes) {
256
+ ws.send(JSON.stringify({ type: 'error', message: '只能撤回2分钟内的消息' }));
257
+ return;
258
+ }
259
+
260
+ message.isRecalled = true;
261
+ message.recalledAt = new Date();
262
+ message.content = '[消息已撤回]';
263
+ await message.save();
264
+
265
+ // 广播撤回消息
266
+ broadcastToGroup(wss, message.group.toString(), {
267
+ type: 'message_recalled',
268
+ messageId: data.messageId,
269
+ groupId: message.group.toString(),
270
+ userId: ws.userId
271
+ });
272
+ } catch (error) {
273
+ console.error('撤回消息失败:', error);
274
+ ws.send(JSON.stringify({ type: 'error', message: '撤回失败' }));
275
+ }
276
+ };
277
+
278
+ const handleMessageRead = async (ws, data, wss) => {
279
+ if (!ws.userId) {
280
+ return;
281
+ }
282
+
283
+ try {
284
+ const message = await Message.findById(data.messageId);
285
+
286
+ if (!message) {
287
+ return;
288
+ }
289
+
290
+ // 检查是否已标记为已读
291
+ const alreadyRead = message.readBy.some(
292
+ r => r.user.toString() === ws.userId
293
+ );
294
+
295
+ if (!alreadyRead) {
296
+ message.readBy.push({
297
+ user: ws.userId,
298
+ readAt: new Date()
299
+ });
300
+ await message.save();
301
+
302
+ // 通知消息发送者
303
+ const senderWs = clients.get(message.sender.toString());
304
+ if (senderWs && senderWs.readyState === 1) {
305
+ senderWs.send(JSON.stringify({
306
+ type: 'message_read_receipt',
307
+ messageId: data.messageId,
308
+ readBy: ws.userId,
309
+ readAt: new Date().toISOString()
310
+ }));
311
+ }
312
+ }
313
+ } catch (error) {
314
+ console.error('标记已读失败:', error);
315
+ }
316
+ };
317
+
318
+ const handleUserOnline = (ws, data, wss) => {
319
+ if (!ws.userId) {
320
+ return;
321
+ }
322
+
323
+ // 广播用户在线状态
324
+ if (ws.groupId) {
325
+ broadcastToGroup(wss, ws.groupId, {
326
+ type: 'user_online',
327
+ userId: ws.userId,
328
+ username: data.username,
329
+ isOnline: true
330
+ });
331
+ }
332
+ };
333
+
207
334
  const handleDisconnect = (ws, wss) => {
208
335
  if (ws.userId) {
209
336
  clients.delete(ws.userId);
@@ -211,7 +338,8 @@ const handleDisconnect = (ws, wss) => {
211
338
  if (ws.groupId) {
212
339
  broadcastToGroup(wss, ws.groupId, {
213
340
  type: 'user_offline',
214
- userId: ws.userId
341
+ userId: ws.userId,
342
+ isOnline: false
215
343
  });
216
344
  }
217
345
  }
@@ -226,3 +354,107 @@ const broadcastToGroup = (wss, groupId, message, excludeWs = null) => {
226
354
  });
227
355
  };
228
356
 
357
+ // 白板协作功能处理
358
+ const handleWhiteboardDraw = (ws, data, wss) => {
359
+ if (!ws.userId || !ws.groupId) {
360
+ return;
361
+ }
362
+
363
+ // 广播绘画数据到同一群组的其他用户
364
+ const message = {
365
+ type: 'whiteboard_draw',
366
+ groupId: data.groupId,
367
+ username: data.username,
368
+ data: data.data,
369
+ timestamp: new Date().toISOString()
370
+ };
371
+
372
+ broadcastToGroup(wss, data.groupId, message, ws);
373
+ };
374
+
375
+ const handleWhiteboardClear = (ws, data, wss) => {
376
+ if (!ws.userId || !ws.groupId) {
377
+ return;
378
+ }
379
+
380
+ // 广播清空画布事件
381
+ const message = {
382
+ type: 'whiteboard_clear',
383
+ groupId: data.groupId,
384
+ username: data.username,
385
+ timestamp: new Date().toISOString()
386
+ };
387
+
388
+ broadcastToGroup(wss, data.groupId, message, ws);
389
+ };
390
+
391
+ const handleWhiteboardCollaborationToggle = async (ws, data, wss) => {
392
+ if (!ws.userId || !ws.groupId) {
393
+ return;
394
+ }
395
+
396
+ // 验证是否是管理员
397
+ try {
398
+ const group = await Group.findById(data.groupId).select('admin');
399
+ if (!group) {
400
+ return;
401
+ }
402
+
403
+ const isAdmin = group.admin.toString() === ws.userId || ws.role === 'admin';
404
+ if (!isAdmin) {
405
+ ws.send(JSON.stringify({
406
+ type: 'error',
407
+ message: '只有管理员可以切换协作模式'
408
+ }));
409
+ return;
410
+ }
411
+
412
+ // 广播协作模式变更
413
+ const message = {
414
+ type: 'whiteboard_collaboration_toggle',
415
+ groupId: data.groupId,
416
+ enabled: data.enabled,
417
+ username: data.username,
418
+ timestamp: new Date().toISOString()
419
+ };
420
+
421
+ broadcastToGroup(wss, data.groupId, message);
422
+ } catch (error) {
423
+ console.error('切换协作模式失败:', error);
424
+ }
425
+ };
426
+
427
+ const handleWhiteboardUserJoin = (ws, data, wss) => {
428
+ if (!ws.userId) {
429
+ return;
430
+ }
431
+
432
+ // 广播用户加入白板
433
+ const message = {
434
+ type: 'whiteboard_user_join',
435
+ groupId: data.groupId,
436
+ username: data.username,
437
+ userId: ws.userId,
438
+ timestamp: new Date().toISOString()
439
+ };
440
+
441
+ broadcastToGroup(wss, data.groupId, message, ws);
442
+ };
443
+
444
+ const handleWhiteboardUserLeave = (ws, data, wss) => {
445
+ if (!ws.userId) {
446
+ return;
447
+ }
448
+
449
+ // 广播用户离开白板
450
+ const message = {
451
+ type: 'whiteboard_user_leave',
452
+ groupId: data.groupId,
453
+ username: data.username,
454
+ userId: ws.userId,
455
+ timestamp: new Date().toISOString()
456
+ };
457
+
458
+ broadcastToGroup(wss, data.groupId, message, ws);
459
+ };
460
+