Files
stiftung-management-system/compose.yml
SysAdmin Agent 2a579c83c0
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy (push) Has been cancelled
Code Quality / quality (push) Has been cancelled
Improve GrampsWeb base href patching: find all index.html copies (STI-90)
The previous sed only patched two known paths. Now uses find to discover
and patch all index.html files containing <base href="/"> across the
entire container, with logging to show which files were patched.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 08:44:21 +00:00

235 lines
7.5 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Production Docker Compose Configuration
# This file is used for production deployment via GitHub Actions
# For local development, use: docker-compose -f compose.dev.yml up
#
# IMPORTANT: This configuration requires ALL environment variables to be
# provided via the production server's .env file. No fallback values are
# included for security reasons.
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- dbdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
web:
build:
context: ./app
args:
APP_VERSION: ${APP_VERSION:-unknown}
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- DB_HOST=${DB_HOST}
- DB_PORT=${DB_PORT}
- DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY}
- DJANGO_DEBUG=${DJANGO_DEBUG}
- DJANGO_ALLOWED_HOSTS=${DJANGO_ALLOWED_HOSTS}
- LANGUAGE_CODE=${LANGUAGE_CODE}
- TIME_ZONE=${TIME_ZONE}
- REDIS_URL=${REDIS_URL}
- SESSION_COOKIE_NAME=${SESSION_COOKIE_NAME}
- CSRF_COOKIE_NAME=${CSRF_COOKIE_NAME}
- GRAMPS_URL=${GRAMPS_URL}
- GRAMPS_USERNAME=${GRAMPS_USERNAME}
- GRAMPS_PASSWORD=${GRAMPS_PASSWORD}
- GRAMPS_API_TOKEN=${GRAMPS_API_TOKEN}
- IMAP_HOST=${IMAP_HOST}
- IMAP_PORT=${IMAP_PORT}
- IMAP_USER=${IMAP_USER}
- IMAP_PASSWORD=${IMAP_PASSWORD}
- IMAP_FOLDER=${IMAP_FOLDER}
- IMAP_USE_SSL=${IMAP_USE_SSL}
ports:
- "8081:8000"
volumes:
- ./app:/app
command: ["gunicorn", "core.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "3"]
worker:
build:
context: ./app
args:
APP_VERSION: ${APP_VERSION:-unknown}
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- DB_HOST=${DB_HOST}
- DB_PORT=${DB_PORT}
- DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY}
- DJANGO_DEBUG=${DJANGO_DEBUG}
- REDIS_URL=${REDIS_URL}
- GRAMPS_URL=${GRAMPS_URL}
- GRAMPS_USERNAME=${GRAMPS_USERNAME}
- GRAMPS_PASSWORD=${GRAMPS_PASSWORD}
- GRAMPS_API_TOKEN=${GRAMPS_API_TOKEN}
- IMAP_HOST=${IMAP_HOST}
- IMAP_PORT=${IMAP_PORT}
- IMAP_USER=${IMAP_USER}
- IMAP_PASSWORD=${IMAP_PASSWORD}
- IMAP_FOLDER=${IMAP_FOLDER}
- IMAP_USE_SSL=${IMAP_USE_SSL}
depends_on:
- redis
- db
command: ["celery", "-A", "core", "worker", "-l", "info"]
beat:
build:
context: ./app
args:
APP_VERSION: ${APP_VERSION:-unknown}
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- DB_HOST=${DB_HOST}
- DB_PORT=${DB_PORT}
- DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY}
- DJANGO_DEBUG=${DJANGO_DEBUG}
- REDIS_URL=${REDIS_URL}
- GRAMPS_URL=${GRAMPS_URL}
- GRAMPS_USERNAME=${GRAMPS_USERNAME}
- GRAMPS_PASSWORD=${GRAMPS_PASSWORD}
- GRAMPS_API_TOKEN=${GRAMPS_API_TOKEN}
- IMAP_HOST=${IMAP_HOST}
- IMAP_PORT=${IMAP_PORT}
- IMAP_USER=${IMAP_USER}
- IMAP_PASSWORD=${IMAP_PASSWORD}
- IMAP_FOLDER=${IMAP_FOLDER}
- IMAP_USE_SSL=${IMAP_USE_SSL}
depends_on:
- redis
- db
command: ["celery", "-A", "core", "beat", "-l", "info"]
mcp:
build:
context: ./app
args:
APP_VERSION: ${APP_VERSION:-unknown}
depends_on:
db:
condition: service_healthy
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- DB_HOST=${DB_HOST}
- DB_PORT=${DB_PORT}
- DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY}
- DJANGO_DEBUG=0
- DJANGO_ALLOWED_HOSTS=localhost
- LANGUAGE_CODE=${LANGUAGE_CODE}
- TIME_ZONE=${TIME_ZONE}
- MCP_TOKEN_READONLY=${MCP_TOKEN_READONLY}
- MCP_TOKEN_EDITOR=${MCP_TOKEN_EDITOR}
- MCP_TOKEN_ADMIN=${MCP_TOKEN_ADMIN}
# Kein Port-Mapping nur internes Netz
# Start via: docker compose run --rm -e MCP_AUTH_TOKEN=<token> mcp
stdin_open: true
command: ["python", "-m", "mcp_server"]
ollama:
image: ollama/ollama:latest
# Kein externes Port-Mapping — nur über internes Docker-Netzwerk erreichbar
# Django-App: http://ollama:11434
environment:
- OLLAMA_MAX_LOADED_MODELS=1
- OLLAMA_NUM_PARALLEL=1
- OLLAMA_DEFAULT_MODEL=${OLLAMA_DEFAULT_MODEL:-qwen2.5:3b}
volumes:
- ollama_data:/root/.ollama
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "curl -sf http://localhost:11434/api/tags || exit 1"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
# Beim ersten Start: Ollama starten, dann Modell laden (falls nicht vorhanden)
entrypoint: >
sh -c "
ollama serve &
OLLAMA_PID=$$!
echo '[ollama] Warte auf API...'
RETRIES=0
until curl -sf http://localhost:11434/api/tags > /dev/null 2>&1; do
RETRIES=$$((RETRIES + 1))
[ $$RETRIES -ge 60 ] && echo '[ollama] FEHLER: API nicht bereit.' && exit 1
sleep 1
done
MODEL=$${OLLAMA_DEFAULT_MODEL:-qwen2.5:3b}
if ollama list | grep -q \"$$MODEL\"; then
echo \"[ollama] Modell '$$MODEL' bereits vorhanden.\"
else
echo \"[ollama] Lade Modell '$$MODEL'...\"
ollama pull \"$$MODEL\"
fi
wait $$OLLAMA_PID
"
grampsweb:
image: ghcr.io/gramps-project/grampsweb:latest
ports:
- "8090:5000"
environment:
- GRAMPSWEB_SECRET_KEY=${GRAMPSWEB_SECRET_KEY}
- GRAMPSWEB_ADMIN_EMAIL=${GRAMPSWEB_ADMIN_EMAIL}
- GRAMPSWEB_ADMIN_PASSWORD=${GRAMPSWEB_ADMIN_PASSWORD}
- GRAMPSWEB_TREE=${GRAMPSWEB_TREE:-Stiftung}
- GRAMPSWEB_BASE_URL=${GRAMPSWEB_BASE_URL:-http://localhost:8090}
- GRAMPSWEB_CELERY_CONFIG__broker_url=redis://redis:6379/0
- GRAMPSWEB_CELERY_CONFIG__result_backend=redis://redis:6379/0
- GRAMPSWEB_RATELIMIT_STORAGE_URI=redis://redis:6379/1
- GRAMPSWEB_NEW_DB_BACKEND=sqlite
- GRAMPSWEB_SUBPATH=${GRAMPSWEB_SUBPATH:-/ahnenforschung}
command:
- sh
- -c
- |
if [ -n "$$GRAMPSWEB_SUBPATH" ] && [ "$$GRAMPSWEB_SUBPATH" != "/" ]; then
SUBPATH="$$GRAMPSWEB_SUBPATH"
case "$$SUBPATH" in */) ;; *) SUBPATH="$${SUBPATH}/" ;; esac
echo "[grampsweb] Patching <base href> to $$SUBPATH ..."
find / -name index.html -path "*/gramps*" -o -name index.html -path "*/static/*" 2>/dev/null | while read f; do
if grep -q '<base href="/">' "$$f" 2>/dev/null; then
sed -i "s|<base href=\"/\">|<base href=\"$$SUBPATH\">|g" "$$f"
echo "[grampsweb] patched: $$f"
fi
done
echo "[grampsweb] Done."
fi
exec gunicorn -w $${GUNICORN_NUM_WORKERS:-8} -b 0.0.0.0:5000 \
gramps_webapi.wsgi:app --timeout $${GUNICORN_TIMEOUT:-120} \
--limit-request-line 8190
volumes:
- gramps_data:/app/data
depends_on:
- db
- redis
volumes:
dbdata:
gramps_data:
ollama_data: