@builder6/rooms 0.9.3 → 0.10.2
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/README.md +1 -0
- package/dist/rooms/app.controller.d.ts +10 -0
- package/dist/rooms/app.controller.js +73 -0
- package/dist/rooms/app.controller.js.map +1 -0
- package/dist/rooms/dtos/notifications.dto.d.ts +10 -0
- package/dist/rooms/dtos/notifications.dto.js +7 -0
- package/dist/rooms/dtos/notifications.dto.js.map +1 -0
- package/dist/rooms/dtos/room_members.dto.d.ts +7 -0
- package/dist/rooms/dtos/room_members.dto.js +52 -0
- package/dist/rooms/dtos/room_members.dto.js.map +1 -0
- package/dist/rooms/dtos/rooms.dto.d.ts +12 -0
- package/dist/rooms/dtos/rooms.dto.js +59 -0
- package/dist/rooms/dtos/rooms.dto.js.map +1 -0
- package/dist/rooms/notifications.service.d.ts +18 -0
- package/dist/rooms/notifications.service.js +66 -0
- package/dist/rooms/notifications.service.js.map +1 -0
- package/dist/rooms/rooms.controller.d.ts +31 -6
- package/dist/rooms/rooms.controller.js +119 -24
- package/dist/rooms/rooms.controller.js.map +1 -1
- package/dist/rooms/rooms.gateway.d.ts +3 -1
- package/dist/rooms/rooms.gateway.js +12 -5
- package/dist/rooms/rooms.gateway.js.map +1 -1
- package/dist/rooms/rooms.guard.d.ts +1 -3
- package/dist/rooms/rooms.guard.js +2 -5
- package/dist/rooms/rooms.guard.js.map +1 -1
- package/dist/rooms/rooms.module.js +7 -2
- package/dist/rooms/rooms.module.js.map +1 -1
- package/dist/rooms/rooms.moleculer.d.ts +11 -0
- package/dist/rooms/rooms.moleculer.js +91 -0
- package/dist/rooms/rooms.moleculer.js.map +1 -0
- package/dist/rooms/rooms.service.d.ts +35 -4
- package/dist/rooms/rooms.service.js +146 -8
- package/dist/rooms/rooms.service.js.map +1 -1
- package/package.json +12 -3
- package/src/rooms/app.controller.ts +63 -0
- package/src/rooms/dtos/notifications.dto.ts +10 -0
- package/src/rooms/dtos/room_members.dto.ts +29 -0
- package/src/rooms/dtos/rooms.dto.ts +51 -0
- package/src/rooms/notifications.service.ts +82 -0
- package/src/rooms/rooms.controller.ts +132 -16
- package/src/rooms/rooms.gateway.ts +17 -4
- package/src/rooms/rooms.guard.ts +1 -1
- package/src/rooms/rooms.module.ts +7 -2
- package/src/rooms/rooms.moleculer.ts +78 -0
- package/src/rooms/rooms.service.ts +210 -6
- package/.eslintrc.js +0 -25
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { AuthGuard } from '@builder6/core';
|
|
1
|
+
import { AuthGuard, FilesService } from '@builder6/core';
|
|
2
2
|
import { RoomsService } from './rooms.service';
|
|
3
|
+
import { omit, uniqBy } from 'lodash';
|
|
3
4
|
import {
|
|
4
5
|
Controller,
|
|
5
6
|
Get,
|
|
@@ -18,9 +19,10 @@ import { JwtService } from '@nestjs/jwt';
|
|
|
18
19
|
import { RoomsGuard } from './rooms.guard';
|
|
19
20
|
import * as rawBody from 'raw-body';
|
|
20
21
|
import { Request } from 'express';
|
|
21
|
-
import { FilesService } from '@builder6/core';
|
|
22
22
|
import { RoomsGateway } from './rooms.gateway';
|
|
23
23
|
import { ServerMsgCode } from './protocol/ServerMsg';
|
|
24
|
+
import { READ_ACCESS } from './dtos/rooms.dto';
|
|
25
|
+
import { NotificationsService } from './notifications.service';
|
|
24
26
|
|
|
25
27
|
@Controller('v2/c/')
|
|
26
28
|
export class RoomsController {
|
|
@@ -29,6 +31,7 @@ export class RoomsController {
|
|
|
29
31
|
private filesService: FilesService,
|
|
30
32
|
private jwtService: JwtService,
|
|
31
33
|
private roomsGateway: RoomsGateway,
|
|
34
|
+
private notificationsService: NotificationsService,
|
|
32
35
|
) {}
|
|
33
36
|
|
|
34
37
|
@UseGuards(AuthGuard)
|
|
@@ -38,18 +41,12 @@ export class RoomsController {
|
|
|
38
41
|
@Req() req: Request,
|
|
39
42
|
) {
|
|
40
43
|
const user = req['user'];
|
|
41
|
-
const { room = '
|
|
42
|
-
|
|
43
|
-
if (permission === 'write') {
|
|
44
|
-
perm = ['room:write'];
|
|
45
|
-
}
|
|
46
|
-
const perms = {
|
|
47
|
-
[room]: perm,
|
|
48
|
-
};
|
|
49
|
-
return await this.roomsService.getToken({
|
|
44
|
+
const { room = 'test', permission = 'read' } = body;
|
|
45
|
+
return await this.roomsService.getRoomToken({
|
|
50
46
|
spaceId: user.space,
|
|
51
47
|
userId: user.user,
|
|
52
|
-
|
|
48
|
+
roomId: room,
|
|
49
|
+
readonly: permission === 'read',
|
|
53
50
|
});
|
|
54
51
|
}
|
|
55
52
|
|
|
@@ -78,11 +75,18 @@ export class RoomsController {
|
|
|
78
75
|
|
|
79
76
|
@UseGuards(RoomsGuard)
|
|
80
77
|
@Get('rooms/:roomId/threads')
|
|
81
|
-
async getThreads(@Param('roomId') roomId: string) {
|
|
78
|
+
async getThreads(@Req() req: Request, @Param('roomId') roomId: string) {
|
|
82
79
|
const threads = await this.roomsService.getThreads(roomId);
|
|
80
|
+
const inboxNotifications =
|
|
81
|
+
await this.notificationsService.findNotifications({
|
|
82
|
+
userId: req['jwt'].uid,
|
|
83
|
+
roomId,
|
|
84
|
+
});
|
|
83
85
|
return {
|
|
84
86
|
data: threads,
|
|
85
|
-
inboxNotifications:
|
|
87
|
+
inboxNotifications: inboxNotifications.map((inboxNotification) =>
|
|
88
|
+
omit(inboxNotification, ['userId', '_id']),
|
|
89
|
+
),
|
|
86
90
|
meta: {
|
|
87
91
|
nextCursor: null,
|
|
88
92
|
requestedAt: new Date().toISOString() as string,
|
|
@@ -98,14 +102,23 @@ export class RoomsController {
|
|
|
98
102
|
@UseGuards(RoomsGuard)
|
|
99
103
|
@Get('rooms/:roomId/threads/delta')
|
|
100
104
|
async getThreadsDelta(
|
|
105
|
+
@Req() req: Request,
|
|
101
106
|
@Param('roomId') roomId: string,
|
|
102
107
|
@Query('since') since: string,
|
|
103
108
|
) {
|
|
104
109
|
const sinceDate = new Date(since);
|
|
105
110
|
const threads = await this.roomsService.getThreads(roomId, sinceDate);
|
|
111
|
+
const inboxNotifications =
|
|
112
|
+
await this.notificationsService.findNotifications({
|
|
113
|
+
userId: req['jwt'].uid,
|
|
114
|
+
roomId,
|
|
115
|
+
sinceDate,
|
|
116
|
+
});
|
|
106
117
|
return {
|
|
107
118
|
data: threads,
|
|
108
|
-
inboxNotifications:
|
|
119
|
+
inboxNotifications: inboxNotifications.map((inboxNotification) =>
|
|
120
|
+
omit(inboxNotification, ['userId', '_id']),
|
|
121
|
+
),
|
|
109
122
|
meta: {
|
|
110
123
|
nextCursor: null,
|
|
111
124
|
requestedAt: new Date().toISOString() as string,
|
|
@@ -156,7 +169,18 @@ export class RoomsController {
|
|
|
156
169
|
@Param('threadId') threadId: string,
|
|
157
170
|
) {
|
|
158
171
|
const thread = await this.roomsService.getThread(roomId, threadId);
|
|
159
|
-
|
|
172
|
+
const inboxNotifications =
|
|
173
|
+
await this.notificationsService.findNotifications({
|
|
174
|
+
userId: req['jwt'].uid,
|
|
175
|
+
roomId,
|
|
176
|
+
threadId,
|
|
177
|
+
});
|
|
178
|
+
return {
|
|
179
|
+
thread,
|
|
180
|
+
inboxNotification: inboxNotifications[0]
|
|
181
|
+
? omit(inboxNotifications[0], ['userId', '_id'])
|
|
182
|
+
: undefined,
|
|
183
|
+
};
|
|
160
184
|
}
|
|
161
185
|
|
|
162
186
|
@UseGuards(RoomsGuard)
|
|
@@ -269,7 +293,98 @@ export class RoomsController {
|
|
|
269
293
|
return {};
|
|
270
294
|
}
|
|
271
295
|
|
|
296
|
+
// 获取通知
|
|
297
|
+
@UseGuards(RoomsGuard)
|
|
298
|
+
@Get('inbox-notifications')
|
|
299
|
+
async getInboxNotifications(
|
|
300
|
+
@Req() req: Request,
|
|
301
|
+
@Query('unread') unread: string,
|
|
302
|
+
@Query('limit') limit: string,
|
|
303
|
+
) {
|
|
304
|
+
const userId = req['jwt'].uid;
|
|
305
|
+
const requestedAt = new Date();
|
|
306
|
+
const notifications = await this.notificationsService.findNotifications({
|
|
307
|
+
userId,
|
|
308
|
+
unread: unread === 'true',
|
|
309
|
+
limit: Number(limit),
|
|
310
|
+
});
|
|
311
|
+
const threads = await this.roomsService.getThreadsByIds(
|
|
312
|
+
uniqBy(
|
|
313
|
+
notifications,
|
|
314
|
+
(notification) => `${notification.roomId}-${notification.threadId}`,
|
|
315
|
+
),
|
|
316
|
+
);
|
|
317
|
+
return {
|
|
318
|
+
inboxNotifications: notifications.map((notification) =>
|
|
319
|
+
omit(notification, ['userId', '_id']),
|
|
320
|
+
),
|
|
321
|
+
threads,
|
|
322
|
+
meta: {
|
|
323
|
+
nextCursor: null,
|
|
324
|
+
requestedAt,
|
|
325
|
+
},
|
|
326
|
+
deletedThreads: [],
|
|
327
|
+
deletedInboxNotifications: [],
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// 获取增量通知
|
|
332
|
+
@UseGuards(RoomsGuard)
|
|
333
|
+
@Get('inbox-notifications/delta')
|
|
334
|
+
async getInboxNotificationsSince(
|
|
335
|
+
@Req() req: Request,
|
|
336
|
+
@Query('since') since: string,
|
|
337
|
+
) {
|
|
338
|
+
const userId = req['jwt'].uid;
|
|
339
|
+
const sinceDate = new Date(since);
|
|
340
|
+
const requestedAt = new Date();
|
|
341
|
+
const notifications = await this.notificationsService.findNotifications({
|
|
342
|
+
userId,
|
|
343
|
+
sinceDate,
|
|
344
|
+
});
|
|
345
|
+
const threads = await this.roomsService.getThreadsByIds(
|
|
346
|
+
uniqBy(
|
|
347
|
+
notifications,
|
|
348
|
+
(notification) => `${notification.roomId}-${notification.threadId}`,
|
|
349
|
+
),
|
|
350
|
+
);
|
|
351
|
+
return {
|
|
352
|
+
inboxNotifications: notifications.map((notification) =>
|
|
353
|
+
omit(notification, ['userId', '_id']),
|
|
354
|
+
),
|
|
355
|
+
threads,
|
|
356
|
+
meta: { requestedAt },
|
|
357
|
+
deletedThreads: [],
|
|
358
|
+
deletedInboxNotifications: [],
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// read
|
|
363
|
+
@UseGuards(RoomsGuard)
|
|
364
|
+
@Post('rooms/:roomId/inbox-notifications/read')
|
|
365
|
+
async read(
|
|
366
|
+
@Req() req: Request,
|
|
367
|
+
@Param('roomId') roomId: string,
|
|
368
|
+
@Body('inboxNotificationIds') inboxNotificationIds: string[],
|
|
369
|
+
) {
|
|
370
|
+
const userId = req['jwt'].uid;
|
|
371
|
+
const notifications = await this.notificationsService.markAsRead(
|
|
372
|
+
roomId,
|
|
373
|
+
userId,
|
|
374
|
+
inboxNotificationIds,
|
|
375
|
+
);
|
|
376
|
+
return {
|
|
377
|
+
data:
|
|
378
|
+
notifications?.length > 0
|
|
379
|
+
? notifications.map((notification) =>
|
|
380
|
+
omit(notification, ['userId', '_id']),
|
|
381
|
+
)
|
|
382
|
+
: undefined,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
272
386
|
// 获取下载Url
|
|
387
|
+
@UseGuards(RoomsGuard)
|
|
273
388
|
@Post('rooms/:roomId/attachments/presigned-urls')
|
|
274
389
|
async presignedUrls(@Body('attachmentIds') attachmentIds: string[]) {
|
|
275
390
|
const urls = await Promise.all(
|
|
@@ -284,6 +399,7 @@ export class RoomsController {
|
|
|
284
399
|
}
|
|
285
400
|
|
|
286
401
|
// 上传文件
|
|
402
|
+
@UseGuards(RoomsGuard)
|
|
287
403
|
@Put('rooms/:roomId/attachments/:attachmentId/upload/:fileName')
|
|
288
404
|
async uploadFile(
|
|
289
405
|
@Param('roomId') roomId: string,
|
|
@@ -11,6 +11,7 @@ import * as url from 'url';
|
|
|
11
11
|
import { v4 as uuidv4 } from 'uuid';
|
|
12
12
|
import { ServerMsgCode, ServerMsg } from './protocol/ServerMsg';
|
|
13
13
|
import { JwtService } from '@nestjs/jwt';
|
|
14
|
+
import { RoomsService } from './rooms.service';
|
|
14
15
|
|
|
15
16
|
interface RoomState {
|
|
16
17
|
roomId: string;
|
|
@@ -37,7 +38,10 @@ export class RoomsGateway implements OnGatewayConnection {
|
|
|
37
38
|
private redis: Redis;
|
|
38
39
|
private pubRedis: Redis;
|
|
39
40
|
|
|
40
|
-
constructor(
|
|
41
|
+
constructor(
|
|
42
|
+
private jwtService: JwtService,
|
|
43
|
+
private roomsService: RoomsService,
|
|
44
|
+
) {
|
|
41
45
|
this.redis = new Redis('redis://localhost:6379');
|
|
42
46
|
this.pubRedis = new Redis('redis://localhost:6379');
|
|
43
47
|
}
|
|
@@ -113,6 +117,12 @@ export class RoomsGateway implements OnGatewayConnection {
|
|
|
113
117
|
payload: { roomId: string; userId: string },
|
|
114
118
|
) {
|
|
115
119
|
const { roomId, userId } = payload;
|
|
120
|
+
|
|
121
|
+
const room = await this.roomsService.getRoom(roomId);
|
|
122
|
+
if (!room) {
|
|
123
|
+
throw new Error('Room not found');
|
|
124
|
+
}
|
|
125
|
+
|
|
116
126
|
let roomState = this.roomStates.get(roomId);
|
|
117
127
|
|
|
118
128
|
if (!roomState) {
|
|
@@ -142,11 +152,14 @@ export class RoomsGateway implements OnGatewayConnection {
|
|
|
142
152
|
Object.keys(roomState.connections).map(async (connId) => {
|
|
143
153
|
const connection = roomState.connections[parseInt(connId)];
|
|
144
154
|
const roomId = connection.roomId;
|
|
145
|
-
const
|
|
155
|
+
const member = await this.roomsService.getRoomMember(
|
|
156
|
+
roomId,
|
|
157
|
+
connection.userId,
|
|
158
|
+
);
|
|
146
159
|
return {
|
|
147
160
|
connectionId: parseInt(connId),
|
|
148
161
|
userId: connection.userId,
|
|
149
|
-
scopes,
|
|
162
|
+
scopes: member.scopes,
|
|
150
163
|
};
|
|
151
164
|
}),
|
|
152
165
|
);
|
|
@@ -155,7 +168,7 @@ export class RoomsGateway implements OnGatewayConnection {
|
|
|
155
168
|
type: ServerMsgCode.ROOM_STATE, // 使用 ServerMsgCode 枚举
|
|
156
169
|
actor: connectionId,
|
|
157
170
|
nonce: nonce,
|
|
158
|
-
scopes:
|
|
171
|
+
scopes: room.defaultScopes,
|
|
159
172
|
users: users.reduce(
|
|
160
173
|
(acc, user) => {
|
|
161
174
|
acc[user.connectionId] = {
|
package/src/rooms/rooms.guard.ts
CHANGED
|
@@ -6,6 +6,10 @@ import { JwtModule } from '@nestjs/jwt';
|
|
|
6
6
|
import { AuthModule } from '@builder6/core';
|
|
7
7
|
import { RoomsGateway } from './rooms.gateway';
|
|
8
8
|
import { FilesModule } from '@builder6/core';
|
|
9
|
+
import { PagesModule } from '@builder6/pages';
|
|
10
|
+
import { NotificationsService } from './notifications.service';
|
|
11
|
+
import { RoomsAppController } from './app.controller';
|
|
12
|
+
import { RoomsMoleculer } from './rooms.moleculer';
|
|
9
13
|
|
|
10
14
|
/* 按照 liveblocks.io 规范实现的API */
|
|
11
15
|
@Module({
|
|
@@ -13,12 +17,13 @@ import { FilesModule } from '@builder6/core';
|
|
|
13
17
|
AuthModule,
|
|
14
18
|
MongodbModule,
|
|
15
19
|
FilesModule,
|
|
20
|
+
PagesModule,
|
|
16
21
|
JwtModule.register({
|
|
17
22
|
secret: process.env.B6_JWT_SECRET || 'secret',
|
|
18
23
|
signOptions: { expiresIn: '60s' },
|
|
19
24
|
}),
|
|
20
25
|
],
|
|
21
|
-
controllers: [RoomsController],
|
|
22
|
-
providers: [RoomsService, RoomsGateway],
|
|
26
|
+
controllers: [RoomsAppController, RoomsController],
|
|
27
|
+
providers: [RoomsService, RoomsGateway, NotificationsService, RoomsMoleculer],
|
|
23
28
|
})
|
|
24
29
|
export class RoomsModule {}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Service, Context, ServiceBroker } from 'moleculer';
|
|
2
|
+
import { Injectable } from '@nestjs/common';
|
|
3
|
+
import { InjectBroker } from '@builder6/moleculer';
|
|
4
|
+
import { RoomsService } from './rooms.service';
|
|
5
|
+
|
|
6
|
+
@Injectable()
|
|
7
|
+
export class RoomsMoleculer extends Service {
|
|
8
|
+
constructor(
|
|
9
|
+
private readonly roomsService: RoomsService,
|
|
10
|
+
@InjectBroker() broker: ServiceBroker,
|
|
11
|
+
) {
|
|
12
|
+
super(broker);
|
|
13
|
+
|
|
14
|
+
this.parseServiceSchema({
|
|
15
|
+
name: '@builder6/rooms',
|
|
16
|
+
settings: {},
|
|
17
|
+
actions: {
|
|
18
|
+
createComment: this.createComment,
|
|
19
|
+
createThread: this.createThread,
|
|
20
|
+
},
|
|
21
|
+
created: this.serviceCreated,
|
|
22
|
+
started: this.serviceStarted,
|
|
23
|
+
stopped: this.serviceStopped,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
serviceCreated() {}
|
|
28
|
+
|
|
29
|
+
async serviceStarted() {}
|
|
30
|
+
|
|
31
|
+
async serviceStopped() {}
|
|
32
|
+
|
|
33
|
+
async createThread(ctx: Context) {
|
|
34
|
+
const { id, comment, metadata, resolved, roomId, userId } =
|
|
35
|
+
ctx.params as any;
|
|
36
|
+
try {
|
|
37
|
+
const result = this.roomsService.createThread({
|
|
38
|
+
id,
|
|
39
|
+
comment,
|
|
40
|
+
metadata,
|
|
41
|
+
resolved,
|
|
42
|
+
roomId,
|
|
43
|
+
userId,
|
|
44
|
+
});
|
|
45
|
+
return result;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error(error);
|
|
48
|
+
return {
|
|
49
|
+
error: {
|
|
50
|
+
message: error.message,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async createComment(ctx: Context) {
|
|
57
|
+
const { id, attachmentIds, body, roomId, threadId, userId } =
|
|
58
|
+
ctx.params as any;
|
|
59
|
+
try {
|
|
60
|
+
const result = this.roomsService.createComment({
|
|
61
|
+
id,
|
|
62
|
+
attachmentIds,
|
|
63
|
+
body,
|
|
64
|
+
roomId,
|
|
65
|
+
threadId,
|
|
66
|
+
userId,
|
|
67
|
+
});
|
|
68
|
+
return result;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error(error);
|
|
71
|
+
return {
|
|
72
|
+
error: {
|
|
73
|
+
message: error.message,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { MongodbService } from '@builder6/core';
|
|
2
2
|
import { JwtService } from '@nestjs/jwt';
|
|
3
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 { uniq } from 'lodash';
|
|
7
|
+
import { NotificationsService } from './notifications.service';
|
|
8
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
9
|
+
import { CommentBody, CommentBodyMention } from './protocol/Comments';
|
|
4
10
|
|
|
5
11
|
export interface CreateThreadParams {
|
|
6
12
|
id?: string;
|
|
@@ -28,17 +34,23 @@ export interface Attachment {
|
|
|
28
34
|
type: string;
|
|
29
35
|
}
|
|
30
36
|
|
|
37
|
+
export enum CommentType {
|
|
38
|
+
Paragraph = 'paragraph',
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export enum CommentBodyInlineType {
|
|
42
|
+
Mention = 'mention',
|
|
43
|
+
Link = 'link',
|
|
44
|
+
}
|
|
45
|
+
|
|
31
46
|
@Injectable()
|
|
32
47
|
export class RoomsService {
|
|
33
48
|
constructor(
|
|
34
49
|
private mongodbService: MongodbService,
|
|
35
50
|
private jwtService: JwtService,
|
|
51
|
+
private notificationsService: NotificationsService,
|
|
36
52
|
) {}
|
|
37
53
|
|
|
38
|
-
async getRoom(roomId: string) {
|
|
39
|
-
return { _id: roomId };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
54
|
async getToken({
|
|
43
55
|
spaceId,
|
|
44
56
|
userId,
|
|
@@ -54,14 +66,52 @@ export class RoomsService {
|
|
|
54
66
|
sid: spaceId,
|
|
55
67
|
uid: userId,
|
|
56
68
|
mcpr: 10,
|
|
57
|
-
perms:
|
|
69
|
+
perms: perms,
|
|
58
70
|
jti: 'S4EMiESTDe6k',
|
|
59
71
|
};
|
|
60
|
-
payload.perms = perms || {};
|
|
61
72
|
const token = await this.jwtService.signAsync(payload);
|
|
62
73
|
return { token };
|
|
63
74
|
}
|
|
64
75
|
|
|
76
|
+
async getRoomToken({
|
|
77
|
+
roomId,
|
|
78
|
+
spaceId,
|
|
79
|
+
userId,
|
|
80
|
+
readonly = false,
|
|
81
|
+
}: {
|
|
82
|
+
roomId: string;
|
|
83
|
+
spaceId: string;
|
|
84
|
+
userId: string;
|
|
85
|
+
readonly: boolean;
|
|
86
|
+
}) {
|
|
87
|
+
let room = await this.getRoom(roomId);
|
|
88
|
+
if (!room) {
|
|
89
|
+
room = await this.createRoom({
|
|
90
|
+
_id: roomId,
|
|
91
|
+
name: 'Room ' + roomId,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let member = await this.getRoomMember(roomId, userId);
|
|
96
|
+
if (!member) {
|
|
97
|
+
member = await this.createRoomMember(room, {
|
|
98
|
+
userId,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const scopes = readonly ? READ_ACCESS : member.scopes;
|
|
103
|
+
|
|
104
|
+
const perms = {
|
|
105
|
+
[roomId]: scopes,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
return await this.getToken({
|
|
109
|
+
spaceId,
|
|
110
|
+
userId,
|
|
111
|
+
perms,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
65
115
|
async getAttachmentById(attachmentId: string): Promise<Attachment> {
|
|
66
116
|
// 模拟从数据库获取附件
|
|
67
117
|
// 实际实现中应替换为数据库查询
|
|
@@ -147,6 +197,30 @@ export class RoomsService {
|
|
|
147
197
|
return threads;
|
|
148
198
|
}
|
|
149
199
|
|
|
200
|
+
async getThreadsByIds(
|
|
201
|
+
notifications: {
|
|
202
|
+
id: string;
|
|
203
|
+
userId: string;
|
|
204
|
+
kind: string;
|
|
205
|
+
notifiedAt?: Date;
|
|
206
|
+
readAt?: Date;
|
|
207
|
+
roomId: string;
|
|
208
|
+
threadId: string;
|
|
209
|
+
}[],
|
|
210
|
+
) {
|
|
211
|
+
const threadPromises = [];
|
|
212
|
+
for (let i = 0; i < notifications.length; i++) {
|
|
213
|
+
const notification = notifications[i];
|
|
214
|
+
threadPromises.push(
|
|
215
|
+
this.getThread(notification.roomId, notification.threadId),
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
if (threadPromises.length > 0) {
|
|
219
|
+
return Promise.all(threadPromises);
|
|
220
|
+
}
|
|
221
|
+
return [];
|
|
222
|
+
}
|
|
223
|
+
|
|
150
224
|
async getComments(roomId: string, threadId: string) {
|
|
151
225
|
const comments = await this.mongodbService.find('b6_comments', {
|
|
152
226
|
roomId: roomId,
|
|
@@ -245,6 +319,35 @@ export class RoomsService {
|
|
|
245
319
|
result.attachments.push(attachment);
|
|
246
320
|
}
|
|
247
321
|
|
|
322
|
+
// 如果有 @ mention 的时候,自动创建 b6_notifications 记录。
|
|
323
|
+
const { content } = body as CommentBody;
|
|
324
|
+
let notificationsPromises: Promise<any>[];
|
|
325
|
+
const mentionUsers: string[] = [];
|
|
326
|
+
for (const message of content) {
|
|
327
|
+
if (message.type === CommentType.Paragraph) {
|
|
328
|
+
message.children
|
|
329
|
+
.filter(
|
|
330
|
+
(child: CommentBodyMention) =>
|
|
331
|
+
child?.type === CommentBodyInlineType.Mention,
|
|
332
|
+
)
|
|
333
|
+
.forEach((child: CommentBodyMention) => mentionUsers.push(child.id));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (mentionUsers && mentionUsers.length > 0) {
|
|
337
|
+
notificationsPromises = uniq(mentionUsers).map((userId) => {
|
|
338
|
+
return this.notificationsService.createThreadNotification({
|
|
339
|
+
_id: uuidv4(),
|
|
340
|
+
id: uuidv4(),
|
|
341
|
+
userId,
|
|
342
|
+
kind: 'thread',
|
|
343
|
+
notifiedAt: new Date(),
|
|
344
|
+
readAt: null,
|
|
345
|
+
roomId,
|
|
346
|
+
threadId,
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
await Promise.all(notificationsPromises);
|
|
350
|
+
}
|
|
248
351
|
delete result['_id'];
|
|
249
352
|
delete result['attachmentIds'];
|
|
250
353
|
return result;
|
|
@@ -387,4 +490,105 @@ export class RoomsService {
|
|
|
387
490
|
|
|
388
491
|
return null;
|
|
389
492
|
}
|
|
493
|
+
|
|
494
|
+
async createRoom({
|
|
495
|
+
_id,
|
|
496
|
+
name,
|
|
497
|
+
description = '',
|
|
498
|
+
membershipType = 'standard',
|
|
499
|
+
members = [],
|
|
500
|
+
defaultNotifications = ['replies', 'mentions'],
|
|
501
|
+
defaultScopes = ['room:write'],
|
|
502
|
+
}: RoomDTO): Promise<RoomDTO> {
|
|
503
|
+
const newRoom = {
|
|
504
|
+
createdAt: new Date().toISOString(),
|
|
505
|
+
updatedAt: new Date().toISOString(),
|
|
506
|
+
_id,
|
|
507
|
+
name,
|
|
508
|
+
description,
|
|
509
|
+
membershipType,
|
|
510
|
+
defaultNotifications,
|
|
511
|
+
defaultScopes,
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
const room = await this.mongodbService.insertOne('b6_rooms', newRoom);
|
|
515
|
+
|
|
516
|
+
// 循环 members,执行 createRoomMember,并保存到 result.members
|
|
517
|
+
room.members = [];
|
|
518
|
+
for (const member of members) {
|
|
519
|
+
const newMember = await this.createRoomMember(room, member);
|
|
520
|
+
room.members.push(newMember);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return room;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
async createRoomMember(room: RoomDTO, member: RoomMemberDTO) {
|
|
527
|
+
const {
|
|
528
|
+
_id,
|
|
529
|
+
userId,
|
|
530
|
+
roles = [],
|
|
531
|
+
notifications = room.defaultNotifications,
|
|
532
|
+
scopes = room.defaultScopes,
|
|
533
|
+
} = member;
|
|
534
|
+
const newMember = {
|
|
535
|
+
_id,
|
|
536
|
+
userId,
|
|
537
|
+
roles,
|
|
538
|
+
roomId: room._id,
|
|
539
|
+
notifications,
|
|
540
|
+
scopes,
|
|
541
|
+
createdAt: new Date().toISOString(),
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
const result = await this.mongodbService.insertOne(
|
|
545
|
+
'b6_room_members',
|
|
546
|
+
newMember,
|
|
547
|
+
);
|
|
548
|
+
return result;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
async getRoom(roomId: string) {
|
|
552
|
+
const room = await this.mongodbService.findOne('b6_rooms', {
|
|
553
|
+
_id: roomId,
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
if (!room) {
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
room.members = await this.getRoomMembers(roomId);
|
|
561
|
+
return room;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
async getRoomMembers(roomId: string) {
|
|
565
|
+
const members = await this.mongodbService.find('b6_room_members', {
|
|
566
|
+
roomId,
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
return members;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
async getRoomMember(roomId: string, userId: string) {
|
|
573
|
+
const member = await this.mongodbService.findOne('b6_room_members', {
|
|
574
|
+
roomId,
|
|
575
|
+
userId,
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
return member;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
async getMyRooms(userId: string) {
|
|
582
|
+
const roomMembers = await this.mongodbService.find('b6_room_members', {
|
|
583
|
+
userId,
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
const rooms = [];
|
|
587
|
+
for (const roomMember of roomMembers) {
|
|
588
|
+
const room = await this.getRoom(roomMember.roomId);
|
|
589
|
+
rooms.push(room);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return rooms;
|
|
593
|
+
}
|
|
390
594
|
}
|
package/.eslintrc.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
parser: '@typescript-eslint/parser',
|
|
3
|
-
parserOptions: {
|
|
4
|
-
project: 'tsconfig.json',
|
|
5
|
-
tsconfigRootDir: __dirname,
|
|
6
|
-
sourceType: 'module',
|
|
7
|
-
},
|
|
8
|
-
plugins: ['@typescript-eslint/eslint-plugin'],
|
|
9
|
-
extends: [
|
|
10
|
-
'plugin:@typescript-eslint/recommended',
|
|
11
|
-
'plugin:prettier/recommended',
|
|
12
|
-
],
|
|
13
|
-
root: true,
|
|
14
|
-
env: {
|
|
15
|
-
node: true,
|
|
16
|
-
jest: true,
|
|
17
|
-
},
|
|
18
|
-
ignorePatterns: ['.eslintrc.js'],
|
|
19
|
-
rules: {
|
|
20
|
-
'@typescript-eslint/interface-name-prefix': 'off',
|
|
21
|
-
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
22
|
-
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
|
23
|
-
'@typescript-eslint/no-explicit-any': 'off',
|
|
24
|
-
},
|
|
25
|
-
};
|