From f32a04c56f66bf9d98335f452b7c576664a57f3c Mon Sep 17 00:00:00 2001 From: Nicolas Date: Wed, 9 Sep 2026 23:12:21 +0200 Subject: [PATCH] Ajout d'un Dockerfile multi-stage + .dockerignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Build en 3 étapes (deps prod / compilation TS / runtime), image node:22-bookworm-slim exécutée en non-root (utilisateur `node`). - Debian slim et non Alpine : @fastify/secure-session dépend de sodium-native, dont les prebuilds linux-x64/arm64 ciblent la glibc. - config.json et dossiers de dépôt montés en volumes, jamais dans l'image (.dockerignore exclut .env, config.json, uploads, node_modules, dist...). - HEALTHCHECK sur GET /health ; section Docker ajoutée au README. Co-Authored-By: Claude Sonnet 5 --- .dockerignore | 29 +++++++++++++++++++++++++++++ Dockerfile | 45 +++++++++++++++++++++++++++++++++++++++++++++ README.md | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..7a0dbed --- /dev/null +++ b/.dockerignore @@ -0,0 +1,29 @@ +# Build artefacts / dépendances (réinstallées / recompilées dans l'image) +node_modules +dist +coverage + +# Secrets & données locales — ne JAMAIS envoyer dans le contexte de build +.env +config.json +uploads + +# VCS / outillage / docs sans intérêt au build +.git +.gitattributes +.gitignore +.github +.vscode +.idea +*.log + +# Non nécessaires à la compilation de l'app +tests +vitest.config.ts +eslint.config.mjs +.prettierrc.json +.prettierignore +Dockerfile +.dockerignore +README.md +Claude.md diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..766900e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,45 @@ +# syntax=docker/dockerfile:1 + +# Image Debian slim (glibc) : @fastify/secure-session -> sodium-native embarque +# des prebuilds linux-x64 / linux-arm64 compilés pour la glibc (pas de variante +# musl), donc Alpine ne conviendrait pas. + +# ---- deps : node_modules de production uniquement ---------------------------- +FROM node:22-bookworm-slim AS deps +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci --omit=dev --no-audit --no-fund + +# ---- build : compilation TypeScript ---------------------------------------------- +FROM node:22-bookworm-slim AS build +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci --no-audit --no-fund +COPY tsconfig.json tsconfig.build.json ./ +COPY src ./src +RUN npm run build + +# ---- runtime ---------------------------------------------------------------------- +FROM node:22-bookworm-slim AS runtime +ENV NODE_ENV=production \ + MEDIADROP_CONFIG=/app/config.json +WORKDIR /app + +COPY --from=deps /app/node_modules ./node_modules +COPY --from=build /app/dist ./dist +COPY public ./public +COPY package.json ./ + +# Dossier de dépôt par défaut, inscriptible par l'utilisateur non-root `node`. +# (Pour des cibles hors de /app, monter des volumes appartenant à l'uid 1000.) +RUN mkdir -p /app/uploads && chown -R node:node /app/uploads + +USER node +EXPOSE 3000 + +# GET /health ne demande aucune authentification. +# Adapter le port si `port` diffère de 3000 dans config.json. +HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ + CMD ["node", "-e", "fetch('http://127.0.0.1:3000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"] + +CMD ["node", "dist/index.js"] diff --git a/README.md b/README.md index 394ba2f..8d1f489 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,41 @@ npm run build npm start ``` +## Docker + +Image multi-stage (Debian slim ; Alpine exclu à cause du module natif +`sodium-native`). Le `config.json` et les dossiers de dépôt sont montés, jamais +inclus dans l'image. + +```bash +docker build -t mediadrop . + +docker run --rm -p 3000:3000 \ + -v "$PWD/config.json:/app/config.json:ro" \ + -v "$PWD/data/photos:/data/photos" \ + mediadrop +``` + +Les `dir` des `targets` doivent pointer vers des chemins montés, inscriptibles +par l'uid `1000` (utilisateur `node`). Exemple `compose.yaml` : + +```yaml +services: + mediadrop: + build: . + ports: + - '3000:3000' + volumes: + - ./config.json:/app/config.json:ro + - ./data/photos:/data/photos + - ./data/videos:/data/videos + restart: unless-stopped +``` + +La découverte OIDC a lieu au démarrage : si l'issuer est injoignable, le +conteneur s'arrête (utiliser `restart: unless-stopped`). Le `HEALTHCHECK` +interroge `GET /health` sur le port `3000` — l'adapter si `port` diffère. + ## Endpoints | Route | Auth | Rôle | @@ -151,6 +186,7 @@ Seule variable d'environnement : `MEDIADROP_CONFIG` (chemin du fichier, défaut ``` config.example.json modèle de configuration (à copier en config.json) config.schema.json schéma JSON (validation / autocomplétion éditeur) +Dockerfile image multi-stage (deps / build / runtime non-root) src/ index.ts entrée : config -> app -> écoute + arrêt propre app.ts construction de l'instance Fastify (exportée pour les tests)