collabdocchat 2.4.4 → 2.4.5
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/package.json +2 -2
- package/scripts/add-button-hover.js +2 -0
- package/scripts/add-missing-braces.js +27 -0
- package/scripts/add-missing-functions.js +2 -0
- package/scripts/add-more-features.js +2 -0
- package/scripts/add-user-functions.js +2 -0
- package/scripts/auto-publish.js +2 -0
- package/scripts/beautify-buttons.js +2 -0
- package/scripts/beautify-ui.js +2 -0
- package/scripts/check-brackets.js +50 -0
- package/scripts/check-encoding.js +2 -0
- package/scripts/check-syntax.js +2 -0
- package/scripts/delete-orphan-block.js +27 -0
- package/scripts/find-buttons.js +2 -0
- package/scripts/find-duplicate.js +2 -0
- package/scripts/find-extra-brace.js +63 -0
- package/scripts/find-sidebar-buttons.js +2 -0
- package/scripts/fix-file-end.js +46 -0
- package/scripts/fix-help.js +2 -0
- package/scripts/fix-issues-step1.js +2 -0
- package/scripts/fix-issues-step2.js +2 -0
- package/scripts/fix-issues-step3.js +2 -0
- package/scripts/fix-issues-step4.js +2 -0
- package/scripts/fix-optimized-views.js +2 -0
- package/scripts/fix-settings.js +2 -0
- package/scripts/fix-syntax-error.js +38 -0
- package/scripts/fix-workflow.js +2 -0
- package/scripts/refactor-step1.js +2 -0
- package/scripts/refactor-step2.js +2 -0
- package/scripts/refactor-step3.js +2 -0
- package/scripts/refactor-step4.js +2 -0
- package/scripts/refactor-step5.js +2 -0
- package/scripts/refactor-step6.js +2 -0
- package/scripts/refactor-step7.js +2 -0
- package/scripts/remove-orphan-code.js +57 -0
- package/scripts/update-port-user.js +2 -0
- package/scripts/update-port.js +2 -0
- package/server/index.js +4 -0
- package/server/index.js.bak +97 -0
- package/server/models/Document.js +5 -0
- package/server/models/KnowledgeBase.js +259 -254
- package/server/models/Poll.js +97 -0
- package/server/routes/ai.js +391 -327
- package/server/routes/audit.js +61 -0
- package/server/routes/documents.js +74 -5
- package/server/routes/export.js +171 -10
- package/server/routes/files.js +27 -4
- package/server/routes/knowledge.js +31 -22
- package/server/routes/messages.js +142 -0
- package/server/routes/polls.js +241 -0
- package/server/routes/tasks.js +1 -0
- package/server/routes/workflows.js +27 -0
- package/server/utils/auditLogger.js +268 -238
- package/src/pages/admin-dashboard.js +1431 -335
- package/src/pages/admin-dashboard.js.audit-optimize.bak +4134 -0
- package/src/pages/admin-dashboard.js.bak +4041 -0
- package/src/pages/admin-dashboard.js.broken.bak +4099 -0
- package/src/pages/admin-dashboard.js.comprehensive.bak +4099 -0
- package/src/pages/admin-dashboard.js.escape.bak +4099 -0
- package/src/pages/admin-dashboard.js.final-final-fix.bak +4099 -0
- package/src/pages/admin-dashboard.js.final-fix.bak +4099 -0
- package/src/pages/admin-dashboard.js.final.bak +4099 -0
- package/src/pages/admin-dashboard.js.indent-fix.bak +4099 -0
- package/src/pages/admin-dashboard.js.last-fix.bak +4099 -0
- package/src/pages/admin-dashboard.js.line595-fix.bak +4099 -0
- package/src/pages/admin-dashboard.js.pre-manual-fix.bak +4099 -0
- package/src/pages/admin-dashboard.js.syntax.bak +4099 -0
- package/src/pages/admin-dashboard.js.test.bak +4099 -0
- package/src/pages/optimized-task-detail-original.js +838 -0
- package/src/pages/optimized-task-detail.js +324 -22
- package/src/pages/optimized-task-detail.js.bak +1162 -0
- package/src/pages/poll-detail-enhanced.js +394 -0
- package/src/pages/update-poll-display.js +380 -0
- package/src/pages/user-dashboard.js +1860 -1006
- package/src/services/api.js +326 -265
- package/src/services/auth.js +54 -54
- package/src/services/websocket.js +88 -80
- package/src/pages/simplified-workflows.js +0 -652
- package/src/utils/ai-assistant.js +0 -1384
|
@@ -1,238 +1,268 @@
|
|
|
1
|
-
import AuditLog from '../models/AuditLog.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
5
|
-
* @param {string}
|
|
6
|
-
* @
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
1
|
+
import AuditLog from '../models/AuditLog.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 去除HTML标签,只保留纯文本
|
|
5
|
+
* @param {string} html - HTML内容
|
|
6
|
+
* @returns {string} 纯文本内容
|
|
7
|
+
*/
|
|
8
|
+
function stripHtmlTags(html) {
|
|
9
|
+
if (!html || typeof html !== 'string') return '';
|
|
10
|
+
|
|
11
|
+
// 移除HTML标签
|
|
12
|
+
let text = html.replace(/<[^>]*>/g, '');
|
|
13
|
+
|
|
14
|
+
// 解码HTML实体
|
|
15
|
+
text = text
|
|
16
|
+
.replace(/ /g, ' ')
|
|
17
|
+
.replace(/</g, '<')
|
|
18
|
+
.replace(/>/g, '>')
|
|
19
|
+
.replace(/&/g, '&')
|
|
20
|
+
.replace(/"/g, '"')
|
|
21
|
+
.replace(/'/g, "'");
|
|
22
|
+
|
|
23
|
+
// 移除多余的空白字符
|
|
24
|
+
text = text.trim();
|
|
25
|
+
|
|
26
|
+
return text;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 计算文本差异
|
|
31
|
+
* @param {string} oldText - 原始文本
|
|
32
|
+
* @param {string} newText - 新文本
|
|
33
|
+
* @returns {Object} 包含插入和删除操作的对象
|
|
34
|
+
*/
|
|
35
|
+
function calculateTextDiff(oldText = '', newText = '') {
|
|
36
|
+
// 去除HTML标签,只比较纯文本
|
|
37
|
+
const cleanOldText = stripHtmlTags(oldText);
|
|
38
|
+
const cleanNewText = stripHtmlTags(newText);
|
|
39
|
+
|
|
40
|
+
const insertions = [];
|
|
41
|
+
const deletions = [];
|
|
42
|
+
|
|
43
|
+
// 简单的差异计算算法
|
|
44
|
+
// 这里使用基础实现,生产环境可以使用更复杂的diff算法
|
|
45
|
+
if (cleanOldText !== cleanNewText) {
|
|
46
|
+
// 如果完全不同,记录为完全替换
|
|
47
|
+
if (cleanOldText.length > 0) {
|
|
48
|
+
deletions.push({
|
|
49
|
+
position: 0,
|
|
50
|
+
content: cleanOldText,
|
|
51
|
+
length: cleanOldText.length
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (cleanNewText.length > 0) {
|
|
56
|
+
insertions.push({
|
|
57
|
+
position: 0,
|
|
58
|
+
content: cleanNewText,
|
|
59
|
+
length: cleanNewText.length
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { insertions, deletions };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 记录审计日志
|
|
69
|
+
* @param {Object} logData - 日志数据
|
|
70
|
+
* @param {Object} req - Express请求对象(可选,用于获取IP等信息)
|
|
71
|
+
*/
|
|
72
|
+
export async function logAuditAction(logData, req = null) {
|
|
73
|
+
try {
|
|
74
|
+
const auditData = {
|
|
75
|
+
action: logData.action,
|
|
76
|
+
user: logData.userId,
|
|
77
|
+
resourceType: logData.resourceType,
|
|
78
|
+
resourceId: logData.resourceId,
|
|
79
|
+
resourceTitle: logData.resourceTitle || '',
|
|
80
|
+
details: {
|
|
81
|
+
oldValue: stripHtmlTags(logData.oldValue),
|
|
82
|
+
newValue: stripHtmlTags(logData.newValue),
|
|
83
|
+
field: logData.field,
|
|
84
|
+
description: logData.description
|
|
85
|
+
},
|
|
86
|
+
metadata: {
|
|
87
|
+
groupId: logData.groupId
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// 如果是内容编辑,计算文本差异
|
|
92
|
+
if (logData.action === 'content_edit' && logData.oldValue && logData.newValue) {
|
|
93
|
+
auditData.changes = calculateTextDiff(logData.oldValue, logData.newValue);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 从请求中提取元数据
|
|
97
|
+
if (req) {
|
|
98
|
+
auditData.metadata.ipAddress = req.ip || req.connection.remoteAddress;
|
|
99
|
+
auditData.metadata.userAgent = req.get('User-Agent');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const auditLog = new AuditLog(auditData);
|
|
103
|
+
await auditLog.save();
|
|
104
|
+
|
|
105
|
+
return auditLog;
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.error('记录审计日志失败:', error);
|
|
108
|
+
// 审计日志记录失败不应该影响主业务逻辑
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 批量记录审计日志
|
|
115
|
+
* @param {Array} logDataArray - 日志数据数组
|
|
116
|
+
* @param {Object} req - Express请求对象
|
|
117
|
+
*/
|
|
118
|
+
export async function logMultipleAuditActions(logDataArray, req = null) {
|
|
119
|
+
const promises = logDataArray.map(logData => logAuditAction(logData, req));
|
|
120
|
+
return Promise.allSettled(promises);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 查询审计日志
|
|
125
|
+
* @param {Object} filters - 查询过滤条件
|
|
126
|
+
* @param {Object} options - 查询选项(分页、排序等)
|
|
127
|
+
*/
|
|
128
|
+
export async function queryAuditLogs(filters = {}, options = {}) {
|
|
129
|
+
try {
|
|
130
|
+
const {
|
|
131
|
+
page = 1,
|
|
132
|
+
limit = 50,
|
|
133
|
+
sortBy = 'createdAt',
|
|
134
|
+
sortOrder = -1
|
|
135
|
+
} = options;
|
|
136
|
+
|
|
137
|
+
const query = {};
|
|
138
|
+
|
|
139
|
+
// 构建查询条件
|
|
140
|
+
if (filters.userId) {
|
|
141
|
+
query.user = filters.userId;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (filters.resourceId) {
|
|
145
|
+
query.resourceId = filters.resourceId;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (filters.resourceType) {
|
|
149
|
+
query.resourceType = filters.resourceType;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (filters.action) {
|
|
153
|
+
query.action = filters.action;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (filters.groupId) {
|
|
157
|
+
query['metadata.groupId'] = filters.groupId;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (filters.startDate && filters.endDate) {
|
|
161
|
+
query.createdAt = {
|
|
162
|
+
$gte: new Date(filters.startDate),
|
|
163
|
+
$lte: new Date(filters.endDate)
|
|
164
|
+
};
|
|
165
|
+
} else if (filters.startDate) {
|
|
166
|
+
query.createdAt = { $gte: new Date(filters.startDate) };
|
|
167
|
+
} else if (filters.endDate) {
|
|
168
|
+
query.createdAt = { $lte: new Date(filters.endDate) };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const skip = (page - 1) * limit;
|
|
172
|
+
|
|
173
|
+
const [logs, total] = await Promise.all([
|
|
174
|
+
AuditLog.find(query)
|
|
175
|
+
.populate('user', 'username email avatar')
|
|
176
|
+
.populate('metadata.groupId', 'name')
|
|
177
|
+
.sort({ [sortBy]: sortOrder })
|
|
178
|
+
.skip(skip)
|
|
179
|
+
.limit(limit)
|
|
180
|
+
.lean(),
|
|
181
|
+
AuditLog.countDocuments(query)
|
|
182
|
+
]);
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
logs,
|
|
186
|
+
pagination: {
|
|
187
|
+
page,
|
|
188
|
+
limit,
|
|
189
|
+
total,
|
|
190
|
+
pages: Math.ceil(total / limit)
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
} catch (error) {
|
|
194
|
+
console.error('查询审计日志失败:', error);
|
|
195
|
+
throw new Error('查询审计日志失败');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* 获取用户活动统计
|
|
201
|
+
* @param {string} userId - 用户ID
|
|
202
|
+
* @param {Object} timeRange - 时间范围
|
|
203
|
+
*/
|
|
204
|
+
export async function getUserActivityStats(userId, timeRange = {}) {
|
|
205
|
+
try {
|
|
206
|
+
const { startDate, endDate } = timeRange;
|
|
207
|
+
const matchStage = { user: userId };
|
|
208
|
+
|
|
209
|
+
if (startDate && endDate) {
|
|
210
|
+
matchStage.createdAt = {
|
|
211
|
+
$gte: new Date(startDate),
|
|
212
|
+
$lte: new Date(endDate)
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const stats = await AuditLog.aggregate([
|
|
217
|
+
{ $match: matchStage },
|
|
218
|
+
{
|
|
219
|
+
$group: {
|
|
220
|
+
_id: '$action',
|
|
221
|
+
count: { $sum: 1 },
|
|
222
|
+
lastActivity: { $max: '$createdAt' }
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
{ $sort: { count: -1 } }
|
|
226
|
+
]);
|
|
227
|
+
|
|
228
|
+
const totalActions = stats.reduce((sum, stat) => sum + stat.count, 0);
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
totalActions,
|
|
232
|
+
actionBreakdown: stats,
|
|
233
|
+
timeRange
|
|
234
|
+
};
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error('获取用户活动统计失败:', error);
|
|
237
|
+
throw new Error('获取用户活动统计失败');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* 获取文档编辑历史摘要
|
|
243
|
+
* @param {string} documentId - 文档ID
|
|
244
|
+
* @param {number} limit - 返回记录数限制
|
|
245
|
+
*/
|
|
246
|
+
export async function getDocumentEditHistory(documentId, limit = 20) {
|
|
247
|
+
try {
|
|
248
|
+
const history = await AuditLog.find({
|
|
249
|
+
resourceId: documentId,
|
|
250
|
+
resourceType: 'document',
|
|
251
|
+
action: { $in: ['content_edit', 'title_edit', 'document_update'] }
|
|
252
|
+
})
|
|
253
|
+
.populate('user', 'username avatar')
|
|
254
|
+
.sort({ createdAt: -1 })
|
|
255
|
+
.limit(limit)
|
|
256
|
+
.lean();
|
|
257
|
+
|
|
258
|
+
return history;
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.error('获取文档编辑历史失败:', error);
|
|
261
|
+
throw new Error('获取文档编辑历史失败');
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
|