diff --git a/.gitignore b/.gitignore index 32a1257..b551081 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,6 @@ npm-debug.log* # Build outputs dist/ build/ + +# Backend data (uploaded images, database, etc.) +backend/src/data/ diff --git a/README.dev.md b/README.dev.md index 79f5ad3..96dbcf6 100644 --- a/README.dev.md +++ b/README.dev.md @@ -1,52 +1,79 @@ -## Dev: Schnellstart +# Development Setup -Kurz und knapp — so startest und nutzt du die lokale Dev‑Umgebung mit HMR (nginx als Proxy vor dem CRA dev server): +## Schnellstart -Voraussetzungen -- Docker & Docker Compose (Docker Compose Plugin) +### Starten (Development Environment) -Starten (Dev) -1. Build & Start (daemon): ```bash -docker compose up --build -d image-uploader-frontend -``` -2. Logs verfolgen: -```bash -docker compose logs -f image-uploader-frontend -``` -3. Browser öffnen: http://localhost:3000 (HMR aktiv) +# Mit Script (empfohlen): +./dev.sh -Ändern & Testen -- Dateien editieren im `frontend/src/...` → HMR übernimmt Änderungen sofort. -- Wenn du nginx‑Konfiguration anpassen willst, editiere `frontend/conf/conf.d/default.conf` (Dev‑Variante wird beim Containerstart benutzt). Nach Änderung: nginx reload ohne Neustart: -```bash -docker compose exec image-uploader-frontend nginx -s reload +# Oder manuell: +docker compose -f docker/dev/docker-compose.yml up -d ``` -Probleme mit `node_modules` -- Wenn du ein host‑seitiges `frontend/node_modules` hast, lösche es (konsistenter ist der container‑verwaltete Volume): -```bash -rm -rf frontend/node_modules -``` -Danach `docker compose up --build -d image-uploader-frontend` erneut ausführen. +### Zugriff +- **Frontend**: http://localhost:3000 (Hot Module Reloading aktiv) +- **Backend**: http://localhost:5001 (API) +- **Slideshow**: http://localhost:3000/slideshow -Stoppen +### Logs verfolgen ```bash -docker compose down +# Alle Services: +docker compose -f docker/dev/docker-compose.yml logs -f + +# Nur Frontend: +docker compose -f docker/dev/docker-compose.yml logs -f frontend-dev + +# Nur Backend: +docker compose -f docker/dev/docker-compose.yml logs -f backend-dev ``` -Hinweis -- Diese Dev‑Konfiguration läuft lokal mit erweiterten Rechten (nur für Entwicklung). Produktions‑Images/Configs bleiben unverändert. +### Entwicklung +#### Frontend-Entwicklung +- Code in `frontend/src/` editieren → Hot Module Reload übernimmt Änderungen +- Volumes: Source-Code wird live in Container gemountet +- Container-Namen: `image-uploader-frontend-dev` -Build and start: -docker compose up --build -d image-uploader-frontend +#### Backend-Entwicklung +- Code in `backend/src/` editieren → Container restart für Änderungen +- Container-Namen: `image-uploader-backend-dev` +- Environment: `NODE_ENV=development` -Tail logs: -docker compose logs -f image-uploader-frontend +#### Konfiguration anpassen +- **Frontend**: `docker/dev/frontend/config/.env` +- **Backend**: `docker/dev/backend/config/.env` +- **Nginx**: `docker/dev/frontend/nginx.conf` -Reload nginx (after editing conf in container): -docker compose exec image-uploader-frontend nginx -s reload +### Container-Management + +```bash +# Status anzeigen: +docker compose -f docker/dev/docker-compose.yml ps + +# Container neustarten: +docker compose -f docker/dev/docker-compose.yml restart + +# Container neu bauen: +docker compose -f docker/dev/docker-compose.yml build --no-cache + +# Stoppen: +docker compose -f docker/dev/docker-compose.yml down + +# Mit Volumes löschen: +docker compose -f docker/dev/docker-compose.yml down -v +``` + +### Shell-Zugriff + +```bash +# Frontend Container: +docker compose -f docker/dev/docker-compose.yml exec frontend-dev bash + +# Backend Container: +docker compose -f docker/dev/docker-compose.yml exec backend-dev bash +``` docker compose exec image-uploader-frontend nginx -s reload docker compose down \ No newline at end of file diff --git a/README.md b/README.md index 7ab7800..15cb47b 100644 --- a/README.md +++ b/README.md @@ -37,63 +37,36 @@ This project extends the original [Image-Uploader by vallezw](https://github.com ### Docker Deployment (Recommended) -1. **Create docker-compose.yml**: - -```yaml -services: - image-uploader-frontend: - image: gitea.lan.hobbyhimmel.de/hobbyhimmel/image-uploader-frontend:latest - ports: - - "80:80" - build: - context: ./frontend - dockerfile: ./Dockerfile - depends_on: - - "image-uploader-backend" - environment: - - "API_URL=http://image-uploader-backend:5000" - - "CLIENT_URL=http://localhost" - container_name: "image-uploader-frontend" - networks: - - npm-nw - - image-uploader-internal - - image-uploader-backend: - image: gitea.lan.hobbyhimmel.de/hobbyhimmel/image-uploader-backend:latest - ports: - - "5000:5000" - build: - context: ./backend - dockerfile: ./Dockerfile - container_name: "image-uploader-backend" - networks: - - image-uploader-internal - volumes: - - app-data:/usr/src/app/src/data - -volumes: - app-data: - driver: local - -networks: - npm-nw: - external: true - image-uploader-internal: - driver: bridge -``` - -2. **Start the application**: +#### Production Environment ```bash -docker compose up -d +# Start production environment +./prod.sh + +# Or manually: +docker compose -f docker/prod/docker-compose.yml up -d ``` +#### Development Environment -3. **Access the application**: +```bash +# Start development environment +./dev.sh -- Upload Interface: `http://localhost` -- Backend: `http://localhost:5000` +# Or manually: +docker compose -f docker/dev/docker-compose.yml up -d +### Access URLs + +#### Production (Port 80): +- Upload Interface: `http://localhost` - Slideshow Mode: `http://localhost/slideshow` +- Groups Overview: `http://localhost/groups` +- Admin Panel: `http://localhost/moderation` (requires authentication) + +#### Development (Port 3000): +- Upload Interface: `http://localhost:3000` +- Backend API: `http://localhost:5001` +- Slideshow Mode: `http://localhost:3000/slideshow` ### Multi-Image Upload @@ -166,6 +139,44 @@ The application automatically generates optimized preview thumbnails for all upl - View group statistics and metadata +## Docker Structure + +The application uses separate Docker configurations for development and production: + +``` +docker/ +├── .env.backend.example # Backend environment variables documentation +├── .env.frontend.example # Frontend environment variables documentation +├── dev/ # Development environment +│ ├── docker-compose.yml # Development services configuration +│ ├── backend/ +│ │ ├── config/.env # Development backend configuration +│ │ └── Dockerfile # Development backend container +│ └── frontend/ +│ ├── config/.env # Development frontend configuration +│ ├── config/env.sh # Runtime configuration script +│ ├── Dockerfile # Development frontend container +│ ├── nginx.conf # Development nginx configuration +│ └── start.sh # Development startup script +└── prod/ # Production environment + ├── docker-compose.yml # Production services configuration + ├── backend/ + │ ├── config/.env # Production backend configuration + │ └── Dockerfile # Production backend container + └── frontend/ + ├── config/.env # Production frontend configuration + ├── config/env.sh # Runtime configuration script + ├── config/htpasswd # HTTP Basic Auth credentials + ├── Dockerfile # Production frontend container + └── nginx.conf # Production nginx configuration +``` + +### Environment Configuration + +- **Development**: Uses `docker/dev/` configuration with live reloading +- **Production**: Uses `docker/prod/` configuration with optimized builds +- **Scripts**: Use `./dev.sh` or `./prod.sh` for easy deployment + ## Data Structure Data are stored in sqlite database. The structure is as follows: diff --git a/backend/.env.example b/backend/.env.example index 84a1929..ddad885 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -1 +1,15 @@ -REMOVE_IMAGES= \ No newline at end of file +# Backend Environment Variables +# Copy this file to .env and adjust values for local development + +# Whether to remove images when starting the server (cleanup) +REMOVE_IMAGES=false + +# Node.js environment (development, production, test) +NODE_ENV=development + +# Port for the backend server +PORT=5000 + +# Database settings (if needed in future) +# DB_HOST=localhost +# DB_PORT=3306 \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile deleted file mode 100644 index e3f529f..0000000 --- a/backend/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM node:24 - -WORKDIR /usr/src/app - -# Note: Node 24 LTS (v24.11.0) uses Debian Bookworm - -# Install sqlite3 CLI -RUN apt-get update && apt-get install -y sqlite3 && rm -rf /var/lib/apt/lists/* - -COPY package*.json ./ - -# Development -RUN npm install - -# Production -# RUN npm ci --only=production - -COPY . . - -EXPOSE 5000 -CMD [ "node", "src/index.js" ] \ No newline at end of file diff --git a/dev.sh b/dev.sh new file mode 100755 index 0000000..cf5de60 --- /dev/null +++ b/dev.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# Development Environment Startup Script +# Starts the Project Image Uploader in development mode + +set -euo pipefail + +echo "🚀 Starting Project Image Uploader - Development Environment" +echo " Frontend: http://localhost:3000" +echo " Backend: http://localhost:5001" +echo "" + +# Check if production is running +if docker compose ps | grep -q "image-uploader-frontend.*Up"; then + echo "⚠️ Production environment is running (Port 80)" + echo " Development will run on Port 3000 (no conflict)" + echo "" +fi + +# Start development environment +echo "📦 Starting development containers..." +docker compose -f docker/dev/docker-compose.yml up -d + +echo "" +echo "✅ Development environment started!" +echo "" +echo "📊 Container Status:" +docker compose -f docker/dev/docker-compose.yml ps + +echo "" +echo "🔗 Access URLs:" +echo " 📱 Frontend (Development): http://localhost:3000" +echo " 🔧 Backend API (Development): http://localhost:5001" +echo "" +echo "📝 Useful Commands:" +echo " 📋 Show logs: docker compose -f docker/dev/docker-compose.yml logs -f" +echo " 🛑 Stop: docker compose -f docker/dev/docker-compose.yml down" +echo " 🔄 Restart: docker compose -f docker/dev/docker-compose.yml restart" +echo " 🏗️ Rebuild: docker compose -f docker/dev/docker-compose.yml build --no-cache" +echo "" \ No newline at end of file diff --git a/docker-compose.override.yml b/docker-compose.override.yml deleted file mode 100644 index 93bfedd..0000000 --- a/docker-compose.override.yml +++ /dev/null @@ -1,68 +0,0 @@ -# Development override to mount the frontend source into a node container -# and run the React dev server with HMR so you can edit files locally -# without rebuilding images. This file is intended to be used together -# with the existing docker-compose.yml from the repository. - -services: - image-uploader-frontend: - container_name: image-uploader-frontend-dev - # For dev convenience nginx needs to be able to bind to port 80 and write the pid file - # and we also adjust file permissions on bind-mounted node_modules; run as root in dev. - user: root - # Build and run a development image that contains both nginx and the - # React dev server. nginx will act as a reverse proxy to the dev server - # so the app behaves more like production while HMR still works. - build: - context: ./frontend - dockerfile: Dockerfile.dev - working_dir: /app - # Map host port 3000 to the nginx listener (container:80) so you can open - # http://localhost:3000 and see the nginx-served dev site. - ports: - - "3000:80" - volumes: - - ./frontend:/app:cached - # Keep container node_modules separate so host node_modules doesn't conflict - - node_modules:/app/node_modules - environment: - # Use the backend service name so the dev frontend (running in the same - # compose project) can reach the backend via the internal docker network. - - CHOKIDAR_USEPOLLING=true - - HOST=0.0.0.0 - - API_URL=http://image-uploader-backend:5000 - - CLIENT_URL=http://localhost:3000 - networks: - - npm-nw - - image-uploader-internal - depends_on: - - image-uploader-backend - image-uploader-backend: - container_name: image-uploader-backend-dev - build: - context: ./backend - dockerfile: Dockerfile - working_dir: /usr/src/app - ports: - - "5000:5000" - volumes: - - ./backend:/usr/src/app:cached - - backend_node_modules:/usr/src/app/node_modules - environment: - - NODE_ENV=development - networks: - - image-uploader-internal - command: [ "npm", "run", "server" ] - # The Dockerfile.dev provides a proper CMD that starts nginx and the - # react dev server; no ad-hoc command is required here. - -networks: - npm-nw: - external: true - image-uploader-internal: - driver: bridge - -volumes: - node_modules: - driver: local - backend_node_modules: - driver: local \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index b9a820d..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,40 +0,0 @@ -services: - image-uploader-frontend: - image: gitea.lan.hobbyhimmel.de/hobbyhimmel/image-uploader-frontend:latest - ports: - - "80:80" - build: - context: ./frontend - dockerfile: ./Dockerfile - depends_on: - - "image-uploader-backend" - environment: - - "API_URL=http://image-uploader-backend:5000" - - "CLIENT_URL=http://localhost" - container_name: "image-uploader-frontend" - networks: - - npm-nw - - image-uploader-internal - - image-uploader-backend: - image: gitea.lan.hobbyhimmel.de/hobbyhimmel/image-uploader-backend:latest - ports: - - "5000:5000" - build: - context: ./backend - dockerfile: ./Dockerfile - container_name: "image-uploader-backend" - networks: - - image-uploader-internal - volumes: - - app-data:/usr/src/app/src/data - -volumes: - app-data: - driver: local - -networks: - npm-nw: - external: true - image-uploader-internal: - driver: bridge \ No newline at end of file diff --git a/docker/.env.backend.example b/docker/.env.backend.example new file mode 100644 index 0000000..ddad885 --- /dev/null +++ b/docker/.env.backend.example @@ -0,0 +1,15 @@ +# Backend Environment Variables +# Copy this file to .env and adjust values for local development + +# Whether to remove images when starting the server (cleanup) +REMOVE_IMAGES=false + +# Node.js environment (development, production, test) +NODE_ENV=development + +# Port for the backend server +PORT=5000 + +# Database settings (if needed in future) +# DB_HOST=localhost +# DB_PORT=3306 \ No newline at end of file diff --git a/docker/.env.frontend.example b/docker/.env.frontend.example new file mode 100644 index 0000000..57f1b3e --- /dev/null +++ b/docker/.env.frontend.example @@ -0,0 +1,12 @@ +# Frontend Environment Variables +# These variables are used in both development and production containers + +# Backend API URL - where the frontend should connect to the backend +# Development: http://backend-dev:5000 (container-to-container) +# Production: http://backend:5000 (container-to-container) +API_URL=http://backend:5000 + +# Client URL - the URL where users access the frontend +# Development: http://localhost:3000 (dev server) +# Production: http://localhost (nginx on port 80) +CLIENT_URL=http://localhost \ No newline at end of file diff --git a/docker/dev/backend/Dockerfile b/docker/dev/backend/Dockerfile new file mode 100644 index 0000000..dd0f13e --- /dev/null +++ b/docker/dev/backend/Dockerfile @@ -0,0 +1,22 @@ +FROM node:24 + +WORKDIR /usr/src/app + +# Install SQLite for database operations +RUN apt-get update && apt-get install -y sqlite3 && rm -rf /var/lib/apt/lists/* + +# Copy package files and install dependencies +COPY backend/package*.json ./ +RUN npm install + +# Copy backend source code +COPY backend/ . + +# Copy development environment configuration +COPY docker/dev/backend/config/.env ./.env + +# Expose port +EXPOSE 5000 + +# Development command (will be overridden by docker-compose) +CMD ["npm", "run", "server"] \ No newline at end of file diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml new file mode 100644 index 0000000..57fc2d8 --- /dev/null +++ b/docker/dev/docker-compose.yml @@ -0,0 +1,52 @@ +# Development Environment +# Usage: docker compose -f docker/dev/docker-compose.yml up -d +# Or use: ./dev.sh + +services: + frontend-dev: + container_name: image-uploader-frontend-dev + user: root + build: + context: ../../ + dockerfile: docker/dev/frontend/Dockerfile + working_dir: /app + ports: + - "3000:80" + volumes: + - ../../frontend:/app:cached + - dev_frontend_node_modules:/app/node_modules + environment: + - CHOKIDAR_USEPOLLING=true + - API_URL=http://backend-dev:5000 + - CLIENT_URL=http://localhost:3000 + depends_on: + - backend-dev + networks: + - dev-internal + + backend-dev: + container_name: image-uploader-backend-dev + build: + context: ../../ + dockerfile: docker/dev/backend/Dockerfile + working_dir: /usr/src/app + ports: + - "5001:5000" + volumes: + - ../../backend:/usr/src/app:cached + - dev_backend_node_modules:/usr/src/app/node_modules + environment: + - NODE_ENV=development + networks: + - dev-internal + command: [ "npm", "run", "server" ] + +networks: + dev-internal: + driver: bridge + +volumes: + dev_frontend_node_modules: + driver: local + dev_backend_node_modules: + driver: local \ No newline at end of file diff --git a/frontend/Dockerfile.dev b/docker/dev/frontend/Dockerfile similarity index 78% rename from frontend/Dockerfile.dev rename to docker/dev/frontend/Dockerfile index 1537ca4..edfffeb 100644 --- a/frontend/Dockerfile.dev +++ b/docker/dev/frontend/Dockerfile @@ -11,9 +11,12 @@ RUN useradd -m appuser || true WORKDIR /app # Copy package files first to leverage Docker cache for npm install -COPY package*.json ./ -COPY env.sh ./ -COPY nginx.dev.conf /etc/nginx/conf.d/default.conf +COPY frontend/package*.json ./ +COPY docker/dev/frontend/config/env.sh ./ +COPY docker/dev/frontend/config/.env ./ + +# Copy nginx configuration for development +COPY docker/dev/frontend/nginx.conf /etc/nginx/conf.d/default.conf # Make /app owned by the non-root user, then run npm as that user so # node_modules are created with the correct owner and we avoid an expensive @@ -27,8 +30,8 @@ RUN npm ci --no-audit --no-fund # Switch back to root to add the start script and adjust nginx paths USER root -COPY start-dev.sh /start-dev.sh -RUN chmod +x /start-dev.sh +COPY docker/dev/frontend/start.sh /start.sh +RUN chmod +x /start.sh # Ensure nginx log/lib dirs are writable by the app user (small set) RUN chown -R appuser:appuser /var/lib/nginx /var/log/nginx || true @@ -39,4 +42,4 @@ USER appuser EXPOSE 80 3000 -CMD ["/start-dev.sh"] +CMD ["/start.sh"] \ No newline at end of file diff --git a/frontend/env.sh b/docker/dev/frontend/config/env.sh similarity index 100% rename from frontend/env.sh rename to docker/dev/frontend/config/env.sh diff --git a/frontend/nginx.dev.conf b/docker/dev/frontend/nginx.conf similarity index 64% rename from frontend/nginx.dev.conf rename to docker/dev/frontend/nginx.conf index a39328b..2cbfb05 100644 --- a/frontend/nginx.dev.conf +++ b/docker/dev/frontend/nginx.conf @@ -1,11 +1,12 @@ server { listen 80; server_name localhost; + client_max_body_size 200M; - # API proxy to backend - must come before / location + # API proxy to development backend # Upload endpoint location /api/upload { - proxy_pass http://image-uploader-backend:5000/upload; + proxy_pass http://backend-dev:5000/upload; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -15,7 +16,7 @@ server { # Download original images location /api/download { - proxy_pass http://image-uploader-backend:5000/download; + proxy_pass http://backend-dev:5000/download; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -24,7 +25,7 @@ server { # Preview/thumbnail images (optimized for gallery views) location /api/previews { - proxy_pass http://image-uploader-backend:5000/previews; + proxy_pass http://backend-dev:5000/previews; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -33,57 +34,51 @@ server { # Groups API location /api/groups { - proxy_pass http://image-uploader-backend:5000/groups; + proxy_pass http://backend-dev:5000/groups; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } - # Moderation API (groups) + # Moderation Groups API location /moderation/groups { - proxy_pass http://image-uploader-backend:5000/moderation/groups; + proxy_pass http://backend-dev:5000/moderation/groups; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } - # Groups routes (both API and page routes) - location /groups { - # Try to serve as static file first, then proxy to React dev server - try_files $uri @proxy; + # Groups dynamic routes + location ~ ^/groups/[a-zA-Z0-9_-]+(/.*)?$ { + proxy_pass http://backend-dev:5000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; } - # Download endpoint (legacy, without /api prefix) + # Moderation routes + location /moderation { + proxy_pass http://backend-dev:5000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Legacy download endpoint (backwards compatibility) location /download { - proxy_pass http://image-uploader-backend:5000/download; + proxy_pass http://backend-dev:5000/download; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } - # Proxy requests to the CRA dev server so nginx can be used as reverse proxy - location /sockjs-node/ { - proxy_pass http://127.0.0.1:3000; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - - location /sockjs-node { - proxy_pass http://127.0.0.1:3000; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - } - - location @proxy { + # WebSocket support for hot reloading (React Dev Server) + location /ws { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; @@ -93,6 +88,7 @@ server { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } + # All other requests go to React Dev Server for Hot Module Reloading location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; @@ -102,10 +98,4 @@ server { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } - - # If a production build exists, serve static files directly for speed. - location /static/ { - alias /app/build/static/; - try_files $uri $uri/ =404; - } -} +} \ No newline at end of file diff --git a/docker/dev/frontend/start.sh b/docker/dev/frontend/start.sh new file mode 100644 index 0000000..6f96eae --- /dev/null +++ b/docker/dev/frontend/start.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -euo pipefail + +# Make public writable so env.sh can write env-config.js +chmod -R a+rw ./public || true + +# Run env.sh if present +if [ -x ./env.sh ]; then + ./env.sh || true +fi + +# Ensure node cache exists and is writable +mkdir -p /app/node_modules/.cache || true +chmod -R a+rw /app/node_modules || true + +# Ensure HOST is set so CRA binds to 0.0.0.0 +export HOST=${HOST:-0.0.0.0} + +echo "🚀 Starting React development server..." +# Start the React development server in background +npm run dev & +DEV_PID=$! + +echo "🌐 Starting nginx proxy..." +# Start nginx in foreground so container stays alive; nginx will proxy to the dev server +exec nginx -g 'daemon off;' \ No newline at end of file diff --git a/docker/prod/backend/Dockerfile b/docker/prod/backend/Dockerfile new file mode 100644 index 0000000..3facaad --- /dev/null +++ b/docker/prod/backend/Dockerfile @@ -0,0 +1,24 @@ +FROM node:24-alpine + +WORKDIR /usr/src/app + +# Copy package.json and package-lock.json (if available) +COPY backend/package*.json ./ + +# Install dependencies +RUN npm install --production + +# Copy the source code +COPY backend/src ./src + +# Copy production environment configuration +COPY docker/prod/backend/config/.env ./.env + +# Create data directories for file storage +RUN mkdir -p src/data/images src/data/previews src/data/groups + +# Expose port 5000 +EXPOSE 5000 + +# Start the application +CMD ["node", "src/index.js"] \ No newline at end of file diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml new file mode 100644 index 0000000..6c798bf --- /dev/null +++ b/docker/prod/docker-compose.yml @@ -0,0 +1,46 @@ +# Production Environment +# Usage: docker compose -f docker/prod/docker-compose.yml up -d +# Or use: ./prod.sh + +services: + frontend: + container_name: image-uploader-frontend + image: gitea.lan.hobbyhimmel.de/hobbyhimmel/image-uploader-frontend:latest + ports: + - "80:80" + build: + context: ../../ + dockerfile: docker/prod/frontend/Dockerfile + depends_on: + - backend + environment: + - API_URL=http://backend:5000 + - CLIENT_URL=http://localhost + networks: + - npm-nw + - prod-internal + + backend: + container_name: image-uploader-backend + image: gitea.lan.hobbyhimmel.de/hobbyhimmel/image-uploader-backend:latest + build: + context: ../../ + dockerfile: docker/prod/backend/Dockerfile + ports: + - "5000:5000" + volumes: + - image_data:/usr/src/app/src/data + networks: + - prod-internal + environment: + - NODE_ENV=production + +networks: + npm-nw: + external: true + prod-internal: + driver: bridge + +volumes: + image_data: + driver: local \ No newline at end of file diff --git a/frontend/Dockerfile b/docker/prod/frontend/Dockerfile similarity index 69% rename from frontend/Dockerfile rename to docker/prod/frontend/Dockerfile index d0b9c9a..202695c 100644 --- a/frontend/Dockerfile +++ b/docker/prod/frontend/Dockerfile @@ -1,9 +1,9 @@ # => Build container FROM node:18-alpine AS build WORKDIR /app -COPY package.json ./ +COPY frontend/package.json ./ RUN npm install --silent -COPY . ./ +COPY frontend/ ./ ENV NODE_OPTIONS=--openssl-legacy-provider RUN npm run build @@ -12,10 +12,11 @@ FROM nginx:stable-alpine # Nginx config RUN rm -rf /etc/nginx/conf.d -COPY conf /etc/nginx +COPY docker/prod/frontend/nginx.conf /etc/nginx/nginx.conf +COPY frontend/conf /etc/nginx # Copy htpasswd file for authentication -COPY htpasswd /etc/nginx/.htpasswd +COPY docker/prod/frontend/config/htpasswd /etc/nginx/.htpasswd # Static build COPY --from=build /app/build /usr/share/nginx/html @@ -25,8 +26,8 @@ EXPOSE 80 # Copy .env file and shell script to container WORKDIR /usr/share/nginx/html -COPY ./env.sh ./ -COPY ./.env ./ +COPY docker/prod/frontend/config/env.sh ./ +COPY docker/prod/frontend/config/.env ./ # Add bash RUN apk add --no-cache bash diff --git a/docker/prod/frontend/config/env.sh b/docker/prod/frontend/config/env.sh new file mode 100755 index 0000000..e8ce862 --- /dev/null +++ b/docker/prod/frontend/config/env.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Recreate config file +rm -rf ./env-config.js +touch ./env-config.js + +# Add assignment +echo "window._env_ = {" >> ./env-config.js + +# Read each line in .env file +# Each line represents key=value pairs +while read -r line || [[ -n "$line" ]]; +do + # Split env variables by character `=` + if printf '%s\n' "$line" | grep -q -e '='; then + varname=$(printf '%s\n' "$line" | sed -e 's/=.*//') + varvalue=$(printf '%s\n' "$line" | sed -e 's/^[^=]*=//') + fi + + # Read value of current variable if exists as Environment variable + value=$(printf '%s\n' "${!varname}") + # Otherwise use value from .env file + [[ -z $value ]] && value=${varvalue} + + # Append configuration property to JS file + echo " $varname: \"$value\"," >> ./env-config.js +done < .env + +echo "}" >> ./env-config.js diff --git a/frontend/htpasswd b/docker/prod/frontend/config/htpasswd similarity index 100% rename from frontend/htpasswd rename to docker/prod/frontend/config/htpasswd diff --git a/docker/prod/frontend/nginx.conf b/docker/prod/frontend/nginx.conf new file mode 100644 index 0000000..8464f72 --- /dev/null +++ b/docker/prod/frontend/nginx.conf @@ -0,0 +1,159 @@ +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Logging + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + error_log /var/log/nginx/error.log warn; + + # Gzip Settings + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_proxied any; + gzip_comp_level 6; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/atom+xml + image/svg+xml; + + # Server Config + server { + listen 80; + + # Allow large uploads (50MB) + client_max_body_size 50M; + + # API proxy to image-uploader-backend service + location /upload { + proxy_pass http://image-uploader-backend:5000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Allow large uploads for API too + client_max_body_size 50M; + } + + # API routes for new multi-upload features + location /api/upload { + proxy_pass http://image-uploader-backend:5000/upload; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Allow large uploads for batch upload + client_max_body_size 100M; + } + + # API - Download original images + location /api/download { + proxy_pass http://image-uploader-backend:5000/download; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # API - Preview/thumbnail images (optimized for gallery views) + location /api/previews { + proxy_pass http://image-uploader-backend:5000/previews; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # API - Groups (NO PASSWORD PROTECTION) + location /api/groups { + proxy_pass http://image-uploader-backend:5000/groups; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Protected API - Moderation API routes (password protected) - must come before /groups + location /moderation/groups { + auth_basic "Restricted Area - Moderation API"; + auth_basic_user_file /etc/nginx/.htpasswd; + + proxy_pass http://image-uploader-backend:5000/moderation/groups; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # API - Groups API routes (NO PASSWORD PROTECTION) + location ~ ^/groups/[a-zA-Z0-9_-]+(/.*)?$ { + proxy_pass http://image-uploader-backend:5000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /download { + proxy_pass http://image-uploader-backend:5000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Frontend page - Groups overview (NO PASSWORD PROTECTION) + location /groups { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + expires -1; + + # Prevent indexing + add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive" always; + } + + # Protected routes - Moderation (password protected) + location /moderation { + auth_basic "Restricted Area - Moderation"; + auth_basic_user_file /etc/nginx/.htpasswd; + + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + expires -1; + + # Prevent indexing + add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive" always; + } + + # Frontend files + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + expires -1; # Set it to different value depending on your standard requirements + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + } +} \ No newline at end of file diff --git a/frontend/.env b/frontend/.env deleted file mode 100644 index 2257bfe..0000000 --- a/frontend/.env +++ /dev/null @@ -1,2 +0,0 @@ -API_URL=http://localhost -CLIENT_URL=http://localhost \ No newline at end of file diff --git a/frontend/conf/conf.d/default.conf b/frontend/conf/conf.d/default.conf deleted file mode 100644 index 0617d86..0000000 --- a/frontend/conf/conf.d/default.conf +++ /dev/null @@ -1,124 +0,0 @@ -server { - listen 80; - - # Allow large uploads (50MB) - client_max_body_size 50M; - - # API proxy to image-uploader-backend service - location /upload { - proxy_pass http://image-uploader-backend:5000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - # Allow large uploads for API too - client_max_body_size 50M; - } - - # API routes for new multi-upload features - location /api/upload { - proxy_pass http://image-uploader-backend:5000/upload; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - # Allow large uploads for batch upload - client_max_body_size 100M; - } - - # API - Download original images - location /api/download { - proxy_pass http://image-uploader-backend:5000/download; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # API - Preview/thumbnail images (optimized for gallery views) - location /api/previews { - proxy_pass http://image-uploader-backend:5000/previews; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # API - Groups (NO PASSWORD PROTECTION) - location /api/groups { - proxy_pass http://image-uploader-backend:5000/groups; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # Protected API - Moderation API routes (password protected) - must come before /groups - location /moderation/groups { - auth_basic "Restricted Area - Moderation API"; - auth_basic_user_file /etc/nginx/.htpasswd; - - proxy_pass http://image-uploader-backend:5000/moderation/groups; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # API - Groups API routes (NO PASSWORD PROTECTION) - location ~ ^/groups/[a-zA-Z0-9_-]+(/.*)?$ { - proxy_pass http://image-uploader-backend:5000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location /download { - proxy_pass http://image-uploader-backend:5000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # Frontend page - Groups overview (NO PASSWORD PROTECTION) - location /groups { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - expires -1; - - # Prevent indexing - add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive" always; - } - - # Protected routes - Moderation (password protected) - location /moderation { - auth_basic "Restricted Area - Moderation"; - auth_basic_user_file /etc/nginx/.htpasswd; - - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - expires -1; - - # Prevent indexing - add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive" always; - } - - # Frontend files - location / { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - expires -1; # Set it to different value depending on your standard requirements - } - - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root /usr/share/nginx/html; - } -} \ No newline at end of file diff --git a/frontend/conf/conf.d/default.conf.backup b/frontend/conf/conf.d/default.conf.backup deleted file mode 100644 index 0617d86..0000000 --- a/frontend/conf/conf.d/default.conf.backup +++ /dev/null @@ -1,124 +0,0 @@ -server { - listen 80; - - # Allow large uploads (50MB) - client_max_body_size 50M; - - # API proxy to image-uploader-backend service - location /upload { - proxy_pass http://image-uploader-backend:5000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - # Allow large uploads for API too - client_max_body_size 50M; - } - - # API routes for new multi-upload features - location /api/upload { - proxy_pass http://image-uploader-backend:5000/upload; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - # Allow large uploads for batch upload - client_max_body_size 100M; - } - - # API - Download original images - location /api/download { - proxy_pass http://image-uploader-backend:5000/download; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # API - Preview/thumbnail images (optimized for gallery views) - location /api/previews { - proxy_pass http://image-uploader-backend:5000/previews; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # API - Groups (NO PASSWORD PROTECTION) - location /api/groups { - proxy_pass http://image-uploader-backend:5000/groups; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # Protected API - Moderation API routes (password protected) - must come before /groups - location /moderation/groups { - auth_basic "Restricted Area - Moderation API"; - auth_basic_user_file /etc/nginx/.htpasswd; - - proxy_pass http://image-uploader-backend:5000/moderation/groups; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # API - Groups API routes (NO PASSWORD PROTECTION) - location ~ ^/groups/[a-zA-Z0-9_-]+(/.*)?$ { - proxy_pass http://image-uploader-backend:5000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location /download { - proxy_pass http://image-uploader-backend:5000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # Frontend page - Groups overview (NO PASSWORD PROTECTION) - location /groups { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - expires -1; - - # Prevent indexing - add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive" always; - } - - # Protected routes - Moderation (password protected) - location /moderation { - auth_basic "Restricted Area - Moderation"; - auth_basic_user_file /etc/nginx/.htpasswd; - - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - expires -1; - - # Prevent indexing - add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive" always; - } - - # Frontend files - location / { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - expires -1; # Set it to different value depending on your standard requirements - } - - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root /usr/share/nginx/html; - } -} \ No newline at end of file diff --git a/frontend/conf/conf.d/gzip.conf b/frontend/conf/conf.d/gzip.conf deleted file mode 100644 index 4e46d9d..0000000 --- a/frontend/conf/conf.d/gzip.conf +++ /dev/null @@ -1,24 +0,0 @@ -gzip on; -gzip_http_version 1.0; -gzip_comp_level 5; # 1-9 -gzip_min_length 256; -gzip_proxied any; -gzip_vary on; - -# MIME-types -gzip_types - application/atom+xml - application/javascript - application/json - application/rss+xml - application/vnd.ms-fontobject - application/x-font-ttf - application/x-web-app-manifest+json - application/xhtml+xml - application/xml - font/opentype - image/svg+xml - image/x-icon - text/css - text/plain - text/x-component; diff --git a/frontend/env-config 2.js b/frontend/env-config 2.js deleted file mode 100644 index 7e03887..0000000 --- a/frontend/env-config 2.js +++ /dev/null @@ -1,4 +0,0 @@ -window._env_ = { - API_URL: "http://localhost:5000", - CLIENT_URL: "http://localhost", -} diff --git a/frontend/start-dev.sh b/frontend/start-dev.sh deleted file mode 100644 index 2143643..0000000 --- a/frontend/start-dev.sh +++ /dev/null @@ -1,134 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# Make public writable so env.sh can write env-config.js -chmod -R a+rw ./public || true - -# Run env.sh if present -if [ -x ./env.sh ]; then - ./env.sh || true -fi - -# Copy nginx config from mounted source if available so dev config can be -# edited on the host without rebuilding the image. Priority: -# 1) ./conf/conf.d/default.conf (project's conf folder) -# 2) ./nginx.dev.conf (bundled with the dev image at build time) -if [ -f /app/conf/conf.d/default.conf ]; then - echo "Using nginx config from /app/conf/conf.d/default.conf (creating dev variant)" - # Backup original - cp /app/conf/conf.d/default.conf /app/conf/conf.d/default.conf.backup || true - # Write a deterministic dev nginx config that proxies known API routes to - # the backend and proxies '/' to the CRA dev server so HMR works through nginx. - cat > /etc/nginx/conf.d/default.conf <<'NGINXDEV' -server { - listen 80 default_server; - listen [::]:80 default_server; - client_max_body_size 200M; - - location /upload { - proxy_pass http://image-uploader-backend:5000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - client_max_body_size 200M; - } - - location /api/upload { - proxy_pass http://image-uploader-backend:5000/upload; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - client_max_body_size 200M; - } - - location /api/download { - proxy_pass http://image-uploader-backend:5000/download; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location /api/previews { - proxy_pass http://image-uploader-backend:5000/previews; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location /api/groups { - proxy_pass http://image-uploader-backend:5000/groups; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location /moderation/groups { - proxy_pass http://image-uploader-backend:5000/moderation/groups; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location ~ ^/groups/[a-zA-Z0-9_-]+(/.*)?$ { - proxy_pass http://image-uploader-backend:5000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location /download { - proxy_pass http://image-uploader-backend:5000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # Proxy webpack dev server (supports HMR / WebSocket upgrades) - location /sockjs-node/ { - proxy_pass http://127.0.0.1:3000; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - - location / { - proxy_pass http://127.0.0.1:3000; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - -} -NGINXDEV -elif [ -f /app/nginx.dev.conf ]; then - echo "Using bundled nginx.dev.conf" - cp /app/nginx.dev.conf /etc/nginx/conf.d/default.conf || true -fi - -# Ensure node cache exists and is writable -mkdir -p /app/node_modules/.cache || true -chmod -R a+rw /app/node_modules || true - -# Ensure HOST is set so CRA binds to 0.0.0.0 -export HOST=${HOST:-0.0.0.0} - -# Start the React development server in background -npm run dev & -DEV_PID=$! - -# Start nginx in foreground so container stays alive; nginx will proxy to the dev server -exec nginx -g 'daemon off;' diff --git a/prod.sh b/prod.sh index 96619b1..183a434 100755 --- a/prod.sh +++ b/prod.sh @@ -10,14 +10,8 @@ BLUE='\033[0;34m' YELLOW='\033[1;33m' NC='\033[0m' # No Color -# Image Namen definieren -FRONTEND_IMAGE="gitea.lan.hobbyhimmel.de/hobbyhimmel/image-uploader-frontend" -BACKEND_IMAGE="gitea.lan.hobbyhimmel.de/hobbyhimmel/image-uploader-backend" -VERSION="latest" - echo -e "${BLUE}=== Image-Uploader Production Environment ===${NC}" -echo -e "${BLUE}Frontend Image: ${FRONTEND_IMAGE}:${VERSION}${NC}" -echo -e "${BLUE}Backend Image: ${BACKEND_IMAGE}:${VERSION}${NC}" +echo -e "${BLUE}Using docker/prod/docker-compose.yml${NC}" echo # Menü anzeigen @@ -43,84 +37,76 @@ read -p "Deine Wahl (0-13): " choice case $choice in 1) echo -e "${GREEN}Starte Production Container...${NC}" - docker compose -f docker-compose.yml up -d + docker compose -f docker/prod/docker-compose.yml up -d echo -e "${GREEN}Container gestartet!${NC}" echo -e "${BLUE}Frontend: http://localhost${NC}" echo -e "${BLUE}Backend: http://localhost:5000${NC}" echo -e "${BLUE}Slideshow: http://localhost/slideshow${NC}" - echo -e "${BLUE}Logs verfolgen mit: docker compose -f docker-compose.yml logs -f${NC}" + echo -e "${BLUE}Logs verfolgen mit: docker compose -f docker/prod/docker-compose.yml logs -f${NC}" ;; 2) echo -e "${GREEN}Baue Production Images...${NC}" - echo -e "${BLUE}Baue Frontend Image...${NC}" - docker build -t ${FRONTEND_IMAGE}:${VERSION} ./frontend - echo -e "${BLUE}Baue Backend Image...${NC}" - docker build -t ${BACKEND_IMAGE}:${VERSION} ./backend + echo -e "${BLUE}Baue mit docker/prod/docker-compose.yml...${NC}" + docker compose -f docker/prod/docker-compose.yml build echo -e "${GREEN}Production Images erfolgreich gebaut!${NC}" ;; 3) echo -e "${GREEN}Pushe Production Images zur Registry...${NC}" - echo -e "${BLUE}Pushe Frontend Image...${NC}" - docker push ${FRONTEND_IMAGE}:${VERSION} - echo -e "${BLUE}Pushe Backend Image...${NC}" - docker push ${BACKEND_IMAGE}:${VERSION} + docker compose -f docker/prod/docker-compose.yml push echo -e "${GREEN}Production Images erfolgreich gepusht!${NC}" ;; 4) echo -e "${GREEN}Baue Container neu...${NC}" - docker compose -f docker-compose.yml down - docker compose -f docker-compose.yml up --build -d + docker compose -f docker/prod/docker-compose.yml down + docker compose -f docker/prod/docker-compose.yml up --build -d echo -e "${GREEN}Container neu gebaut und gestartet!${NC}" echo -e "${BLUE}Frontend: http://localhost${NC}" echo -e "${BLUE}Slideshow: http://localhost/slideshow${NC}" ;; 5) echo -e "${YELLOW}Stoppe Container...${NC}" - docker compose -f docker-compose.yml down + docker compose -f docker/prod/docker-compose.yml down echo -e "${GREEN}Container gestoppt!${NC}" ;; 6) echo -e "${GREEN}Öffne Frontend Container Shell...${NC}" - docker compose -f docker-compose.yml exec image-uploader-frontend bash + docker compose -f docker/prod/docker-compose.yml exec frontend bash ;; 7) echo -e "${GREEN}Öffne Backend Container Shell...${NC}" - docker compose -f docker-compose.yml exec image-uploader-backend bash + docker compose -f docker/prod/docker-compose.yml exec backend bash ;; 8) echo -e "${GREEN}Zeige Frontend Logs...${NC}" - docker compose -f docker-compose.yml logs -f image-uploader-frontend + docker compose -f docker/prod/docker-compose.yml logs -f frontend ;; 9) echo -e "${GREEN}Zeige Backend Logs...${NC}" - docker compose -f docker-compose.yml logs -f image-uploader-backend + docker compose -f docker/prod/docker-compose.yml logs -f backend ;; 10) echo -e "${GREEN}Zeige alle Logs...${NC}" - docker compose -f docker-compose.yml logs -f + docker compose -f docker/prod/docker-compose.yml logs -f ;; 11) echo -e "${GREEN}Container Status:${NC}" - docker compose -f docker-compose.yml ps + docker compose -f docker/prod/docker-compose.yml ps echo echo -e "${BLUE}Detaillierte Informationen:${NC}" docker images | grep "image-uploader" || echo "Keine lokalen Images gefunden" ;; 12) echo -e "${GREEN}Upload-Verzeichnis Inhalt:${NC}" - if docker compose -f docker-compose.yml ps -q image-uploader-backend > /dev/null 2>&1; then + if docker compose -f docker/prod/docker-compose.yml ps -q backend > /dev/null 2>&1; then echo -e "${BLUE}Hochgeladene Bilder (data/images):${NC}" - docker compose -f docker-compose.yml exec image-uploader-backend ls -la /usr/src/app/data/images/ || echo "Upload-Verzeichnis ist leer" - echo - echo -e "${BLUE}JSON Metadaten (data/db):${NC}" - docker compose -f docker-compose.yml exec image-uploader-backend ls -la /usr/src/app/data/db/ || echo "Keine Metadaten vorhanden" + docker compose -f docker/prod/docker-compose.yml exec backend ls -la /usr/src/app/src/data/ || echo "Upload-Verzeichnis ist leer" else echo -e "${YELLOW}Backend Container ist nicht gestartet.${NC}" fi ;; 13) echo -e "${YELLOW}Stoppe und lösche Container inkl. Volumes...${NC}" - docker compose -f docker-compose.yml down -v + docker compose -f docker/prod/docker-compose.yml down -v echo -e "${GREEN}Container und Volumes gelöscht!${NC}" ;; 0) @@ -128,7 +114,7 @@ case $choice in exit 0 ;; *) - echo -e "${RED}Ungültige Option! Bitte wähle 0-12.${NC}" + echo -e "${RED}Ungültige Option! Bitte wähle 0-13.${NC}" exit 1 ;; esac \ No newline at end of file