collabdocchat 2.5.10 → 2.5.13

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/index.html CHANGED
@@ -6,7 +6,7 @@
6
6
  <title>CollabDocChat - 协作文档聊天平台</title>
7
7
  <link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
8
8
  <script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
9
- <script type="module" crossorigin src="/assets/index-9XAJPSRa.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-1ylqJADA.js"></script>
10
10
  <link rel="stylesheet" crossorigin href="/assets/index-D8ZqeoaM.css">
11
11
  </head>
12
12
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "collabdocchat",
3
- "version": "2.5.10",
3
+ "version": "2.5.13",
4
4
  "description": "开源的实时协作文档聊天平台 - 集成任务管理、多人文档编辑、智能点名功能",
5
5
  "main": "./server/index.js",
6
6
  "type": "module",
@@ -89,9 +89,13 @@
89
89
  "yjs": "^13.6.10"
90
90
  },
91
91
  "devDependencies": {
92
+ "buffer": "^6.0.3",
92
93
  "concurrently": "^8.2.2",
94
+ "crypto-browserify": "^3.12.0",
93
95
  "nodemon": "^3.0.2",
94
96
  "open": "^11.0.0",
97
+ "process": "^0.11.10",
98
+ "stream-browserify": "^3.0.0",
95
99
  "vite": "^5.0.8"
96
100
  }
97
101
  }
@@ -145,3 +145,6 @@ console.log('\n');
145
145
 
146
146
 
147
147
 
148
+
149
+
150
+
@@ -141,3 +141,6 @@ console.log(' 或: npm start\n');
141
141
 
142
142
 
143
143
 
