Skip to content

Commit f4b9015

Browse files
authored
Merge pull request #184 from import-ai/feat/chat_share
feat(share): add chat
2 parents 5ed4f0c + c57a82e commit f4b9015

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1063
-343
lines changed

src/app/app.module.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ import { FeedbackModule } from 'omniboxd/feedback/feedback.module';
5454
import { Feedback1757100000000 } from 'omniboxd/migrations/1757100000000-feedback';
5555
import { UserInterceptor } from 'omniboxd/interceptor/user.interceptor';
5656
import { WebSocketModule } from 'omniboxd/websocket/websocket.module';
57+
import { NullableUserId1757844448000 } from 'omniboxd/migrations/1757844448000-nullable-user-id';
58+
import { AddShareIdToConversations1757844449000 } from 'omniboxd/migrations/1757844449000-add-share-id-to-conversations';
59+
import { ShareUser1760171824000 } from 'omniboxd/migrations/1760171824000-share-user';
5760

5861
@Module({})
5962
export class AppModule implements NestModule {
@@ -148,6 +151,9 @@ export class AppModule implements NestModule {
148151
SharesAllResources1754471311959,
149152
Applications1756914379375,
150153
Feedback1757100000000,
154+
NullableUserId1757844448000,
155+
AddShareIdToConversations1757844449000,
156+
ShareUser1760171824000,
151157
...extraMigrations,
152158
],
153159
migrationsRun: true,

src/attachments/attachments.service.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
} from './dto/upload-attachments-response.dto';
1616
import { SharesService } from 'omniboxd/shares/shares.service';
1717
import { SharedResourcesService } from 'omniboxd/shared-resources/shared-resources.service';
18+
import { Share } from 'omniboxd/shares/entities/share.entity';
1819

