collabdocchat 1.2.12 → 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 (61) 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/websocket.js +124 -16
  46. package/src/styles/collaboration-modern.js +708 -0
  47. package/src/styles/enhancements.css +392 -0
  48. package/src/styles/main.css +620 -1420
  49. package/src/styles/responsive.css +1000 -0
  50. package/src/styles/sidebar-fix.css +60 -0
  51. package/src/utils/ai-assistant.js +1398 -0
  52. package/src/utils/chat-enhancements.js +509 -0
  53. package/src/utils/collaboration-enhancer.js +1151 -0
  54. package/src/utils/feature-integrator.js +1724 -0
  55. package/src/utils/onboarding-guide.js +734 -0
  56. package/src/utils/performance.js +394 -0
  57. package/src/utils/permission-manager.js +890 -0
  58. package/src/utils/responsive-handler.js +491 -0
  59. package/src/utils/theme-manager.js +811 -0
  60. package/src/utils/ui-enhancements-loader.js +329 -0
  61. package/USAGE.md +0 -298
@@ -1,210 +1,245 @@
1
- import express from 'express';
2
- import { authenticate, isAdmin } from '../middleware/auth.js';
3
- import {
4
- queryAuditLogs,
5
- getUserActivityStats,
6
- getDocumentEditHistory
7
- } from '../utils/auditLogger.js';
8
-
9
- const router = express.Router();
10
-
11
- // 获取审计日志列表(仅管理员)
12
- router.get('/', authenticate, isAdmin, async (req, res) => {
13
- try {
14
- const {
15
- page = 1,
16
- limit = 50,
17
- userId,
18
- resourceId,
19
- resourceType,
20
- action,
21
- groupId,
22
- startDate,
23
- endDate,
24
- sortBy = 'createdAt',
25
- sortOrder = -1
26
- } = req.query;
27
-
28
- const filters = {};
29
- if (userId) filters.userId = userId;
30
- if (resourceId) filters.resourceId = resourceId;
31
- if (resourceType) filters.resourceType = resourceType;
32
- if (action) filters.action = action;
33
- if (groupId) filters.groupId = groupId;
34
- if (startDate) filters.startDate = startDate;
35
- if (endDate) filters.endDate = endDate;
36
-
37
- const options = {
38
- page: parseInt(page),
39
- limit: parseInt(limit),
40
- sortBy,
41
- sortOrder: parseInt(sortOrder)
42
- };
43
-
44
- const result = await queryAuditLogs(filters, options);
45
-
46
- res.json({
47
- message: '获取审计日志成功',
48
- ...result
49
- });
50
- } catch (error) {
51
- console.error('获取审计日志失败:', error);
52
- res.status(500).json({
53
- message: '获取审计日志失败',
54
- error: error.message
55
- });
56
- }
57
- });
58
-
59
- // 获取特定用户的活动统计(仅管理员)
60
- router.get('/user-stats/:userId', authenticate, isAdmin, async (req, res) => {
61
- try {
62
- const { userId } = req.params;
63
- const { startDate, endDate } = req.query;
64
-
65
- const timeRange = {};
66
- if (startDate) timeRange.startDate = startDate;
67
- if (endDate) timeRange.endDate = endDate;
68
-
69
- const stats = await getUserActivityStats(userId, timeRange);
70
-
71
- res.json({
72
- message: '获取用户活动统计成功',
73
- stats
74
- });
75
- } catch (error) {
76
- console.error('获取用户活动统计失败:', error);
77
- res.status(500).json({
78
- message: '获取用户活动统计失败',
79
- error: error.message
80
- });
81
- }
82
- });
83
-
84
- // 获取文档编辑历史(仅管理员)
85
- router.get('/document-history/:documentId', authenticate, isAdmin, async (req, res) => {
86
- try {
87
- const { documentId } = req.params;
88
- const { limit = 20 } = req.query;
89
-
90
- const history = await getDocumentEditHistory(documentId, parseInt(limit));
91
-
92
- res.json({
93
- message: '获取文档编辑历史成功',
94
- history
95
- });
96
- } catch (error) {
97
- console.error('获取文档编辑历史失败:', error);
98
- res.status(500).json({
99
- message: '获取文档编辑历史失败',
100
- error: error.message
101
- });
102
- }
103
- });
104
-
105
- // 获取群组活动日志(仅管理员)
106
- router.get('/group/:groupId', authenticate, isAdmin, async (req, res) => {
107
- try {
108
- const { groupId } = req.params;
109
- const {
110
- page = 1,
111
- limit = 50,
112
- action,
113
- userId,
114
- startDate,
115
- endDate
116
- } = req.query;
117
-
118
- const filters = { groupId };
119
- if (action) filters.action = action;
120
- if (userId) filters.userId = userId;
121
- if (startDate) filters.startDate = startDate;
122
- if (endDate) filters.endDate = endDate;
123
-
124
- const options = {
125
- page: parseInt(page),
126
- limit: parseInt(limit),
127
- sortBy: 'createdAt',
128
- sortOrder: -1
129
- };
130
-
131
- const result = await queryAuditLogs(filters, options);
132
-
133
- res.json({
134
- message: '获取群组活动日志成功',
135
- ...result
136
- });
137
- } catch (error) {
138
- console.error('获取群组活动日志失败:', error);
139
- res.status(500).json({
140
- message: '获取群组活动日志失败',
141
- error: error.message
142
- });
143
- }
144
- });
145
-
146
- // 获取审计日志统计信息(仅管理员)
147
- router.get('/stats/summary', authenticate, isAdmin, async (req, res) => {
148
- try {
149
- const { startDate, endDate, groupId } = req.query;
150
-
151
- const filters = {};
152
- if (groupId) filters.groupId = groupId;
153
- if (startDate || endDate) {
154
- filters.startDate = startDate;
155
- filters.endDate = endDate;
156
- }
157
-
158
- // 获取总体统计
159
- const result = await queryAuditLogs(filters, { limit: 1 });
160
- const totalLogs = result.pagination.total;
161
-
162
- // 获取按操作类型统计
163
- const actionStats = await queryAuditLogs(filters, { limit: totalLogs });
164
- const actionBreakdown = {};
165
-
166
- actionStats.logs.forEach(log => {
167
- actionBreakdown[log.action] = (actionBreakdown[log.action] || 0) + 1;
168
- });
169
-
170
- // 获取最活跃用户
171
- const userActivity = {};
172
- actionStats.logs.forEach(log => {
173
- const userId = log.user._id.toString();
174
- if (!userActivity[userId]) {
175
- userActivity[userId] = {
176
- user: log.user,
177
- count: 0
178
- };
179
- }
180
- userActivity[userId].count++;
181
- });
182
-
183
- const topUsers = Object.values(userActivity)
184
- .sort((a, b) => b.count - a.count)
185
- .slice(0, 10);
186
-
187
- res.json({
188
- message: '获取统计信息成功',
189
- summary: {
190
- totalLogs,
191
- actionBreakdown,
192
- topUsers,
193
- timeRange: { startDate, endDate }
194
- }
195
- });
196
- } catch (error) {
197
- console.error('获取统计信息失败:', error);
198
- res.status(500).json({
199
- message: '获取统计信息失败',
200
- error: error.message
201
- });
202
- }
203
- });
204
-
205
- export default router;
206
-
207
-
208
-
209
-
210
-
1
+ import express from 'express';
2
+ import { authenticate, isAdmin } from '../middleware/auth.js';
3
+ import {
4
+ queryAuditLogs,
5
+ getUserActivityStats,
6
+ getDocumentEditHistory
7
+ } from '../utils/auditLogger.js';
8
+ import AuditLog from '../models/AuditLog.js';
9
+
10
+ const router = express.Router();
11
+
12
+ // 注意:路由顺序很重要!具体路由必须在通用路由之前
13
+
14
+ // 获取审计日志统计信息(仅管理员)
15
+ router.get('/stats/summary', authenticate, isAdmin, async (req, res) => {
16
+ try {
17
+ const { startDate, endDate, groupId } = req.query;
18
+
19
+ const filters = {};
20
+ if (groupId) filters.groupId = groupId;
21
+ if (startDate || endDate) {
22
+ filters.startDate = startDate;
23
+ filters.endDate = endDate;
24
+ }
25
+
26
+ // 获取总体统计
27
+ const result = await queryAuditLogs(filters, { limit: 1 });
28
+ const totalLogs = result.pagination.total;
29
+
30
+ // 获取按操作类型统计
31
+ const actionStats = await queryAuditLogs(filters, { limit: totalLogs });
32
+ const actionBreakdown = {};
33
+
34
+ actionStats.logs.forEach(log => {
35
+ actionBreakdown[log.action] = (actionBreakdown[log.action] || 0) + 1;
36
+ });
37
+
38
+ // 获取最活跃用户
39
+ const userActivity = {};
40
+ actionStats.logs.forEach(log => {
41
+ const userId = log.user._id.toString();
42
+ if (!userActivity[userId]) {
43
+ userActivity[userId] = {
44
+ user: log.user,
45
+ count: 0
46
+ };
47
+ }
48
+ userActivity[userId].count++;
49
+ });
50
+
51
+ const topUsers = Object.values(userActivity)
52
+ .sort((a, b) => b.count - a.count)
53
+ .slice(0, 10);
54
+
55
+ res.json({
56
+ message: '获取统计信息成功',
57
+ summary: {
58
+ totalLogs,
59
+ actionBreakdown,
60
+ topUsers,
61
+ timeRange: { startDate, endDate }
62
+ }
63
+ });
64
+ } catch (error) {
65
+ console.error('获取统计信息失败:', error);
66
+ res.status(500).json({
67
+ message: '获取统计信息失败',
68
+ error: error.message
69
+ });
70
+ }
71
+ });
72
+
73
+ // 获取特定用户的活动统计(仅管理员)
74
+ router.get('/user-stats/:userId', authenticate, isAdmin, async (req, res) => {
75
+ try {
76
+ const { userId } = req.params;
77
+ const { startDate, endDate } = req.query;
78
+
79
+ const timeRange = {};
80
+ if (startDate) timeRange.startDate = startDate;
81
+ if (endDate) timeRange.endDate = endDate;
82
+
83
+ const stats = await getUserActivityStats(userId, timeRange);
84
+
85
+ res.json({
86
+ message: '获取用户活动统计成功',
87
+ stats
88
+ });
89
+ } catch (error) {
90
+ console.error('获取用户活动统计失败:', error);
91
+ res.status(500).json({
92
+ message: '获取用户活动统计失败',
93
+ error: error.message
94
+ });
95
+ }
96
+ });
97
+
98
+ // 获取文档编辑历史(仅管理员)
99
+ router.get('/document-history/:documentId', authenticate, isAdmin, async (req, res) => {
100
+ try {
101
+ const { documentId } = req.params;
102
+ const { limit = 20 } = req.query;
103
+
104
+ const history = await getDocumentEditHistory(documentId, parseInt(limit));
105
+
106
+ res.json({
107
+ message: '获取文档编辑历史成功',
108
+ history
109
+ });
110
+ } catch (error) {
111
+ console.error('获取文档编辑历史失败:', error);
112
+ res.status(500).json({
113
+ message: '获取文档编辑历史失败',
114
+ error: error.message
115
+ });
116
+ }
117
+ });
118
+
119
+ // 获取群组活动日志(仅管理员)
120
+ router.get('/group/:groupId', authenticate, isAdmin, async (req, res) => {
121
+ try {
122
+ const { groupId } = req.params;
123
+ const {
124
+ page = 1,
125
+ limit = 50,
126
+ action,
127
+ userId,
128
+ startDate,
129
+ endDate
130
+ } = req.query;
131
+
132
+ const filters = { groupId };
133
+ if (action) filters.action = action;
134
+ if (userId) filters.userId = userId;
135
+ if (startDate) filters.startDate = startDate;
136
+ if (endDate) filters.endDate = endDate;
137
+
138
+ const options = {
139
+ page: parseInt(page),
140
+ limit: parseInt(limit),
141
+ sortBy: 'createdAt',
142
+ sortOrder: -1
143
+ };
144
+
145
+ const result = await queryAuditLogs(filters, options);
146
+
147
+ res.json({
148
+ message: '获取群组活动日志成功',
149
+ ...result
150
+ });
151
+ } catch (error) {
152
+ console.error('获取群组活动日志失败:', error);
153
+ res.status(500).json({
154
+ message: '获取群组活动日志失败',
155
+ error: error.message
156
+ });
157
+ }
158
+ });
159
+
160
+ // 获取单个审计日志详情(仅管理员)- 必须在 / 之前
161
+ router.get('/:logId', authenticate, isAdmin, async (req, res) => {
162
+ try {
163
+ const { logId } = req.params;
164
+
165
+ // 验证 ObjectId 格式
166
+ if (!logId.match(/^[0-9a-fA-F]{24}$/)) {
167
+ return res.status(400).json({ message: '无效的日志ID格式' });
168
+ }
169
+
170
+ const log = await AuditLog.findById(logId)
171
+ .populate('user', 'username email role')
172
+ .populate('metadata.groupId', 'name description')
173
+ .lean();
174
+
175
+ if (!log) {
176
+ return res.status(404).json({ message: '审计日志不存在' });
177
+ }
178
+
179
+ res.json({
180
+ message: '获取审计日志详情成功',
181
+ log
182
+ });
183
+ } catch (error) {
184
+ console.error('获取审计日志详情失败:', error);
185
+ res.status(500).json({
186
+ message: '获取审计日志详情失败',
187
+ error: error.message
188
+ });
189
+ }
190
+ });
191
+
192
+ // 获取审计日志列表(仅管理员)- 必须在最后
193
+ router.get('/', authenticate, isAdmin, async (req, res) => {
194
+ try {
195
+ const {
196
+ page = 1,
197
+ limit = 50,
198
+ userId,
199
+ resourceId,
200
+ resourceType,
201
+ action,
202
+ groupId,
203
+ startDate,
204
+ endDate,
205
+ sortBy = 'createdAt',
206
+ sortOrder = -1
207
+ } = req.query;
208
+
209
+ const filters = {};
210
+ if (userId) filters.userId = userId;
211
+ if (resourceId) filters.resourceId = resourceId;
212
+ if (resourceType) filters.resourceType = resourceType;
213
+ if (action) filters.action = action;
214
+ if (groupId) filters.groupId = groupId;
215
+ if (startDate) filters.startDate = startDate;
216
+ if (endDate) filters.endDate = endDate;
217
+
218
+ const options = {
219
+ page: parseInt(page),
220
+ limit: parseInt(limit),
221
+ sortBy,
222
+ sortOrder: parseInt(sortOrder)
223
+ };
224
+
225
+ const result = await queryAuditLogs(filters, options);
226
+
227
+ res.json({
228
+ message: '获取审计日志成功',
229
+ ...result
230
+ });
231
+ } catch (error) {
232
+ console.error('获取审计日志失败:', error);
233
+ res.status(500).json({
234
+ message: '获取审计日志失败',
235
+ error: error.message
236
+ });
237
+ }
238
+ });
239
+
240
+ export default router;
241
+
242
+
243
+
244
+
245
+
@@ -0,0 +1,108 @@
1
+ import express from 'express';
2
+ import backupService from '../utils/backup.js';
3
+ import { authenticate, isAdmin } from '../middleware/auth.js';
4
+ import { asyncHandler, ValidationError } from '../middleware/errorHandler.js';
5
+
6
+ const router = express.Router();
7
+
8
+ // 创建备份(仅管理员)
9
+ router.post('/create', authenticate, isAdmin, asyncHandler(async (req, res) => {
10
+ const { description } = req.body;
11
+
12
+ const result = await backupService.createBackup('manual', description || '');
13
+
14
+ res.json({
15
+ success: true,
16
+ message: '备份创建成功',
17
+ data: result
18
+ });
19
+ }));
20
+
21
+ // 获取备份列表(仅管理员)
22
+ router.get('/list', authenticate, isAdmin, asyncHandler(async (req, res) => {
23
+ const backups = backupService.getBackupList();
24
+
25
+ res.json({
26
+ success: true,
27
+ data: {
28
+ backups: backups.map(b => ({
29
+ name: b.name,
30
+ filename: b.filename,
31
+ size: backupService.formatSize(b.size),
32
+ sizeBytes: b.size,
33
+ createdAt: b.createdAt
34
+ })),
35
+ total: backups.length
36
+ }
37
+ });
38
+ }));
39
+
40
+ // 恢复备份(仅管理员)
41
+ router.post('/restore/:backupName', authenticate, isAdmin, asyncHandler(async (req, res) => {
42
+ const { backupName } = req.params;
43
+
44
+ if (!backupName) {
45
+ throw new ValidationError('备份名称不能为空');
46
+ }
47
+
48
+ const result = await backupService.restoreBackup(backupName);
49
+
50
+ res.json({
51
+ success: true,
52
+ message: '备份恢复成功',
53
+ data: result
54
+ });
55
+ }));
56
+
57
+ // 删除备份(仅管理员)
58
+ router.delete('/:backupName', authenticate, isAdmin, asyncHandler(async (req, res) => {
59
+ const { backupName } = req.params;
60
+
61
+ if (!backupName) {
62
+ throw new ValidationError('备份名称不能为空');
63
+ }
64
+
65
+ const result = backupService.deleteBackup(backupName);
66
+
67
+ res.json({
68
+ success: true,
69
+ message: '备份删除成功',
70
+ data: result
71
+ });
72
+ }));
73
+
74
+ // 下载备份文件(仅管理员)
75
+ router.get('/download/:backupName', authenticate, isAdmin, asyncHandler(async (req, res) => {
76
+ const { backupName } = req.params;
77
+ const backups = backupService.getBackupList();
78
+ const backup = backups.find(b => b.name === backupName);
79
+
80
+ if (!backup) {
81
+ throw new ValidationError('备份文件不存在');
82
+ }
83
+
84
+ res.download(backup.path, backup.filename);
85
+ }));
86
+
87
+ // 获取备份统计信息(仅管理员)
88
+ router.get('/stats', authenticate, isAdmin, asyncHandler(async (req, res) => {
89
+ const backups = backupService.getBackupList();
90
+ const totalSize = backups.reduce((sum, b) => sum + b.size, 0);
91
+
92
+ const stats = {
93
+ totalBackups: backups.length,
94
+ totalSize: backupService.formatSize(totalSize),
95
+ totalSizeBytes: totalSize,
96
+ oldestBackup: backups.length > 0 ? backups[backups.length - 1].createdAt : null,
97
+ newestBackup: backups.length > 0 ? backups[0].createdAt : null,
98
+ maxBackups: backupService.maxBackups
99
+ };
100
+
101
+ res.json({
102
+ success: true,
103
+ data: stats
104
+ });
105
+ }));
106
+
107
+ export default router;
108
+