Deployment-Setup (PostgreSQL+nginx-Installer) + Sprint-0-Fixes aus Subagenten-Reviews
CI / backend-tests (push) Failing after 1s

Installer (deploy/):
- install_server.sh: PostgreSQL+nginx auf Debian 13, idempotente Rolle/DB-Anlage,
  pgcrypto-Extension, Speicher-Tuning für 4GB-VPS, Zugangsdaten in chmod-600-Datei
  statt stdout (postgres-expert/owasp-Review)
- nginx-Template mit Security-Headern + Rate-Limit auf /auth/login
- systemd-Unit-Template mit Sandboxing (NoNewPrivileges/ProtectSystem/PrivateTmp)

Backend-Fixes (fastapi-expert-Review):
- get_db: einheitliche commit/rollback-Konvention statt Endpunkt-Copy-Paste
- Test-Fixtures auf SQLAlchemy-2.0-Savepoint-Pattern umgestellt (join_transaction_mode),
  da get_db jetzt selbst committet
- Lifespan-Handler: Startup-Guard gegen JWT-Secret-Platzhalter, engine.dispose() beim Shutdown
- JWT-Payload ohne ungenutztes roles-Claim (Rollen kommen immer frisch aus der DB)

Zusätzlich: Subagenten-Definitionen (~/.claude/agents/) auf lauffähiges Modell fixiert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L85hmKbvX7Cqkq47KnQhFt
This commit is contained in:
2026-09-03 23:00:46 +02:00
co-authored by Claude Sonnet 5
parent 48b6d6a5be
commit 446e4f912d
10 changed files with 487 additions and 8 deletions
+89
View File
@@ -0,0 +1,89 @@
#!/usr/bin/env bash
# MABEA Server-Setup (Debian 13). Installiert PostgreSQL + nginx, legt DB/Rolle an,
# richtet nginx als Reverse-Proxy vor dem späteren FastAPI/Uvicorn-Backend ein.
# Idempotent: mehrfacher Aufruf überschreibt keine bestehende DB-Rolle/DB.
#
# Ausführung auf dem Zielserver als root:
# DB_PASSWORD=... APP_DOMAIN=mabea.example.org ./install_server.sh
# Ohne DB_PASSWORD wird ein zufälliges Passwort erzeugt und am Ende ausgegeben
# (sofort sichern, z. B. in backend/.env auf dem Server nicht committen).
set -euo pipefail
DB_NAME="${DB_NAME:-mabea}"
DB_USER="${DB_USER:-mabea}"
DB_PASSWORD="${DB_PASSWORD:-$(openssl rand -base64 24)}"
JWT_SECRET_KEY="${JWT_SECRET_KEY:-$(openssl rand -base64 48)}"
APP_DOMAIN="${APP_DOMAIN:-_}" # "_" = nginx catch-all, solange keine Domain feststeht
echo "== apt update/install =="
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq postgresql nginx python3-venv python3-pip git openssl
echo "== PostgreSQL: Rolle + Datenbank =="
if ! sudo -u postgres psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='${DB_USER}'" | grep -q 1; then
sudo -u postgres psql -c "CREATE ROLE ${DB_USER} WITH LOGIN PASSWORD '${DB_PASSWORD}';"
echo "Rolle ${DB_USER} angelegt."
else
echo "Rolle ${DB_USER} existiert bereits, überspringe."
fi
if ! sudo -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname='${DB_NAME}'" | grep -q 1; then
sudo -u postgres psql -c "CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};"
echo "Datenbank ${DB_NAME} angelegt."
else
echo "Datenbank ${DB_NAME} existiert bereits, überspringe."
fi
# gen_random_uuid() ist seit PG13 built-in, aber pgcrypto schadet nicht und macht es
# explizit/robust gegen ältere Backports (Review-Fund postgres-expert).
sudo -u postgres psql -d "${DB_NAME}" -c "CREATE EXTENSION IF NOT EXISTS pgcrypto;"
echo "== PostgreSQL: Speicher-Tuning für kleinen VPS (4GB RAM) =="
PG_CONF_DIR=$(sudo -u postgres psql -tAc "SHOW config_file;" | xargs dirname)
mkdir -p "${PG_CONF_DIR}/conf.d"
cat > "${PG_CONF_DIR}/conf.d/mabea-tuning.conf" <<'PGCONF'
# MABEA: konservatives Tuning für ~4GB RAM VPS, nicht enterprise-scale.
shared_buffers = 1GB
effective_cache_size = 3GB
maintenance_work_mem = 256MB
work_mem = 8MB
max_connections = 50
PGCONF
grep -q "conf.d/\*.conf" "${PG_CONF_DIR}/postgresql.conf" || \
echo "include_dir = 'conf.d'" >> "${PG_CONF_DIR}/postgresql.conf"
echo "== nginx Reverse-Proxy Config (inkl. Security-Header) =="
sed -e "s/__APP_DOMAIN__/${APP_DOMAIN}/g" \
"$(dirname "$0")/nginx_mabea.conf.template" > /etc/nginx/sites-available/mabea.conf
ln -sf /etc/nginx/sites-available/mabea.conf /etc/nginx/sites-enabled/mabea.conf
rm -f /etc/nginx/sites-enabled/default
nginx -t
echo "== Dienste aktivieren =="
systemctl enable --now postgresql
systemctl restart postgresql # Tuning-Konfiguration laden
systemctl enable --now nginx
systemctl reload nginx
echo "== Zugangsdaten sichern =="
CRED_FILE="/root/mabea_db_credentials.env"
umask 077
cat > "${CRED_FILE}" <<EOF
DB_NAME=${DB_NAME}
DB_USER=${DB_USER}
DB_PASSWORD=${DB_PASSWORD}
DATABASE_URL=postgresql+asyncpg://${DB_USER}:${DB_PASSWORD}@localhost:5432/${DB_NAME}
JWT_SECRET_KEY=${JWT_SECRET_KEY}
EOF
chmod 600 "${CRED_FILE}"
echo
echo "=== Fertig ==="
echo "Zugangsdaten liegen (chmod 600) in ${CRED_FILE} NICHT auf stdout ausgegeben (Review-Fund: keine Secrets im Terminal-Scrollback)."
echo "DATABASE_URL von dort in backend/.env auf dem Server übernehmen (siehe backend/example.env)."
echo "Backend selbst ist noch nicht deployt siehe deploy/mabea-backend.service.template für den späteren systemd-Dienst."
echo
echo "Hinweis: nginx läuft aktuell nur über Port 80 (kein TLS). Für echten Betrieb vor Domain-Vergabe"
echo "TLS nachziehen (Let's Encrypt bei Domain, sonst selbstsigniertes Zertifikat fürs LAN)."