import { Body, Controller, Delete, Get, Param, Patch, Post, Query, StreamableFile, UploadedFile, UseGuards, UseInterceptors, } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import { memoryStorage } from 'multer'; import { JwtAuthGuard } from '../auth/jwt-auth.guard'; import { CurrentUser } from '../auth/current-user.decorator'; import { CreateNoteDto, ListNotesQueryDto, UpdateNoteDto, ApplyNoteAiSuggestionDto } from './dto'; import { NoteAiService } from './note-ai.service'; import { NotesService } from './notes.service'; type AuthUser = { organizationId: string; userId: string; role: string }; @Controller('notes') @UseGuards(JwtAuthGuard) export class NotesController { constructor( private readonly notesService: NotesService, private readonly noteAi: NoteAiService, ) {} @Get() list( @CurrentUser() user: AuthUser, @Query() query: ListNotesQueryDto, ) { return this.notesService.list(user.organizationId, user, { q: query.q, pinned: query.pinned, personId: query.personId, companyId: query.companyId, dealId: query.dealId, tag: query.tag, noteType: query.noteType, sort: query.sort, page: query.page ?? 1, limit: query.limit ?? 50, }); } @Get(':id') get(@CurrentUser() user: AuthUser, @Param('id') id: string) { return this.notesService.get(user.organizationId, id, user); } @Post() create(@CurrentUser() user: AuthUser, @Body() dto: CreateNoteDto) { return this.notesService.create( user.organizationId, user.userId, dto, user, ); } @Patch(':id') update( @CurrentUser() user: AuthUser, @Param('id') id: string, @Body() dto: UpdateNoteDto, ) { return this.notesService.update(user.organizationId, id, user, dto); } @Patch(':id/pin') togglePin(@CurrentUser() user: AuthUser, @Param('id') id: string) { return this.notesService.togglePin(user.organizationId, id, user); } @Post(':id/convert-to-task') convertToTask(@CurrentUser() user: AuthUser, @Param('id') id: string) { return this.notesService.convertToTask( user.organizationId, user.userId, id, user, ); } @Post(':id/analyze') analyze(@CurrentUser() user: AuthUser, @Param('id') id: string) { return this.noteAi.analyze(user.organizationId, id, user); } @Get(':id/ai-suggestion') getAiSuggestion(@CurrentUser() user: AuthUser, @Param('id') id: string) { return this.noteAi.getSuggestion(user.organizationId, id, user); } @Post(':id/ai-suggestion/apply') applyAiSuggestion( @CurrentUser() user: AuthUser, @Param('id') id: string, @Body() dto: ApplyNoteAiSuggestionDto, ) { return this.noteAi.apply(user.organizationId, user.userId, id, user, dto); } @Post(':id/ai-suggestion/dismiss') dismissAiSuggestion(@CurrentUser() user: AuthUser, @Param('id') id: string) { return this.noteAi.dismiss(user.organizationId, id, user); } @Post(':id/attachments') @UseInterceptors( FileInterceptor('file', { storage: memoryStorage(), limits: { fileSize: 10 * 1024 * 1024 }, }), ) uploadAttachment( @CurrentUser() user: AuthUser, @Param('id') id: string, @UploadedFile() file: Express.Multer.File, ) { return this.notesService.addAttachment( user.organizationId, user.userId, id, file, user, ); } @Get(':id/attachments/:attachmentId/download') async downloadAttachment( @CurrentUser() user: AuthUser, @Param('id') id: string, @Param('attachmentId') attachmentId: string, ): Promise { const { stream, fileName, mimeType } = await this.notesService.openAttachmentDownload( user.organizationId, id, attachmentId, user, ); const encoded = encodeURIComponent(fileName); return new StreamableFile(stream, { type: mimeType, disposition: `attachment; filename="${fileName.replace(/"/g, '')}"; filename*=UTF-8''${encoded}`, }); } @Delete(':id/attachments/:attachmentId') async removeAttachment( @CurrentUser() user: AuthUser, @Param('id') id: string, @Param('attachmentId') attachmentId: string, ) { await this.notesService.removeAttachment( user.organizationId, id, attachmentId, user, ); return { ok: true }; } @Delete(':id') async remove(@CurrentUser() user: AuthUser, @Param('id') id: string) { await this.notesService.remove(user.organizationId, id, user); return { ok: true }; } }