build: Docker-Images, Compose-Setup und Gitea-Workflows

- Mehrstufige Dockerfiles je Dienst mit Stufen für Entwicklung und Betrieb,
  beide Images laufen als unprivilegierter Benutzer
- Backend-Entrypoint wartet auf die Datenbank, migriert und seedt nur bei
  leerem Kategoriebaum (neues Flag --if-empty)
- docker-compose.yml mit Netz-Trennung, Healthchecks und benannten Volumes;
  die Datenbank ist ausschließlich im internen Netz erreichbar
- nginx liefert das Bundle aus und reicht /api weiter; index.html ungecacht,
  gehashte Assets ein Jahr
- Gitea-Workflows: ci.yml für Lint, Tests und Bundle-Bau, build.yml für die
  Images nach linux/amd64 ohne Deploy-Schritt
- docs/runner.md und docs/deployment.md, .dockerignore je Dienst

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014e7t8UpmoVNMtWivY5LiSH
This commit is contained in:
moneyfy
2026-09-09 17:14:44 +02:00
co-authored by Claude Opus 5
parent 54c59c9f71
commit 87bee9b72a
17 changed files with 1184 additions and 10 deletions
+49
View File
@@ -0,0 +1,49 @@
#!/bin/sh
# Startskript des Backend-Containers.
#
# Wartet auf die Datenbank, bringt das Schema auf den aktuellen Stand und legt
# beim allerersten Start die Stammdaten an. Erst danach startet die Anwendung.
set -eu
WARTEZEIT_SEKUNDEN="${DB_WAIT_SECONDS:-60}"
warte_auf_datenbank() {
i=0
while [ "$i" -lt "$WARTEZEIT_SEKUNDEN" ]; do
if python -c "
import asyncio, sys
from sqlalchemy import text
from app.db.session import engine
async def pruefen():
async with engine.connect() as verbindung:
await verbindung.execute(text('SELECT 1'))
await engine.dispose()
try:
asyncio.run(pruefen())
except Exception:
sys.exit(1)
" 2>/dev/null; then
return 0
fi
i=$((i + 1))
[ "$i" -eq 1 ] && echo "Warte auf die Datenbank …"
sleep 1
done
echo "Die Datenbank war nach ${WARTEZEIT_SEKUNDEN} Sekunden nicht erreichbar." >&2
return 1
}
warte_auf_datenbank
echo "Migrationen werden angewendet …"
alembic upgrade head
# Idempotent und nur beim leeren Kategoriebaum bewusst gelöschte Kategorien
# kehren dadurch nicht bei jedem Neustart zurück.
python -m app.scripts.seed --if-empty
echo "moneyfy-Backend startet."
exec "$@"