Files
AfiS-Aktensystem/start.sh
T
Harlekin 4652dd08ec
Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
Virenpruefung an clamd anbinden, wo clamd tatsaechlich lauscht
Die Adresse zeigte auf /var/run/clamav/clamd.sock in einem geteilten
Volume. Das offizielle clamav-Image legt seinen Unix-Socket aber unter
/tmp im eigenen Container an - nachgemessen, das Verzeichnis im Volume
bleibt leer. Jeder Verbindungsversuch lief damit in ENOENT, und die
Statusseite meldete die Virenpruefung dauerhaft als gestoert.

Folgenreich war das nicht nur fuer die Anzeige: ohne erreichbaren Scanner
bleiben Uploads auf "Pruefung unvollstaendig" liegen und brauchen die
Freigabe von Hand. Die Kette hat also gehalten, aber niemand kam durch.

Dasselbe Image oeffnet ab Werk TCPSocket 3310. Darueber laeuft die
Anbindung jetzt; der Port ist im Stack nicht veroeffentlicht und nur im
internen Netz erreichbar. CLAMAV_ADDRESS nimmt weiterhin einen Pfad an,
falls irgendwo doch ein Unix-Socket erreichbar ist.

Das Socket-Volume faellt weg, es enthielt nie etwas. Der Scanner sieht
ausserdem die Dateiablage nicht mehr: uebertragen wird per INSTREAM durch
die Verbindung, gelesen hat er dort nie etwas.

Geprueft gegen ein laufendes clamd, einschliesslich EICAR. Diese Pruefung
bleibt als abschaltbarer Teil der Suite stehen:

  CLAMAV_LIVE=clamav:3310 pnpm --filter @afis/web test pruefkette

Die uebrigen neuen Pruefungen decken das Zerlegen der Adresse ab. Sie
haetten den Fehler nicht gefunden - eine Adresse, die auf nichts zeigt,
ist syntaktisch tadellos. Nur eine echte Verbindung beantwortet das.
2026-09-04 15:04:51 +02:00

