diff --git a/backend/Dockerfile b/backend/Dockerfile index bf25c92..359d907 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,16 +1,26 @@ -FROM pnpm/pnpm:22 AS base +FROM node:22-slim AS base ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" +RUN corepack enable && corepack prepare pnpm@latest --activate FROM base AS build WORKDIR /usr/src/app +COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./ +COPY backend/package.json ./backend/ +COPY frontend/package.json ./frontend/ +COPY documentation/package.json ./documentation/ +RUN pnpm install --no-frozen-lockfile COPY . . -RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile +# On réinstalle après COPY pour s'assurer que tous les scripts de cycle de vie et les liens sont corrects +RUN pnpm install --no-frozen-lockfile RUN pnpm run --filter @memegoat/backend build -RUN pnpm deploy --filter=@memegoat/backend --prod /app +RUN pnpm deploy --filter=@memegoat/backend --prod --legacy /app +RUN cp -r backend/dist /app/dist +RUN cp -r backend/.migrations /app/.migrations FROM base AS runtime WORKDIR /app COPY --from=build /app . EXPOSE 3000 -CMD [ "node", "dist/main" ] +ENV NODE_ENV=production +CMD [ "node", "dist/src/main" ] diff --git a/backend/package.json b/backend/package.json index 7a7996f..3e07cc6 100644 --- a/backend/package.json +++ b/backend/package.json @@ -6,7 +6,9 @@ "private": true, "license": "UNLICENSED", "files": [ - "dist" + "dist", + ".migrations", + "drizzle.config.ts" ], "scripts": { "build": "nest build", @@ -60,24 +62,11 @@ "rxjs": "^7.8.1", "sharp": "^0.34.5", "uuid": "^13.0.0", - "zod": "^4.3.5" + "zod": "^4.3.5", + "drizzle-kit": "^0.31.8" }, "devDependencies": { "@nestjs/cli": "^11.0.0", - "@nestjs/schematics": "^11.0.0", - "@nestjs/testing": "^11.0.1", - "@types/express": "^5.0.0", - "@types/fluent-ffmpeg": "^2.1.28", - "@types/jest": "^30.0.0", - "@types/multer": "^2.0.0", - "@types/node": "^22.10.7", - "@types/nodemailer": "^7.0.4", - "@types/pg": "^8.16.0", - "@types/qrcode": "^1.5.6", - "@types/sharp": "^0.32.0", - "@types/supertest": "^6.0.2", - "@types/uuid": "^11.0.0", - "drizzle-kit": "^0.31.8", "globals": "^16.0.0", "jest": "^30.0.0", "source-map-support": "^0.5.21", @@ -87,6 +76,12 @@ "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", "tsx": "^4.21.0", + "@types/express": "^5.0.0", + "@types/multer": "^1.4.12", + "@types/jest": "^29.5.14", + "@types/node": "^22.10.7", + "@types/pg": "^8.11.10", + "@types/supertest": "^6.0.2", "typescript": "^5.7.3", "typescript-eslint": "^8.20.0" }, diff --git a/backend/src/api-keys/api-keys.module.ts b/backend/src/api-keys/api-keys.module.ts index 9f49ed9..b3f644a 100644 --- a/backend/src/api-keys/api-keys.module.ts +++ b/backend/src/api-keys/api-keys.module.ts @@ -1,4 +1,4 @@ -import { Module } from "@nestjs/common"; +import { forwardRef, Module } from "@nestjs/common"; import { AuthModule } from "../auth/auth.module"; import { CryptoModule } from "../crypto/crypto.module"; import { DatabaseModule } from "../database/database.module"; @@ -7,9 +7,9 @@ import { ApiKeysService } from "./api-keys.service"; import { ApiKeysRepository } from "./repositories/api-keys.repository"; @Module({ - imports: [DatabaseModule, AuthModule, CryptoModule], + imports: [DatabaseModule, forwardRef(() => AuthModule), CryptoModule], controllers: [ApiKeysController], providers: [ApiKeysService, ApiKeysRepository], - exports: [ApiKeysService], + exports: [ApiKeysService, ApiKeysRepository], }) export class ApiKeysModule {} diff --git a/backend/src/auth/auth.module.ts b/backend/src/auth/auth.module.ts index 5d44d45..303727c 100644 --- a/backend/src/auth/auth.module.ts +++ b/backend/src/auth/auth.module.ts @@ -17,6 +17,6 @@ import { RbacRepository } from "./repositories/rbac.repository"; ], controllers: [AuthController], providers: [AuthService, RbacService, RbacRepository], - exports: [AuthService, RbacService], + exports: [AuthService, RbacService, RbacRepository], }) export class AuthModule {} diff --git a/backend/src/auth/dto/register.dto.ts b/backend/src/auth/dto/register.dto.ts index 51c371b..2dbc5ec 100644 --- a/backend/src/auth/dto/register.dto.ts +++ b/backend/src/auth/dto/register.dto.ts @@ -1,10 +1,21 @@ -import { IsEmail, IsNotEmpty, IsString, MinLength } from "class-validator"; +import { + IsEmail, + IsNotEmpty, + IsString, + MaxLength, + MinLength, +} from "class-validator"; export class RegisterDto { @IsString() @IsNotEmpty() + @MaxLength(32) username!: string; + @IsString() + @MaxLength(32) + displayName?: string; + @IsEmail() email!: string; diff --git a/backend/src/categories/categories.module.ts b/backend/src/categories/categories.module.ts index adf89be..e66aa18 100644 --- a/backend/src/categories/categories.module.ts +++ b/backend/src/categories/categories.module.ts @@ -1,13 +1,15 @@ import { Module } from "@nestjs/common"; +import { AuthModule } from "../auth/auth.module"; +import { CryptoModule } from "../crypto/crypto.module"; import { DatabaseModule } from "../database/database.module"; import { CategoriesController } from "./categories.controller"; import { CategoriesService } from "./categories.service"; import { CategoriesRepository } from "./repositories/categories.repository"; @Module({ - imports: [DatabaseModule], + imports: [DatabaseModule, AuthModule, CryptoModule], controllers: [CategoriesController], providers: [CategoriesService, CategoriesRepository], - exports: [CategoriesService], + exports: [CategoriesService, CategoriesRepository], }) export class CategoriesModule {} diff --git a/backend/src/common/common.module.ts b/backend/src/common/common.module.ts index 83f544c..279b7c4 100644 --- a/backend/src/common/common.module.ts +++ b/backend/src/common/common.module.ts @@ -1,10 +1,20 @@ -import { Global, Module } from "@nestjs/common"; +import { forwardRef, Global, Module } from "@nestjs/common"; +import { ContentsModule } from "../contents/contents.module"; import { DatabaseModule } from "../database/database.module"; +import { ReportsModule } from "../reports/reports.module"; +import { SessionsModule } from "../sessions/sessions.module"; +import { UsersModule } from "../users/users.module"; import { PurgeService } from "./services/purge.service"; @Global() @Module({ - imports: [DatabaseModule], + imports: [ + DatabaseModule, + forwardRef(() => SessionsModule), + forwardRef(() => ReportsModule), + forwardRef(() => UsersModule), + forwardRef(() => ContentsModule), + ], providers: [PurgeService], exports: [PurgeService], }) diff --git a/backend/src/contents/contents.module.ts b/backend/src/contents/contents.module.ts index 971cc58..bf08949 100644 --- a/backend/src/contents/contents.module.ts +++ b/backend/src/contents/contents.module.ts @@ -12,5 +12,6 @@ import { ContentsRepository } from "./repositories/contents.repository"; imports: [DatabaseModule, S3Module, AuthModule, CryptoModule, MediaModule], controllers: [ContentsController], providers: [ContentsService, ContentsRepository], + exports: [ContentsRepository], }) export class ContentsModule {} diff --git a/backend/src/favorites/favorites.module.ts b/backend/src/favorites/favorites.module.ts index cd58de9..72d1943 100644 --- a/backend/src/favorites/favorites.module.ts +++ b/backend/src/favorites/favorites.module.ts @@ -1,13 +1,15 @@ import { Module } from "@nestjs/common"; +import { AuthModule } from "../auth/auth.module"; +import { CryptoModule } from "../crypto/crypto.module"; import { DatabaseModule } from "../database/database.module"; import { FavoritesController } from "./favorites.controller"; import { FavoritesService } from "./favorites.service"; import { FavoritesRepository } from "./repositories/favorites.repository"; @Module({ - imports: [DatabaseModule], + imports: [DatabaseModule, AuthModule, CryptoModule], controllers: [FavoritesController], providers: [FavoritesService, FavoritesRepository], - exports: [FavoritesService], + exports: [FavoritesService, FavoritesRepository], }) export class FavoritesModule {} diff --git a/backend/src/main.ts b/backend/src/main.ts index 5eb3a59..3e892ce 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -24,14 +24,31 @@ async function bootstrap() { } // Sécurité - app.use(helmet()); - app.enableCors({ - origin: - configService.get("NODE_ENV") === "production" - ? [configService.get("DOMAIN_NAME") as string] - : true, - credentials: true, - }); + app.use( + helmet({ + crossOriginResourcePolicy: { policy: "cross-origin" }, + }), + ); + const corsEnabled = Boolean(configService.get("ENABLE_CORS")); + if (corsEnabled) { + const domainName = configService.get("CORS_DOMAIN_NAME"); + app.enableCors({ + origin: (origin, callback) => { + if (!origin || domainName === "*") { + callback(null, true); + return; + } + + const allowedOrigins = domainName?.split(",").map((o) => o.trim()) || []; + if (allowedOrigins.includes(origin)) { + callback(null, true); + } else { + callback(null, false); + } + }, + credentials: true, + }); + } // Validation Globale app.useGlobalPipes( @@ -49,4 +66,4 @@ async function bootstrap() { await app.listen(port); logger.log(`Application is running on: http://localhost:${port}`); } -bootstrap(); +bootstrap().then(); diff --git a/backend/src/media/media.service.ts b/backend/src/media/media.service.ts index 982261d..ca26c08 100644 --- a/backend/src/media/media.service.ts +++ b/backend/src/media/media.service.ts @@ -5,7 +5,7 @@ import { Logger, } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; -import * as NodeClam from "clamscan"; +import NodeClam from "clamscan"; import type { IMediaService, MediaProcessingResult, diff --git a/backend/src/reports/reports.module.ts b/backend/src/reports/reports.module.ts index 6e6254a..0f4f576 100644 --- a/backend/src/reports/reports.module.ts +++ b/backend/src/reports/reports.module.ts @@ -1,4 +1,4 @@ -import { Module } from "@nestjs/common"; +import { forwardRef, Module } from "@nestjs/common"; import { AuthModule } from "../auth/auth.module"; import { CryptoModule } from "../crypto/crypto.module"; import { DatabaseModule } from "../database/database.module"; @@ -7,8 +7,9 @@ import { ReportsService } from "./reports.service"; import { ReportsRepository } from "./repositories/reports.repository"; @Module({ - imports: [DatabaseModule, AuthModule, CryptoModule], + imports: [DatabaseModule, forwardRef(() => AuthModule), CryptoModule], controllers: [ReportsController], providers: [ReportsService, ReportsRepository], + exports: [ReportsRepository, ReportsService], }) export class ReportsModule {} diff --git a/backend/src/sessions/sessions.module.ts b/backend/src/sessions/sessions.module.ts index a5c4367..70aca84 100644 --- a/backend/src/sessions/sessions.module.ts +++ b/backend/src/sessions/sessions.module.ts @@ -7,6 +7,6 @@ import { SessionsService } from "./sessions.service"; @Module({ imports: [DatabaseModule, CryptoModule], providers: [SessionsService, SessionsRepository], - exports: [SessionsService], + exports: [SessionsService, SessionsRepository], }) export class SessionsModule {} diff --git a/backend/src/tags/tags.module.ts b/backend/src/tags/tags.module.ts index 456c36a..0e21af0 100644 --- a/backend/src/tags/tags.module.ts +++ b/backend/src/tags/tags.module.ts @@ -1,13 +1,15 @@ import { Module } from "@nestjs/common"; +import { AuthModule } from "../auth/auth.module"; +import { CryptoModule } from "../crypto/crypto.module"; import { DatabaseModule } from "../database/database.module"; import { TagsRepository } from "./repositories/tags.repository"; import { TagsController } from "./tags.controller"; import { TagsService } from "./tags.service"; @Module({ - imports: [DatabaseModule], + imports: [DatabaseModule, AuthModule, CryptoModule], controllers: [TagsController], providers: [TagsService, TagsRepository], - exports: [TagsService], + exports: [TagsService, TagsRepository], }) export class TagsModule {} diff --git a/backend/src/users/users.module.ts b/backend/src/users/users.module.ts index a15fae5..21678c9 100644 --- a/backend/src/users/users.module.ts +++ b/backend/src/users/users.module.ts @@ -10,6 +10,6 @@ import { UsersService } from "./users.service"; imports: [DatabaseModule, CryptoModule, forwardRef(() => AuthModule)], controllers: [UsersController], providers: [UsersService, UsersRepository], - exports: [UsersService], + exports: [UsersService, UsersRepository], }) export class UsersModule {}