144
+
145
+
146
+
@@ -101,3 +101,6 @@ setTimeout(() => {
101
101
 
102
102
 
103
103
 
104
+
105
+
106
+
@@ -93,29 +93,44 @@ router.get('/:pollId', authenticate, async (req, res) => {
93
93
  return res.status(404).json({ error: '投票不存在' });
94
94
  }
95
95
 
96
+ // 确保创建者信息正确
97
+ if (!poll.creatorName && poll.creator && poll.creator.username) {
98
+ poll.creatorName = poll.creator.username;
99
+ }
100
+
96
101
  // 验证用户是群组成员
97
102
  const group = await Group.findById(poll.group);
98
103
  if (!group.members.includes(req.user.userId)) {
99
104
  return res.status(403).json({ error: '您不是该群组成员' });
100
105
  }
101
106
 
102
- // 手动 populate 每个 vote 的 user(Mongoose 的嵌套 populate 可能不工作)
107
+ // 确保投票者信息正确返回
103
108
  if (!poll.anonymous) {
109
+ // 对于非匿名投票,确保每个 vote 都有 username
104
110
  for (let option of poll.options) {
105
111
  for (let vote of option.votes) {
106
- // 如果 user ObjectId 且没有 username,尝试 populate
107
- if (vote.user && (!vote.user.username || typeof vote.user === 'object' && vote.user.toString && vote.user.toString().length === 24)) {
108
- try {
109
- const userId = vote.user.toString ? vote.user.toString() : vote.user;
110
- const user = await User.findById(userId).select('username');
111
- if (user) {
112
- vote.user = { _id: user._id, username: user.username };
112
+ // 如果 vote user 字段但没有 username,尝试从 user 对象获取或 populate
113
+ if (vote.user && !vote.username) {
114
+ // 如果 user 已经被 populate(有 username 属性)
115
+ if (vote.user.username) {
116
+ vote.username = vote.user.username;
117
+ } else {
118
+ // 如果 user ObjectId,尝试 populate
119
+ try {
120
+ const userId = vote.user.toString ? vote.user.toString() : vote.user;
121
+ const user = await User.findById(userId).select('username');
122
+ if (user) {
123
+ vote.username = user.username;
124
+ }
125
+ } catch (err) {
126
+ console.error('Populate vote user failed:', err);
113
127
  }
114
- } catch (err) {
115
- // 如果 populate 失败,使用已保存的 username
116
- console.error('Populate vote user failed:', err);
117
128
  }
118
129
  }
130
+ // 确保 username 字段存在(投票时应该已经保存了)
131
+ if (!vote.username) {
132
+ console.warn('投票记录缺少 username 字段:', vote);
133
+ }
119
134
  }
120
135
  }
121
136
  } else {
@@ -127,7 +142,30 @@ router.get('/:pollId', authenticate, async (req, res) => {
127
142
  });
128
143
  }
129
144
 
130
- res.json({ poll });
145
+ // 转换为普通对象,确保所有字段都被包含
146
+ const pollData = poll.toObject ? poll.toObject({ virtuals: true }) : JSON.parse(JSON.stringify(poll));
147
+
148
+ // 确保创建者名称存在
149
+ if (!pollData.creatorName && pollData.creator) {
150
+ pollData.creatorName = pollData.creator.username || '未知用户';
151
+ }
152
+
153
+ // 确保每个投票都有 username(再次检查)
154
+ if (!poll.anonymous && pollData.options) {
155
+ pollData.options.forEach(option => {
156
+ if (option.votes) {
157
+ option.votes.forEach(vote => {
158
+ if (vote.user && !vote.username) {
159
+ if (vote.user.username) {
160
+ vote.username = vote.user.username;
161
+ }
162
+ }
163
+ });
164
+ }
165
+ });
166
+ }
167
+
168
+ res.json({ poll: pollData });
131
169
  } catch (error) {
132
170
  console.error('获取投票详情失败:', error);
133
171
  res.status(500).json({ error: '获取投票详情失败' });
package/src/main.js CHANGED
@@ -1,3 +1,6 @@
1
+ // Polyfills for browser compatibility
2
+ import './polyfills.js';
3
+
1
4
  import './styles/main.css';
2
5
  import { AuthService } from './services/auth.js';
3
6
  import { WebSocketService } from './services/websocket.js';
@@ -1299,6 +1299,14 @@ export function renderAdminDashboard(user, wsService) {
1299
1299
 
1300
1300
  const result = await response.json();
1301
1301
  const poll = result.poll;
1302
+
1303
+ // 调试:打印投票数据
1304
+ console.log('投票详情数据:', poll);
1305
+ console.log('创建者信息:', poll.creatorName, poll.creator);
1306
+ poll.options.forEach((opt, idx) => {
1307
+ console.log(`选项 ${idx} (${opt.text}) 的投票记录:`, opt.votes);
1308
+ });
1309
+
1302
1310
  const totalVotes = poll.options.reduce((sum, opt) => sum + opt.votes.length, 0);
1303
1311
  const isEnded = poll.status === 'ended' || (poll.endTime && new Date(poll.endTime) < new Date());
1304
1312
 
@@ -1356,19 +1364,20 @@ export function renderAdminDashboard(user, wsService) {
1356
1364
  let votersList = '';
1357
1365
  if (!poll.anonymous && option.votes.length > 0) {
1358
1366
  const voters = option.votes.map(v => {
1359
- // 处理不同的数据结构:优先使用 populate 后的 user.username,其次使用直接保存的 username
1367
+ // 优先使用直接保存的 username 字段
1360
1368
  if (v && typeof v === 'object') {
1361
- // 如果 user populate 了,使用 user.username
1362
- if (v.user && typeof v.user === 'object' && v.user.username) {
1363
- return v.user.username;
1369
+ if (v.username && typeof v.username === 'string' && v.username.trim()) {
1370
+ return v.username.trim();
1364
1371
  }
1365
- // 如果 user ObjectId,但直接保存了 username
1366
- if (v.username && typeof v.username === 'string') {
1367
- return v.username;
1372
+ // 如果 user populate 了,使用 user.username
1373
+ if (v.user && typeof v.user === 'object') {
1374
+ if (v.user.username && typeof v.user.username === 'string') {
1375
+ return v.user.username.trim();
1376
+ }
1368
1377
  }
1369
1378
  }
1370
1379
  return null;
1371
- }).filter(name => name !== null && name !== '未知用户');
1380
+ }).filter(name => name !== null && name !== '未知用户' && name.trim() !== '');
1372
1381
 
1373
1382
  if (voters.length > 0) {
1374
1383
  votersList = `
@@ -1376,6 +1385,9 @@ export function renderAdminDashboard(user, wsService) {
1376
1385
  <strong style="color: var(--text-primary);">👥 投票者:</strong> ${voters.join(', ')}
1377
1386
  </div>
1378
1387
  `;
1388
+ } else {
1389
+ // 如果没有找到投票者,显示调试信息(开发时使用)
1390
+ console.warn('投票者信息缺失:', option.votes);
1379
1391
  }
1380
1392
  }
1381
1393
 
@@ -0,0 +1,11 @@
1
+ // Polyfills for browser compatibility
2
+ import { Buffer } from 'buffer';
3
+ import process from 'process';
4
+
5
+ // 注入全局变量
6
+ window.Buffer = Buffer;
7
+ window.process = process;
8
+ window.global = window;
9
+
10
+ export { Buffer, process };
11
+
package/vite.config.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { defineConfig } from 'vite';
2
+ import { Buffer } from 'buffer';
2
3
 
3
4
  export default defineConfig({
4
5
  server: {
@@ -14,7 +15,35 @@ export default defineConfig({
14
15
  build: {
15
16
  rollupOptions: {
16
17
  // 移除 external 配置,让 Vite 打包所有依赖
18
+ },
19
+ commonjsOptions: {
20
+ transformMixedEsModules: true
17
21
  }
22
+ },
23
+ define: {
24
+ // 修复 crypto 模块在构建时的问题
25
+ 'process.env': {},
26
+ 'global': 'globalThis',
27
+ 'Buffer': ['buffer', 'Buffer']
28
+ },
29
+ resolve: {
30
+ alias: {
31
+ // 确保使用浏览器兼容的模块
32
+ 'crypto': 'crypto-browserify',
33
+ 'stream': 'stream-browserify',
34
+ 'buffer': 'buffer',
35
+ 'process': 'process/browser'
36
+ }
37
+ },
38
+ optimizeDeps: {
39
+ esbuildOptions: {
40
+ // Node.js 全局变量注入
41
+ define: {
42
+ global: 'globalThis'
43
+ },
44
+ plugins: []
45
+ },
46
+ include: ['buffer', 'process']
18
47
  }
19
48
  });
20
49