collabdocchat 2.2.0 → 2.4.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.
@@ -0,0 +1,181 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+
8
+ const filePath = path.join(__dirname, '../src/pages/admin-dashboard.js');
9
+
10
+ console.log('读取文件...');
11
+ let content = fs.readFileSync(filePath, 'utf8');
12
+
13
+ // 替换工作流功能为完整实现
14
+ console.log('完善工作流功能...');
15
+ const workflowPattern = /\/\/ 工作流管理[\s\S]*?async function renderWorkflowView\(container\)[\s\S]*?container\.innerHTML = '<div class="empty-state">工作流功能开发中\.\.\.<\/div>';[\s\S]*?}/;
16
+ const workflowReplacement = `// 工作流管理
17
+ async function renderWorkflowView(container) {
18
+ if (!currentGroup) {
19
+ container.innerHTML = '<div class="empty-state">请先选择一个群组</div>';
20
+ return;
21
+ }
22
+
23
+ try {
24
+ const token = localStorage.getItem('token');
25
+ const response = await fetch(\`http://localhost:3000/api/workflows/group/\${currentGroup._id}\`, {
26
+ headers: { 'Authorization': \`Bearer \${token}\` }
27
+ });
28
+ const result = await response.json();
29
+ const workflows = result.data?.workflows || [];
30
+
31
+ container.innerHTML = \`
32
+ <div class="view-header">
33
+ <h2>⚙️ 工作流管理 - \${currentGroup.name}</h2>
34
+ <button class="btn-primary" id="createWorkflowBtn">➕ 创建工作流</button>
35
+ </div>
36
+ <div class="workflow-grid" id="workflowList" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); gap: 20px; padding: 20px;"></div>
37
+ <div id="workflowModal" class="modal hidden">
38
+ <div class="modal-content">
39
+ <h3>创建工作流</h3>
40
+ <form id="workflowForm">
41
+ <div class="form-group">
42
+ <label>⚙️ 工作流名称</label>
43
+ <input type="text" name="name" required style="width: 100%; padding: 10px; border: 1px solid var(--border); border-radius: 8px;">
44
+ </div>
45
+ <div class="form-group">
46
+ <label>📝 描述</label>
47
+ <textarea name="description" rows="3" style="width: 100%; padding: 10px; border: 1px solid var(--border); border-radius: 8px;"></textarea>
48
+ </div>
49
+ <div class="form-group">
50
+ <label>🔔 触发条件</label>
51
+ <select name="trigger" style="width: 100%; padding: 10px; border: 1px solid var(--border); border-radius: 8px;">
52
+ <option value="document_create">文档创建时</option>
53
+ <option value="document_update">文档更新时</option>
54
+ <option value="task_complete">任务完成时</option>
55
+ <option value="manual">手动触发</option>
56
+ </select>
57
+ </div>
58
+ <div style="display: flex; gap: 10px; margin-top: 20px;">
59
+ <button type="submit" class="btn-primary" style="flex: 1;">创建</button>
60
+ <button type="button" class="btn-secondary" id="closeWorkflowModal" style="flex: 1;">取消</button>
61
+ </div>
62
+ </form>
63
+ </div>
64
+ </div>
65
+ \`;
66
+
67
+ const workflowList = document.getElementById('workflowList');
68
+ if (workflows.length === 0) {
69
+ workflowList.innerHTML = '<div class="empty-state" style="grid-column: 1/-1;">暂无工作流</div>';
70
+ } else {
71
+ workflows.forEach(workflow => {
72
+ const card = document.createElement('div');
73
+ card.className = 'workflow-card';
74
+ card.style.cssText = 'background: var(--bg-secondary); padding: 20px; border-radius: 12px; border: 1px solid var(--border); transition: transform 0.2s, box-shadow 0.2s;';
75
+ const isActive = workflow.status === 'active';
76
+ card.innerHTML = \`
77
+ <div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 10px;">
78
+ <h3 style="margin: 0; font-size: 18px;">\${workflow.name}</h3>
79
+ <span style="padding: 4px 10px; border-radius: 12px; font-size: 12px; background: \${isActive ? 'var(--success)' : 'var(--warning)'}; color: white;">\${isActive ? '✅ 活跃' : '⏸️ 暂停'}</span>
80
+ </div>
81
+ <p style="color: var(--text-secondary); margin: 10px 0; line-height: 1.6;">\${workflow.description || '无描述'}</p>
82
+ <div class="workflow-meta" style="font-size: 12px; color: var(--text-tertiary); margin: 10px 0;">
83
+ <span>🔔 触发: \${workflow.trigger}</span>
84
+ </div>
85
+ <div style="display: flex; gap: 10px; margin-top: 15px;">
86
+ <button class="btn-secondary btn-sm" data-id="\${workflow._id}" data-action="toggle" style="flex: 1;">
87
+ \${isActive ? '⏸️ 暂停' : '▶️ 启用'}
88
+ </button>
89
+ <button class="btn-danger btn-sm" data-id="\${workflow._id}" data-action="delete" style="flex: 1;">🗑️ 删除</button>
90
+ </div>
91
+ \`;
92
+ card.onmouseenter = () => {
93
+ card.style.transform = 'translateY(-4px)';
94
+ card.style.boxShadow = '0 8px 16px rgba(0,0,0,0.1)';
95
+ };
96
+ card.onmouseleave = () => {
97
+ card.style.transform = 'translateY(0)';
98
+ card.style.boxShadow = 'none';
99
+ };
100
+ workflowList.appendChild(card);
101
+ });
102
+
103
+ document.querySelectorAll('[data-action="toggle"]').forEach(btn => {
104
+ btn.addEventListener('click', async () => {
105
+ try {
106
+ await fetch(\`http://localhost:3000/api/workflows/\${btn.dataset.id}/toggle\`, {
107
+ method: 'POST',
108
+ headers: { 'Authorization': \`Bearer \${token}\` }
109
+ });
110
+ await renderWorkflowView(container);
111
+ } catch (error) {
112
+ alert('操作失败: ' + error.message);
113
+ }
114
+ });
115
+ });
116
+
117
+ document.querySelectorAll('[data-action="delete"]').forEach(btn => {
118
+ btn.addEventListener('click', async () => {
119
+ if (confirm('确定要删除这个工作流吗?')) {
120
+ try {
121
+ await fetch(\`http://localhost:3000/api/workflows/\${btn.dataset.id}\`, {
122
+ method: 'DELETE',
123
+ headers: { 'Authorization': \`Bearer \${token}\` }
124
+ });
125
+ alert('删除成功!');
126
+ await renderWorkflowView(container);
127
+ } catch (error) {
128
+ alert('删除失败: ' + error.message);
129
+ }
130
+ }
131
+ });
132
+ });
133
+ }
134
+
135
+ document.getElementById('createWorkflowBtn').addEventListener('click', () => {
136
+ document.getElementById('workflowModal').classList.remove('hidden');
137
+ });
138
+
139
+ document.getElementById('closeWorkflowModal').addEventListener('click', () => {
140
+ document.getElementById('workflowModal').classList.add('hidden');
141
+ });
142
+
143
+ document.getElementById('workflowForm').addEventListener('submit', async (e) => {
144
+ e.preventDefault();
145
+ const formData = new FormData(e.target);
146
+ const data = {
147
+ name: formData.get('name'),
148
+ description: formData.get('description'),
149
+ trigger: formData.get('trigger'),
150
+ groupId: currentGroup._id,
151
+ actions: []
152
+ };
153
+
154
+ try {
155
+ await fetch('http://localhost:3000/api/workflows', {
156
+ method: 'POST',
157
+ headers: {
158
+ 'Content-Type': 'application/json',
159
+ 'Authorization': \`Bearer \${token}\`
160
+ },
161
+ body: JSON.stringify(data)
162
+ });
163
+ alert('创建成功!');
164
+ document.getElementById('workflowModal').classList.add('hidden');
165
+ await renderWorkflowView(container);
166
+ } catch (error) {
167
+ alert('创建失败: ' + error.message);
168
+ }
169
+ });
170
+ } catch (error) {
171
+ container.innerHTML = \`<div class="empty-state">加载失败: \${error.message}</div>\`;
172
+ }
173
+ }`;
174
+
175
+ content = content.replace(workflowPattern, workflowReplacement);
176
+
177
+ console.log('写入文件...');
178
+ fs.writeFileSync(filePath, content, 'utf8');
179
+
180
+ console.log('✅ 第五步完成!已完善工作流功能');
181
+
@@ -0,0 +1,254 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+
8
+ const filePath = path.join(__dirname, '../src/pages/admin-dashboard.js');
9
+
10
+ console.log('读取文件...');
11
+ let content = fs.readFileSync(filePath, 'utf8');
12
+
13
+ // 替换备份管理功能为完整实现
14
+ console.log('完善备份管理功能...');
15
+ const backupPattern = /\/\/ 备份管理[\s\S]*?async function renderBackupView\(container\)[\s\S]*?container\.innerHTML = '<div class="empty-state">备份功能开发中\.\.\.<\/div>';[\s\S]*?}/;
16
+ const backupReplacement = `// 备份管理
17
+ async function renderBackupView(container) {
18
+ try {
19
+ const token = localStorage.getItem('token');
20
+ const response = await fetch('http://localhost:3000/api/backup/list', {
21
+ headers: { 'Authorization': \`Bearer \${token}\` }
22
+ });
23
+ const result = await response.json();
24
+ const backups = result.data?.backups || [];
25
+
26
+ container.innerHTML = \`
27
+ <div class="view-header">
28
+ <h2>💾 备份管理</h2>
29
+ <button class="btn-primary" id="createBackupBtn">➕ 创建备份</button>
30
+ </div>
31
+ <div class="backup-grid" id="backupList" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); gap: 20px; padding: 20px;"></div>
32
+ \`;
33
+
34
+ const backupList = document.getElementById('backupList');
35
+ if (backups.length === 0) {
36
+ backupList.innerHTML = '<div class="empty-state" style="grid-column: 1/-1;">暂无备份</div>';
37
+ } else {
38
+ backups.forEach(backup => {
39
+ const card = document.createElement('div');
40
+ card.className = 'backup-card';
41
+ card.style.cssText = 'background: var(--bg-secondary); padding: 20px; border-radius: 12px; border: 1px solid var(--border); transition: transform 0.2s, box-shadow 0.2s;';
42
+ const size = (backup.size / 1024 / 1024).toFixed(2);
43
+ card.innerHTML = \`
44
+ <div style="display: flex; align-items: center; gap: 15px; margin-bottom: 15px;">
45
+ <div style="font-size: 48px;">📦</div>
46
+ <div style="flex: 1;">
47
+ <h3 style="margin: 0 0 5px 0; font-size: 16px;">\${backup.filename}</h3>
48
+ <p style="margin: 0; font-size: 14px; color: var(--text-secondary);">\${size} MB</p>
49
+ </div>
50
+ </div>
51
+ <div class="backup-meta" style="font-size: 12px; color: var(--text-tertiary); margin-bottom: 15px;">
52
+ <span>📅 \${new Date(backup.createdAt).toLocaleString()}</span>
53
+ </div>
54
+ <div style="display: flex; gap: 10px;">
55
+ <a href="http://localhost:3000/api/backup/download/\${backup.filename}" class="btn-primary btn-sm" download style="flex: 1; text-align: center; text-decoration: none; display: block; padding: 8px;">⬇️ 下载</a>
56
+ <button class="btn-danger btn-sm" data-filename="\${backup.filename}" data-action="delete" style="flex: 1;">🗑️ 删除</button>
57
+ </div>
58
+ \`;
59
+ card.onmouseenter = () => {
60
+ card.style.transform = 'translateY(-4px)';
61
+ card.style.boxShadow = '0 8px 16px rgba(0,0,0,0.1)';
62
+ };
63
+ card.onmouseleave = () => {
64
+ card.style.transform = 'translateY(0)';
65
+ card.style.boxShadow = 'none';
66
+ };
67
+ backupList.appendChild(card);
68
+ });
69
+
70
+ document.querySelectorAll('[data-action="delete"]').forEach(btn => {
71
+ btn.addEventListener('click', async () => {
72
+ if (confirm('确定要删除这个备份吗?')) {
73
+ try {
74
+ await fetch(\`http://localhost:3000/api/backup/\${btn.dataset.filename}\`, {
75
+ method: 'DELETE',
76
+ headers: { 'Authorization': \`Bearer \${token}\` }
77
+ });
78
+ alert('删除成功!');
79
+ await renderBackupView(container);
80
+ } catch (error) {
81
+ alert('删除失败: ' + error.message);
82
+ }
83
+ }
84
+ });
85
+ });
86
+ }
87
+
88
+ document.getElementById('createBackupBtn').addEventListener('click', async () => {
89
+ if (confirm('确定要创建新备份吗?这可能需要一些时间。')) {
90
+ const btn = document.getElementById('createBackupBtn');
91
+ btn.disabled = true;
92
+ btn.textContent = '⏳ 创建中...';
93
+ try {
94
+ await fetch('http://localhost:3000/api/backup/create', {
95
+ method: 'POST',
96
+ headers: { 'Authorization': \`Bearer \${token}\` }
97
+ });
98
+ alert('备份创建成功!');
99
+ await renderBackupView(container);
100
+ } catch (error) {
101
+ alert('创建失败: ' + error.message);
102
+ } finally {
103
+ btn.disabled = false;
104
+ btn.textContent = '➕ 创建备份';
105
+ }
106
+ }
107
+ });
108
+ } catch (error) {
109
+ container.innerHTML = \`<div class="empty-state">加载失败: \${error.message}</div>\`;
110
+ }
111
+ }`;
112
+
113
+ content = content.replace(backupPattern, backupReplacement);
114
+
115
+ // 替换数据导出功能为完整实现(从 switch case 中添加)
116
+ console.log('完善数据导出功能...');
117
+ const exportCasePattern = /(case 'backup':[\s\S]*?await renderBackupView\(contentArea\);[\s\S]*?break;)/;
118
+ const exportCaseReplacement = `$1
119
+ case 'export':
120
+ await renderExportView(contentArea);
121
+ break;`;
122
+
123
+ content = content.replace(exportCasePattern, exportCaseReplacement);
124
+
125
+ // 替换数据导出函数为完整实现
126
+ const exportPattern = /\/\/ 数据导出[\s\S]*?async function renderExportView\(container\)[\s\S]*?container\.innerHTML = '<div class="empty-state">数据导出功能开发中\.\.\.<\/div>';[\s\S]*?}/;
127
+ const exportReplacement = `// 数据导出
128
+ async function renderExportView(container) {
129
+ container.innerHTML = \`
130
+ <div class="view-header">
131
+ <h2>📤 数据导出</h2>
132
+ </div>
133
+ <div class="export-container" style="padding: 20px;">
134
+ <div class="export-options" style="background: var(--bg-secondary); padding: 30px; border-radius: 12px; margin-bottom: 20px;">
135
+ <h3 style="margin: 0 0 20px 0;">选择导出内容</h3>
136
+ <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 15px; margin-bottom: 25px;">
137
+ <label style="display: flex; align-items: center; gap: 10px; padding: 12px; background: var(--bg); border-radius: 8px; cursor: pointer;">
138
+ <input type="checkbox" id="exportGroups" checked style="width: 18px; height: 18px;">
139
+ <span>👥 群组信息</span>
140
+ </label>
141
+ <label style="display: flex; align-items: center; gap: 10px; padding: 12px; background: var(--bg); border-radius: 8px; cursor: pointer;">
142
+ <input type="checkbox" id="exportDocuments" checked style="width: 18px; height: 18px;">
143
+ <span>📄 文档</span>
144
+ </label>
145
+ <label style="display: flex; align-items: center; gap: 10px; padding: 12px; background: var(--bg); border-radius: 8px; cursor: pointer;">
146
+ <input type="checkbox" id="exportTasks" checked style="width: 18px; height: 18px;">
147
+ <span>📋 任务</span>
148
+ </label>
149
+ <label style="display: flex; align-items: center; gap: 10px; padding: 12px; background: var(--bg); border-radius: 8px; cursor: pointer;">
150
+ <input type="checkbox" id="exportMessages" checked style="width: 18px; height: 18px;">
151
+ <span>💬 消息</span>
152
+ </label>
153
+ <label style="display: flex; align-items: center; gap: 10px; padding: 12px; background: var(--bg); border-radius: 8px; cursor: pointer;">
154
+ <input type="checkbox" id="exportFiles" style="width: 18px; height: 18px;">
155
+ <span>📎 文件</span>
156
+ </label>
157
+ </div>
158
+
159
+ <h3 style="margin: 25px 0 15px 0;">导出格式</h3>
160
+ <select id="exportFormat" style="width: 100%; padding: 12px; border: 1px solid var(--border); border-radius: 8px; margin-bottom: 25px;">
161
+ <option value="json">JSON</option>
162
+ <option value="csv">CSV</option>
163
+ <option value="excel">Excel</option>
164
+ </select>
165
+
166
+ <button class="btn-primary" id="exportBtn" style="width: 100%; padding: 15px; font-size: 16px;">🚀 开始导出</button>
167
+ </div>
168
+
169
+ <div class="export-history" style="background: var(--bg-secondary); padding: 30px; border-radius: 12px;">
170
+ <h3 style="margin: 0 0 20px 0;">📜 导出历史</h3>
171
+ <div id="historyList">加载中...</div>
172
+ </div>
173
+ </div>
174
+ \`;
175
+
176
+ // 加载导出历史
177
+ try {
178
+ const token = localStorage.getItem('token');
179
+ const response = await fetch('http://localhost:3000/api/export/history', {
180
+ headers: { 'Authorization': \`Bearer \${token}\` }
181
+ });
182
+ const result = await response.json();
183
+ const historyList = document.getElementById('historyList');
184
+
185
+ if (result.exports && result.exports.length > 0) {
186
+ historyList.innerHTML = result.exports.map(exp => \`
187
+ <div class="export-item" style="display: flex; justify-content: space-between; align-items: center; padding: 15px; background: var(--bg); border-radius: 8px; margin-bottom: 10px;">
188
+ <div>
189
+ <div style="font-weight: 600; margin-bottom: 5px;">📦 \${exp.format.toUpperCase()} 导出</div>
190
+ <div style="font-size: 12px; color: var(--text-secondary);">📅 \${new Date(exp.createdAt).toLocaleString()}</div>
191
+ </div>
192
+ <a href="http://localhost:3000/api/export/download/\${exp.filename}" class="btn-sm btn-primary" download style="text-decoration: none;">⬇️ 下载</a>
193
+ </div>
194
+ \`).join('');
195
+ } else {
196
+ historyList.innerHTML = '<div class="empty-state">暂无导出记录</div>';
197
+ }
198
+ } catch (error) {
199
+ document.getElementById('historyList').innerHTML = '<div class="empty-state">加载失败</div>';
200
+ }
201
+
202
+ document.getElementById('exportBtn').addEventListener('click', async () => {
203
+ const options = {
204
+ groups: document.getElementById('exportGroups').checked,
205
+ documents: document.getElementById('exportDocuments').checked,
206
+ tasks: document.getElementById('exportTasks').checked,
207
+ messages: document.getElementById('exportMessages').checked,
208
+ files: document.getElementById('exportFiles').checked,
209
+ format: document.getElementById('exportFormat').value
210
+ };
211
+
212
+ const btn = document.getElementById('exportBtn');
213
+ btn.disabled = true;
214
+ btn.textContent = '⏳ 导出中...';
215
+
216
+ try {
217
+ const token = localStorage.getItem('token');
218
+ const response = await fetch('http://localhost:3000/api/export', {
219
+ method: 'POST',
220
+ headers: {
221
+ 'Content-Type': 'application/json',
222
+ 'Authorization': \`Bearer \${token}\`
223
+ },
224
+ body: JSON.stringify(options)
225
+ });
226
+
227
+ if (response.ok) {
228
+ const blob = await response.blob();
229
+ const url = window.URL.createObjectURL(blob);
230
+ const a = document.createElement('a');
231
+ a.href = url;
232
+ a.download = \`export-\${Date.now()}.\${options.format}\`;
233
+ a.click();
234
+ alert('导出成功!');
235
+ await renderExportView(container);
236
+ } else {
237
+ throw new Error('导出失败');
238
+ }
239
+ } catch (error) {
240
+ alert('导出失败: ' + error.message);
241
+ } finally {
242
+ btn.disabled = false;
243
+ btn.textContent = '🚀 开始导出';
244
+ }
245
+ });
246
+ }`;
247
+
248
+ content = content.replace(exportPattern, exportReplacement);
249
+
250
+ console.log('写入文件...');
251
+ fs.writeFileSync(filePath, content, 'utf8');
252
+
253
+ console.log('✅ 第六步完成!已完善备份管理和数据导出功能');
254
+