From 4d31b03116ebf91aa1a80c73b4035e41861cd88f Mon Sep 17 00:00:00 2001 From: Alexis SANOGO Date: Sun, 2 Aug 2026 08:28:46 +0200 Subject: [PATCH 01/14] =?UTF-8?q?Pr=C3=A9cision=20projet=20fictif=20sur=20?= =?UTF-8?q?les=20pages=20footer,=20mentions=20l=C3=A9gales=20et=20CGU?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/Footer.jsx | 6 +++++- frontend/src/pages/Cgu.jsx | 9 +++++++++ frontend/src/pages/MentionsLegales.jsx | 9 +++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/Footer.jsx b/frontend/src/components/Footer.jsx index e16923d..1c673df 100644 --- a/frontend/src/components/Footer.jsx +++ b/frontend/src/components/Footer.jsx @@ -210,8 +210,12 @@ export default function Footer() {
-

+

© {new Date().getFullYear()} Thé Tip Top. Tous droits réservés. +
+ + Projet étudiant fictif — aucun achat ni réservation réel ne peut être effectué sur ce site. +

diff --git a/frontend/src/pages/Cgu.jsx b/frontend/src/pages/Cgu.jsx index 4735651..212870c 100644 --- a/frontend/src/pages/Cgu.jsx +++ b/frontend/src/pages/Cgu.jsx @@ -7,6 +7,15 @@ export default function Cgu() { Conditions générales d'utilisation +
+

+ Projet étudiant fictif — Ce site a été conçu dans un cadre pédagogique. La société + Thé Tip Top, l'agence Furious Ducks et l'ensemble des conditions présentées + ci-dessous sont fictives. Aucun achat, paiement ou réservation réel ne peut être + effectué sur ce site. +

+
+

Les présentes Conditions Générales d'Utilisation (CGU) précisent les modalités d'accès et d'utilisation du site internet du jeu-concours « 100% Gagnant » de la société Thé Tip Top. diff --git a/frontend/src/pages/MentionsLegales.jsx b/frontend/src/pages/MentionsLegales.jsx index cb5cbb3..0a11183 100644 --- a/frontend/src/pages/MentionsLegales.jsx +++ b/frontend/src/pages/MentionsLegales.jsx @@ -7,6 +7,15 @@ export default function MentionsLegales() { Mentions légales +

+

+ Projet étudiant fictif — Ce site a été conçu dans un cadre pédagogique. La société + Thé Tip Top, l'agence Furious Ducks et l'ensemble des informations légales présentées + ci-dessous sont fictives. Aucun achat, paiement ou réservation réel ne peut être + effectué sur ce site. +

+
+

Conformément à la loi n°2004-575 du 21 juin 2004 pour la Confiance dans l'Économie Numérique (LCEN), les présentes mentions légales précisent l'identité des différents From 2bbd2d16e44f405fda4b3b20255f7653e46cebc2 Mon Sep 17 00:00:00 2001 From: Andil Date: Fri, 14 Aug 2026 22:45:59 +0000 Subject: [PATCH 02/14] ci: migrate deployments to VPS HTTPS infrastructure --- Jenkinsfile | 62 +++++++++++++++++++------------ deploy/dev/docker-compose.yml | 44 ++++++++++++++-------- deploy/preprod/docker-compose.yml | 42 ++++++++++++--------- deploy/prod/docker-compose.yml | 43 ++++++++++++--------- 4 files changed, 116 insertions(+), 75 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index d57f215..5a59586 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -8,8 +8,7 @@ pipeline { } environment { - REGISTRY = "localhost:5000" - SERVER_IP = "192.168.1.95" + REGISTRY = "registry.wk-archi-o24a-4-5-g4.fr" BACKEND_IMAGE = "${REGISTRY}/the-tip-top/backend" FRONTEND_IMAGE = "${REGISTRY}/the-tip-top/frontend" @@ -49,7 +48,7 @@ pipeline { env.COMPOSE_PROJECT = 'tiptop-dev' env.DEPLOY_NETWORK = 'tiptop-dev' - env.FRONTEND_API_URL = "http://${SERVER_IP}:5001" + env.FRONTEND_API_URL = "https://api-dev.dsp5-archi-o24a-4-5-g4.fr" } else if (env.BRANCH_NAME.startsWith('release-')) { env.DEPLOY_ENABLED = 'true' @@ -59,7 +58,7 @@ pipeline { env.COMPOSE_PROJECT = 'tiptop-preprod' env.DEPLOY_NETWORK = 'tiptop-preprod' - env.FRONTEND_API_URL = "http://${SERVER_IP}:5002" + env.FRONTEND_API_URL = "https://api-preprod.dsp5-archi-o24a-4-5-g4.fr" } else if (env.BRANCH_NAME == 'main') { env.DEPLOY_ENABLED = 'true' @@ -69,7 +68,7 @@ pipeline { env.COMPOSE_PROJECT = 'tiptop-prod' env.DEPLOY_NETWORK = 'tiptop-prod' - env.FRONTEND_API_URL = "http://${SERVER_IP}:5003" + env.FRONTEND_API_URL = "https://api.dsp5-archi-o24a-4-5-g4.fr" } else { env.DEPLOY_ENABLED = 'false' @@ -85,7 +84,6 @@ pipeline { echo "Tag Docker : ${env.IMAGE_TAG}" echo "Environnement : ${env.DEPLOY_ENV}" echo "Déploiement activé : ${env.DEPLOY_ENABLED}" - echo "Serveur : ${env.SERVER_IP}" echo "API frontend : ${env.FRONTEND_API_URL}" } } @@ -229,19 +227,34 @@ pipeline { } stage('Publication dans le Registry') { - when { - expression { - env.DEPLOY_ENABLED == 'true' - } - } - - steps { - sh ''' - docker push "${BACKEND_IMAGE}:${IMAGE_TAG}" - docker push "${FRONTEND_IMAGE}:${IMAGE_TAG}" - ''' - } + when { + expression { + env.DEPLOY_ENABLED == 'true' } + } + + steps { + withCredentials([ + usernamePassword( + credentialsId: 'registry-credentials', + usernameVariable: 'REGISTRY_USER', + passwordVariable: 'REGISTRY_PASSWORD' + ) + ]) { + sh ''' + echo "${REGISTRY_PASSWORD}" | \ + docker login "${REGISTRY}" \ + -u "${REGISTRY_USER}" \ + --password-stdin + + docker push "${BACKEND_IMAGE}:${IMAGE_TAG}" + docker push "${FRONTEND_IMAGE}:${IMAGE_TAG}" + ''' + } + } +} + + } stage('Validation production') { when { @@ -333,12 +346,13 @@ pipeline { } post { - always { - sh ''' - docker rm -f "${CI_DATABASE}" 2>/dev/null || true - docker network rm "${CI_NETWORK}" 2>/dev/null || true - ''' - } + always { + sh ''' + docker logout "${REGISTRY}" 2>/dev/null || true + docker rm -f "${CI_DATABASE}" 2>/dev/null || true + docker network rm "${CI_NETWORK}" 2>/dev/null || true + ''' + } success { echo """ diff --git a/deploy/dev/docker-compose.yml b/deploy/dev/docker-compose.yml index 8602afa..a23ae31 100644 --- a/deploy/dev/docker-compose.yml +++ b/deploy/dev/docker-compose.yml @@ -9,7 +9,7 @@ services: POSTGRES_DB: the_tip_top_db volumes: - - tiptop_postgres_dev_data:/var/lib/postgresql/data + - tiptop_dev_postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U tiptop -d the_tip_top_db"] @@ -30,14 +30,10 @@ services: environment: PORT: 5000 + NODE_ENV: development DATABASE_URL: postgresql://tiptop:tiptop_dev_password@postgres:5432/the_tip_top_db JWT_SECRET: dev_secret_change_me - APP_URL: http://192.168.1.23:5001 - MAIL_HOST: smtp.gmail.com - MAIL_PORT: 587 - MAIL_USER: "" - MAIL_PASS: "" - MAIL_FROM: "The Tip Top " + APP_URL: https://api-dev.dsp5-archi-o24a-4-5-g4.fr command: > sh -c " @@ -46,11 +42,18 @@ services: npm start " - ports: - - "5001:5000" - networks: - tiptop-dev + - proxy + + labels: + - "traefik.enable=true" + - "traefik.docker.network=proxy" + - "traefik.http.routers.tiptop-dev-api.rule=Host(`api-dev.dsp5-archi-o24a-4-5-g4.fr`)" + - "traefik.http.routers.tiptop-dev-api.entrypoints=websecure" + - "traefik.http.routers.tiptop-dev-api.tls=true" + - "traefik.http.routers.tiptop-dev-api.tls.certresolver=letsencrypt" + - "traefik.http.services.tiptop-dev-api.loadbalancer.server.port=5000" frontend: image: ${REGISTRY}/the-tip-top/frontend:${IMAGE_TAG} @@ -59,17 +62,26 @@ services: depends_on: - backend - ports: - - "5173:80" - networks: - tiptop-dev + - proxy + + labels: + - "traefik.enable=true" + - "traefik.docker.network=proxy" + - "traefik.http.routers.tiptop-dev.rule=Host(`dev.dsp5-archi-o24a-4-5-g4.fr`)" + - "traefik.http.routers.tiptop-dev.entrypoints=websecure" + - "traefik.http.routers.tiptop-dev.tls=true" + - "traefik.http.routers.tiptop-dev.tls.certresolver=letsencrypt" + - "traefik.http.services.tiptop-dev.loadbalancer.server.port=80" volumes: - tiptop_postgres_dev_data: - external: true - name: the-tip-top_tiptop_postgres_dev_data + tiptop_dev_postgres_data: + name: tiptop_dev_postgres_data networks: tiptop-dev: name: tiptop-dev + + proxy: + external: true diff --git a/deploy/preprod/docker-compose.yml b/deploy/preprod/docker-compose.yml index b61eef5..e8463b9 100644 --- a/deploy/preprod/docker-compose.yml +++ b/deploy/preprod/docker-compose.yml @@ -12,11 +12,7 @@ services: - tiptop_preprod_postgres_data:/var/lib/postgresql/data healthcheck: - test: - [ - "CMD-SHELL", - "pg_isready -U tiptop_preprod -d the_tip_top_preprod" - ] + test: ["CMD-SHELL", "pg_isready -U tiptop_preprod -d the_tip_top_preprod"] interval: 10s timeout: 5s retries: 10 @@ -37,12 +33,7 @@ services: NODE_ENV: preproduction DATABASE_URL: postgresql://tiptop_preprod:tiptop_preprod_password@postgres:5432/the_tip_top_preprod JWT_SECRET: preprod_secret_change_me - APP_URL: http://192.168.1.23:5002 - MAIL_HOST: smtp.gmail.com - MAIL_PORT: 587 - MAIL_USER: "" - MAIL_PASS: "" - MAIL_FROM: "The Tip Top Preprod " + APP_URL: https://api-preprod.dsp5-archi-o24a-4-5-g4.fr command: > sh -c " @@ -51,11 +42,18 @@ services: npm start " - ports: - - "5002:5000" - networks: - tiptop-preprod + - proxy + + labels: + - "traefik.enable=true" + - "traefik.docker.network=proxy" + - "traefik.http.routers.tiptop-preprod-api.rule=Host(`api-preprod.dsp5-archi-o24a-4-5-g4.fr`)" + - "traefik.http.routers.tiptop-preprod-api.entrypoints=websecure" + - "traefik.http.routers.tiptop-preprod-api.tls=true" + - "traefik.http.routers.tiptop-preprod-api.tls.certresolver=letsencrypt" + - "traefik.http.services.tiptop-preprod-api.loadbalancer.server.port=5000" frontend: image: ${REGISTRY}/the-tip-top/frontend:${IMAGE_TAG} @@ -64,11 +62,18 @@ services: depends_on: - backend - ports: - - "5174:80" - networks: - tiptop-preprod + - proxy + + labels: + - "traefik.enable=true" + - "traefik.docker.network=proxy" + - "traefik.http.routers.tiptop-preprod.rule=Host(`preprod.dsp5-archi-o24a-4-5-g4.fr`)" + - "traefik.http.routers.tiptop-preprod.entrypoints=websecure" + - "traefik.http.routers.tiptop-preprod.tls=true" + - "traefik.http.routers.tiptop-preprod.tls.certresolver=letsencrypt" + - "traefik.http.services.tiptop-preprod.loadbalancer.server.port=80" volumes: tiptop_preprod_postgres_data: @@ -77,3 +82,6 @@ volumes: networks: tiptop-preprod: name: tiptop-preprod + + proxy: + external: true diff --git a/deploy/prod/docker-compose.yml b/deploy/prod/docker-compose.yml index beeda63..e784adb 100644 --- a/deploy/prod/docker-compose.yml +++ b/deploy/prod/docker-compose.yml @@ -12,11 +12,7 @@ services: - tiptop_prod_postgres_data:/var/lib/postgresql/data healthcheck: - test: - [ - "CMD-SHELL", - "pg_isready -U tiptop_prod -d the_tip_top_prod" - ] + test: ["CMD-SHELL", "pg_isready -U tiptop_prod -d the_tip_top_prod"] interval: 10s timeout: 5s retries: 10 @@ -37,13 +33,7 @@ services: NODE_ENV: production DATABASE_URL: postgresql://tiptop_prod:tiptop_prod_password@postgres:5432/the_tip_top_prod JWT_SECRET: prod_secret_change_me - APP_URL: http://192.168.1.23:5003 - - MAIL_HOST: smtp.gmail.com - MAIL_PORT: 587 - MAIL_USER: "" - MAIL_PASS: "" - MAIL_FROM: "The Tip Top " + APP_URL: https://api.dsp5-archi-o24a-4-5-g4.fr command: > sh -c " @@ -52,11 +42,18 @@ services: npm start " - ports: - - "5003:5000" - networks: - tiptop-prod + - proxy + + labels: + - "traefik.enable=true" + - "traefik.docker.network=proxy" + - "traefik.http.routers.tiptop-prod-api.rule=Host(`api.dsp5-archi-o24a-4-5-g4.fr`)" + - "traefik.http.routers.tiptop-prod-api.entrypoints=websecure" + - "traefik.http.routers.tiptop-prod-api.tls=true" + - "traefik.http.routers.tiptop-prod-api.tls.certresolver=letsencrypt" + - "traefik.http.services.tiptop-prod-api.loadbalancer.server.port=5000" frontend: image: ${REGISTRY}/the-tip-top/frontend:${IMAGE_TAG} @@ -65,11 +62,18 @@ services: depends_on: - backend - ports: - - "5175:80" - networks: - tiptop-prod + - proxy + + labels: + - "traefik.enable=true" + - "traefik.docker.network=proxy" + - "traefik.http.routers.tiptop-prod.rule=Host(`dsp5-archi-o24a-4-5-g4.fr`) || Host(`www.dsp5-archi-o24a-4-5-g4.fr`)" + - "traefik.http.routers.tiptop-prod.entrypoints=websecure" + - "traefik.http.routers.tiptop-prod.tls=true" + - "traefik.http.routers.tiptop-prod.tls.certresolver=letsencrypt" + - "traefik.http.services.tiptop-prod.loadbalancer.server.port=80" volumes: tiptop_prod_postgres_data: @@ -78,3 +82,6 @@ volumes: networks: tiptop-prod: name: tiptop-prod + + proxy: + external: true From 61296dda0ccf630195865e2f9305b24cfd6d65c5 Mon Sep 17 00:00:00 2001 From: Andil Date: Fri, 14 Aug 2026 22:56:32 +0000 Subject: [PATCH 03/14] fix: correct Jenkinsfile pipeline syntax --- Jenkinsfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 5a59586..b205bf0 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -254,7 +254,6 @@ pipeline { } } - } stage('Validation production') { when { From 825fedf11865c9d79540e7331ba78ea2e3f31271 Mon Sep 17 00:00:00 2001 From: Andil Date: Fri, 14 Aug 2026 23:07:42 +0000 Subject: [PATCH 04/14] fix: initialize npm cache permissions in Jenkins --- Jenkinsfile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index b205bf0..b7b2a71 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -137,6 +137,10 @@ pipeline { sleep 2 done + docker run --rm \ + -v tiptop-npm-cache-backend:/npm-cache \ + alpine \ + sh -c 'chown -R 1000:1000 /npm-cache' docker run --rm \ --network "${CI_NETWORK}" \ @@ -184,6 +188,11 @@ pipeline { steps { sh ''' docker run --rm \ + -v tiptop-npm-cache-frontend:/npm-cache \ + alpine \ + sh -c 'chown -R 1000:1000 /npm-cache' + + docker run --rm \ --volumes-from furious-jenkins \ -v tiptop-npm-cache-frontend:/npm-cache \ --user "$(id -u):$(id -g)" \ From 02cf98f6ca4427c55b597041b647bb08a9091568 Mon Sep 17 00:00:00 2001 From: Andil Date: Fri, 14 Aug 2026 23:34:17 +0000 Subject: [PATCH 05/14] ci: trigger preprod pipeline From cdbacfebb5d164ebd656b9532f57317707d5da9c Mon Sep 17 00:00:00 2001 From: Andil Date: Sat, 15 Aug 2026 08:39:14 +0000 Subject: [PATCH 06/14] ci: validate end-to-end deployment From 3e12fa2d01ab89d55ec9be2b68c0bd069057d63d Mon Sep 17 00:00:00 2001 From: Andil Date: Mon, 17 Aug 2026 12:27:14 +0000 Subject: [PATCH 07/14] fix: configure frontend services for environment API URLs --- backend/src/docs/swagger.js | 4 ++-- frontend/src/services/admin.js | 13 ++++++++----- frontend/src/services/auth.js | 3 ++- frontend/src/services/employee.js | 3 ++- frontend/src/services/game.js | 8 ++++++-- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/backend/src/docs/swagger.js b/backend/src/docs/swagger.js index a9809e3..510a6cc 100644 --- a/backend/src/docs/swagger.js +++ b/backend/src/docs/swagger.js @@ -10,7 +10,7 @@ const options = { }, servers: [ { - url: "http://localhost:5000", + url: process.env.API_PUBLIC_URL || "http://localhost:5000", }, ], components: { @@ -26,4 +26,4 @@ const options = { apis: ["./src/routes/*.js"], }; -module.exports = swaggerJsdoc(options); \ No newline at end of file +module.exports = swaggerJsdoc(options); diff --git a/frontend/src/services/admin.js b/frontend/src/services/admin.js index bf5bf4f..73da954 100644 --- a/frontend/src/services/admin.js +++ b/frontend/src/services/admin.js @@ -1,3 +1,6 @@ +const API_URL = import.meta.env.VITE_API_URL || '' +const BASE = `${API_URL}/api/admin` + function authHeaders() { return { 'Content-Type': 'application/json', @@ -12,35 +15,35 @@ function apiError(message, status) { } export async function getStatsApi() { - const res = await fetch('/api/admin/stats', { headers: authHeaders() }) + const res = await fetch(`${BASE}/stats`, { headers: authHeaders() }) const data = await res.json() if (!res.ok) throw apiError(data.message || 'Erreur stats.', res.status) return data } export async function getUsersApi() { - const res = await fetch('/api/admin/users', { headers: authHeaders() }) + const res = await fetch(`${BASE}/users`, { headers: authHeaders() }) const data = await res.json() if (!res.ok) throw apiError(data.message || 'Erreur utilisateurs.', res.status) return data } export async function drawWinnerApi() { - const res = await fetch('/api/admin/draw', { method: 'POST', headers: authHeaders() }) + const res = await fetch(`${BASE}/draw`, { method: 'POST', headers: authHeaders() }) const data = await res.json() if (!res.ok) throw apiError(data.message || 'Erreur tirage.', res.status) return data } export async function getEmailingContactsApi() { - const res = await fetch('/api/admin/emailing', { headers: authHeaders() }) + const res = await fetch(`${BASE}/emailing`, { headers: authHeaders() }) const data = await res.json() if (!res.ok) throw apiError(data.message || 'Erreur contacts emailing.', res.status) return data } export async function getWinnerApi() { - const res = await fetch('/api/admin/draw', { headers: authHeaders() }) + const res = await fetch(`${BASE}/draw`, { headers: authHeaders() }) if (res.status === 404) return null const data = await res.json() if (!res.ok) throw apiError(data.message || 'Erreur gagnant.', res.status) diff --git a/frontend/src/services/auth.js b/frontend/src/services/auth.js index eb55d8a..593a1ae 100644 --- a/frontend/src/services/auth.js +++ b/frontend/src/services/auth.js @@ -1,4 +1,5 @@ -const BASE = '/api/auth' +const API_URL = import.meta.env.VITE_API_URL || '' +const BASE = `${API_URL}/api/auth function apiError(message, status) { const err = new Error(message) diff --git a/frontend/src/services/employee.js b/frontend/src/services/employee.js index a127fb0..2db310c 100644 --- a/frontend/src/services/employee.js +++ b/frontend/src/services/employee.js @@ -1,4 +1,5 @@ -const BASE = '/api/employee' +const API_URL = import.meta.env.VITE_API_URL || '' +const BASE = `${API_URL}/api/employee` function authHeaders() { const token = localStorage.getItem('token') diff --git a/frontend/src/services/game.js b/frontend/src/services/game.js index 1312a56..fc81d9c 100644 --- a/frontend/src/services/game.js +++ b/frontend/src/services/game.js @@ -1,3 +1,7 @@ +const API_URL = import.meta.env.VITE_API_URL || '' +const BASE = `${API_URL}/api/game` + + function apiError(message, status) { const err = new Error(message) err.status = status @@ -6,7 +10,7 @@ function apiError(message, status) { export async function myGainsApi() { const token = localStorage.getItem('token') - const res = await fetch('/api/game/my-gains', { + const res = await fetch(`${BASE}/my-gains`, { headers: { Authorization: `Bearer ${token}` }, }) const data = await res.json() @@ -16,7 +20,7 @@ export async function myGainsApi() { export async function participateApi(code) { const token = localStorage.getItem('token') - const res = await fetch('/api/game/participate', { + const res = await fetch(`${BASE}/participate`, { method: 'POST', headers: { 'Content-Type': 'application/json', From a1da7b9e1768e03437adedbba0f78a7e546b596e Mon Sep 17 00:00:00 2001 From: Andil Date: Mon, 17 Aug 2026 14:12:58 +0000 Subject: [PATCH 08/14] fix: finalize API configuration and backend lockfile --- backend/package-lock.json | 39 +++++++++++++++++++++++++++-------- frontend/src/services/auth.js | 2 +- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index d7d2e38..e94be78 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -109,7 +109,6 @@ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -605,13 +604,39 @@ "dev": true, "license": "MIT" }, - "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==", + "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, + "peer": 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, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "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, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -1963,7 +1988,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -2282,7 +2306,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.38", "caniuse-lite": "^1.0.30001799", @@ -3149,7 +3172,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -5902,7 +5924,6 @@ "integrity": "sha512-++ZJ0ijLrDJF6hNB4t4uxg2br3fC4H9Yc9tcbjr2fcNFP3rh/SBNrAgjhsqBU4Ght8JPrVofG/ZkXfnSfnYsFg==", "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@prisma/config": "6.19.3", "@prisma/engines": "6.19.3" diff --git a/frontend/src/services/auth.js b/frontend/src/services/auth.js index 593a1ae..6602ce3 100644 --- a/frontend/src/services/auth.js +++ b/frontend/src/services/auth.js @@ -1,5 +1,5 @@ const API_URL = import.meta.env.VITE_API_URL || '' -const BASE = `${API_URL}/api/auth +const BASE = `${API_URL}/api/auth` function apiError(message, status) { const err = new Error(message) From 047d34f59bd9dfaa14928b3511934d7bf4696d61 Mon Sep 17 00:00:00 2001 From: Andil Date: Mon, 17 Aug 2026 14:54:58 +0000 Subject: [PATCH 09/14] fix: resolve frontend lint issues --- frontend/src/components/Footer.jsx | 147 +++++++++++------------- frontend/src/context/AuthContext.jsx | 2 + frontend/src/pages/Admin.jsx | 4 +- frontend/src/pages/MesGains.jsx | 15 ++- frontend/src/pages/Profile.jsx | 10 +- frontend/src/pages/SaisirCodeUnique.jsx | 3 - 6 files changed, 85 insertions(+), 96 deletions(-) diff --git a/frontend/src/components/Footer.jsx b/frontend/src/components/Footer.jsx index 1c673df..b2189dd 100644 --- a/frontend/src/components/Footer.jsx +++ b/frontend/src/components/Footer.jsx @@ -1,125 +1,119 @@ import { Link, useLocation } from 'react-router-dom' import { useAuth } from '../context/AuthContext' - + +function FooterLink({ to, children, className = '', pathname }) { + return pathname === to ? ( + {children} + ) : ( + + {children} + + ) +} + export default function Footer() { const { pathname } = useLocation() const { user } = useAuth() const isLoggedIn = !!user - - function FooterLink({ to, children, className=''}) { - return pathname === to ? ( - {children} - ) : ( - - {children} - - ) - } + return (

- - + {/* Partie principale */}
- - + {/* Logo */}
- +

Thé Tip Top

- +

Participez au jeu-concours Thé Tip Top et tentez de gagner des lots gourmands autour du thé.

-
- - - + {/* Navigation */}
-

Navigation

- - +
    - +
  • Accueil
  • - - +
  • Règles du jeu
  • - - +
  • Tester mon code
  • - - { !isLoggedIn && ( + + {!isLoggedIn && (
  • Créer un compte
  • )} - +
-
- - - - + {/* Informations légales */}
-

Informations

- - +
    - +
  • Mentions légales
  • - - +
  • Politique de confidentialité @@ -129,6 +123,7 @@ export default function Footer() {
  • Accessibilité @@ -138,78 +133,73 @@ export default function Footer() {
  • CGU
  • -
  • Contact
  • - +
  • Plan du site
  • - +
-
- - - - + {/* Contact */}
-

Contact

- +

Une question sur le jeu ?

- - + + 01 84 80 20 34 - - + + contact@thetiptop.fr - +

18 rue Léon Frot
75011 Paris, France

- -
- - +
- - - + {/* Barre basse */} -
-
- - +

© {new Date().getFullYear()} Thé Tip Top. Tous droits réservés.
@@ -217,8 +207,9 @@ export default function Footer() { Projet étudiant fictif — aucun achat ni réservation réel ne peut être effectué sur ce site.

- + - -
-
- - +
) -} \ No newline at end of file +} diff --git a/frontend/src/context/AuthContext.jsx b/frontend/src/context/AuthContext.jsx index 552cb66..5035bb7 100644 --- a/frontend/src/context/AuthContext.jsx +++ b/frontend/src/context/AuthContext.jsx @@ -1,3 +1,5 @@ +/* eslint-disable react-refresh/only-export-components */ + import { createContext, useContext, useState } from 'react' const AuthContext = createContext(null) diff --git a/frontend/src/pages/Admin.jsx b/frontend/src/pages/Admin.jsx index 4272e81..94c0750 100644 --- a/frontend/src/pages/Admin.jsx +++ b/frontend/src/pages/Admin.jsx @@ -56,7 +56,7 @@ export default function Admin() { if (!handleAuthError(e)) setError(e.message) }) .finally(() => setLoading(false)) - }, []) + }, [handleAuthError]) const handleDraw = async () => { setShowConfirm(false) @@ -94,7 +94,7 @@ export default function Admin() { const csv = [header, ...rows] .map((row) => row.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(';')) .join('\n') - const blob = new Blob([`${csv}`], { type: 'text/csv;charset=utf-8;' }) + const blob = new Blob(['\uFEFF' + csv], { type: 'text/csv;charset=utf-8;' }) const url = URL.createObjectURL(blob) const link = document.createElement('a') link.href = url diff --git a/frontend/src/pages/MesGains.jsx b/frontend/src/pages/MesGains.jsx index dcb45f3..d432a44 100644 --- a/frontend/src/pages/MesGains.jsx +++ b/frontend/src/pages/MesGains.jsx @@ -50,7 +50,20 @@ export default function MesGains() { } }, [handleAuthError]) - useEffect(() => { fetchGains() }, [fetchGains]) + useEffect(() => { + const loadGains = async () => { + try { + const data = await myGainsApi() + setGains(data.gains) + } catch (err) { + if (!handleAuthError(err)) setError(err.message) + } finally { + setLoading(false) + } + } + + loadGains() +}, [handleAuthError]) return (
diff --git a/frontend/src/pages/Profile.jsx b/frontend/src/pages/Profile.jsx index 612d848..5861aef 100644 --- a/frontend/src/pages/Profile.jsx +++ b/frontend/src/pages/Profile.jsx @@ -1,20 +1,12 @@ import { useState } from 'react' -import { useNavigate } from 'react-router-dom' import UserNav from '../components/UserNav' -import { changePasswordApi, deleteAccountApi } from '../services/auth' +import { changePasswordApi} from '../services/auth' import { useHandleAuthError } from '../hooks/useHandleAuthError' -import { useAuth } from '../context/AuthContext' export default function Profile() { const user = JSON.parse(localStorage.getItem('user') || '{}') const initials = `${(user.firstname || '?')[0]}${(user.lastname || '?')[0]}`.toUpperCase() const handleAuthError = useHandleAuthError() - const { logout } = useAuth() - const navigate = useNavigate() - - const [showDeleteModal, setShowDeleteModal] = useState(false) - const [deleting, setDeleting] = useState(false) - const [deleteError, setDeleteError] = useState('') const [pwForm, setPwForm] = useState({ currentPassword: '', newPassword: '', confirmPassword: '' }) const [showPw, setShowPw] = useState({ current: false, new: false, confirm: false }) diff --git a/frontend/src/pages/SaisirCodeUnique.jsx b/frontend/src/pages/SaisirCodeUnique.jsx index 60e4bf9..3b3ccba 100644 --- a/frontend/src/pages/SaisirCodeUnique.jsx +++ b/frontend/src/pages/SaisirCodeUnique.jsx @@ -17,7 +17,6 @@ export default function SaisirCodeUnique() { const [code, setCode] = useState('') const [loading, setLoading] = useState(false) const [gain, setGain] = useState(null) - const [claim, setClaim] = useState(null) const [wonAt, setWonAt] = useState(null) const [showDetail, setShowDetail] = useState(false) @@ -27,7 +26,6 @@ export default function SaisirCodeUnique() { try { const data = await participateApi(code.trim().toUpperCase()) setGain(data.gain) - setClaim(data.claim) setWonAt(new Date()) } catch (err) { if (!handleAuthError(err)) { @@ -40,7 +38,6 @@ export default function SaisirCodeUnique() { const handleClose = () => { setGain(null) - setClaim(null) setWonAt(null) setShowDetail(false) setCode('') From 117ebac77067962e950301c128e5e96975658571 Mon Sep 17 00:00:00 2001 From: Andil Date: Mon, 17 Aug 2026 15:19:32 +0000 Subject: [PATCH 10/14] fix: configure SMTP and public API URLs for deployments --- deploy/dev/docker-compose.yml | 4 ++++ deploy/preprod/docker-compose.yml | 4 ++++ deploy/prod/docker-compose.yml | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/deploy/dev/docker-compose.yml b/deploy/dev/docker-compose.yml index a23ae31..c1bafc4 100644 --- a/deploy/dev/docker-compose.yml +++ b/deploy/dev/docker-compose.yml @@ -24,6 +24,9 @@ services: image: ${REGISTRY}/the-tip-top/backend:${IMAGE_TAG} restart: unless-stopped + env_file: + - /opt/furious-ducks/secrets/tiptop-mail.env + depends_on: postgres: condition: service_healthy @@ -34,6 +37,7 @@ services: DATABASE_URL: postgresql://tiptop:tiptop_dev_password@postgres:5432/the_tip_top_db JWT_SECRET: dev_secret_change_me APP_URL: https://api-dev.dsp5-archi-o24a-4-5-g4.fr + API_PUBLIC_URL: https://api-dev.dsp5-archi-o24a-4-5-g4.fr command: > sh -c " diff --git a/deploy/preprod/docker-compose.yml b/deploy/preprod/docker-compose.yml index e8463b9..5296846 100644 --- a/deploy/preprod/docker-compose.yml +++ b/deploy/preprod/docker-compose.yml @@ -24,6 +24,9 @@ services: image: ${REGISTRY}/the-tip-top/backend:${IMAGE_TAG} restart: unless-stopped + env_file: + - /opt/furious-ducks/secrets/tiptop-mail.env + depends_on: postgres: condition: service_healthy @@ -34,6 +37,7 @@ services: DATABASE_URL: postgresql://tiptop_preprod:tiptop_preprod_password@postgres:5432/the_tip_top_preprod JWT_SECRET: preprod_secret_change_me APP_URL: https://api-preprod.dsp5-archi-o24a-4-5-g4.fr + API_PUBLIC_URL: https://api-preprod.dsp5-archi-o24a-4-5-g4.fr command: > sh -c " diff --git a/deploy/prod/docker-compose.yml b/deploy/prod/docker-compose.yml index e784adb..cae0a67 100644 --- a/deploy/prod/docker-compose.yml +++ b/deploy/prod/docker-compose.yml @@ -24,6 +24,9 @@ services: image: ${REGISTRY}/the-tip-top/backend:${IMAGE_TAG} restart: unless-stopped + env_file: + - /opt/furious-ducks/secrets/tiptop-mail.env + depends_on: postgres: condition: service_healthy @@ -34,6 +37,7 @@ services: DATABASE_URL: postgresql://tiptop_prod:tiptop_prod_password@postgres:5432/the_tip_top_prod JWT_SECRET: prod_secret_change_me APP_URL: https://api.dsp5-archi-o24a-4-5-g4.fr + API_PUBLIC_URL: https://api.dsp5-archi-o24a-4-5-g4.fr command: > sh -c " From 1f11a4e3dd38fcdbf313015401e2343d73fdda32 Mon Sep 17 00:00:00 2001 From: Andil Date: Mon, 17 Aug 2026 15:24:50 +0000 Subject: [PATCH 11/14] ci: retry develop deployment after SMTP secret setup From ecda263518b5eea5322b240703783655c206463d Mon Sep 17 00:00:00 2001 From: Andil Date: Mon, 17 Aug 2026 15:30:55 +0000 Subject: [PATCH 12/14] ci: retry deployment with Jenkins SMTP secret mount From 21648ab171417d46229605e005e428ce302a7612 Mon Sep 17 00:00:00 2001 From: Alexis SANOGO Date: Mon, 17 Aug 2026 21:50:21 +0200 Subject: [PATCH 13/14] Ajout de logo authentification Facebook / Google --- frontend/src/components/UserNav.jsx | 3 +-- frontend/src/pages/Connexion.jsx | 26 ++++++++++++++++++-------- frontend/src/pages/Inscription.jsx | 18 ++++++++++++++++-- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/UserNav.jsx b/frontend/src/components/UserNav.jsx index 6582af3..88631c2 100644 --- a/frontend/src/components/UserNav.jsx +++ b/frontend/src/components/UserNav.jsx @@ -47,8 +47,7 @@ export default function UserNav({ active }) { }`} > {tab.icon} - {tab.label} - {tab.label.split(' ')[0]} + {tab.label} ))}
diff --git a/frontend/src/pages/Connexion.jsx b/frontend/src/pages/Connexion.jsx index 3a9b1d2..81d5329 100644 --- a/frontend/src/pages/Connexion.jsx +++ b/frontend/src/pages/Connexion.jsx @@ -125,19 +125,29 @@ export default function Connexion() { type="button" className="flex items-center justify-center gap-2 border border-gray-200 rounded-lg py-3 text-sm text-gray-700 hover:bg-gray-50 transition-colors" > -
- G -
- Google + + + +Google
diff --git a/frontend/src/pages/Inscription.jsx b/frontend/src/pages/Inscription.jsx index 0e15336..0424790 100644 --- a/frontend/src/pages/Inscription.jsx +++ b/frontend/src/pages/Inscription.jsx @@ -215,11 +215,25 @@ export default function Inscription() {
From 61dcbda58119e383a0f1653236e89ec4a3195878 Mon Sep 17 00:00:00 2001 From: Alexis SANOGO Date: Mon, 17 Aug 2026 23:21:04 +0200 Subject: [PATCH 14/14] FAQ depuis la page d'accueil --- frontend/src/pages/Accueil.jsx | 61 ++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/frontend/src/pages/Accueil.jsx b/frontend/src/pages/Accueil.jsx index e8f3a49..1bc0e85 100644 --- a/frontend/src/pages/Accueil.jsx +++ b/frontend/src/pages/Accueil.jsx @@ -52,6 +52,67 @@ export default function Accueil() {
+ {/* Questions fréquentes */} +
+

+ Questions fréquentes +

+
+ {[ + { + question: 'Comment obtenir un code ?', + answer: "Achetez un produit Thé Tip Top d'une valeur supérieure à 49 € en boutique ou en ligne : un code unique à 10 caractères figure sur votre ticket de caisse ou votre facture.", + }, + { + question: 'Quels sont les lots à gagner ?', + answer: ( + <> + Tous les tickets sont gagnants ! Selon votre ticket, vous pouvez remporter : +
    +
  • Un infuseur à thé
  • +
  • Une boîte de 100 g de thé détox ou d'infusion
  • +
  • Une boîte de 100 g de thé signature
  • +
  • Un coffret découverte d'une valeur de 39 €
  • +
  • Un coffret découverte d'une valeur de 69 €
  • +
+ Un tirage au sort désigne en plus un grand gagnant qui remporte une année de thé d'une valeur de 360 €. + + ), + }, + { + question: 'Jusqu\'à quand puis-je jouer ?', + answer: 'La participation est possible pendant toute la durée du jeu-concours, soit 30 jours à compter de son ouverture. Consultez la page Règles du jeu pour le détail des dates.', + }, + { + question: 'Que faire si mon code n\'est pas reconnu ?', + answer: "Vérifiez que le code a été saisi sans espace ni erreur. Si le problème persiste, contactez-nous depuis la page Contact.", + }, + { + question: 'Où retirer mon lot ?', + answer: 'En boutique, sur présentation de votre compte utilisateur validé et de la confirmation de gain affichée dans votre espace « Mes gains ».', + }, + ].map((faq) => ( +
+ + {faq.question} + + + + +
+ {faq.answer} +
+
+ ))} +
+
+ ) }