diff --git a/README.md b/README.md index 116304a..d557896 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,38 @@ Ein-Befehl-Installer, der den Rapport-Self-Hosting-Stack in einen **Proxmox-LXC-Container** deployt. Für Leute, die einen Proxmox-Host haben und Rapport nicht auf dem Mac (SERVER-APP), sondern auf dem Server laufen lassen wollen. +## Zwei Scripts + +| Script | Was es deployt | +|---|---| +| `rapport-lxc.sh` | **Nur der Rapport-Self-Hosting-Stack** (SERVER-CONTAINER: Supabase + Rapport-Frontend) | +| `rapport-stack-lxc.sh` | **Die ganze Hosting-Plattform** (RAPPORT-STACK: Supabase + Rapport-Frontend + Marketing-Website + RAPPORT-HOST Hosting-/Abo-Plattform + Admin-Cockpit) | + +### Hosting-Plattform deployen (RAPPORT-STACK) + +Auf der **Proxmox-Host-Shell** (RAPPORT-HOST/RAPPORT-STACK sind privat → Gitea-Token nötig): + +```bash +GITEA_TOKEN=dein_token bash -c "$(curl -fsSL http://git.kgva.ch/karim/RAPPORT-SERVER-PROXMOX-LXC/raw/branch/main/rapport-stack-lxc.sh)" +``` + +Token erstellen: Gitea → Einstellungen → Anwendungen → Token generieren (Scope `read:repository`). + +Danach erreichbar: +- **Hosting-Plattform** `http://:8787` (Marketing/Login/Konto) +- **Admin-Cockpit** `http://:8787/admin/` (Passwort wird generiert, am Ende ausgegeben) +- **Rapport-App** `http://:8080` · **API** `http://:8000` + +**Arbeits-Workflow wie bei openbureau** — Änderungen live holen: + +```bash +pct exec -- bash -c 'cd /opt/rapport && ./update.sh' +``` + +`update.sh` macht `git pull` in allen vier Repos, synct Migrations und `docker compose up -d --build` → deine Änderungen sind live. + +--- + Der vierte Deployment-Pfad der Rapport-Familie: | Repo | Zielgruppe | diff --git a/rapport-stack-lxc.sh b/rapport-stack-lxc.sh new file mode 100755 index 0000000..61f9fb7 --- /dev/null +++ b/rapport-stack-lxc.sh @@ -0,0 +1,206 @@ +#!/usr/bin/env bash +# ───────────────────────────────────────────────────────────────────────────── +# RAPPORT-STACK — Proxmox-LXC-Installer (All-in-One: Supabase + Website + HOST) +# +# Läuft auf der PROXMOX-VE-HOST-SHELL. Baut einen unprivilegierten Debian-12-LXC +# mit Docker, klont die vier Rapport-Repos, generiert Secrets + JWT-Keys, setzt +# die LAN-URLs und fährt den kompletten Stack hoch. Danach Arbeits-Workflow wie +# bei openbureau: pct enter → cd /opt/rapport && ./update.sh +# (git pull in allen Repos + Rebuild + Restart → Änderungen sind live). +# +# GITEA_TOKEN=xxxx bash -c "$(curl -fsSL http://git.kgva.ch/karim/RAPPORT-SERVER-PROXMOX-LXC/raw/branch/main/rapport-stack-lxc.sh)" +# +# GITEA_TOKEN ist PFLICHT: RAPPORT-HOST und RAPPORT-STACK sind private Repos. +# Token erstellen: Gitea → Einstellungen → Anwendungen → Token generieren +# (Scope: read:repository genügt). +# ───────────────────────────────────────────────────────────────────────────── +set -euo pipefail + +VERSION="0.1.0" + +# ═══ Konfiguration (per Env-Var überschreibbar) ═════════════════════════════ +CTID="${CTID:-$(pvesh get /cluster/nextid)}" +CT_HOSTNAME="${CT_HOSTNAME:-rapport-stack}" +DISK_GB="${DISK_GB:-30}" # Supabase-Images + Build brauchen Platz +CORES="${CORES:-4}" +RAM_MB="${RAM_MB:-8192}" # Supabase + HOST + Build, min. 6 GB +SWAP_MB="${SWAP_MB:-2048}" +BRIDGE="${BRIDGE:-vmbr0}" +STORAGE="${STORAGE:-local-lvm}" +TMPL_STORAGE="${TMPL_STORAGE:-local}" +NET_IP="${NET_IP:-dhcp}" # dhcp ODER z.B. 192.168.1.60/24 +NET_GW="${NET_GW:-}" # Pflicht bei statischer IP +PASSWORD="${PASSWORD:-}" # root-PW im Container (leer = kein Login) +TEMPLATE="${TEMPLATE:-debian-12-standard}" + +GITEA="${GITEA:-https://git.kgva.ch}" +GITEA_USER="${GITEA_USER:-karim}" +GITEA_TOKEN="${GITEA_TOKEN:-}" +RAPPORT_APP_TAG="${RAPPORT_APP_TAG:-main}" + +GN='\033[0;32m'; YW='\033[0;33m'; RD='\033[0;31m'; BL='\033[1;34m'; CL='\033[0m' +msg() { echo -e "${GN}✔${CL} $*"; } +info() { echo -e "${YW}→${CL} $*"; } +die() { echo -e "${RD}✗${CL} $*" >&2; exit 1; } + +echo -e "${BL}RAPPORT-STACK — Proxmox-LXC-Installer v${VERSION}${CL}" + +# ═══ Vorbedingungen ═════════════════════════════════════════════════════════ +[[ $EUID -eq 0 ]] || die "Bitte als root auf der Proxmox-Host-Shell ausführen." +command -v pct >/dev/null || die "pct nicht gefunden — Proxmox-VE-Host erwartet." +command -v pveam >/dev/null || die "pveam nicht gefunden — Proxmox-VE-Host erwartet." +[[ -n "$GITEA_TOKEN" ]] || die "GITEA_TOKEN fehlt. Private Repos (HOST/STACK) brauchen ihn. + Beispiel: GITEA_TOKEN=xxxx bash -c \"\$(curl -fsSL …/rapport-stack-lxc.sh)\"" + +# ═══ 1 · Debian-Template ════════════════════════════════════════════════════ +info "Suche Debian-12-Template …" +pveam update >/dev/null 2>&1 || true +TMPL_FILE="$(pveam available --section system | awk -v t="$TEMPLATE" '$2 ~ t {print $2}' | sort -V | tail -n1)" +[[ -n "$TMPL_FILE" ]] || die "Kein Template '$TEMPLATE*' verfügbar." +if ! pveam list "$TMPL_STORAGE" 2>/dev/null | grep -q "$TMPL_FILE"; then + info "Lade Template $TMPL_FILE …"; pveam download "$TMPL_STORAGE" "$TMPL_FILE" +fi +TMPL_REF="${TMPL_STORAGE}:vztmpl/${TMPL_FILE}" +msg "Template: $TMPL_REF" + +# ═══ 2 · Netzwerk ═══════════════════════════════════════════════════════════ +if [[ "$NET_IP" == "dhcp" ]]; then + NETCFG="name=eth0,bridge=${BRIDGE},ip=dhcp" +else + [[ -n "$NET_GW" ]] || die "Bei statischer IP (NET_IP=$NET_IP) muss NET_GW gesetzt sein." + NETCFG="name=eth0,bridge=${BRIDGE},ip=${NET_IP},gw=${NET_GW}" +fi + +# ═══ 3 · Container erstellen ════════════════════════════════════════════════ +info "Erstelle LXC #${CTID} (${CT_HOSTNAME}) …" +CREATE_ARGS=( + "$CTID" "$TMPL_REF" + --hostname "$CT_HOSTNAME" + --cores "$CORES" --memory "$RAM_MB" --swap "$SWAP_MB" + --rootfs "${STORAGE}:${DISK_GB}" + --net0 "$NETCFG" + --features "nesting=1,keyctl=1" + --unprivileged 1 --onboot 1 --ostype debian +) +[[ -n "$PASSWORD" ]] && CREATE_ARGS+=(--password "$PASSWORD") +pct create "${CREATE_ARGS[@]}" +pct start "$CTID" +msg "Container #${CTID} läuft." + +info "Warte auf Netzwerk …" +for _ in $(seq 1 30); do + pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1 && break; sleep 2 +done + +# ═══ 4 · Docker + Node + git ════════════════════════════════════════════════ +info "Installiere Docker + Node + git (kann Minuten dauern) …" +pct exec "$CTID" -- bash -c ' + set -euo pipefail + export DEBIAN_FRONTEND=noninteractive + apt-get update -qq + apt-get install -y -qq ca-certificates curl git openssl nodejs >/dev/null + curl -fsSL https://get.docker.com | sh >/dev/null 2>&1 + systemctl enable --now docker >/dev/null 2>&1 || true +' +msg "Docker + Node installiert." + +# ═══ 5 · Repos klonen ═══════════════════════════════════════════════════════ +# Token-URL nur für die privaten Repos; public über klare URL. +AUTH="${GITEA_USER}:${GITEA_TOKEN}@${GITEA#https://}" +info "Klone Repos nach /opt/rapport …" +pct exec "$CTID" -- bash -c " + set -euo pipefail + rm -rf /opt/rapport && mkdir -p /opt/rapport && cd /opt/rapport + git clone --depth 1 '${GITEA}/${GITEA_USER}/RAPPORT-SERVER.git' SERVER-CONTAINER + git clone --depth 1 '${GITEA}/${GITEA_USER}/RAPPORT-WEBSITE.git' RAPPORT-WEBSITE + git clone --depth 1 'https://${AUTH}/${GITEA_USER}/RAPPORT-HOST.git' RAPPORT-HOST + git clone --depth 1 'https://${AUTH}/${GITEA_USER}/RAPPORT-STACK.git' RAPPORT-STACK +" +msg "Repos geklont." + +# ═══ 6 · IP ermitteln ═══════════════════════════════════════════════════════ +info "Ermittle Container-IP …" +CT_IP="" +for _ in $(seq 1 15); do + CT_IP="$(pct exec "$CTID" -- hostname -I 2>/dev/null | awk '{print $1}')" + [[ -n "$CT_IP" ]] && break; sleep 2 +done +[[ -n "$CT_IP" ]] || die "Konnte keine Container-IP ermitteln." +msg "Container-IP: $CT_IP" + +# ═══ 7 · .env generieren (Secrets + Keys + LAN-URLs) ════════════════════════ +info "Generiere Secrets + JWT-Keys, setze LAN-URLs …" +pct exec "$CTID" -- env CT_IP="$CT_IP" APP_TAG="$RAPPORT_APP_TAG" bash -c ' + set -euo pipefail + cd /opt/rapport/RAPPORT-STACK + cp -n .env.example .env + + PWD_DB=$(openssl rand -hex 32) + JWT=$(openssl rand -hex 32) + HJWT=$(openssl rand -hex 32) + ADMINPW=$(openssl rand -hex 12) + + # ANON/SERVICE-Keys aus JWT_SECRET ableiten (Skript liegt in SERVER-CONTAINER) + KEYS=$(node /opt/rapport/SERVER-CONTAINER/scripts/generate-keys.mjs "$JWT" 2>/dev/null) + ANON=$(echo "$KEYS" | sed -n "s|^ANON_KEY=||p") + SVC=$(echo "$KEYS" | sed -n "s|^SERVICE_ROLE_KEY=||p") + [ -n "$ANON" ] && [ -n "$SVC" ] || { echo "Key-Generierung fehlgeschlagen" >&2; exit 1; } + + set_kv() { sed -i "s|^$1=.*|$1=$2|" .env; } + set_kv POSTGRES_PASSWORD "$PWD_DB" + set_kv JWT_SECRET "$JWT" + set_kv ANON_KEY "$ANON" + set_kv SERVICE_ROLE_KEY "$SVC" + set_kv HOST_JWT_SECRET "$HJWT" + set_kv ADMIN_PASSWORD "$ADMINPW" + set_kv RAPPORT_APP_TAG "$APP_TAG" + set_kv SITE_URL "http://${CT_IP}:8080" + set_kv API_EXTERNAL_URL "http://${CT_IP}:8000" + set_kv PUBLIC_BASE_URL "http://${CT_IP}:8787" + set_kv RAPPORT_INSTANCE_URL_TEMPLATE "http://${CT_IP}:8080/?studio={slug}" + chmod 600 .env + echo "ADMIN_PASSWORD=$ADMINPW" > /opt/rapport/ADMIN_CREDENTIALS.txt +' +msg ".env erzeugt (Secrets zufällig, Keys passend)." + +# ═══ 8 · DB-Migrations holen ════════════════════════════════════════════════ +info "Hole Rapport-DB-Migrations …" +pct exec "$CTID" -- bash -c "cd /opt/rapport/SERVER-CONTAINER && bash scripts/sync-migrations.sh" +msg "Migrations synchronisiert." + +# ═══ 9 · update.sh im Container hinterlegen (Workflow git pull + restart) ════ +info "Lege /opt/rapport/update.sh an …" +pct exec "$CTID" -- bash -c 'cat > /opt/rapport/update.sh <<'\''EOF'\'' +#!/usr/bin/env bash +# RAPPORT-STACK aktualisieren: git pull in allen Repos + Rebuild + Restart. +set -euo pipefail +cd /opt/rapport +for d in SERVER-CONTAINER RAPPORT-WEBSITE RAPPORT-HOST RAPPORT-STACK; do + echo "→ git pull $d"; git -C "$d" pull --ff-only +done +echo "→ Migrations synchronisieren" +( cd SERVER-CONTAINER && bash scripts/sync-migrations.sh ) +echo "→ Rebuild + Restart" +cd RAPPORT-STACK +docker compose up -d --build +docker compose ps +EOF +chmod +x /opt/rapport/update.sh' +msg "update.sh bereit." + +# ═══ 10 · Stack hochfahren ══════════════════════════════════════════════════ +info "Starte Stack (Images pullen + host bauen, Erststart dauert mehrere Min) …" +pct exec "$CTID" -- bash -c "cd /opt/rapport/RAPPORT-STACK && docker compose up -d --build" + +# ═══ Fertig ═════════════════════════════════════════════════════════════════ +ADMINPW="$(pct exec "$CTID" -- bash -c 'sed -n "s|^ADMIN_PASSWORD=||p" /opt/rapport/ADMIN_CREDENTIALS.txt' 2>/dev/null || true)" +echo +msg "RAPPORT-STACK läuft in LXC #${CTID} (${CT_HOSTNAME})" +echo -e " ${GN}Hosting-Plattform:${CL} http://${CT_IP}:8787 (Marketing/Login/Konto)" +echo -e " ${GN}Admin-Cockpit:${CL} http://${CT_IP}:8787/admin/ PW: ${ADMINPW}" +echo -e " ${GN}Rapport-App:${CL} http://${CT_IP}:8080" +echo -e " ${GN}Rapport-API:${CL} http://${CT_IP}:8000" +echo +info "Updaten: pct exec ${CTID} -- bash -c 'cd /opt/rapport && ./update.sh'" +info "Status: pct exec ${CTID} -- bash -c 'cd /opt/rapport/RAPPORT-STACK && docker compose ps'" +info "Shell: pct enter ${CTID}"