From b78762155fad3955843cb35b7cdf60b164fa4f8e Mon Sep 17 00:00:00 2001 From: Wolfgang Date: Fri, 9 Jan 2026 17:12:49 +0100 Subject: [PATCH] Initial commit --- install.sh | 91 +++++++++++++++++++++++++++++++++ libsupabase.sh | 97 ++++++++++++++++++++++++++++++++++++ sql/init_pgvector.sql | 5 ++ templates/docker-compose.yml | 95 +++++++++++++++++++++++++++++++++++ 4 files changed, 288 insertions(+) create mode 100755 install.sh create mode 100755 libsupabase.sh create mode 100644 sql/init_pgvector.sql create mode 100644 templates/docker-compose.yml diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..cb2c4f9 --- /dev/null +++ b/install.sh @@ -0,0 +1,91 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "$SCRIPT_DIR/libsupabase.sh" +setup_traps + +######################################## +# Defaults +######################################## + +cores=2 +memory=4096 +swap=512 +disk=50 +bridge="vmbr0" +storage="local-zfs" +ip="dhcp" +unpriv=1 +ctid="" + +######################################## +# Args +######################################## + +while [[ $# -gt 0 ]]; do + case "$1" in + --cores) cores="$2"; shift 2 ;; + --memory) memory="$2"; shift 2 ;; + --swap) swap="$2"; shift 2 ;; + --disk) disk="$2"; shift 2 ;; + --bridge) bridge="$2"; shift 2 ;; + --storage) storage="$2"; shift 2 ;; + --ip) ip="$2"; shift 2 ;; + --ctid) ctid="$2"; shift 2 ;; + --help) + echo "Usage: bash install.sh [options]" + exit 0 + ;; + *) + die "Unknown argument: $1" + ;; + esac +done + +info "Argument-Parsing OK" + +######################################## +# Checks +######################################## + +need_cmd pct pvesm pveam pvesh awk grep sort + +pve_bridge_exists "$bridge" || die "Bridge $bridge not found" +pve_storage_exists "$storage" || die "Storage $storage not found" + +template="$(pve_template_ensure_debian12 "$storage")" +info "Template OK: $template" + +if [[ -z "$ctid" ]]; then + ctid="$(pve_next_free_ctid)" +fi + +[[ -n "$ctid" ]] || die "CTID empty" + +hostname="sb-$(date +%s)" + +info "Using CTID=$ctid Hostname=$hostname" + +######################################## +# Create CT +######################################## + +net0="$(pve_build_net0 "$bridge" "$ip")" +rootfs="$storage:$disk" +features="nesting=1,keyctl=1,fuse=1" + +info "Creating CT $ctid" + +pct create "$ctid" "$template" \ + --hostname "$hostname" \ + --cores "$cores" \ + --memory "$memory" \ + --swap "$swap" \ + --net0 "$net0" \ + --rootfs "$rootfs" \ + --unprivileged "$unpriv" \ + --features "$features" \ + --start 0 + +info "CT created successfully" diff --git a/libsupabase.sh b/libsupabase.sh new file mode 100755 index 0000000..b5b04ff --- /dev/null +++ b/libsupabase.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +######################################## +# Logging / Errors +######################################## + +_ts() { date '+%F %T'; } + +log() { echo "[$(_ts)] $*"; } +info() { log "INFO: $*"; } +warn() { log "WARN: $*"; } +die() { log "ERROR: $*"; exit 1; } + +on_error() { + die "Failed at line $1: $2 (exit=$3)" +} +setup_traps() { + trap 'on_error $LINENO "$BASH_COMMAND" "$?"' ERR +} + +######################################## +# Preconditions +######################################## + +need_cmd() { + for c in "$@"; do + command -v "$c" >/dev/null 2>&1 || die "Missing command: $c" + done +} + +######################################## +# Proxmox helpers +######################################## + +pve_bridge_exists() { + [[ -d "/sys/class/net/$1/bridge" ]] +} + +pve_storage_exists() { + pvesm status --storage "$1" >/dev/null 2>&1 +} + +######################################## +# Template handling +######################################## + +pve_template_ensure_debian12() { + local tpl="debian-12-standard_12.12-1_amd64.tar.zst" + local store="$1" + + if ! pveam list "$store" >/dev/null 2>&1; then + warn "pveam storage '$store' not available for templates; falling back to 'local'" + store="local" + fi + + if ! pveam list "$store" | awk '{print $2}' | grep -qx "$tpl"; then + info "Downloading CT template to $store: $tpl" + pveam update + pveam download "$store" "$tpl" + fi + + echo "$store:vztmpl/$tpl" +} + +######################################## +# Cluster-wide CTID +######################################## + +pve_next_free_ctid() { + local used + used="$(pvesh get /cluster/resources --type vm | awk 'NR>1 {print $1}' | sort -n)" + + for id in $(seq 100 9999); do + if ! echo "$used" | grep -qx "$id"; then + echo "$id" + return + fi + done + + die "No free CTID found" +} + +######################################## +# Networking +######################################## + +pve_build_net0() { + local bridge="$1" + local ip="$2" + + if [[ "$ip" == "dhcp" ]]; then + echo "name=eth0,bridge=$bridge,ip=dhcp" + else + echo "name=eth0,bridge=$bridge,ip=$ip" + fi +} diff --git a/sql/init_pgvector.sql b/sql/init_pgvector.sql new file mode 100644 index 0000000..11fa6ae --- /dev/null +++ b/sql/init_pgvector.sql @@ -0,0 +1,5 @@ +CREATE EXTENSION IF NOT EXISTS vector; + +-- Optional, aber hilfreich: +-- CREATE EXTENSION IF NOT EXISTS pgcrypto; + diff --git a/templates/docker-compose.yml b/templates/docker-compose.yml new file mode 100644 index 0000000..9351e5d --- /dev/null +++ b/templates/docker-compose.yml @@ -0,0 +1,95 @@ +services: + # --- Vectorstore DB (pgvector) --- + supabase-db: + image: pgvector/pgvector:pg15 + container_name: supabase-db + restart: unless-stopped + environment: + POSTGRES_DB: ${SB_DB_NAME} + POSTGRES_USER: ${SB_DB_USER} + POSTGRES_PASSWORD: ${SB_DB_PASS} + volumes: + - ./volumes/supabase-db:/var/lib/postgresql/data + - ./init:/docker-entrypoint-initdb.d:ro + ports: + - "5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${SB_DB_USER} -d ${SB_DB_NAME}"] + interval: 10s + timeout: 5s + retries: 15 + + # Optional: REST API auf die Vector-DB (wenn du es brauchst) + # Wenn du "von außen keinen DB-Zugriff" willst: später im Reverse Proxy sperren oder Port entfernen. + postgrest: + image: postgrest/postgrest:v12.2.8 + container_name: postgrest + restart: unless-stopped + depends_on: + supabase-db: + condition: service_healthy + environment: + PGRST_DB_URI: postgres://${SB_DB_USER}:${SB_DB_PASS}@supabase-db:5432/${SB_DB_NAME} + PGRST_DB_SCHEMA: public + PGRST_DB_ANON_ROLE: anon + PGRST_SERVER_PORT: 3000 + ports: + - "3000:3000" + + # --- n8n DB (separat, sauber getrennt) --- + n8n-db: + image: postgres:15-alpine + container_name: n8n-db + restart: unless-stopped + environment: + POSTGRES_DB: ${N8N_DB_NAME} + POSTGRES_USER: ${N8N_DB_USER} + POSTGRES_PASSWORD: ${N8N_DB_PASS} + volumes: + - ./volumes/n8n-db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${N8N_DB_USER} -d ${N8N_DB_NAME}"] + interval: 10s + timeout: 5s + retries: 15 + + # --- n8n --- + n8n: + image: docker.n8n.io/n8nio/n8n:latest + container_name: n8n + restart: unless-stopped + depends_on: + n8n-db: + condition: service_healthy + ports: + - "5678:5678" + environment: + DB_TYPE: postgresdb + DB_POSTGRESDB_HOST: n8n-db + DB_POSTGRESDB_PORT: 5432 + DB_POSTGRESDB_DATABASE: ${N8N_DB_NAME} + DB_POSTGRESDB_USER: ${N8N_DB_USER} + DB_POSTGRESDB_PASSWORD: ${N8N_DB_PASS} + + GENERIC_TIMEZONE: Europe/Berlin + NODE_ENV: production + + N8N_HOST: ${N8N_HOST} + N8N_PORT: 5678 + WEBHOOK_URL: ${N8N_WEBHOOK_URL} + + N8N_BASIC_AUTH_ACTIVE: "true" + N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER} + N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASS} + + N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY} + + # Solange du KEIN HTTPS hast, sonst bekommst du genau die "secure cookie"-Meldung: + N8N_SECURE_COOKIE: "false" + + # Optional: wenn du später Community Nodes brauchst + N8N_COMMUNITY_PACKAGES_ENABLED: "true" + + volumes: + - ./volumes/n8n-data:/home/node/.n8n +