1920
@Injectable()
2021
export class AttachmentsService {
@@ -181,18 +182,11 @@ export class AttachmentsService {
181182
}
182183

183184
async downloadAttachmentViaShare(
184-
shareId: string,
185+
share: Share,
185186
resourceId: string,
186187
attachmentId: string,
187-
password: string,
188-
userId: string | undefined,
189188
httpResponse: Response,
190189
) {
191-
const share = await this.sharesService.getAndValidateShare(
192-
shareId,
193-
password,
194-
userId,
195-
);
196190
await this.sharedResourcesService.getAndValidateResource(share, resourceId);
197191
await this.resourceAttachmentsService.getResourceAttachmentOrFail(
198192
share.namespaceId,

src/attachments/share-attachments.controller.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,32 @@
1-
import { Controller, Get, Param, Res } from '@nestjs/common';
1+
import { Controller, Get, Param, Res, UseInterceptors } from '@nestjs/common';
22
import { Response } from 'express';
33
import { AttachmentsService } from './attachments.service';
4-
import { UserId } from 'omniboxd/decorators/user-id.decorator';
54
import { CookieAuth } from 'omniboxd/auth/decorators';
6-
import { Cookies } from 'omniboxd/decorators/cookie.decorators';
5+
import {
6+
ValidateShare,
7+
ValidatedShare,
8+
} from 'omniboxd/decorators/validate-share.decorator';
9+
import { ValidateShareInterceptor } from 'omniboxd/interceptor/validate-share.interceptor';
10+
import { Share } from 'omniboxd/shares/entities/share.entity';
711

812
@Controller('api/v1/shares/:shareId/resources/:resourceId/attachments')
13+
@UseInterceptors(ValidateShareInterceptor)
914
export class ShareAttachmentsController {
1015
constructor(private readonly attachmentsService: AttachmentsService) {}
1116

1217
@CookieAuth({ onAuthFail: 'continue' })
18+
@ValidateShare()
1319
@Get(':attachmentId')
1420
async downloadAttachment(
15-
@Param('shareId') shareId: string,
1621
@Param('resourceId') resourceId: string,
1722
@Param('attachmentId') attachmentId: string,
18-
@Cookies('share-password') password: string,
23+
@ValidatedShare() share: Share,
1924
@Res() res: Response,
20-
@UserId({ optional: true }) userId?: string,
2125
) {
2226
return await this.attachmentsService.downloadAttachmentViaShare(
23-
shareId,
27+
share,
2428
resourceId,
2529
attachmentId,
26-
password,
27-
userId,
2830
res,
2931
);
3032
}

src/auth/decorators/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './public.auth.decorator';
2+
export * from './ws-auth-options.decorator';
23
export * from '../api-key/api-key.auth.decorator';
34
export * from '../api-key/api-key.decorator';
45
export * from '../cookie/cookie.auth.decorator';
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { SetMetadata } from '@nestjs/common';
2+
3+
export interface WsAuthConfig {
4+
optional?: boolean;
5+
}
6+
7+
export const WS_AUTH_CONFIG_KEY = 'wsAuthConfig';
8+
9+
export const WsAuthOptions = (config: WsAuthConfig = {}) =>
10+
SetMetadata(WS_AUTH_CONFIG_KEY, config);

src/conversations/conversations.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export class ConversationsController {
5858
@Param('id') id: string,
5959
@Req() req,
6060
): Promise<ConversationDetailDto> {
61-
return await this.conversationsService.getDetail(id, req.user);
61+
return await this.conversationsService.getConversationForUser(id, req.user);
6262
}
6363

6464
@Post(':id/title')

src/conversations/conversations.module.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,22 @@ import { TypeOrmModule } from '@nestjs/typeorm';
33
import { Conversation } from 'omniboxd/conversations/entities/conversation.entity';
44
import { ConversationsService } from 'omniboxd/conversations/conversations.service';
55
import { ConversationsController } from 'omniboxd/conversations/conversations.controller';
6+
import { SharedConversationsController } from 'omniboxd/conversations/shared-conversations.controller';
67
import { MessagesModule } from '../messages/messages.module';
78
import { UserModule } from '../user/user.module';
89
import { TasksModule } from 'omniboxd/tasks/tasks.module';
10+
import { SharesModule } from 'omniboxd/shares/shares.module';
911

1012
@Module({
1113
imports: [
1214
MessagesModule,
1315
UserModule,
1416
TasksModule,
17+
SharesModule,
1518
TypeOrmModule.forFeature([Conversation]),
1619
],
1720
providers: [ConversationsService],
18-
controllers: [ConversationsController],
21+
controllers: [ConversationsController, SharedConversationsController],
1922
exports: [ConversationsService],
2023
})
2124
export class ConversationsModule {}

src/conversations/conversations.service.ts

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
} from 'omniboxd/messages/entities/message.entity';
1919
import { WizardTaskService } from 'omniboxd/tasks/wizard-task.service';
2020
import { Task } from 'omniboxd/tasks/tasks.entity';
21+
import { Share } from 'omniboxd/shares/entities/share.entity';
2122

2223
const TASK_PRIORITY = 5;
2324

@@ -50,6 +51,16 @@ export class ConversationsService {
5051
return await this.conversationRepository.save(conversation);
5152
}
5253

54+
async createConversationForShare(share: Share) {
55+
const conversation = this.conversationRepository.create({
56+
namespaceId: share.namespaceId,
57+
userId: null,
58+
title: '',
59+
shareId: share.id,
60+
});
61+
return await this.conversationRepository.save(conversation);
62+
}
63+
5364
async update(id: string, title: string) {
5465
const conversation = await this.findOne(id);
5566
conversation.title = title;
@@ -192,22 +203,17 @@ export class ConversationsService {
192203
};
193204
}
194205

195-
async getDetail(id: string, user: User): Promise<ConversationDetailDto> {
196-
const conversation = await this.conversationRepository.findOneOrFail({
197-
where: { id, userId: user.id },
198-
});
199-
206+
private convertToConversationDetail(
207+
conversation: Conversation,
208+
messages: Message[],
209+
): ConversationDetailDto {
200210
const detail: ConversationDetailDto = {
201211
id: conversation.id,
202212
title: conversation.title,
203213
created_at: conversation.createdAt.toISOString(),
204214
updated_at: conversation.updatedAt?.toISOString(),
205215
mapping: {},
206216
};
207-
const messages = await this.messagesService.findAll(
208-
user.id,
209-
conversation.id,
210-
);
211217
if (messages.length === 0) {
212218
return detail;
213219
}
@@ -251,6 +257,34 @@ export class ConversationsService {
251257
return detail;
252258
}
253259

260+
async getConversationForUser(
261+
conversationId: string,
262+
user: User,
263+
): Promise<ConversationDetailDto> {
264+
const conversation = await this.conversationRepository.findOneOrFail({
265+
where: { id: conversationId, userId: user.id },
266+
});
267+
const messages = await this.messagesService.findAll(
268+
user.id,
269+
conversation.id,
270+
);
271+
return this.convertToConversationDetail(conversation, messages);
272+
}
273+
274+
async getConversationForShare(
275+
conversationId: string,
276+
share: Share,
277+
): Promise<ConversationDetailDto> {
278+
const conversation = await this.conversationRepository.findOneOrFail({
279+
where: { id: conversationId, shareId: share.id },
280+
});
281+
const messages = await this.messagesService.findAll(
282+
undefined,
283+
conversation.id,
284+
);
285+
return this.convertToConversationDetail(conversation, messages);
286+
}
287+
254288
async findOne(id: string) {
255289
return await this.conversationRepository.findOneOrFail({
256290
where: { id },

src/conversations/entities/conversation.entity.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ export class Conversation extends Base {
99
@Column()
1010
namespaceId: string;
1111

12-
@Column()
13-
userId: string;
12+
@Column('varchar', { nullable: true })
13+
userId: string | null;
1414

1515
@Column()
1616
title: string;
17+
18+
@Column('varchar', { nullable: true })
19+
shareId: string | null;
1720
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Controller, Get, Param, Post, UseInterceptors } from '@nestjs/common';
2+
import { ConversationsService } from './conversations.service';
3+
import { CookieAuth } from 'omniboxd/auth/decorators';
4+
import {
5+
ValidateShare,
6+
ValidatedShare,
7+
} from 'omniboxd/decorators/validate-share.decorator';
8+
import { ValidateShareInterceptor } from 'omniboxd/interceptor/validate-share.interceptor';
9+
import { Share } from 'omniboxd/shares/entities/share.entity';
10+
11+
@Controller('api/v1/shares/:shareId/conversations')
12+
@UseInterceptors(ValidateShareInterceptor)
13+
export class SharedConversationsController {
14+
constructor(private readonly conversationsService: ConversationsService) {}
15+
16+
@CookieAuth({ onAuthFail: 'continue' })
17+
@ValidateShare({ requireChat: true })
18+
@Post()
19+
async createConversationForShare(@ValidatedShare() share: Share) {
20+
return await this.conversationsService.createConversationForShare(share);
21+
}
22+
23+
@CookieAuth({ onAuthFail: 'continue' })
24+
@ValidateShare({ requireChat: true })
25+
@Get(':conversationId')
26+
async getSharedConversation(
27+
@Param('conversationId') conversationId: string,
28+
@ValidatedShare() share: Share,
29+
) {
30+
return await this.conversationsService.getConversationForShare(
31+
conversationId,
32+
share,
33+
);
34+
}
35+
}

0 commit comments

Comments
 (0)