🚀 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:
parent
7ea95341c0
commit
2678ad9b12
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -24,3 +24,6 @@ npm-debug.log*
|
|||
# Build outputs
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Backend data (uploaded images, database, etc.)
|
||||
backend/src/data/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
111
README.md
111
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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
40
dev.sh
Executable 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 ""
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
15
docker/.env.backend.example
Normal file
15
docker/.env.backend.example
Normal 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
|
||||
12
docker/.env.frontend.example
Normal file
12
docker/.env.frontend.example
Normal 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
|
||||
22
docker/dev/backend/Dockerfile
Normal file
22
docker/dev/backend/Dockerfile
Normal 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"]
|
||||
52
docker/dev/docker-compose.yml
Normal file
52
docker/dev/docker-compose.yml
Normal 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
|
||||
|
|
@ -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"]
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
26
docker/dev/frontend/start.sh
Normal file
26
docker/dev/frontend/start.sh
Normal 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;'
|
||||
24
docker/prod/backend/Dockerfile
Normal file
24
docker/prod/backend/Dockerfile
Normal 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"]
|
||||
46
docker/prod/docker-compose.yml
Normal file
46
docker/prod/docker-compose.yml
Normal 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
|
||||
|
|
@ -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
|
||||
29
docker/prod/frontend/config/env.sh
Executable file
29
docker/prod/frontend/config/env.sh
Executable 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
|
||||
159
docker/prod/frontend/nginx.conf
Normal file
159
docker/prod/frontend/nginx.conf
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
API_URL=http://localhost
|
||||
CLIENT_URL=http://localhost
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
window._env_ = {
|
||||
API_URL: "http://localhost:5000",
|
||||
CLIENT_URL: "http://localhost",
|
||||
}
|
||||
|
|
@ -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
52
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
|
||||
Loading…
Reference in New Issue
Block a user