149 lines
5.1 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Startet die Anwendung für die lokale Entwicklung: Datenbank, Worker, Web.
#
# Das hier ist ausdrücklich **nicht** der Betrieb. Im Betrieb läuft alles als
# ein Dockge-Stack, siehe docs/11-betrieb.md. Dieses Skript ist für den Fall,
# dass der Stack gerade nicht läuft und man am Code arbeiten will.
#
# ./start.sh Datenbank, Worker und Web starten
# ./start.sh --ohne-worker nur Datenbank und Web (kein Discord nötig)
# ./start.sh --nur-db nur die Datenbank hochfahren
#
# Beenden mit Strg+C, das nimmt alle Kindprozesse mit.
set -euo pipefail
cd "$(dirname "$0")"
DB_CONTAINER="afis-dev-db"
DB_PORT="${AFIS_DB_PORT:-5434}"
DB_IMAGE="postgres:16-alpine"
MIT_WORKER=1
NUR_DB=0
for arg in "$@"; do
case "$arg" in
--ohne-worker) MIT_WORKER=0 ;;
--nur-db) NUR_DB=1 ;;
-h|--help) awk 'NR>1 && /^#/ { sub(/^# ?/, ""); print; next } NR>1 { exit }' "$0"; exit 0 ;;
*) echo "Unbekannte Option: $arg" >&2; exit 2 ;;
esac
done
info() { printf '\033[36m▸\033[0m %s\n' "$*"; }
warn() { printf '\033[33m!\033[0m %s\n' "$*"; }
fehler() { printf '\033[31m✗\033[0m %s\n' "$*" >&2; exit 1; }
# --- Node aus nvm holen, falls es nicht schon im PATH liegt -------------------
if ! command -v node >/dev/null 2>&1; then
if [ -s "$HOME/.nvm/nvm.sh" ]; then
# shellcheck disable=SC1091
. "$HOME/.nvm/nvm.sh" >/dev/null
nvm use --lts >/dev/null 2>&1 || nvm use node >/dev/null 2>&1 || true
fi
fi
command -v node >/dev/null 2>&1 || fehler "Node nicht gefunden. Node 22 installieren oder nvm laden."
if ! command -v pnpm >/dev/null 2>&1; then
corepack enable >/dev/null 2>&1 || true
fi
command -v pnpm >/dev/null 2>&1 || fehler "pnpm nicht gefunden. 'corepack enable' ausführen."
command -v docker >/dev/null 2>&1 || fehler "Docker nicht gefunden, die Datenbank läuft im Container."
[ -f .env ] || fehler ".env fehlt. Von .env.example abschreiben und die Discord-Werte eintragen."
# --- Datenbank ---------------------------------------------------------------
if [ -n "$(docker ps -q -f "name=^${DB_CONTAINER}$")" ]; then
info "Datenbank läuft bereits"
elif [ -n "$(docker ps -aq -f "name=^${DB_CONTAINER}$")" ]; then
info "Datenbank starten"
docker start "$DB_CONTAINER" >/dev/null
else
info "Datenbank anlegen (Port ${DB_PORT})"
docker run -d --name "$DB_CONTAINER" \
-e POSTGRES_DB=afis -e POSTGRES_USER=afis -e POSTGRES_PASSWORD=afis \
-p "${DB_PORT}:5432" "$DB_IMAGE" >/dev/null \
|| fehler "Container nicht gestartet. Läuft schon etwas auf Port ${DB_PORT}?"
fi
printf ' warte auf Postgres'
for _ in $(seq 1 60); do
if docker exec "$DB_CONTAINER" pg_isready -U afis -d afis >/dev/null 2>&1; then
printf ' bereit\n'
break
fi
printf '.'
sleep 1
done
docker exec "$DB_CONTAINER" pg_isready -U afis -d afis >/dev/null 2>&1 \
|| fehler "Postgres antwortet nicht."
# --- Schema und Grundzustand -------------------------------------------------
info "Prisma-Client erzeugen"
pnpm --filter @afis/db run build >/dev/null
info "Migrationen anwenden"
pnpm db:deploy >/dev/null
info "Grundzustand einspielen"
pnpm db:seed >/dev/null
# --- Dateiablage -------------------------------------------------------------
# Im Stack ist das ein Volume, hier ein Verzeichnis. Ohne es scheitert der
# erste Upload mit einem Fehler, der nichts erklärt.
FILES_DIR_LOKAL="$(grep -E '^FILES_DIR=' .env 2>/dev/null | cut -d= -f2- || true)"
FILES_DIR_LOKAL="${FILES_DIR_LOKAL:-/tmp/afis-files}"
mkdir -p "${FILES_DIR_LOKAL}/quarantine" "${FILES_DIR_LOKAL}/store"
info "Dateiablage unter ${FILES_DIR_LOKAL}"
# Die Prüfkette braucht beides. Fehlt eines, bleiben die betroffenen Uploads in
# der Quarantäne - sie gehen nicht ungeprüft durch. Siehe docs/07.
CLAMAV_ADRESSE="$(grep -E '^CLAMAV_ADDRESS=' .env 2>/dev/null | cut -d= -f2- || true)"
CLAMAV_ADRESSE="${CLAMAV_ADRESSE:-clamav:3310}"
clamd_erreichbar() {
case "$CLAMAV_ADRESSE" in
/*) [ -S "$CLAMAV_ADRESSE" ] ;;
*) (exec 3<>"/dev/tcp/${CLAMAV_ADRESSE%:*}/${CLAMAV_ADRESSE##*:}") 2>/dev/null ;;
esac
}
clamd_erreichbar \
|| warn "Kein clamd unter ${CLAMAV_ADRESSE}: Uploads landen auf 'Prüfung unvollständig' und brauchen die Freigabe von Hand"
command -v qpdf >/dev/null \
|| warn "qpdf fehlt: PDF-Uploads landen auf 'Prüfung unvollständig' (im Container ist es enthalten)"
if [ "$NUR_DB" = "1" ]; then
info "Datenbank läuft auf localhost:${DB_PORT}. Beenden mit: docker stop ${DB_CONTAINER}"
exit 0
fi
# --- Kindprozesse ------------------------------------------------------------
PIDS=()
aufraeumen() {
echo
info "Beenden"
for pid in "${PIDS[@]:-}"; do
kill "$pid" 2>/dev/null || true
done
wait 2>/dev/null || true
info "Die Datenbank läuft weiter. Anhalten mit: docker stop ${DB_CONTAINER}"
}
trap aufraeumen INT TERM EXIT
if [ "$MIT_WORKER" = "1" ]; then
info "Worker starten (Discord-Rollenabgleich)"
pnpm worker:dev 2>&1 | sed 's/^/[worker] /' &
PIDS+=($!)
fi
info "Web starten"
pnpm dev 2>&1 | sed 's/^/[web] /' &
PIDS+=($!)
echo
info "Anwendung unter http://localhost:3000"
[ "$MIT_WORKER" = "1" ] || warn "Ohne Worker: Rollen werden nicht abgeglichen, /api/health meldet das Gateway als gestört"
echo
wait