From dd50bdc933aac7ac310e64fc2aa4d6cf8fddcf0e Mon Sep 17 00:00:00 2001 From: cori Date: Sun, 20 Sep 2026 14:20:48 +0000 Subject: [PATCH] fix: add Caddy proxy to InvenTree for static file serving MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit InvenTree's Django server does not serve static files in production mode — it requires a separate file server (Caddy/nginx) for /static/ and /media/. Without it, the SPA shell loads but JS assets 302 redirect to the Django server which can't serve them, causing INVE-E1 error. Changes: - Add inventree-proxy service (caddy:alpine) as isMain on port 80 - Move isMain/internalPort off inventree-server (now internal-only) - Caddy proxies API requests to inventree-server:8000 - Caddy serves /static/ and /media/ from the shared data volume - Include Caddyfile in data/ directory - Bump tipi_version to 2, port to 80 Verified: login page renders correctly, JS assets load, API auth works. --- apps/inventree/config.json | 4 +-- apps/inventree/data/Caddyfile | 39 ++++++++++++++++++++++++++++++ apps/inventree/docker-compose.json | 22 +++++++++++++++-- 3 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 apps/inventree/data/Caddyfile diff --git a/apps/inventree/config.json b/apps/inventree/config.json index 1f60b09..ec12977 100644 --- a/apps/inventree/config.json +++ b/apps/inventree/config.json @@ -5,10 +5,10 @@ "available": true, "short_desc": "Open-source inventory management for parts, components, BOMs, and stock control", "author": "InvenTree", - "port": 8000, + "port": 80, "categories": ["data", "utilities"], "description": "InvenTree is an open-source inventory management system providing intuitive parts management, stock control, BOM management, and supplier tracking. Designed for makers, hobbyists, and small businesses. Features hierarchical categories, custom fields, build orders, REST API, mobile app with barcode scanning, and a plugin ecosystem.", - "tipi_version": 1, + "tipi_version": 2, "version": "1.5.5", "source": "https://github.com/inventree/InvenTree", "website": "https://inventree.org", diff --git a/apps/inventree/data/Caddyfile b/apps/inventree/data/Caddyfile new file mode 100644 index 0000000..370d56b --- /dev/null +++ b/apps/inventree/data/Caddyfile @@ -0,0 +1,39 @@ +:80 { + encode gzip + + request_body { + max_size 100MB + } + + # Health check endpoint + handle /api/system/health/* { + reverse_proxy inventree-server:8000 + } + + # Serve static files (JS, CSS, images) + handle_path /static/* { + header Allow GET,HEAD,OPTIONS + header Access-Control-Allow-Origin * + header Access-Control-Allow-Methods GET,HEAD,OPTIONS + header Access-Control-Allow-Headers Authorization,Content-Type,User-Agent,traceparent + root * /var/www/static + file_server + } + + # Serve media files (user uploads) with auth + handle_path /media/* { + header Allow GET,HEAD,OPTIONS + header Access-Control-Allow-Origin * + header Access-Control-Allow-Methods GET,HEAD,OPTIONS + header Access-Control-Allow-Headers Authorization,Content-Type,User-Agent,traceparent + root * /var/www/media + file_server + header Content-Disposition attachment + forward_auth inventree-server:8000 { + uri /auth/ + } + } + + # All other requests go to the InvenTree Django server + reverse_proxy inventree-server:8000 +} diff --git a/apps/inventree/docker-compose.json b/apps/inventree/docker-compose.json index 074c239..f98afd2 100644 --- a/apps/inventree/docker-compose.json +++ b/apps/inventree/docker-compose.json @@ -44,8 +44,6 @@ { "name": "inventree-server", "image": "inventree/inventree:1.5.5", - "isMain": true, - "internalPort": "8000", "dependsOn": [ "inventree-db", "inventree-cache" @@ -125,6 +123,26 @@ "timeout": "10s", "retries": 3 } + }, + { + "name": "inventree-proxy", + "image": "caddy:alpine", + "isMain": true, + "internalPort": "80", + "dependsOn": [ + "inventree-server" + ], + "volumes": [ + { "hostPath": "${APP_DATA_DIR}/Caddyfile", "containerPath": "/etc/caddy/Caddyfile" }, + { "hostPath": "${APP_DATA_DIR}/data/static", "containerPath": "/var/www/static" }, + { "hostPath": "${APP_DATA_DIR}/data/media", "containerPath": "/var/www/media" } + ], + "healthCheck": { + "test": "wget --spider -q http://127.0.0.1:80/", + "interval": "20s", + "timeout": "5s", + "retries": 10 + } } ] } \ No newline at end of file