From 4a537d6b19e3b99e5c61aa21b3df3685592dfe97 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 11 May 2026 18:57:47 +0200 Subject: [PATCH] feat(infra): bind-mount /import for backend mass-import endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `MassImportService` reads the ODS spreadsheet and referenced PDFs from a hardcoded `/import` path inside the backend container. Dev compose already bind-mounts `./import:/import`, but the prod compose had no equivalent, so `POST /api/admin/import` would always fail on staging/prod with "no spreadsheet found". Mount strategy: - Source path is env-driven (`IMPORT_HOST_DIR`), defaulting to `/srv/familienarchiv/import` so the host path is stable across CI deploys (the compose working dir is recreated each run, so `./import` would not persist). - Read-only — `MassImportService` only reads (`Files.list` / `Files.walk`), never writes. Read-only mount makes that contract explicit and prevents the backend container from mutating the source PDFs. - Empty / missing path is harmless: the import API just returns the existing "no spreadsheet found" error rather than crashing the container. To use on staging: rsync the import folder to `/srv/familienarchiv-staging/import/` on the host, set `IMPORT_HOST_DIR=/srv/familienarchiv-staging/import` in `.env.staging`, redeploy, trigger import from `/admin/system`. Co-Authored-By: Claude Opus 4.7 --- docker-compose.prod.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index e56ac956..b4044014 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -26,6 +26,14 @@ # MAIL_HOST, MAIL_PORT, SMTP relay (production only; staging uses mailpit) # MAIL_USERNAME, MAIL_PASSWORD # APP_MAIL_FROM sender address (e.g. noreply@raddatz.cloud) +# +# Optional env vars: +# IMPORT_HOST_DIR absolute host path holding the ODS spreadsheet +# and PDFs for /admin/system mass import. Mounted +# read-only at /import inside the backend. +# Defaults to /srv/familienarchiv/import. When the +# path is empty the import API simply reports +# "no spreadsheet found" — no crash. networks: archiv-net: @@ -173,6 +181,12 @@ services: # Bound to localhost only — Caddy fronts external traffic. ports: - "127.0.0.1:${PORT_BACKEND}:8080" + # Host path holding the ODS spreadsheet + PDFs for the mass-import endpoint. + # Read-only; MassImportService only reads (Files.list / Files.walk on /import). + # Outside the compose working dir on purpose — that dir is recreated per CI + # deploy. See IMPORT_HOST_DIR in the header for the env-var contract. + volumes: + - ${IMPORT_HOST_DIR:-/srv/familienarchiv/import}:/import:ro environment: SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/archiv SPRING_DATASOURCE_USERNAME: archiv