6c82958c79
- Implement React frontend with TypeScript and Tailwind CSS - Add scrappbook.de-inspired UI design with photo galleries - Implement authentication, photo viewing, and download features - Add Docker Swarm configuration with Traefik reverse proxy - Set up Drone CI/CD pipeline for automated deployments - Add monitoring stack with Prometheus and Grafana - Create comprehensive deployment documentation - Add simple local development setup with docker-compose.local.yml Features: - Password-protected galleries with expiration warnings - Responsive photo grid with lightbox viewer - Bulk download functionality - Hot reload development environment - Email testing with Mailhog - Production-ready deployment scripts 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
124 lines
5.0 KiB
YAML
124 lines
5.0 KiB
YAML
version: '3.8'
|
|
|
|
services:
|
|
traefik:
|
|
image: traefik:v2.10
|
|
ports:
|
|
- target: 80
|
|
published: 80
|
|
mode: host
|
|
- target: 443
|
|
published: 443
|
|
mode: host
|
|
networks:
|
|
- traefik-public
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
- traefik-certificates:/letsencrypt
|
|
environment:
|
|
- TRAEFIK_API=true
|
|
- TRAEFIK_API_DASHBOARD=true
|
|
- TRAEFIK_API_DEBUG=false
|
|
- TRAEFIK_LOG_LEVEL=INFO
|
|
- TRAEFIK_PROVIDERS_DOCKER=true
|
|
- TRAEFIK_PROVIDERS_DOCKER_SWARMMODE=true
|
|
- TRAEFIK_PROVIDERS_DOCKER_EXPOSEDBYDEFAULT=false
|
|
- TRAEFIK_PROVIDERS_DOCKER_NETWORK=traefik-public
|
|
- TRAEFIK_ENTRYPOINTS_HTTP_ADDRESS=:80
|
|
- TRAEFIK_ENTRYPOINTS_HTTPS_ADDRESS=:443
|
|
# Redirect HTTP to HTTPS
|
|
- TRAEFIK_ENTRYPOINTS_HTTP_HTTP_REDIRECTIONS_ENTRYPOINT_TO=https
|
|
- TRAEFIK_ENTRYPOINTS_HTTP_HTTP_REDIRECTIONS_ENTRYPOINT_SCHEME=https
|
|
# Let's Encrypt
|
|
- TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_EMAIL=${ACME_EMAIL}
|
|
- TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_STORAGE=/letsencrypt/acme.json
|
|
- TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_HTTPCHALLENGE=true
|
|
- TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_HTTPCHALLENGE_ENTRYPOINT=http
|
|
# Enable metrics
|
|
- TRAEFIK_METRICS_PROMETHEUS=true
|
|
- TRAEFIK_METRICS_PROMETHEUS_ENTRYPOINT=metrics
|
|
- TRAEFIK_ENTRYPOINTS_METRICS_ADDRESS=:8082
|
|
deploy:
|
|
mode: global
|
|
placement:
|
|
constraints:
|
|
- node.role == manager
|
|
update_config:
|
|
parallelism: 1
|
|
delay: 10s
|
|
restart_policy:
|
|
condition: on-failure
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.docker.network=traefik-public"
|
|
- "traefik.constraint-label=traefik-public"
|
|
# Dashboard
|
|
- "traefik.http.routers.traefik-dashboard.rule=Host(`${TRAEFIK_HOST}`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
|
|
- "traefik.http.routers.traefik-dashboard.entrypoints=https"
|
|
- "traefik.http.routers.traefik-dashboard.tls=true"
|
|
- "traefik.http.routers.traefik-dashboard.tls.certresolver=letsencrypt"
|
|
- "traefik.http.routers.traefik-dashboard.service=api@internal"
|
|
- "traefik.http.routers.traefik-dashboard.middlewares=admin-auth"
|
|
# Basic auth for dashboard
|
|
- "traefik.http.middlewares.admin-auth.basicauth.users=${TRAEFIK_DASHBOARD_AUTH}"
|
|
# Global redirect to https
|
|
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
|
|
# Security headers
|
|
- "traefik.http.middlewares.security-headers.headers.frameDeny=true"
|
|
- "traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true"
|
|
- "traefik.http.middlewares.security-headers.headers.browserXssFilter=true"
|
|
- "traefik.http.middlewares.security-headers.headers.stsSeconds=31536000"
|
|
- "traefik.http.middlewares.security-headers.headers.stsIncludeSubdomains=true"
|
|
- "traefik.http.middlewares.security-headers.headers.stsPreload=true"
|
|
# Rate limiting
|
|
- "traefik.http.middlewares.rate-limit.ratelimit.average=100"
|
|
- "traefik.http.middlewares.rate-limit.ratelimit.burst=50"
|
|
# API service
|
|
- "traefik.http.services.traefik.loadbalancer.server.port=8080"
|
|
healthcheck:
|
|
test: ["CMD", "traefik", "healthcheck"]
|
|
interval: 30s
|
|
timeout: 3s
|
|
retries: 3
|
|
|
|
# Traefik Forward Auth for advanced authentication (optional)
|
|
traefik-forward-auth:
|
|
image: thomseddon/traefik-forward-auth:latest
|
|
networks:
|
|
- traefik-public
|
|
environment:
|
|
- DEFAULT_PROVIDER=generic-oauth
|
|
- PROVIDERS_GENERIC_OAUTH_AUTH_URL=${OAUTH_AUTH_URL}
|
|
- PROVIDERS_GENERIC_OAUTH_TOKEN_URL=${OAUTH_TOKEN_URL}
|
|
- PROVIDERS_GENERIC_OAUTH_USER_URL=${OAUTH_USER_URL}
|
|
- PROVIDERS_GENERIC_OAUTH_CLIENT_ID=${OAUTH_CLIENT_ID}
|
|
- PROVIDERS_GENERIC_OAUTH_CLIENT_SECRET=${OAUTH_CLIENT_SECRET}
|
|
- SECRET=${OAUTH_SECRET}
|
|
- COOKIE_DOMAIN=${COOKIE_DOMAIN}
|
|
- INSECURE_COOKIE=false
|
|
- LOG_LEVEL=info
|
|
- URL_PATH=/_oauth
|
|
- WHITELIST=${OAUTH_WHITELIST}
|
|
deploy:
|
|
replicas: 2
|
|
restart_policy:
|
|
condition: on-failure
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.docker.network=traefik-public"
|
|
- "traefik.http.routers.traefik-forward-auth.rule=Host(`${FRONTEND_HOST}`) && PathPrefix(`/_oauth`)"
|
|
- "traefik.http.routers.traefik-forward-auth.entrypoints=https"
|
|
- "traefik.http.routers.traefik-forward-auth.tls=true"
|
|
- "traefik.http.routers.traefik-forward-auth.tls.certresolver=letsencrypt"
|
|
- "traefik.http.routers.traefik-forward-auth.middlewares=auth-verify"
|
|
- "traefik.http.services.traefik-forward-auth.loadbalancer.server.port=4181"
|
|
- "traefik.http.middlewares.auth-verify.forwardauth.address=http://traefik-forward-auth:4181"
|
|
- "traefik.http.middlewares.auth-verify.forwardauth.authResponseHeaders=X-Forwarded-User"
|
|
|
|
networks:
|
|
traefik-public:
|
|
external: true
|
|
|
|
volumes:
|
|
traefik-certificates:
|
|
driver: local |