diff --git a/backend/src/favorites/favorites.controller.ts b/backend/src/favorites/favorites.controller.ts new file mode 100644 index 0000000..9a074fb --- /dev/null +++ b/backend/src/favorites/favorites.controller.ts @@ -0,0 +1,43 @@ +import { + Controller, + DefaultValuePipe, + Delete, + Get, + Param, + ParseIntPipe, + Post, + Query, + Req, + UseGuards, +} from "@nestjs/common"; +import { AuthGuard } from "../auth/guards/auth.guard"; +import type { AuthenticatedRequest } from "../common/interfaces/request.interface"; +import { FavoritesService } from "./favorites.service"; + +@UseGuards(AuthGuard) +@Controller("favorites") +export class FavoritesController { + constructor(private readonly favoritesService: FavoritesService) {} + + @Post(":contentId") + add(@Req() req: AuthenticatedRequest, @Param("contentId") contentId: string) { + return this.favoritesService.addFavorite(req.user.sub, contentId); + } + + @Delete(":contentId") + remove( + @Req() req: AuthenticatedRequest, + @Param("contentId") contentId: string, + ) { + return this.favoritesService.removeFavorite(req.user.sub, contentId); + } + + @Get() + list( + @Req() req: AuthenticatedRequest, + @Query("limit", new DefaultValuePipe(10), ParseIntPipe) limit: number, + @Query("offset", new DefaultValuePipe(0), ParseIntPipe) offset: number, + ) { + return this.favoritesService.getUserFavorites(req.user.sub, limit, offset); + } +} diff --git a/backend/src/favorites/favorites.module.ts b/backend/src/favorites/favorites.module.ts new file mode 100644 index 0000000..e6cc606 --- /dev/null +++ b/backend/src/favorites/favorites.module.ts @@ -0,0 +1,12 @@ +import { Module } from "@nestjs/common"; +import { DatabaseModule } from "../database/database.module"; +import { FavoritesController } from "./favorites.controller"; +import { FavoritesService } from "./favorites.service"; + +@Module({ + imports: [DatabaseModule], + controllers: [FavoritesController], + providers: [FavoritesService], + exports: [FavoritesService], +}) +export class FavoritesModule {} diff --git a/backend/src/favorites/favorites.service.ts b/backend/src/favorites/favorites.service.ts new file mode 100644 index 0000000..ab9fe2b --- /dev/null +++ b/backend/src/favorites/favorites.service.ts @@ -0,0 +1,63 @@ +import { + ConflictException, + Injectable, + NotFoundException, +} from "@nestjs/common"; +import { and, eq } from "drizzle-orm"; +import { DatabaseService } from "../database/database.service"; +import { contents, favorites } from "../database/schemas"; + +@Injectable() +export class FavoritesService { + constructor(private readonly databaseService: DatabaseService) {} + + async addFavorite(userId: string, contentId: string) { + // Vérifier si le contenu existe + const content = await this.databaseService.db + .select() + .from(contents) + .where(eq(contents.id, contentId)) + .limit(1); + + if (content.length === 0) { + throw new NotFoundException("Content not found"); + } + + try { + return await this.databaseService.db + .insert(favorites) + .values({ userId, contentId }) + .returning(); + } catch (_error) { + // Probablement une violation de clé primaire (déjà en favori) + throw new ConflictException("Content already in favorites"); + } + } + + async removeFavorite(userId: string, contentId: string) { + const result = await this.databaseService.db + .delete(favorites) + .where(and(eq(favorites.userId, userId), eq(favorites.contentId, contentId))) + .returning(); + + if (result.length === 0) { + throw new NotFoundException("Favorite not found"); + } + + return result[0]; + } + + async getUserFavorites(userId: string, limit: number, offset: number) { + const data = await this.databaseService.db + .select({ + content: contents, + }) + .from(favorites) + .innerJoin(contents, eq(favorites.contentId, contents.id)) + .where(eq(favorites.userId, userId)) + .limit(limit) + .offset(offset); + + return data.map((item) => item.content); + } +}