feat: implement ContentsModule with controllers, services, and DTOs
Added a new ContentsModule to handle content creation, upload, and management. Includes controller endpoints for CRUD operations, content exploration, and tagging. Integrated caching, file processing, S3 storage, and database logic.
This commit is contained in:
178
backend/src/contents/contents.controller.ts
Normal file
178
backend/src/contents/contents.controller.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
DefaultValuePipe,
|
||||
Delete,
|
||||
Get,
|
||||
Header,
|
||||
Param,
|
||||
ParseBoolPipe,
|
||||
ParseIntPipe,
|
||||
Post,
|
||||
Query,
|
||||
Req,
|
||||
Res,
|
||||
UploadedFile,
|
||||
UseGuards,
|
||||
UseInterceptors,
|
||||
NotFoundException,
|
||||
} from "@nestjs/common";
|
||||
import { CacheInterceptor, CacheTTL } from "@nestjs/cache-manager";
|
||||
import { FileInterceptor } from "@nestjs/platform-express";
|
||||
import type { Request, Response } from "express";
|
||||
import { AuthGuard } from "../auth/guards/auth.guard";
|
||||
import type { AuthenticatedRequest } from "../common/interfaces/request.interface";
|
||||
import { ContentsService } from "./contents.service";
|
||||
import { CreateContentDto } from "./dto/create-content.dto";
|
||||
import { UploadContentDto } from "./dto/upload-content.dto";
|
||||
|
||||
@Controller("contents")
|
||||
export class ContentsController {
|
||||
constructor(private readonly contentsService: ContentsService) {}
|
||||
|
||||
@Post()
|
||||
@UseGuards(AuthGuard)
|
||||
create(
|
||||
@Req() req: AuthenticatedRequest,
|
||||
@Body() createContentDto: CreateContentDto,
|
||||
) {
|
||||
return this.contentsService.create(req.user.sub, createContentDto);
|
||||
}
|
||||
|
||||
@Post("upload-url")
|
||||
@UseGuards(AuthGuard)
|
||||
getUploadUrl(
|
||||
@Req() req: AuthenticatedRequest,
|
||||
@Query("fileName") fileName: string,
|
||||
) {
|
||||
return this.contentsService.getUploadUrl(req.user.sub, fileName);
|
||||
}
|
||||
|
||||
@Post("upload")
|
||||
@UseGuards(AuthGuard)
|
||||
@UseInterceptors(FileInterceptor("file"))
|
||||
upload(
|
||||
@Req() req: AuthenticatedRequest,
|
||||
@UploadedFile()
|
||||
file: Express.Multer.File,
|
||||
@Body() uploadContentDto: UploadContentDto,
|
||||
) {
|
||||
return this.contentsService.uploadAndProcess(
|
||||
req.user.sub,
|
||||
file,
|
||||
uploadContentDto,
|
||||
);
|
||||
}
|
||||
|
||||
@Get("explore")
|
||||
@UseInterceptors(CacheInterceptor)
|
||||
@CacheTTL(60)
|
||||
@Header("Cache-Control", "public, max-age=60")
|
||||
explore(
|
||||
@Query("limit", new DefaultValuePipe(10), ParseIntPipe) limit: number,
|
||||
@Query("offset", new DefaultValuePipe(0), ParseIntPipe) offset: number,
|
||||
@Query("sort") sort?: "trend" | "recent",
|
||||
@Query("tag") tag?: string,
|
||||
@Query("category") category?: string,
|
||||
@Query("author") author?: string,
|
||||
@Query("query") query?: string,
|
||||
@Query("favoritesOnly", new DefaultValuePipe(false), ParseBoolPipe)
|
||||
favoritesOnly?: boolean,
|
||||
@Query("userId") userId?: string,
|
||||
) {
|
||||
return this.contentsService.findAll({
|
||||
limit,
|
||||
offset,
|
||||
sortBy: sort,
|
||||
tag,
|
||||
category,
|
||||
author,
|
||||
query,
|
||||
favoritesOnly,
|
||||
userId,
|
||||
});
|
||||
}
|
||||
|
||||
@Get("trends")
|
||||
@UseInterceptors(CacheInterceptor)
|
||||
@CacheTTL(300)
|
||||
@Header("Cache-Control", "public, max-age=300")
|
||||
trends(
|
||||
@Query("limit", new DefaultValuePipe(10), ParseIntPipe) limit: number,
|
||||
@Query("offset", new DefaultValuePipe(0), ParseIntPipe) offset: number,
|
||||
) {
|
||||
return this.contentsService.findAll({ limit, offset, sortBy: "trend" });
|
||||
}
|
||||
|
||||
@Get("recent")
|
||||
@UseInterceptors(CacheInterceptor)
|
||||
@CacheTTL(60)
|
||||
@Header("Cache-Control", "public, max-age=60")
|
||||
recent(
|
||||
@Query("limit", new DefaultValuePipe(10), ParseIntPipe) limit: number,
|
||||
@Query("offset", new DefaultValuePipe(0), ParseIntPipe) offset: number,
|
||||
) {
|
||||
return this.contentsService.findAll({ limit, offset, sortBy: "recent" });
|
||||
}
|
||||
|
||||
@Get(":idOrSlug")
|
||||
@UseInterceptors(CacheInterceptor)
|
||||
@CacheTTL(3600)
|
||||
@Header("Cache-Control", "public, max-age=3600")
|
||||
async findOne(
|
||||
@Param("idOrSlug") idOrSlug: string,
|
||||
@Req() req: Request,
|
||||
@Res() res: Response,
|
||||
) {
|
||||
const content = await this.contentsService.findOne(idOrSlug);
|
||||
if (!content) {
|
||||
throw new NotFoundException("Contenu non trouvé");
|
||||
}
|
||||
|
||||
const userAgent = req.headers["user-agent"] || "";
|
||||
const isBot = /bot|googlebot|crawler|spider|robot|crawling|facebookexternalhit|twitterbot/i.test(
|
||||
userAgent,
|
||||
);
|
||||
|
||||
if (isBot) {
|
||||
const imageUrl = this.contentsService.getFileUrl(content.storageKey);
|
||||
const html = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>${content.title}</title>
|
||||
<meta property="og:title" content="${content.title}" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:image" content="${imageUrl}" />
|
||||
<meta property="og:description" content="Découvrez ce meme sur Memegoat" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="${content.title}" />
|
||||
<meta name="twitter:image" content="${imageUrl}" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>${content.title}</h1>
|
||||
<img src="${imageUrl}" alt="${content.title}" />
|
||||
</body>
|
||||
</html>`;
|
||||
return res.send(html);
|
||||
}
|
||||
|
||||
return res.json(content);
|
||||
}
|
||||
|
||||
@Post(":id/view")
|
||||
incrementViews(@Param("id") id: string) {
|
||||
return this.contentsService.incrementViews(id);
|
||||
}
|
||||
|
||||
@Post(":id/use")
|
||||
incrementUsage(@Param("id") id: string) {
|
||||
return this.contentsService.incrementUsage(id);
|
||||
}
|
||||
|
||||
@Delete(":id")
|
||||
@UseGuards(AuthGuard)
|
||||
remove(@Param("id") id: string, @Req() req: AuthenticatedRequest) {
|
||||
return this.contentsService.remove(id, req.user.sub);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user