🚀 Refactor: Saubere Docker-Struktur mit getrennten dev/prod Umgebungen

- Neue Docker-Struktur: docker/{dev,prod}/ für klare Trennung
- Entfernt: docker-compose.override.yml (problematisch)
- Hinzugefügt: ./dev.sh und ./prod.sh Scripts für einfache Bedienung
- Container-spezifische Konfigurationen in docker/{dev,prod}/*/config/
- Aktualisierte READMEs für neue Struktur
- Backend-Daten in .gitignore hinzugefügt
- Bereinigt: Veraltete Dockerfiles und Konfigurationsdateien

Jetzt: Wartungsfreundlich, keine Verwirrung zwischen Umgebungen
This commit is contained in:
Matthias Lotz 2025-11-05 23:00:25 +01:00
parent 7ea95341c0
commit 2678ad9b12
29 changed files with 630 additions and 711 deletions

3
.gitignore vendored
View File

@ -24,3 +24,6 @@ npm-debug.log*
# Build outputs # Build outputs
dist/ dist/
build/ build/
# Backend data (uploaded images, database, etc.)
backend/src/data/

View File

@ -1,52 +1,79 @@
## Dev: Schnellstart # Development Setup
Kurz und knapp — so startest und nutzt du die lokale DevUmgebung mit HMR (nginx als Proxy vor dem CRA dev server): ## Schnellstart
Voraussetzungen ### Starten (Development Environment)
- Docker & Docker Compose (Docker Compose Plugin)
Starten (Dev)
1. Build & Start (daemon):
```bash ```bash
docker compose up --build -d image-uploader-frontend # Mit Script (empfohlen):
``` ./dev.sh
2. Logs verfolgen:
```bash
docker compose logs -f image-uploader-frontend
```
3. Browser öffnen: http://localhost:3000 (HMR aktiv)
Ändern & Testen # Oder manuell:
- Dateien editieren im `frontend/src/...` → HMR übernimmt Änderungen sofort. docker compose -f docker/dev/docker-compose.yml up -d
- Wenn du nginxKonfiguration anpassen willst, editiere `frontend/conf/conf.d/default.conf` (DevVariante wird beim Containerstart benutzt). Nach Änderung: nginx reload ohne Neustart:
```bash
docker compose exec image-uploader-frontend nginx -s reload
``` ```
Probleme mit `node_modules` ### Zugriff
- Wenn du ein hostseitiges `frontend/node_modules` hast, lösche es (konsistenter ist der containerverwaltete Volume): - **Frontend**: http://localhost:3000 (Hot Module Reloading aktiv)
```bash - **Backend**: http://localhost:5001 (API)
rm -rf frontend/node_modules - **Slideshow**: http://localhost:3000/slideshow
```
Danach `docker compose up --build -d image-uploader-frontend` erneut ausführen.
Stoppen ### Logs verfolgen
```bash ```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 ### Entwicklung
- Diese DevKonfiguration läuft lokal mit erweiterten Rechten (nur für Entwicklung). ProduktionsImages/Configs bleiben unverändert.
#### 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: #### Backend-Entwicklung
docker compose up --build -d image-uploader-frontend - Code in `backend/src/` editieren → Container restart für Änderungen
- Container-Namen: `image-uploader-backend-dev`
- Environment: `NODE_ENV=development`
Tail logs: #### Konfiguration anpassen
docker compose logs -f image-uploader-frontend - **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): ### Container-Management
docker compose exec image-uploader-frontend nginx -s reload
```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 exec image-uploader-frontend nginx -s reload
docker compose down docker compose down

111
README.md
View File

@ -37,63 +37,36 @@ This project extends the original [Image-Uploader by vallezw](https://github.com
### Docker Deployment (Recommended) ### Docker Deployment (Recommended)
1. **Create docker-compose.yml**: #### Production Environment
```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**:
```bash ```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` # Or manually:
- Backend: `http://localhost:5000` docker compose -f docker/dev/docker-compose.yml up -d
### Access URLs
#### Production (Port 80):
- Upload Interface: `http://localhost`
- Slideshow Mode: `http://localhost/slideshow` - 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 ### Multi-Image Upload
@ -166,6 +139,44 @@ The application automatically generates optimized preview thumbnails for all upl
- View group statistics and metadata - 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 Structure
Data are stored in sqlite database. The structure is as follows: Data are stored in sqlite database. The structure is as follows:

View File

@ -1 +1,15 @@
REMOVE_IMAGES=<boolean | undefined> # 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

View File

@ -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" ]

40
dev.sh Executable file
View File

@ -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 ""

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"]

View File

@ -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

View File

@ -11,9 +11,12 @@ RUN useradd -m appuser || true
WORKDIR /app WORKDIR /app
# Copy package files first to leverage Docker cache for npm install # Copy package files first to leverage Docker cache for npm install
COPY package*.json ./ COPY frontend/package*.json ./
COPY env.sh ./ COPY docker/dev/frontend/config/env.sh ./
COPY nginx.dev.conf /etc/nginx/conf.d/default.conf 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 # 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 # 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 # Switch back to root to add the start script and adjust nginx paths
USER root USER root
COPY start-dev.sh /start-dev.sh COPY docker/dev/frontend/start.sh /start.sh
RUN chmod +x /start-dev.sh RUN chmod +x /start.sh
# Ensure nginx log/lib dirs are writable by the app user (small set) # 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 RUN chown -R appuser:appuser /var/lib/nginx /var/log/nginx || true
@ -39,4 +42,4 @@ USER appuser
EXPOSE 80 3000 EXPOSE 80 3000
CMD ["/start-dev.sh"] CMD ["/start.sh"]

View File

@ -1,11 +1,12 @@
server { server {
listen 80; listen 80;
server_name localhost; server_name localhost;
client_max_body_size 200M;
# API proxy to backend - must come before / location # API proxy to development backend
# Upload endpoint # Upload endpoint
location /api/upload { 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 Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@ -15,7 +16,7 @@ server {
# Download original images # Download original images
location /api/download { 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 Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@ -24,7 +25,7 @@ server {
# Preview/thumbnail images (optimized for gallery views) # Preview/thumbnail images (optimized for gallery views)
location /api/previews { 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 Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@ -33,57 +34,51 @@ server {
# Groups API # Groups API
location /api/groups { 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 Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
} }
# Moderation API (groups) # Moderation Groups API
location /moderation/groups { 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 Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
} }
# Groups routes (both API and page routes) # Groups dynamic routes
location /groups { location ~ ^/groups/[a-zA-Z0-9_-]+(/.*)?$ {
# Try to serve as static file first, then proxy to React dev server proxy_pass http://backend-dev:5000;
try_files $uri @proxy; 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 { 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 Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
} }
# Proxy requests to the CRA dev server so nginx can be used as reverse proxy # WebSocket support for hot reloading (React Dev Server)
location /sockjs-node/ { location /ws {
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 {
proxy_pass http://127.0.0.1:3000; proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
@ -93,6 +88,7 @@ server {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
} }
# All other requests go to React Dev Server for Hot Module Reloading
location / { location / {
proxy_pass http://127.0.0.1:3000; proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1; proxy_http_version 1.1;
@ -102,10 +98,4 @@ server {
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 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;
}
}

View File

@ -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;'

View File

@ -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"]

View File

@ -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

View File

@ -1,9 +1,9 @@
# => Build container # => Build container
FROM node:18-alpine AS build FROM node:18-alpine AS build
WORKDIR /app WORKDIR /app
COPY package.json ./ COPY frontend/package.json ./
RUN npm install --silent RUN npm install --silent
COPY . ./ COPY frontend/ ./
ENV NODE_OPTIONS=--openssl-legacy-provider ENV NODE_OPTIONS=--openssl-legacy-provider
RUN npm run build RUN npm run build
@ -12,10 +12,11 @@ FROM nginx:stable-alpine
# Nginx config # Nginx config
RUN rm -rf /etc/nginx/conf.d 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 file for authentication
COPY htpasswd /etc/nginx/.htpasswd COPY docker/prod/frontend/config/htpasswd /etc/nginx/.htpasswd
# Static build # Static build
COPY --from=build /app/build /usr/share/nginx/html COPY --from=build /app/build /usr/share/nginx/html
@ -25,8 +26,8 @@ EXPOSE 80
# Copy .env file and shell script to container # Copy .env file and shell script to container
WORKDIR /usr/share/nginx/html WORKDIR /usr/share/nginx/html
COPY ./env.sh ./ COPY docker/prod/frontend/config/env.sh ./
COPY ./.env ./ COPY docker/prod/frontend/config/.env ./
# Add bash # Add bash
RUN apk add --no-cache bash RUN apk add --no-cache bash

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -1,2 +0,0 @@
API_URL=http://localhost
CLIENT_URL=http://localhost

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -1,4 +0,0 @@
window._env_ = {
API_URL: "http://localhost:5000",
CLIENT_URL: "http://localhost",
}

View File

@ -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;'

52
prod.sh
View File

@ -10,14 +10,8 @@ BLUE='\033[0;34m'
YELLOW='\033[1;33m' YELLOW='\033[1;33m'
NC='\033[0m' # No Color 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}=== Image-Uploader Production Environment ===${NC}"
echo -e "${BLUE}Frontend Image: ${FRONTEND_IMAGE}:${VERSION}${NC}" echo -e "${BLUE}Using docker/prod/docker-compose.yml${NC}"
echo -e "${BLUE}Backend Image: ${BACKEND_IMAGE}:${VERSION}${NC}"
echo echo
# Menü anzeigen # Menü anzeigen
@ -43,84 +37,76 @@ read -p "Deine Wahl (0-13): " choice
case $choice in case $choice in
1) 1)
echo -e "${GREEN}Starte Production Container...${NC}" 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 "${GREEN}Container gestartet!${NC}"
echo -e "${BLUE}Frontend: http://localhost${NC}" echo -e "${BLUE}Frontend: http://localhost${NC}"
echo -e "${BLUE}Backend: http://localhost:5000${NC}" echo -e "${BLUE}Backend: http://localhost:5000${NC}"
echo -e "${BLUE}Slideshow: http://localhost/slideshow${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) 2)
echo -e "${GREEN}Baue Production Images...${NC}" echo -e "${GREEN}Baue Production Images...${NC}"
echo -e "${BLUE}Baue Frontend Image...${NC}" echo -e "${BLUE}Baue mit docker/prod/docker-compose.yml...${NC}"
docker build -t ${FRONTEND_IMAGE}:${VERSION} ./frontend docker compose -f docker/prod/docker-compose.yml build
echo -e "${BLUE}Baue Backend Image...${NC}"
docker build -t ${BACKEND_IMAGE}:${VERSION} ./backend
echo -e "${GREEN}Production Images erfolgreich gebaut!${NC}" echo -e "${GREEN}Production Images erfolgreich gebaut!${NC}"
;; ;;
3) 3)
echo -e "${GREEN}Pushe Production Images zur Registry...${NC}" echo -e "${GREEN}Pushe Production Images zur Registry...${NC}"
echo -e "${BLUE}Pushe Frontend Image...${NC}" docker compose -f docker/prod/docker-compose.yml push
docker push ${FRONTEND_IMAGE}:${VERSION}
echo -e "${BLUE}Pushe Backend Image...${NC}"
docker push ${BACKEND_IMAGE}:${VERSION}
echo -e "${GREEN}Production Images erfolgreich gepusht!${NC}" echo -e "${GREEN}Production Images erfolgreich gepusht!${NC}"
;; ;;
4) 4)
echo -e "${GREEN}Baue Container neu...${NC}" echo -e "${GREEN}Baue Container neu...${NC}"
docker compose -f docker-compose.yml down docker compose -f docker/prod/docker-compose.yml down
docker compose -f docker-compose.yml up --build -d docker compose -f docker/prod/docker-compose.yml up --build -d
echo -e "${GREEN}Container neu gebaut und gestartet!${NC}" echo -e "${GREEN}Container neu gebaut und gestartet!${NC}"
echo -e "${BLUE}Frontend: http://localhost${NC}" echo -e "${BLUE}Frontend: http://localhost${NC}"
echo -e "${BLUE}Slideshow: http://localhost/slideshow${NC}" echo -e "${BLUE}Slideshow: http://localhost/slideshow${NC}"
;; ;;
5) 5)
echo -e "${YELLOW}Stoppe Container...${NC}" 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}" echo -e "${GREEN}Container gestoppt!${NC}"
;; ;;
6) 6)
echo -e "${GREEN}Öffne Frontend Container Shell...${NC}" 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) 7)
echo -e "${GREEN}Öffne Backend Container Shell...${NC}" 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) 8)
echo -e "${GREEN}Zeige Frontend Logs...${NC}" 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) 9)
echo -e "${GREEN}Zeige Backend Logs...${NC}" 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) 10)
echo -e "${GREEN}Zeige alle Logs...${NC}" 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) 11)
echo -e "${GREEN}Container Status:${NC}" echo -e "${GREEN}Container Status:${NC}"
docker compose -f docker-compose.yml ps docker compose -f docker/prod/docker-compose.yml ps
echo echo
echo -e "${BLUE}Detaillierte Informationen:${NC}" echo -e "${BLUE}Detaillierte Informationen:${NC}"
docker images | grep "image-uploader" || echo "Keine lokalen Images gefunden" docker images | grep "image-uploader" || echo "Keine lokalen Images gefunden"
;; ;;
12) 12)
echo -e "${GREEN}Upload-Verzeichnis Inhalt:${NC}" 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}" 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" docker compose -f docker/prod/docker-compose.yml exec backend ls -la /usr/src/app/src/data/ || 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"
else else
echo -e "${YELLOW}Backend Container ist nicht gestartet.${NC}" echo -e "${YELLOW}Backend Container ist nicht gestartet.${NC}"
fi fi
;; ;;
13) 13)
echo -e "${YELLOW}Stoppe und lösche Container inkl. Volumes...${NC}" 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}" echo -e "${GREEN}Container und Volumes gelöscht!${NC}"
;; ;;
0) 0)
@ -128,7 +114,7 @@ case $choice in
exit 0 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 exit 1
;; ;;
esac esac