#!/usr/bin/env bash # # OPENBUREAU — All-in-One LXC für Proxmox VE (Supabase-Kern + CMS). # # AUSFÜHREN AUF DEM PROXMOX-HOST (nicht im Container), als root: # bash create-openbureau-lxc.sh # # Legt einen unprivileged Debian-12-LXC an (Docker-fähig: nesting + keyctl), # installiert Docker, zieht das Repo, generiert alle Secrets (POSTGRES_PASSWORD, # JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY) und befüllt die .env. Optional baut # und startet es den Stack direkt. set -euo pipefail ############################ CONFIG ############################ CTID="${CTID:-$(pvesh get /cluster/nextid)}" HOSTNAME="openbureau" # Storage TEMPLATE_STORAGE="local" ROOTFS_STORAGE="local-lvm" DISK_GB="20" # Supabase + CMS # Ressourcen RAM_MB="4096" SWAP_MB="1024" CORES="2" # Netzwerk BRIDGE="vmbr0" IP="dhcp" # "dhcp" ODER statisch z.B. "192.168.1.50/24" GATEWAY="" # nur bei statischer IP # Zugang SSH_PUBKEY_FILE="${SSH_PUBKEY_FILE:-$HOME/.ssh/id_ed25519.pub}" ROOT_PASSWORD="" # Repo ist öffentlich → Clone braucht KEIN Token. # GIT_TOKEN nur setzen, wenn das CMS später per GIT_PUBLISH nach Gitea # zurückschreiben soll (git push braucht Auth). Format: "tokenname:tokenwert". GIT_TOKEN="${GIT_TOKEN:-}" REPO_HOST="git.kgva.ch/karim/OPENBUREAU.git" APP_DIR="/opt/openbureau" # Admin (sieht/bearbeitet ALLE Beiträge). Wird auch als erster Login-User vorgeschlagen. ADMIN_EMAIL="${ADMIN_EMAIL:-karim@gabrielevarano.ch}" # Stack nach dem Setup direkt bauen + starten? COMPOSE_UP="true" ################################################################## say() { echo -e "\n\033[1;36m▸ $*\033[0m"; } # --- 0. Interaktiv nachfragen (Enter = Default) -------------------------- # Übersprungen, wenn kein Terminal (z.B. CI) — dann gelten die Defaults oben # bzw. vorab gesetzte Umgebungsvariablen. if [ -t 0 ]; then echo "OPENBUREAU LXC-Setup — Enter übernimmt den Default in [Klammern]." read -rp " Storage für die Disk [${ROOTFS_STORAGE}]: " _x; ROOTFS_STORAGE="${_x:-$ROOTFS_STORAGE}" read -rp " Netzwerk-Bridge [${BRIDGE}]: " _x; BRIDGE="${_x:-$BRIDGE}" read -rp " IP (dhcp | x.x.x.x/24) [${IP}]: " _x; IP="${_x:-$IP}" [ "$IP" != "dhcp" ] && { read -rp " Gateway: " GATEWAY; } read -rp " Admin-E-Mail [${ADMIN_EMAIL}]: " _x; ADMIN_EMAIL="${_x:-$ADMIN_EMAIL}" fi # --- 1. Template sicherstellen ------------------------------------------- say "Suche aktuelles Debian-12-Template…" pveam update >/dev/null || true TEMPLATE="$(pveam available --section system \ | awk '/debian-12-standard/{print $2}' | sort -V | tail -1)" [ -n "$TEMPLATE" ] || { echo "Kein debian-12-Template gefunden."; exit 1; } if ! pveam list "$TEMPLATE_STORAGE" | grep -q "$TEMPLATE"; then say "Lade Template $TEMPLATE…" pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" fi TEMPLATE_REF="${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" # --- 2. Netzwerk ---------------------------------------------------------- if [ "$IP" = "dhcp" ]; then NET="name=eth0,bridge=${BRIDGE},ip=dhcp" else [ -n "$GATEWAY" ] || { echo "Statische IP, aber GATEWAY leer."; exit 1; } NET="name=eth0,bridge=${BRIDGE},ip=${IP},gw=${GATEWAY}" fi # --- 3. Container erstellen ---------------------------------------------- say "Erstelle LXC $CTID ($HOSTNAME)…" CREATE_ARGS=( "$CTID" "$TEMPLATE_REF" --hostname "$HOSTNAME" --cores "$CORES" --memory "$RAM_MB" --swap "$SWAP_MB" --rootfs "${ROOTFS_STORAGE}:${DISK_GB}" --net0 "$NET" --unprivileged 1 --features "nesting=1,keyctl=1" --onboot 1 ) [ -n "$ROOT_PASSWORD" ] && CREATE_ARGS+=(--password "$ROOT_PASSWORD") [ -f "$SSH_PUBKEY_FILE" ] && CREATE_ARGS+=(--ssh-public-keys "$SSH_PUBKEY_FILE") pct create "${CREATE_ARGS[@]}" say "Starte Container…" pct start "$CTID" sleep 5 # Clone-URL (mit Token, falls gesetzt) if [ -n "$GIT_TOKEN" ]; then REPO_URL="https://${GIT_TOKEN}@${REPO_HOST}" else REPO_URL="https://${REPO_HOST}" fi # --- 4. Provisionierung im Container ------------------------------------- say "Installiere Docker + Git, ziehe Repo, generiere Secrets…" pct exec "$CTID" -- bash -euo pipefail -c " export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get install -y -qq ca-certificates curl git openssl >/dev/null curl -fsSL https://get.docker.com | sh >/dev/null systemctl enable --now docker if [ ! -d '${APP_DIR}/.git' ]; then git clone --quiet '${REPO_URL}' '${APP_DIR}' || { echo 'WARN: Clone fehlgeschlagen (Token nötig?). Setup hier gestoppt.'; exit 0; } fi cd '${APP_DIR}/cms' if [ ! -f .env ]; then cp .env.example .env # Secrets generieren PW=\$(openssl rand -hex 32) JWT=\$(openssl rand -hex 32) sed -i \"s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=\${PW}|\" .env sed -i \"s|^JWT_SECRET=.*|JWT_SECRET=\${JWT}|\" .env # ANON_KEY + SERVICE_ROLE_KEY via Wegwerf-Node-Container ableiten KEYS=\$(docker run --rm -v \"\$PWD\":/w -w /w node:20-alpine \ node 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') sed -i \"s|^ANON_KEY=.*|ANON_KEY=\${ANON}|\" .env sed -i \"s|^SERVICE_ROLE_KEY=.*|SERVICE_ROLE_KEY=\${SVC}|\" .env # URLs auf die Container-IP setzen HOSTIP=\$(hostname -I | awk '{print \$1}') sed -i \"s|^SITE_URL=.*|SITE_URL=http://\${HOSTIP}:8080|\" .env sed -i \"s|^API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://\${HOSTIP}:8000|\" .env sed -i \"s|^ADMIN_EMAILS=.*|ADMIN_EMAILS=${ADMIN_EMAIL}|\" .env echo 'OK: .env generiert.' fi if [ '${COMPOSE_UP}' = 'true' ]; then echo '→ Baue + starte Stack (dauert beim ersten Mal ein paar Minuten)…' docker compose up -d --build fi " # --- 5. Abschluss -------------------------------------------------------- IPADDR="$(pct exec "$CTID" -- hostname -I 2>/dev/null | awk '{print $1}')" say "Fertig. LXC $CTID läuft${IPADDR:+ unter $IPADDR}." cat <}:8080/admin/ Live: http://${IPADDR:-}:8080/ Supabase: http://${IPADDR:-}:8000 (nur API-Gateway, keine Web-UI — / gibt 404, ist normal) Login-User anlegen (im Container, nach dem Start): pct enter ${CTID} cd ${APP_DIR}/cms source .env curl -s -X POST "http://localhost:8000/auth/v1/admin/users" \\ -H "apikey: \$SERVICE_ROLE_KEY" \\ -H "Authorization: Bearer \$SERVICE_ROLE_KEY" \\ -H "Content-Type: application/json" \\ -d '{"email":"${ADMIN_EMAIL}","password":"DEIN-PASSWORT","email_confirm":true}' Hinweise: • :8000 ist das Supabase-API-Gateway (Kong), keine Web-Oberfläche. Das Admin-Login (:8080/admin/) spricht im Hintergrund damit. • Für Domain/HTTPS: SITE_URL + API_EXTERNAL_URL in .env auf die öffentliche Adresse setzen und 'docker compose up -d --build' neu. • Logs: pct enter ${CTID}; cd ${APP_DIR}/cms; docker compose logs -f EOF