From 5f815d8a543fa8fe65d35b9a489da73a62a51364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joe=20K=C3=BCng?= Date: Tue, 10 Mar 2026 12:42:26 +0100 Subject: [PATCH] fix(back-end) url construction for media --- README.md | 7 +++--- .../service/media/MediaStorageService.java | 24 ++++++++++++++----- .../resources/application-local.properties | 1 - .../src/main/resources/application.properties | 1 - .../AdminMediaControllerServiceTest.java | 2 +- .../media/PublicMediaQueryServiceTest.java | 2 +- docker-compose.deploy.yml | 3 ++- 7 files changed, 26 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 7f45d6f..686dd5a 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Crea un database PostgreSQL chiamato `printcalc`. Lo schema viene gestito dal pr Configura il percorso di OrcaSlicer in `backend/src/main/resources/application.properties` o tramite la variabile d'ambiente `SLICER_PATH`. Per il media service pubblico puoi configurare anche: - `MEDIA_STORAGE_ROOT` per la root `storage_media` usata dal backend (`original/`, `public/`, `private/`) -- `MEDIA_PUBLIC_BASE_URL` per gli URL assoluti restituiti dalle API admin, ad esempio `https://example.com/media` +- `SHOP_STORAGE_ROOT` per la root `storage_shop` usata dal backend per i modelli dei prodotti shop - `MEDIA_FFMPEG_PATH` per il binario `ffmpeg` - `MEDIA_UPLOAD_MAX_FILE_SIZE_BYTES` per il limite per asset immagine @@ -64,12 +64,13 @@ I prezzi non sono più gestiti tramite variabili d'ambiente fisse ma tramite tab * `/frontend`: Applicazione Angular. * `/backend/profiles`: Contiene i file di configurazione per OrcaSlicer. * `/storage_media`: Originali e varianti media pubbliche/private su filesystem. +* `/storage_shop`: Modelli e file prodotti dello shop. ## Media pubblici Il backend salva sempre l'originale in `storage_media/original/` e precomputa le varianti pubbliche in `storage_media/public/`. La cartella `storage_media/private/` è predisposta per asset non pubblici. -Nel deploy Docker il volume media atteso è `/mnt/cache/appdata/print-calculator/${ENV}/storage_media:/app/storage_media`. +Nel deploy Docker i volumi attesi sono `/mnt/cache/appdata/print-calculator/${ENV}/storage_media:/app/storage_media` e `/mnt/cache/appdata/print-calculator/${ENV}/storage_shop:/app/storage_shop`. Nginx non deve passare dal backend per i file pubblici. Configurazione attesa: @@ -106,7 +107,7 @@ Operativamente: Assicurati che `slicer.path` punti al binario corretto. Su macOS è solitamente `/Applications/OrcaSlicer.app/Contents/MacOS/OrcaSlicer`. Su Linux è il percorso all'AppImage (estratta o meno). ### FFmpeg e media pubblici -Verifica che `MEDIA_FFMPEG_PATH` punti a un `ffmpeg` con supporto JPEG, WebP e AVIF. Se gli URL media restituiti dalle API admin non sono raggiungibili, controlla che `MEDIA_PUBLIC_BASE_URL` corrisponda al `location /media/` esposto da Nginx e che il volume `storage_media` sia montato correttamente. +Verifica che `MEDIA_FFMPEG_PATH` punti a un `ffmpeg` con supporto JPEG, WebP e AVIF. Se gli URL media restituiti dalle API admin non sono raggiungibili, controlla che `APP_FRONTEND_BASE_URL` punti al dominio corretto, che `location /media/` sia esposto da Nginx e che il volume `storage_media` sia montato correttamente. ### Database connection Verifica le credenziali in `application.properties`. Se usi Docker, puoi passare `DB_URL`, `DB_USERNAME` e `DB_PASSWORD` come variabili d'ambiente. diff --git a/backend/src/main/java/com/printcalculator/service/media/MediaStorageService.java b/backend/src/main/java/com/printcalculator/service/media/MediaStorageService.java index 1a282d6..d890211 100644 --- a/backend/src/main/java/com/printcalculator/service/media/MediaStorageService.java +++ b/backend/src/main/java/com/printcalculator/service/media/MediaStorageService.java @@ -18,15 +18,15 @@ public class MediaStorageService { private final Path originalRootLocation; private final Path publicRootLocation; private final Path privateRootLocation; - private final String publicBaseUrl; + private final String frontendBaseUrl; public MediaStorageService(@Value("${media.storage.root:storage_media}") String storageRoot, - @Value("${media.public.base-url:http://localhost:8080/media}") String publicBaseUrl) { + @Value("${app.frontend.base-url:${APP_FRONTEND_BASE_URL:http://localhost:8080}}") String frontendBaseUrl) { this.normalizedRootLocation = Paths.get(storageRoot).toAbsolutePath().normalize(); this.originalRootLocation = normalizedRootLocation.resolve("original").normalize(); this.publicRootLocation = normalizedRootLocation.resolve("public").normalize(); this.privateRootLocation = normalizedRootLocation.resolve("private").normalize(); - this.publicBaseUrl = publicBaseUrl; + this.frontendBaseUrl = frontendBaseUrl; init(); } @@ -73,11 +73,12 @@ public class MediaStorageService { if (storageKey == null || storageKey.isBlank()) { return null; } + String mediaBaseUrl = buildMediaBaseUrl(); String normalizedKey = storageKey.startsWith("/") ? storageKey.substring(1) : storageKey; - if (publicBaseUrl.endsWith("/")) { - return publicBaseUrl + normalizedKey; + if (mediaBaseUrl.endsWith("/")) { + return mediaBaseUrl + normalizedKey; } - return publicBaseUrl + "/" + normalizedKey; + return mediaBaseUrl + "/" + normalizedKey; } private void copy(Path source, Path destination) throws IOException { @@ -127,4 +128,15 @@ public class MediaStorageService { } return visibility.trim().toUpperCase(Locale.ROOT); } + + private String buildMediaBaseUrl() { + String normalized = frontendBaseUrl != null ? frontendBaseUrl.trim() : ""; + if (normalized.isBlank()) { + normalized = "http://localhost:4200"; + } + if (normalized.endsWith("/")) { + normalized = normalized.substring(0, normalized.length() - 1); + } + return normalized + "/media"; + } } diff --git a/backend/src/main/resources/application-local.properties b/backend/src/main/resources/application-local.properties index 2366bea..6acebb6 100644 --- a/backend/src/main/resources/application-local.properties +++ b/backend/src/main/resources/application-local.properties @@ -9,5 +9,4 @@ admin.session.ttl-minutes=480 # Local media storage served by a local static server on port 8081. media.storage.root=/Users/joe/IdeaProjects/print-calculator/storage_media -media.public.base-url=http://localhost:8081 media.ffmpeg.path=ffmpeg diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index a9def27..9985c08 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -28,7 +28,6 @@ clamav.enabled=${CLAMAV_ENABLED:false} # Media configuration media.storage.root=${MEDIA_STORAGE_ROOT:storage_media} -media.public.base-url=${MEDIA_PUBLIC_BASE_URL:http://localhost:8080/media} media.ffmpeg.path=${MEDIA_FFMPEG_PATH:ffmpeg} media.upload.max-file-size-bytes=${MEDIA_UPLOAD_MAX_FILE_SIZE_BYTES:26214400} shop.model.max-file-size-bytes=${SHOP_MODEL_MAX_FILE_SIZE_BYTES:104857600} diff --git a/backend/src/test/java/com/printcalculator/service/admin/AdminMediaControllerServiceTest.java b/backend/src/test/java/com/printcalculator/service/admin/AdminMediaControllerServiceTest.java index 9f54e78..55b03d0 100644 --- a/backend/src/test/java/com/printcalculator/service/admin/AdminMediaControllerServiceTest.java +++ b/backend/src/test/java/com/printcalculator/service/admin/AdminMediaControllerServiceTest.java @@ -87,7 +87,7 @@ class AdminMediaControllerServiceTest { storageRoot = tempDir.resolve("storage_media"); MediaStorageService mediaStorageService = new MediaStorageService( storageRoot.toString(), - "https://cdn.example/media" + "https://cdn.example" ); service = new AdminMediaControllerService( diff --git a/backend/src/test/java/com/printcalculator/service/media/PublicMediaQueryServiceTest.java b/backend/src/test/java/com/printcalculator/service/media/PublicMediaQueryServiceTest.java index b3be679..06fae7a 100644 --- a/backend/src/test/java/com/printcalculator/service/media/PublicMediaQueryServiceTest.java +++ b/backend/src/test/java/com/printcalculator/service/media/PublicMediaQueryServiceTest.java @@ -41,7 +41,7 @@ class PublicMediaQueryServiceTest { void setUp() { MediaStorageService mediaStorageService = new MediaStorageService( tempDir.resolve("storage_media").toString(), - "https://cdn.example/media" + "https://cdn.example" ); service = new PublicMediaQueryService(mediaUsageRepository, mediaVariantRepository, mediaStorageService); } diff --git a/docker-compose.deploy.yml b/docker-compose.deploy.yml index 3b2853c..d306e8a 100644 --- a/docker-compose.deploy.yml +++ b/docker-compose.deploy.yml @@ -32,7 +32,7 @@ services: - TEMP_DIR=/app/temp - PROFILES_DIR=/app/profiles - MEDIA_STORAGE_ROOT=${MEDIA_STORAGE_ROOT:-/app/storage_media} - - MEDIA_PUBLIC_BASE_URL=${MEDIA_PUBLIC_BASE_URL:-http://localhost:8080/media} + - SHOP_STORAGE_ROOT=${SHOP_STORAGE_ROOT:-/app/storage_shop} - MEDIA_FFMPEG_PATH=${MEDIA_FFMPEG_PATH:-ffmpeg} - MEDIA_UPLOAD_MAX_FILE_SIZE_BYTES=${MEDIA_UPLOAD_MAX_FILE_SIZE_BYTES:-26214400} restart: always @@ -47,6 +47,7 @@ services: - /mnt/cache/appdata/print-calculator/${ENV}/storage_orders:/app/storage_orders - /mnt/cache/appdata/print-calculator/${ENV}/storage_requests:/app/storage_requests - /mnt/cache/appdata/print-calculator/${ENV}/storage_media:/app/storage_media + - /mnt/cache/appdata/print-calculator/${ENV}/storage_shop:/app/storage_shop extra_hosts: - "host.docker.internal:host-gateway"