@builder6/rooms 3.0.4 → 3.0.6
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 +10 -7
- package/.prettierrc +0 -4
- package/src/emails/UnreadMention.tsx +0 -37
- package/src/emails/UnreadReplies.tsx +0 -49
- package/src/emails/_components/comment.tsx +0 -70
- package/src/emails/_components/header.tsx +0 -26
- package/src/emails/_components/headline.tsx +0 -20
- package/src/emails/_components/layout.tsx +0 -45
- package/src/emails/_lib/types.ts +0 -10
- package/src/emails/_styles/colors.ts +0 -7
- package/src/emails/_utils/cn.ts +0 -6
- package/src/emails/_utils/comments.ts +0 -61
- package/src/emails/_utils/getProps.ts +0 -7
- package/src/plugin.module.ts +0 -3
- package/src/rooms/app.controller.ts +0 -89
- package/src/rooms/dtos/inbox_notifications.dto.ts +0 -13
- package/src/rooms/dtos/notifications.dto.ts +0 -14
- package/src/rooms/dtos/room_members.dto.ts +0 -32
- package/src/rooms/dtos/rooms.dto.ts +0 -51
- package/src/rooms/emailNotification.service.tsx +0 -126
- package/src/rooms/emails/comment-body.tsx +0 -342
- package/src/rooms/emails/comment-with-body.ts +0 -24
- package/src/rooms/emails/index.ts +0 -25
- package/src/rooms/emails/lib/batch-users-resolver.ts +0 -120
- package/src/rooms/emails/lib/css-properties.ts +0 -123
- package/src/rooms/emails/lib/warning.ts +0 -25
- package/src/rooms/emails/thread-notification.tsx +0 -583
- package/src/rooms/globals/augmentation.ts +0 -89
- package/src/rooms/index.ts +0 -5
- package/src/rooms/lib/DateToString.ts +0 -9
- package/src/rooms/lib/Json.ts +0 -34
- package/src/rooms/lib/utils.ts +0 -240
- package/src/rooms/liveblocks.service.ts +0 -25
- package/src/rooms/notifications.service.ts +0 -235
- package/src/rooms/protocol/AuthToken.ts +0 -126
- package/src/rooms/protocol/Authentication.ts +0 -18
- package/src/rooms/protocol/BaseActivitiesData.ts +0 -5
- package/src/rooms/protocol/BaseRoomInfo.ts +0 -15
- package/src/rooms/protocol/BaseUserMeta.ts +0 -28
- package/src/rooms/protocol/ClientMsg.ts +0 -94
- package/src/rooms/protocol/Comments.ts +0 -202
- package/src/rooms/protocol/InboxNotifications.ts +0 -75
- package/src/rooms/protocol/Op.ts +0 -143
- package/src/rooms/protocol/SerializedCrdt.ts +0 -61
- package/src/rooms/protocol/ServerMsg.ts +0 -307
- package/src/rooms/protocol/VersionHistory.ts +0 -9
- package/src/rooms/rooms.controller.ts +0 -587
- package/src/rooms/rooms.gateway.ts +0 -267
- package/src/rooms/rooms.guard.ts +0 -52
- package/src/rooms/rooms.module.ts +0 -38
- package/src/rooms/rooms.moleculer.ts +0 -80
- package/src/rooms/rooms.service.ts +0 -723
- package/tsconfig.json +0 -10
- package/yarn-error.log +0 -17218
|
@@ -1,723 +0,0 @@
|
|
|
1
|
-
import { MongodbService } from '@builder6/core';
|
|
2
|
-
import { JwtService } from '@nestjs/jwt';
|
|
3
|
-
import { Injectable } from '@nestjs/common';
|
|
4
|
-
import { READ_ACCESS, RoomDTO } from './dtos/rooms.dto';
|
|
5
|
-
import { RoomMemberDTO } from './dtos/room_members.dto';
|
|
6
|
-
import { isArray, uniq } from 'lodash';
|
|
7
|
-
import { NotificationsService } from './notifications.service';
|
|
8
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
9
|
-
import { CommentBody, CommentBodyMention } from './protocol/Comments';
|
|
10
|
-
|
|
11
|
-
export interface CreateThreadParams {
|
|
12
|
-
id?: string;
|
|
13
|
-
comment?: CreateCommentParams;
|
|
14
|
-
metadata?: object;
|
|
15
|
-
resolved?: boolean;
|
|
16
|
-
roomId?: string;
|
|
17
|
-
userId?: string;
|
|
18
|
-
participantIds?: string[];
|
|
19
|
-
space?: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface CreateCommentParams {
|
|
23
|
-
id?: string;
|
|
24
|
-
attachmentIds: string[];
|
|
25
|
-
body: object;
|
|
26
|
-
roomId?: string;
|
|
27
|
-
threadId?: string;
|
|
28
|
-
userId?: string;
|
|
29
|
-
space?: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface Attachment {
|
|
33
|
-
id: string;
|
|
34
|
-
mimeType: string;
|
|
35
|
-
name: string;
|
|
36
|
-
size: number;
|
|
37
|
-
type: string;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export enum CommentType {
|
|
41
|
-
Paragraph = 'paragraph',
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export enum CommentBodyInlineType {
|
|
45
|
-
Mention = 'mention',
|
|
46
|
-
Link = 'link',
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
@Injectable()
|
|
50
|
-
export class RoomsService {
|
|
51
|
-
constructor(
|
|
52
|
-
private mongodbService: MongodbService,
|
|
53
|
-
private jwtService: JwtService,
|
|
54
|
-
private notificationsService: NotificationsService,
|
|
55
|
-
) {}
|
|
56
|
-
|
|
57
|
-
async getToken({
|
|
58
|
-
spaceId,
|
|
59
|
-
userId,
|
|
60
|
-
perms,
|
|
61
|
-
}: {
|
|
62
|
-
spaceId: string;
|
|
63
|
-
userId: string;
|
|
64
|
-
perms: object;
|
|
65
|
-
}) {
|
|
66
|
-
const payload = {
|
|
67
|
-
k: 'acc',
|
|
68
|
-
pid: spaceId,
|
|
69
|
-
sid: spaceId,
|
|
70
|
-
uid: userId,
|
|
71
|
-
mcpr: 10,
|
|
72
|
-
perms: perms,
|
|
73
|
-
jti: 'S4EMiESTDe6k',
|
|
74
|
-
};
|
|
75
|
-
const token = await this.jwtService.signAsync(payload);
|
|
76
|
-
return { token };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async getRoomToken({
|
|
80
|
-
roomId,
|
|
81
|
-
spaceId,
|
|
82
|
-
userId,
|
|
83
|
-
readonly = false,
|
|
84
|
-
}: {
|
|
85
|
-
roomId: string;
|
|
86
|
-
spaceId: string;
|
|
87
|
-
userId: string;
|
|
88
|
-
readonly: boolean;
|
|
89
|
-
}) {
|
|
90
|
-
let room = await this.getRoom(roomId);
|
|
91
|
-
if (!room) {
|
|
92
|
-
room = await this.createRoom({
|
|
93
|
-
_id: roomId,
|
|
94
|
-
name: 'Room ' + roomId,
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// 如果当前用户不在 room 中,创建一个 room member
|
|
99
|
-
let member = await this.getRoomMember(roomId, userId);
|
|
100
|
-
if (!member) {
|
|
101
|
-
member = await this.createRoomMember({
|
|
102
|
-
userId,
|
|
103
|
-
roomId,
|
|
104
|
-
notifications: room.defaultNotifications,
|
|
105
|
-
scopes: room.defaultScopes,
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const scopes = readonly ? READ_ACCESS : member.scopes;
|
|
110
|
-
|
|
111
|
-
const perms = {
|
|
112
|
-
[roomId]: scopes,
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
return await this.getToken({
|
|
116
|
-
spaceId,
|
|
117
|
-
userId,
|
|
118
|
-
perms,
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async getAttachmentById(attachmentId: string): Promise<Attachment> {
|
|
123
|
-
// 模拟从数据库获取附件
|
|
124
|
-
// 实际实现中应替换为数据库查询
|
|
125
|
-
const attachment = await this.mongodbService.findOne(
|
|
126
|
-
'cfs.files.filerecord',
|
|
127
|
-
{
|
|
128
|
-
_id: attachmentId,
|
|
129
|
-
},
|
|
130
|
-
);
|
|
131
|
-
if (attachment === null) {
|
|
132
|
-
return null;
|
|
133
|
-
}
|
|
134
|
-
return {
|
|
135
|
-
id: attachmentId,
|
|
136
|
-
mimeType: attachment.original.type,
|
|
137
|
-
name: attachment.original.name,
|
|
138
|
-
size: attachment.original.size,
|
|
139
|
-
type: 'attachment',
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
async resolveUsers({ userIds }: { userIds: string[] }) {
|
|
144
|
-
const users = [];
|
|
145
|
-
|
|
146
|
-
for (const userId of userIds) {
|
|
147
|
-
const user = await this.mongodbService.findOne(
|
|
148
|
-
'users',
|
|
149
|
-
{
|
|
150
|
-
_id: userId,
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
projection: {
|
|
154
|
-
name: 1,
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
);
|
|
158
|
-
users.push({
|
|
159
|
-
name: user ? user.name : 'Unknown',
|
|
160
|
-
avatar: `${process.env.B6_HOST}/api/v6/users/${userId}/avatar`,
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return users;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// 按照 email, name, username 搜索用户, 返回 userId 数组
|
|
168
|
-
async searchUsers(spaceId: string, keyword: string): Promise<string[]> {
|
|
169
|
-
let query: any = {
|
|
170
|
-
space: spaceId,
|
|
171
|
-
};
|
|
172
|
-
if (keyword) {
|
|
173
|
-
query = {
|
|
174
|
-
space: spaceId,
|
|
175
|
-
$or: [
|
|
176
|
-
{ username: { $regex: keyword, $options: 'i' } },
|
|
177
|
-
{ email: { $regex: keyword, $options: 'i' } },
|
|
178
|
-
{ name: { $regex: keyword, $options: 'i' } },
|
|
179
|
-
],
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
// 最多返回5行
|
|
183
|
-
const spaceUsers = await this.mongodbService.find('space_users', query, {
|
|
184
|
-
limit: 5,
|
|
185
|
-
projection: {
|
|
186
|
-
name: 1,
|
|
187
|
-
user: 1,
|
|
188
|
-
},
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
return spaceUsers.map((spaceUser) => spaceUser.user as string);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
async getThread({ roomId, threadId }: { roomId: string; threadId: string }) {
|
|
195
|
-
const thread = await this.mongodbService.findOne('b6_threads', {
|
|
196
|
-
roomId: roomId,
|
|
197
|
-
_id: threadId,
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
if (!thread) {
|
|
201
|
-
return null;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const comments = await this.getComments(roomId, threadId);
|
|
205
|
-
thread.comments = comments;
|
|
206
|
-
return thread;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
async getThreads(roomId: string, since?: Date) {
|
|
210
|
-
const queryOptions = { roomId: roomId } as any;
|
|
211
|
-
if (since) {
|
|
212
|
-
queryOptions.updatedAt = { $gt: since };
|
|
213
|
-
}
|
|
214
|
-
const threads = await this.mongodbService.find('b6_threads', queryOptions);
|
|
215
|
-
|
|
216
|
-
for (const thread of threads) {
|
|
217
|
-
const comments = await this.getComments(roomId, thread.id);
|
|
218
|
-
thread.comments = comments;
|
|
219
|
-
}
|
|
220
|
-
return threads;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
async getThreadsByIds(
|
|
224
|
-
notifications: {
|
|
225
|
-
id: string;
|
|
226
|
-
userId: string;
|
|
227
|
-
kind: string;
|
|
228
|
-
notifiedAt?: Date;
|
|
229
|
-
readAt?: Date;
|
|
230
|
-
roomId: string;
|
|
231
|
-
threadId: string;
|
|
232
|
-
}[],
|
|
233
|
-
) {
|
|
234
|
-
const threadPromises = [];
|
|
235
|
-
for (let i = 0; i < notifications.length; i++) {
|
|
236
|
-
const notification = notifications[i];
|
|
237
|
-
threadPromises.push(
|
|
238
|
-
this.getThread({
|
|
239
|
-
roomId: notification.roomId,
|
|
240
|
-
threadId: notification.threadId,
|
|
241
|
-
}),
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
if (threadPromises.length > 0) {
|
|
245
|
-
return Promise.all(threadPromises);
|
|
246
|
-
}
|
|
247
|
-
return [];
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
async getComments(roomId: string, threadId: string) {
|
|
251
|
-
const comments = await this.mongodbService.find('b6_comments', {
|
|
252
|
-
roomId: roomId,
|
|
253
|
-
threadId: threadId,
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
for (const comment of comments) {
|
|
257
|
-
comment.attachments = [];
|
|
258
|
-
if (isArray(comment.attachmentIds)) {
|
|
259
|
-
for (const attachmentId of comment.attachmentIds) {
|
|
260
|
-
const attachment = await this.getAttachmentById(attachmentId);
|
|
261
|
-
if (attachment) {
|
|
262
|
-
comment.attachments.push(attachment);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
return comments;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
async createThread({
|
|
271
|
-
id = uuidv4(),
|
|
272
|
-
comment,
|
|
273
|
-
metadata,
|
|
274
|
-
resolved = false,
|
|
275
|
-
roomId,
|
|
276
|
-
userId,
|
|
277
|
-
space
|
|
278
|
-
}: CreateThreadParams) {
|
|
279
|
-
const newThread = {
|
|
280
|
-
createdAt: new Date().toISOString(),
|
|
281
|
-
updatedAt: new Date().toISOString(),
|
|
282
|
-
_id: id,
|
|
283
|
-
id,
|
|
284
|
-
resolved,
|
|
285
|
-
metadata,
|
|
286
|
-
type: 'thread',
|
|
287
|
-
roomId,
|
|
288
|
-
userId,
|
|
289
|
-
participantIds: [userId],
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
const result = await this.mongodbService.insertOne('b6_threads', newThread);
|
|
293
|
-
|
|
294
|
-
const newComment = await this.createComment({
|
|
295
|
-
id: comment.id || uuidv4(),
|
|
296
|
-
attachmentIds: comment.attachmentIds,
|
|
297
|
-
body: comment.body,
|
|
298
|
-
roomId,
|
|
299
|
-
threadId: id,
|
|
300
|
-
userId,
|
|
301
|
-
space,
|
|
302
|
-
});
|
|
303
|
-
result.comments = [newComment];
|
|
304
|
-
|
|
305
|
-
return result;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
async updateThread(threadId, thread?: CreateThreadParams) {
|
|
309
|
-
const newThread = {
|
|
310
|
-
updatedAt: new Date().toISOString(),
|
|
311
|
-
...thread,
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
const result = await this.mongodbService.findOneAndUpdateData(
|
|
315
|
-
'b6_threads',
|
|
316
|
-
{ _id: threadId },
|
|
317
|
-
newThread,
|
|
318
|
-
);
|
|
319
|
-
|
|
320
|
-
return result;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
async createComment({
|
|
324
|
-
id,
|
|
325
|
-
attachmentIds,
|
|
326
|
-
body,
|
|
327
|
-
roomId,
|
|
328
|
-
threadId,
|
|
329
|
-
userId,
|
|
330
|
-
space,
|
|
331
|
-
}: CreateCommentParams) {
|
|
332
|
-
const newComment = {
|
|
333
|
-
createdAt: new Date().toISOString(),
|
|
334
|
-
_id: id,
|
|
335
|
-
id,
|
|
336
|
-
reactions: [],
|
|
337
|
-
roomId,
|
|
338
|
-
threadId,
|
|
339
|
-
userId,
|
|
340
|
-
type: 'comment',
|
|
341
|
-
attachmentIds,
|
|
342
|
-
body,
|
|
343
|
-
space,
|
|
344
|
-
};
|
|
345
|
-
|
|
346
|
-
const result = await this.mongodbService.insertOne(
|
|
347
|
-
'b6_comments',
|
|
348
|
-
newComment,
|
|
349
|
-
);
|
|
350
|
-
result.attachments = [];
|
|
351
|
-
|
|
352
|
-
for (const attachmentId of attachmentIds) {
|
|
353
|
-
const attachment = await this.getAttachmentById(attachmentId);
|
|
354
|
-
if (attachment) {
|
|
355
|
-
result.attachments.push(attachment);
|
|
356
|
-
} else {
|
|
357
|
-
throw new Error(`Attachment not found ${attachmentId}`);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// 如果有 @ mention 的时候,自动创建 b6_inbox_notifications 记录。
|
|
362
|
-
const { content } = body as CommentBody;
|
|
363
|
-
const mentionUsers: string[] = [];
|
|
364
|
-
for (const message of content) {
|
|
365
|
-
if (message.type === CommentType.Paragraph) {
|
|
366
|
-
message.children
|
|
367
|
-
.filter(
|
|
368
|
-
(child: CommentBodyMention) =>
|
|
369
|
-
child?.type === CommentBodyInlineType.Mention,
|
|
370
|
-
)
|
|
371
|
-
.forEach((child: CommentBodyMention) => mentionUsers.push(child.id));
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Update Thread participantIds
|
|
376
|
-
const thread = await this.getThread({ roomId, threadId });
|
|
377
|
-
if (thread) {
|
|
378
|
-
thread.participantIds = uniq(
|
|
379
|
-
(thread.participantIds || []).concat([userId]).concat(mentionUsers),
|
|
380
|
-
);
|
|
381
|
-
// participantIds 取唯一值并保存到 thread
|
|
382
|
-
await this.updateThread(threadId, {
|
|
383
|
-
participantIds: thread.participantIds,
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
let onThreadMentionPromises: Promise<any>[];
|
|
388
|
-
if (mentionUsers && mentionUsers.length > 0) {
|
|
389
|
-
const uniqUsers = uniq(mentionUsers).filter(
|
|
390
|
-
(mentionedUserId) => mentionedUserId !== userId,
|
|
391
|
-
);
|
|
392
|
-
onThreadMentionPromises = uniqUsers.map((mentionedUserId) => {
|
|
393
|
-
return this.notificationsService.onThreadNotification({
|
|
394
|
-
userId: mentionedUserId,
|
|
395
|
-
roomId,
|
|
396
|
-
fromUserId: userId,
|
|
397
|
-
threadId,
|
|
398
|
-
type: 'mention',
|
|
399
|
-
spaceId: space,
|
|
400
|
-
});
|
|
401
|
-
});
|
|
402
|
-
await Promise.all([].concat(onThreadMentionPromises));
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
const participantIds = thread.participantIds.filter((participantUserId) => {
|
|
406
|
-
return (
|
|
407
|
-
participantUserId !== userId &&
|
|
408
|
-
!mentionUsers.includes(participantUserId)
|
|
409
|
-
);
|
|
410
|
-
});
|
|
411
|
-
const onThreadReplyPromises: Promise<any>[] = participantIds.map(
|
|
412
|
-
(participantUserId) => {
|
|
413
|
-
return this.notificationsService.onThreadNotification({
|
|
414
|
-
userId: participantUserId,
|
|
415
|
-
roomId,
|
|
416
|
-
fromUserId: userId,
|
|
417
|
-
threadId,
|
|
418
|
-
type: 'reply',
|
|
419
|
-
spaceId: space,
|
|
420
|
-
});
|
|
421
|
-
},
|
|
422
|
-
);
|
|
423
|
-
await Promise.all([].concat(onThreadReplyPromises));
|
|
424
|
-
|
|
425
|
-
delete result['_id'];
|
|
426
|
-
delete result['attachmentIds'];
|
|
427
|
-
return result;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
async updateComment(
|
|
431
|
-
commentId,
|
|
432
|
-
{ attachmentIds, body, userId }: CreateCommentParams,
|
|
433
|
-
) {
|
|
434
|
-
const newComment = {
|
|
435
|
-
updatedAt: new Date().toISOString(),
|
|
436
|
-
type: 'comment',
|
|
437
|
-
attachmentIds,
|
|
438
|
-
body,
|
|
439
|
-
userId,
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
const result = (await this.mongodbService.findOneAndUpdateData(
|
|
443
|
-
'b6_comments',
|
|
444
|
-
{ _id: commentId },
|
|
445
|
-
newComment,
|
|
446
|
-
)) as any;
|
|
447
|
-
result.attachments = [];
|
|
448
|
-
|
|
449
|
-
for (const attachmentId of attachmentIds) {
|
|
450
|
-
const attachment = await this.getAttachmentById(attachmentId);
|
|
451
|
-
if (attachment) {
|
|
452
|
-
result.attachments.push(attachment);
|
|
453
|
-
} else {
|
|
454
|
-
throw new Error(`Attachment not found ${attachmentId}`);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
delete result['_id'];
|
|
459
|
-
delete result['attachmentIds'];
|
|
460
|
-
return result;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
async deleteComment(commentId) {
|
|
464
|
-
// TODO: 应该先删除附件。
|
|
465
|
-
|
|
466
|
-
const result = await this.mongodbService.deleteOne('b6_comments', {
|
|
467
|
-
_id: commentId,
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
return result;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
/* 评论点赞
|
|
474
|
-
保存在 comments 中,格式如下:
|
|
475
|
-
comment: {
|
|
476
|
-
_id,
|
|
477
|
-
reactions: [
|
|
478
|
-
{
|
|
479
|
-
"emoji": "🥵",
|
|
480
|
-
"createdAt": "2024-12-05T01:49:22.395Z",
|
|
481
|
-
"users": [
|
|
482
|
-
{
|
|
483
|
-
"id": "user-5"
|
|
484
|
-
}
|
|
485
|
-
{
|
|
486
|
-
"id": "user-6"
|
|
487
|
-
}
|
|
488
|
-
]
|
|
489
|
-
},
|
|
490
|
-
]
|
|
491
|
-
reactions 数组中的每个元素表示一个用户的点赞,其中 emoji 为表情,createdAt 为点赞时间,users 为点赞用户列表。
|
|
492
|
-
创建时,如果 reactions 中已经有相同 emoji 的点赞,则直接添加到 users 中,否则创建一个新的点赞。
|
|
493
|
-
}
|
|
494
|
-
*/ async createReaction(
|
|
495
|
-
commentId: string,
|
|
496
|
-
{ userId, emoji }: { userId: string; emoji: string },
|
|
497
|
-
) {
|
|
498
|
-
const comment = await this.mongodbService.findOne('b6_comments', {
|
|
499
|
-
_id: commentId,
|
|
500
|
-
});
|
|
501
|
-
if (!comment) {
|
|
502
|
-
throw new Error('Comment not found');
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
// 查找是否已经存在相同 emoji 的 reaction
|
|
506
|
-
let reaction = comment.reactions.find(
|
|
507
|
-
(reaction) => reaction.emoji === emoji,
|
|
508
|
-
);
|
|
509
|
-
if (reaction) {
|
|
510
|
-
// 如果存在,检查用户是否已经点赞,避免重复添加
|
|
511
|
-
const userExists = reaction.users.some((user) => user.id === userId);
|
|
512
|
-
if (!userExists) {
|
|
513
|
-
reaction.users.push({ id: userId });
|
|
514
|
-
}
|
|
515
|
-
} else {
|
|
516
|
-
// 如果不存在,创建新的 reaction 并推入 reactions 数组
|
|
517
|
-
reaction = {
|
|
518
|
-
emoji,
|
|
519
|
-
createdAt: new Date().toISOString(),
|
|
520
|
-
users: [{ id: userId }],
|
|
521
|
-
};
|
|
522
|
-
comment.reactions.push(reaction);
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// 更新数据库
|
|
526
|
-
await this.mongodbService.findOneAndUpdateData(
|
|
527
|
-
'b6_comments',
|
|
528
|
-
{ _id: commentId },
|
|
529
|
-
comment,
|
|
530
|
-
);
|
|
531
|
-
|
|
532
|
-
return {
|
|
533
|
-
emoji,
|
|
534
|
-
createdAt: reaction.createdAt,
|
|
535
|
-
userId,
|
|
536
|
-
};
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
async deleteReaction(
|
|
540
|
-
commentId: string,
|
|
541
|
-
{ userId, emoji }: { userId: string; emoji: string },
|
|
542
|
-
) {
|
|
543
|
-
const comment = await this.mongodbService.findOne('b6_comments', {
|
|
544
|
-
_id: commentId,
|
|
545
|
-
});
|
|
546
|
-
if (!comment) {
|
|
547
|
-
throw new Error('Comment not found');
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
const reaction = comment.reactions.find(
|
|
551
|
-
(reaction) => reaction.emoji === emoji,
|
|
552
|
-
);
|
|
553
|
-
if (!reaction) {
|
|
554
|
-
throw new Error('Reaction not found');
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
reaction.users = reaction.users.filter((user) => user.id !== userId);
|
|
558
|
-
|
|
559
|
-
comment.reactions = comment.reactions.filter(
|
|
560
|
-
(reaction) => reaction.users.length > 0,
|
|
561
|
-
);
|
|
562
|
-
|
|
563
|
-
await this.mongodbService.findOneAndUpdateData(
|
|
564
|
-
'b6_comments',
|
|
565
|
-
{ _id: commentId },
|
|
566
|
-
comment,
|
|
567
|
-
);
|
|
568
|
-
|
|
569
|
-
return null;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
async createRoom({
|
|
573
|
-
_id,
|
|
574
|
-
name,
|
|
575
|
-
description = '',
|
|
576
|
-
membershipType = 'standard',
|
|
577
|
-
members = [],
|
|
578
|
-
defaultNotifications = ['replies', 'mentions'],
|
|
579
|
-
defaultScopes = ['room:write'],
|
|
580
|
-
}: RoomDTO): Promise<RoomDTO> {
|
|
581
|
-
const newRoom = {
|
|
582
|
-
createdAt: new Date().toISOString(),
|
|
583
|
-
updatedAt: new Date().toISOString(),
|
|
584
|
-
_id,
|
|
585
|
-
name,
|
|
586
|
-
description,
|
|
587
|
-
membershipType,
|
|
588
|
-
defaultNotifications,
|
|
589
|
-
defaultScopes,
|
|
590
|
-
};
|
|
591
|
-
|
|
592
|
-
const room = await this.mongodbService.insertOne('b6_rooms', newRoom);
|
|
593
|
-
|
|
594
|
-
// 循环 members,执行 createRoomMember,并保存到 result.members
|
|
595
|
-
room.members = [];
|
|
596
|
-
for (const member of members) {
|
|
597
|
-
const newMember = await this.createRoomMember({
|
|
598
|
-
notifications: room.defaultNotifications,
|
|
599
|
-
scopes: room.defaultScopes,
|
|
600
|
-
...member,
|
|
601
|
-
});
|
|
602
|
-
room.members.push(newMember);
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
return room;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
async createRoomMember(member: RoomMemberDTO) {
|
|
609
|
-
const { _id, userId, roomId, roles = [], notifications, scopes } = member;
|
|
610
|
-
const newMember = {
|
|
611
|
-
_id,
|
|
612
|
-
userId,
|
|
613
|
-
roles,
|
|
614
|
-
roomId,
|
|
615
|
-
notifications,
|
|
616
|
-
scopes,
|
|
617
|
-
createdAt: new Date().toISOString(),
|
|
618
|
-
};
|
|
619
|
-
|
|
620
|
-
const result = await this.mongodbService.insertOne(
|
|
621
|
-
'b6_room_members',
|
|
622
|
-
newMember,
|
|
623
|
-
);
|
|
624
|
-
return result;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
async getRoom(roomId: string) {
|
|
628
|
-
const room = await this.mongodbService.findOne('b6_rooms', {
|
|
629
|
-
_id: roomId,
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
if (!room) {
|
|
633
|
-
return null;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
room.members = await this.getRoomMembers(roomId);
|
|
637
|
-
return room;
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
async getRoomMembers(roomId: string) {
|
|
641
|
-
const members = await this.mongodbService.find('b6_room_members', {
|
|
642
|
-
roomId,
|
|
643
|
-
});
|
|
644
|
-
|
|
645
|
-
return members;
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
async getRoomMember(roomId: string, userId: string) {
|
|
649
|
-
const member = await this.mongodbService.findOne('b6_room_members', {
|
|
650
|
-
roomId,
|
|
651
|
-
userId,
|
|
652
|
-
});
|
|
653
|
-
|
|
654
|
-
return member;
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
async getMyRooms(userId: string) {
|
|
658
|
-
const roomMembers = await this.mongodbService.find('b6_room_members', {
|
|
659
|
-
userId,
|
|
660
|
-
});
|
|
661
|
-
|
|
662
|
-
const rooms = [];
|
|
663
|
-
for (const roomMember of roomMembers) {
|
|
664
|
-
const room = await this.getRoom(roomMember.roomId);
|
|
665
|
-
rooms.push(room);
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
return rooms;
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
async resolveRoomsInfo({ roomIds }: { roomIds: string[] }) {
|
|
672
|
-
const result = [];
|
|
673
|
-
for (const roomId of roomIds) {
|
|
674
|
-
const room = await this.resolveRoomInfo({ roomId });
|
|
675
|
-
result.push(room);
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
return result;
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
async resolveRoomInfo({ roomId }: { roomId: string }) {
|
|
682
|
-
if (
|
|
683
|
-
roomId &&
|
|
684
|
-
roomId.split(':').length === 3 &&
|
|
685
|
-
roomId.split(':')[0] === 'objects'
|
|
686
|
-
) {
|
|
687
|
-
const [, objectName, recordId] = roomId.split(':');
|
|
688
|
-
const record = await this.mongodbService.findOne(
|
|
689
|
-
objectName,
|
|
690
|
-
{
|
|
691
|
-
_id: recordId,
|
|
692
|
-
},
|
|
693
|
-
{
|
|
694
|
-
projection: {
|
|
695
|
-
name: 1,
|
|
696
|
-
},
|
|
697
|
-
},
|
|
698
|
-
);
|
|
699
|
-
|
|
700
|
-
return {
|
|
701
|
-
id: roomId,
|
|
702
|
-
name: record ? record.name : roomId,
|
|
703
|
-
url: record
|
|
704
|
-
? `${process.env.B6_ROOT_URL}/app/-/${objectName}/view/${recordId}`
|
|
705
|
-
: null,
|
|
706
|
-
};
|
|
707
|
-
} else {
|
|
708
|
-
return {
|
|
709
|
-
id: roomId,
|
|
710
|
-
name: roomId,
|
|
711
|
-
url: `${process.env.B6_HOST}/b6/rooms/${roomId}`,
|
|
712
|
-
};
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
async getInboxNotification({ inboxNotificationId, userId }) {
|
|
717
|
-
const notification = await this.notificationsService.getInboxNotification({
|
|
718
|
-
inboxNotificationId,
|
|
719
|
-
userId,
|
|
720
|
-
});
|
|
721
|
-
return notification;
|
|
722
|
-
}
|
|
723
|
-
}
|