feat: add FavoritesModule with service, controller, and CRUD endpoints

Implemented FavoritesModule to manage user favorites. Includes service methods for adding, removing, and listing favorites, along with appropriate database integrations and API endpoints.
This commit is contained in:
Mathis HERRIOT
2026-01-08 15:26:05 +01:00
parent 912394477b
commit 92ea36545a
3 changed files with 118 additions and 0 deletions

View File

@@ -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);
}
}

View File

@@ -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 {}

View File

@@ -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);
}
}