From d6ceefdeda533ad4706f39da198adb2e4ab54120 Mon Sep 17 00:00:00 2001 From: andil Date: Tue, 4 Aug 2026 11:45:04 +0000 Subject: [PATCH] chore: dockerize app and automate database initialization --- backend/.dockerignore | 7 +++ backend/Dockerfile | 15 ++++++ backend/package.json | 3 +- backend/src/scripts/initDatabase.js | 55 +++++++++++++++++++ docker-compose.dev.yml | 84 +++++++++++++++++++++++++++++ frontend/.dockerignore | 7 +++ frontend/Dockerfile | 22 ++++++++ frontend/nginx.conf | 11 ++++ frontend/package-lock.json | 39 +++++++++----- 9 files changed, 229 insertions(+), 14 deletions(-) create mode 100644 backend/.dockerignore create mode 100644 backend/Dockerfile create mode 100644 backend/src/scripts/initDatabase.js create mode 100644 docker-compose.dev.yml create mode 100644 frontend/.dockerignore create mode 100644 frontend/Dockerfile create mode 100644 frontend/nginx.conf diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..7e5a332 --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,7 @@ +node_modules +npm-debug.log +.env +coverage +.git +.gitignore +README.md diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..771b161 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,15 @@ +FROM node:22-alpine + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +COPY prisma ./prisma +RUN npx prisma generate + +COPY . . + +EXPOSE 3000 + +CMD ["npm", "start"] diff --git a/backend/package.json b/backend/package.json index abc8476..5a7af54 100644 --- a/backend/package.json +++ b/backend/package.json @@ -8,7 +8,8 @@ "generate:tickets": "node src/scripts/generateTickets.js", "test": "jest --runInBand", "test:watch": "jest --watch", - "test:coverage": "jest --coverage" + "test:coverage": "jest --coverage", + "init:db": "node src/scripts/initDatabase.js" }, "keywords": [], "author": "", diff --git a/backend/src/scripts/initDatabase.js b/backend/src/scripts/initDatabase.js new file mode 100644 index 0000000..3d7645c --- /dev/null +++ b/backend/src/scripts/initDatabase.js @@ -0,0 +1,55 @@ +const { execSync } = require("child_process"); +const prisma = require("../config/prisma"); + +async function main() { + let lotCount = await prisma.lot.count(); + let ticketCount = await prisma.ticket.count(); + + console.log(`Lots présents : ${lotCount}`); + console.log(`Tickets présents : ${ticketCount}`); + + if (lotCount === 0) { + console.log("Aucun lot trouvé : exécution du seed..."); + execSync("npx prisma db seed", { + stdio: "inherit", + env: process.env, + }); + + lotCount = await prisma.lot.count(); + console.log(`Lots présents après seed : ${lotCount}`); + } else { + console.log("Les lots existent déjà : seed ignoré."); + } + + if (lotCount !== 5) { + throw new Error( + `Initialisation interrompue : 5 lots attendus, ${lotCount} trouvé(s).` + ); + } + + ticketCount = await prisma.ticket.count(); + + if (ticketCount === 0) { + console.log("Aucun ticket trouvé : génération des tickets..."); + execSync("npm run generate:tickets", { + stdio: "inherit", + env: process.env, + }); + + ticketCount = await prisma.ticket.count(); + console.log(`Tickets présents après génération : ${ticketCount}`); + } else { + console.log("Les tickets existent déjà : génération ignorée."); + } + + console.log("Initialisation de la base terminée."); +} + +main() + .catch((error) => { + console.error("Erreur pendant l'initialisation de la base :", error); + process.exitCode = 1; + }) + .finally(async () => { + await prisma.$disconnect(); + }); diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..8df08a2 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,84 @@ +services: + postgres: + image: postgres:16-alpine + container_name: tiptop-postgres-dev + restart: unless-stopped + + environment: + POSTGRES_USER: tiptop + POSTGRES_PASSWORD: tiptop_dev_password + POSTGRES_DB: the_tip_top_db + + volumes: + - tiptop_postgres_dev_data:/var/lib/postgresql/data + + healthcheck: + test: ["CMD-SHELL", "pg_isready -U tiptop -d the_tip_top_db"] + interval: 10s + timeout: 5s + retries: 5 + + networks: + - tiptop-dev + + backend: + build: + context: ./backend + dockerfile: Dockerfile + + container_name: tiptop-backend-dev + restart: unless-stopped + + depends_on: + postgres: + condition: service_healthy + + environment: + PORT: 5000 + DATABASE_URL: postgresql://tiptop:tiptop_dev_password@postgres:5432/the_tip_top_db + JWT_SECRET: dev_secret_change_me + APP_URL: http://localhost:5000 + + MAIL_HOST: smtp.gmail.com + MAIL_PORT: 587 + MAIL_USER: "" + MAIL_PASS: "" + MAIL_FROM: "The Tip Top " + + ports: + - "5001:5000" + + command: > + sh -c " + npx prisma migrate deploy && + npm run init:db && + npm start + " + + networks: + - tiptop-dev + + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + args: + VITE_API_URL: http://localhost:5001 + + container_name: tiptop-frontend-dev + restart: unless-stopped + + depends_on: + - backend + + ports: + - "5173:80" + + networks: + - tiptop-dev + +volumes: + tiptop_postgres_dev_data: + +networks: + tiptop-dev: diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..cf4ed03 --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,7 @@ +node_modules +dist +npm-debug.log +.env +.git +.gitignore +README.md diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..f5e98df --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,22 @@ +FROM node:22-alpine AS build + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +COPY . . + +ARG VITE_API_URL +ENV VITE_API_URL=${VITE_API_URL} + +RUN npm run build + + +FROM nginx:1.27-alpine + +COPY --from=build /app/dist /usr/share/nginx/html + +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 diff --git a/frontend/nginx.conf b/frontend/nginx.conf new file mode 100644 index 0000000..3aa17e6 --- /dev/null +++ b/frontend/nginx.conf @@ -0,0 +1,11 @@ +server { + listen 80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a814f40..2bf4f85 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -57,7 +57,6 @@ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -267,10 +266,33 @@ "node": ">=6.9.0" } }, + "node_modules/@emnapi/core": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", + "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", - "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", + "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==", "dev": true, "license": "MIT", "optional": true, @@ -1159,7 +1181,6 @@ "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -1206,7 +1227,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1297,7 +1317,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -1460,7 +1479,6 @@ "integrity": "sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", @@ -2379,7 +2397,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2441,7 +2458,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -2451,7 +2467,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -2692,7 +2707,6 @@ "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", @@ -2817,7 +2831,6 @@ "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" }