Files
florian.wildeandClaude Opus 5.5 533a821087 feat(betrieb): Docker Compose und LXC-Installer
Ein Laufzeit-Paket (scripts/paket-bauen.sh) für beide Wege: Next standalone, worker als Bundle mit nur den nötigen Paketen, Migrationen ohne Prisma-CLI. Compose lokal komplett hochgefahren; der Installer auf pve-demo in einem echten Debian-13-LXC durchgelaufen.

Closes #019

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:35:41 +02:00

215 lines
7.2 KiB
Bash
Executable File
Raw Permalink 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.
#!/usr/bin/env bash
# Läuft im Debian-13-Container (von install-lxc.sh per `pct exec` aufgerufen):
#
# im-container.sh installieren --url http://10.0.10.50:3000
# im-container.sh aktualisieren
#
# Erwartet das Paket unter /root/proxdeploy-paket.tar.gz.
set -euo pipefail
# Über SSH/pct exec reisen die Locales des Aufrufers mit; im frischen Container gibt es sie nicht.
export LC_ALL=C.UTF-8 LANG=C.UTF-8
unset LANGUAGE
NODE_VERSION="24.21.0"
BASIS="/opt/proxdeploy"
ENV_DATEI="/etc/proxdeploy/proxdeploy.env"
PAKET="/root/proxdeploy-paket.tar.gz"
fehler() { echo "✗ $*" >&2; exit 1; }
schritt() { echo "◐ $*"; }
fertig() { echo "✓ $*"; }
node_installieren() {
case "$(uname -m)" in
x86_64) arch=x64 ;;
aarch64) arch=arm64 ;;
*) fehler "Architektur $(uname -m) wird nicht unterstützt." ;;
esac
if [ -x /opt/node/bin/node ] && [ "$(/opt/node/bin/node --version)" = "v$NODE_VERSION" ]; then return; fi
schritt "Node.js $NODE_VERSION"
local datei="node-v$NODE_VERSION-linux-$arch.tar.xz" tmp
tmp="$(mktemp -d)"
curl -fsSL -o "$tmp/$datei" "https://nodejs.org/dist/v$NODE_VERSION/$datei"
curl -fsSL -o "$tmp/SHASUMS256.txt" "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt"
(cd "$tmp" && grep " $datei\$" SHASUMS256.txt | sha256sum -c --quiet) || fehler "Prüfsumme von $datei stimmt nicht."
rm -rf /opt/node && mkdir -p /opt/node
tar -xJf "$tmp/$datei" -C /opt/node --strip-components=1
rm -rf "$tmp"
fertig "Node.js $(/opt/node/bin/node --version)"
}
paket_auspacken() {
[ -f "$PAKET" ] || fehler "$PAKET fehlt."
local version ziel
version="$(tar -xzOf "$PAKET" ./VERSION)"
ziel="$BASIS/versionen/$version"
rm -rf "$ziel" && mkdir -p "$ziel"
tar -xzf "$PAKET" -C "$ziel"
chown -R root:root "$ziel"
ln -sfn "$ziel" "$BASIS/aktuell"
rm -f "$PAKET"
# Die letzten drei Fassungen behalten – für ein Zurück per Symlink.
find "$BASIS/versionen" -mindepth 1 -maxdepth 1 -type d -printf '%T@ %p\n' | sort -rn | tail -n +4 |
cut -d' ' -f2- | xargs -r rm -rf
echo "$version"
}
dienste_schreiben() {
cat > /etc/systemd/system/proxdeploy-migrate.service <<EOF
[Unit]
Description=ProxDeploy – Datenbank-Migrationen
After=postgresql.service network-online.target
Requires=postgresql.service
[Service]
Type=oneshot
RemainAfterExit=yes
User=proxdeploy
EnvironmentFile=$ENV_DATEI
WorkingDirectory=$BASIS/aktuell/app
ExecStart=/opt/node/bin/node ../worker/migrieren.mjs
EOF
for dienst in app worker; do
if [ "$dienst" = app ]; then start="server.js"; beschreibung="Web-GUI und REST-API"; else start="../worker/worker.mjs"; beschreibung="worker (Aufträge)"; fi
cat > "/etc/systemd/system/proxdeploy-$dienst.service" <<EOF
[Unit]
Description=ProxDeploy – $beschreibung
After=proxdeploy-migrate.service
Requires=proxdeploy-migrate.service
[Service]
User=proxdeploy
EnvironmentFile=$ENV_DATEI
Environment=NODE_ENV=production
WorkingDirectory=$BASIS/aktuell/app
ExecStart=/opt/node/bin/node $start
Restart=on-failure
RestartSec=5
TimeoutStopSec=40
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectControlGroups=true
[Install]
WantedBy=multi-user.target
EOF
done
systemctl daemon-reload
}
auf_gesundheit_warten() {
for _ in $(seq 1 60); do
if curl -fsS -o /dev/null "http://127.0.0.1:3000/api/v1/gesundheit" 2>/dev/null; then return 0; fi
sleep 2
done
journalctl -u proxdeploy-app -u proxdeploy-migrate --no-pager -n 30 >&2
fehler "Die app antwortet nicht."
}
installieren() {
local url=""
while [ $# -gt 0 ]; do
case "$1" in
--url) url="$2"; shift 2 ;;
*) fehler "Unbekannte Option $1" ;;
esac
done
[ -n "$url" ] || fehler "--url fehlt."
[ -f "$ENV_DATEI" ] && fehler "ProxDeploy ist hier schon eingerichtet – zum Aktualisieren: install-lxc.sh --aktualisieren <ctid>"
schritt "Pakete (PostgreSQL, curl)"
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq --no-install-recommends ca-certificates curl xz-utils openssl postgresql >/dev/null
fertig "PostgreSQL $(psql --version | awk '{print $3}')"
node_installieren
schritt "Benutzer und Datenbank"
id proxdeploy >/dev/null 2>&1 || useradd --system --home-dir /var/lib/proxdeploy --create-home --shell /usr/sbin/nologin proxdeploy
local db_passwort admin_passwort
db_passwort="$(openssl rand -hex 24)"
admin_passwort="$(openssl rand -base64 18 | tr -d '/+=' | cut -c1-20)"
su postgres -c "psql -q -v ON_ERROR_STOP=1" <<SQL
DO \$\$ BEGIN
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'proxdeploy') THEN
CREATE ROLE proxdeploy LOGIN PASSWORD '$db_passwort';
ELSE
ALTER ROLE proxdeploy PASSWORD '$db_passwort';
END IF;
END \$\$;
SQL
su postgres -c "psql -tAc \"SELECT 1 FROM pg_database WHERE datname = 'proxdeploy'\"" | grep -q 1 ||
su postgres -c "createdb -O proxdeploy proxdeploy"
fertig "Datenbank proxdeploy"
schritt "Konfiguration $ENV_DATEI"
mkdir -p /etc/proxdeploy
umask 027
cat > "$ENV_DATEI" <<EOF
# ProxDeploy – erzeugt von im-container.sh am $(date -Iseconds)
DATABASE_URL=postgresql://proxdeploy:$db_passwort@127.0.0.1:5432/proxdeploy
PROXDEPLOY_SCHLUESSEL=$(openssl rand -base64 32)
SITZUNG_GEHEIMNIS=$(openssl rand -base64 48)
APP_URL=$url
# Hinter einem Reverse-Proxy auf true setzen (wertet X-Forwarded-* aus).
TRUST_PROXY=false
PORT=3000
HOSTNAME=0.0.0.0
LOG_LEVEL=info
WORKER_PARALLEL=4
ADMIN_BENUTZER=admin
ADMIN_PASSWORT=$admin_passwort
EOF
chown root:proxdeploy "$ENV_DATEI"
chmod 0640 "$ENV_DATEI"
umask 022
schritt "ProxDeploy"
local version
version="$(paket_auspacken)"
dienste_schreiben
systemctl enable --now proxdeploy-app proxdeploy-worker >/dev/null 2>&1
auf_gesundheit_warten
# Der erste Admin ist angelegt – das Passwort hat in der Datei nichts mehr zu suchen.
sed -i '/^ADMIN_PASSWORT=/d' "$ENV_DATEI"
fertig "ProxDeploy $version läuft"
cat <<EOF
┌──────────────────────────────────────────────────────────────┐
ProxDeploy ist bereit: $url
Benutzer: admin
Passwort: $admin_passwort
(wird nur jetzt angezeigt – nach der Anmeldung unter
Einstellungen → Passwort ändern)
Konfiguration: $ENV_DATEI
Logs: journalctl -u proxdeploy-app -u proxdeploy-worker
└──────────────────────────────────────────────────────────────┘
EOF
}
aktualisieren() {
[ -f "$ENV_DATEI" ] || fehler "Hier ist ProxDeploy noch nicht eingerichtet."
node_installieren
local alt version
alt="$(cat "$BASIS/aktuell/VERSION" 2>/dev/null || echo –)"
systemctl stop proxdeploy-app proxdeploy-worker
version="$(paket_auspacken)"
dienste_schreiben
# migrate bleibt nach dem ersten Lauf „aktiv“ – für die neue Fassung ausdrücklich neu starten.
systemctl restart proxdeploy-migrate
systemctl start proxdeploy-app proxdeploy-worker
auf_gesundheit_warten
fertig "ProxDeploy $alt → $version"
}
case "${1:-}" in
installieren) shift; installieren "$@" ;;
aktualisieren) shift; aktualisieren "$@" ;;
*) fehler "Aufruf: $0 installieren --url URL | aktualisieren" ;;
esac