From 1ef51b1e371652bed349e3550bbb094ddabd610e Mon Sep 17 00:00:00 2001 From: dlawler489 <104159223@student.swin.edu.au> Date: Tue, 21 Apr 2026 21:29:07 +1000 Subject: [PATCH] Fix nginx serving blank page by baking client into its own image The runtime cp from etsy-tracker to a shared volume silently failed because the nodejs user lacked write permissions on the root-owned volume. Now a dedicated nginx-frontend Docker stage is built and pushed to GHCR with the React client baked in, eliminating the volume-sharing approach. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/docker-build.yml | 26 +++++++++++++++++++++++++- Dockerfile | 7 ++++++- docker-compose.deploy.yml | 25 ++----------------------- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 3f2a88e..a88f1a7 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -72,7 +72,7 @@ jobs: exit 1 fi - - name: Build and push Docker image + - name: Build and push API image uses: docker/build-push-action@v5 with: context: . @@ -83,6 +83,30 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max + - name: Extract metadata for Nginx image + id: nginx-meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-nginx + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,prefix=sha- + + - name: Build and push Nginx image + uses: docker/build-push-action@v5 + with: + context: . + target: nginx-frontend + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.nginx-meta.outputs.tags }} + labels: ${{ steps.nginx-meta.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max + - name: Make package public if: github.event_name != 'pull_request' run: | diff --git a/Dockerfile b/Dockerfile index e0e4677..de08abf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -62,4 +62,9 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ # Start the API server ENTRYPOINT ["dumb-init", "--"] -CMD ["node", "server/index.js"] \ No newline at end of file +CMD ["node", "server/index.js"] + +# Stage 4: Nginx server with pre-built client +FROM nginx:alpine AS nginx-frontend +COPY --from=client-build /app/client/dist /usr/share/nginx/html +COPY nginx.deploy.conf /etc/nginx/nginx.conf \ No newline at end of file diff --git a/docker-compose.deploy.yml b/docker-compose.deploy.yml index 30e2c64..cfe9c07 100644 --- a/docker-compose.deploy.yml +++ b/docker-compose.deploy.yml @@ -1,13 +1,10 @@ services: - # Nginx reverse proxy and static file server + # Nginx reverse proxy and static file server (client baked into image) nginx: - image: nginx:alpine + image: ghcr.io/dlawler489/etsy-finance-tracker-nginx:main container_name: etsy-nginx ports: - "3000:80" - volumes: - - ./nginx.deploy.conf:/etc/nginx/nginx.conf:ro - - client_dist:/usr/share/nginx/html:ro depends_on: - etsy-tracker restart: unless-stopped @@ -31,12 +28,8 @@ services: - PORT=8080 - CLIENT_URL=http://nginx volumes: - # Mount data directory for persistent storage - etsy_data:/app/data - # Optional: Mount uploads directory if needed - etsy_uploads:/app/uploads - # Share client build with nginx - - client_dist:/usr/share/nginx/html restart: unless-stopped networks: - etsy-network @@ -46,24 +39,10 @@ services: timeout: 10s retries: 3 start_period: 40s - # Copy client files to shared volume and start server - command: > - sh -c " - echo 'Starting Etsy Finance Tracker API Server...'; - if [ ! -f /usr/share/nginx/html/index.html ]; then - echo 'Copying client files to shared volume...'; - cp -r /app/client/dist/* /usr/share/nginx/html/ 2>/dev/null || true; - echo 'Client files copied successfully'; - fi; - echo 'Starting Node.js server...'; - exec node server/dist/index.js - " volumes: etsy_uploads: driver: local - client_dist: - driver: local etsy_data: driver: local