import { Controller, Get, Post, Body, Param, UseGuards, Request, Query, Inject, } from '@nestjs/common'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { Cache } from 'cache-manager'; import { PrismaService } from '../prisma/prisma.service'; import { JwtAuthGuard } from '../auth'; import { CreateCollectionDto, AddArticleDto, CreateQuickNoteDto } from './dto'; interface RequestWithUser extends Request { user: { id: string; username: string; }; } @Controller('api/collections') @UseGuards(JwtAuthGuard) export class CollectionsController { constructor( private readonly prisma: PrismaService, @Inject(CACHE_MANAGER) private cacheManager: Cache, ) {} @Get() async getAllCollections( @Request() req: RequestWithUser, @Query('limit') limit?: number, @Query('cursor') cursor?: string, ) { const cacheKey = `collections:${req.user.id}:${limit}:${cursor}`; const cached = await this.cacheManager.get(cacheKey); if (cached) { return cached; } const take = limit ? Number(limit) : 20; const collections = await this.prisma.collection.findMany({ where: { userId: req.user.id, }, take: take + 1, skip: cursor ? 1 : 0, cursor: cursor ? { id: cursor } : undefined, orderBy: [{ updatedAt: 'desc' }, { id: 'desc' }], include: { articles: true, quickNotes: true, notes: true, }, }); let nextCursor: string | null = null; if (collections.length > take) { const nextItem = collections.pop()!; nextCursor = nextItem.id; } const result = { collections, nextCursor, }; await this.cacheManager.set(cacheKey, result, 300); // Cache for 5 minutes return result; } @Post() async createCollection( @Body() data: CreateCollectionDto, @Request() req: RequestWithUser, ) { const collection = await this.prisma.collection.create({ data: { ...data, userId: req.user.id, }, include: { articles: true, quickNotes: true, notes: true, }, }); // Invalidate user's collections cache const cachePattern = `collections:${req.user.id}:*`; await this.cacheManager.del(cachePattern); return collection; } @Post(':id/add-article') async addArticleToCollection( @Param('id') id: string, @Body() articleData: AddArticleDto, @Request() req: RequestWithUser, ) { const existingArticle = await this.prisma.article.findUnique({ where: { url: articleData.url }, }); if (existingArticle) { await this.prisma.collection.update({ where: { id }, data: { articles: { connect: { id: existingArticle.id }, }, }, }); // Invalidate cache for this collection const cachePattern = `collections:${req.user.id}:*`; await this.cacheManager.del(cachePattern); return existingArticle; } const article = await this.prisma.article.create({ data: { ...articleData, collections: { connect: { id }, }, }, }); // Invalidate cache for this collection const cachePattern = `collections:${req.user.id}:*`; await this.cacheManager.del(cachePattern); return article; } @Post(':id/quick-notes') async addQuickNoteToCollection( @Param('id') id: string, @Body() quickNoteData: CreateQuickNoteDto, @Request() req: RequestWithUser, ) { const quickNote = await this.prisma.quickNote.create({ data: { ...quickNoteData, shared: quickNoteData.shared ?? false, collection: { connect: { id }, }, user: { connect: { id: req.user.id }, }, }, }); // Invalidate cache for this collection const cachePattern = `collections:${req.user.id}:*`; await this.cacheManager.del(cachePattern); return quickNote; } }