#!/bin/sh # Server Up — installeren, bijwerken of verwijderen op een Linux-server. # # curl -fsSL https://git.ramonbesselink.nl/bes-r/server-up/raw/branch/main/install.sh | sh # # Liever eerst lezen wat je uitvoert (aan te raden): # # curl -fsSL https://git.ramonbesselink.nl/bes-r/server-up/raw/branch/main/install.sh -o install.sh # less install.sh # sh install.sh # # POSIX sh, dus het werkt onder dash, ash en bash. set -eu SCRIPT_VERSIE="1.0" GIT_HOST="git.ramonbesselink.nl" GIT_PAD="bes-r/server-up" BRANCH="main" DOEL="/opt/server-up" POORT="5000" BIND="127.0.0.1" TOKEN="" JA=0 DROOG=0 ACTIE="install" # ── Uitvoer ────────────────────────────────────────────────────────────────── # Kleuren alleen als we naar een terminal schrijven; in een pipe of logbestand # leveren escape-codes onleesbare rommel op. if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then C_OK=$(printf '\033[32m'); C_WAARSCHUW=$(printf '\033[33m') C_FOUT=$(printf '\033[31m'); C_KOP=$(printf '\033[1m'); C_UIT=$(printf '\033[0m') else C_OK=''; C_WAARSCHUW=''; C_FOUT=''; C_KOP=''; C_UIT='' fi log() { printf '%s\n' " $*"; } stap() { printf '\n%s%s%s\n' "$C_KOP" "$*" "$C_UIT"; } goed() { printf ' %s✔%s %s\n' "$C_OK" "$C_UIT" "$*"; } waarschuw() { printf ' %s!%s %s\n' "$C_WAARSCHUW" "$C_UIT" "$*"; } fout() { printf '\n %s✖ %s%s\n\n' "$C_FOUT" "$*" "$C_UIT" >&2; exit 1; } # Bij --dry-run alleen waarschuwen: je wilt de hele voorvertoning zien, ook op # een machine waar Docker (nog) niet staat. fout_tenzij_droog() { if [ "$DROOG" -eq 1 ]; then waarschuw "$*"; else fout "$*"; fi } heeft() { command -v "$1" >/dev/null 2>&1; } # Voert uit, of toont alleen wat er zou gebeuren bij --dry-run. doe() { if [ "$DROOG" -eq 1 ]; then printf ' [droog] %s\n' "$*" else "$@" fi } vraag() { # vraag "Vraag?" -> 0 bij ja. Met --yes of zonder terminal: altijd ja. [ "$JA" -eq 1 ] && return 0 [ -t 0 ] || return 0 printf ' %s [J/n] ' "$1" read -r antwoord /dev/null 2>&1; then goed "Draait onder je eigen account; sudo is niet nodig" return 0 fi fi if heeft sudo; then SUDO="sudo" log "sudo wordt gebruikt voor ${DOEL} en docker" else fout "Geen root en geen sudo. Draai als root, of kies met --dir een map waar je zelf in mag schrijven." fi } # ── Vereisten ──────────────────────────────────────────────────────────────── controleer_systeem() { stap "Systeem controleren" bepaal_rechten [ "$(uname -s)" = "Linux" ] || waarschuw "Dit script is voor Linux; op $(uname -s) is het onbeproefd." for hulp in curl tar; do heeft "$hulp" || fout_tenzij_droog "'$hulp' ontbreekt. Installeer het en probeer opnieuw." done goed "$(uname -s) $(uname -m)" } controleer_docker() { if heeft docker; then if $SUDO docker info >/dev/null 2>&1; then goed "Docker draait ($($SUDO docker version --format '{{.Server.Version}}' 2>/dev/null || echo '?'))" else fout_tenzij_droog "Docker is geïnstalleerd maar de daemon reageert niet. Start hem met: systemctl start docker" fi else waarschuw "Docker is niet geïnstalleerd." if vraag "Nu installeren via het officiële script van docker.com?"; then stap "Docker installeren" doe sh -c "curl -fsSL https://get.docker.com | $SUDO sh" doe $SUDO systemctl enable --now docker || true heeft docker || [ "$DROOG" -eq 1 ] || fout "Installeren van Docker is niet gelukt." goed "Docker geïnstalleerd" else fout_tenzij_droog "Docker is vereist. Zie https://docs.docker.com/engine/install/" fi fi if $SUDO docker compose version >/dev/null 2>&1; then goed "Compose-plugin aanwezig" else fout_tenzij_droog "De docker-compose-plugin ontbreekt. Installeer 'docker-compose-plugin' via je pakketbeheerder." fi } # ── Broncode ophalen ───────────────────────────────────────────────────────── bron_url() { if [ -n "$TOKEN" ]; then printf 'https://%s@%s/%s.git' "$TOKEN" "$GIT_HOST" "$GIT_PAD" else printf 'https://%s/%s.git' "$GIT_HOST" "$GIT_PAD" fi } haal_broncode() { stap "Broncode ophalen" if [ -d "$DOEL/.git" ]; then log "Bestaande installatie bijwerken in $DOEL" doe $SUDO git -C "$DOEL" fetch --depth 1 origin "$BRANCH" doe $SUDO git -C "$DOEL" reset --hard "origin/$BRANCH" goed "Bijgewerkt naar de nieuwste $BRANCH" return 0 fi doe $SUDO mkdir -p "$DOEL" if heeft git; then # Zonder .git kunnen we later niet netjes bijwerken, dus git heeft de # voorkeur boven een los archief. if doe $SUDO git clone --depth 1 --branch "$BRANCH" "$(bron_url)" "$DOEL"; then goed "Gekloond naar $DOEL" return 0 fi waarschuw "Klonen mislukte; ik probeer het archief." fi archief="https://${GIT_HOST}/${GIT_PAD}/archive/${BRANCH}.tar.gz" log "Downloaden: $archief" if [ "$DROOG" -eq 0 ]; then tijdelijk="$(mktemp -d)" curl -fsSL ${TOKEN:+-H "Authorization: token $TOKEN"} "$archief" \ | tar xz -C "$tijdelijk" --strip-components=1 \ || fout "Ophalen mislukt. Is de repo openbaar? Zo niet, gebruik --token." $SUDO cp -r "$tijdelijk/." "$DOEL/" rm -rf "$tijdelijk" fi goed "Uitgepakt naar $DOEL" } # ── Instellingen ───────────────────────────────────────────────────────────── schrijf_env() { stap "Instellingen" env_bestand="$DOEL/.env" if [ -f "$env_bestand" ]; then goed "Bestaande .env blijft ongemoeid" # BIND en PORT wel bijwerken als ze expliciet zijn meegegeven. return 0 fi if [ "$DROOG" -eq 1 ]; then printf ' [droog] .env aanmaken met BIND=%s PORT=%s\n' "$BIND" "$POORT" return 0 fi if [ -f "$DOEL/.env.example" ]; then $SUDO cp "$DOEL/.env.example" "$env_bestand" else printf '' | $SUDO tee "$env_bestand" >/dev/null fi $SUDO sh -c "sed -i '/^BIND=/d;/^PORT=/d' '$env_bestand'" $SUDO sh -c "printf 'BIND=%s\nPORT=%s\n' '$BIND' '$POORT' >> '$env_bestand'" goed "Aangemaakt: $env_bestand (BIND=$BIND, PORT=$POORT)" } # ── Starten ────────────────────────────────────────────────────────────────── start() { stap "Bouwen en starten" if [ "$DROOG" -eq 0 ] && [ ! -f "$DOEL/docker-compose.yml" ]; then fout "Geen docker-compose.yml in $DOEL — het ophalen van de broncode is misgegaan." fi log "Dit duurt de eerste keer enkele minuten." doe $SUDO docker compose -f "$DOEL/docker-compose.yml" --project-directory "$DOEL" \ up -d --build --remove-orphans goed "Container gestart" } wacht_op_gereed() { [ "$DROOG" -eq 1 ] && return 0 stap "Wachten tot Server Up reageert" printf ' ' i=0 while [ "$i" -lt 45 ]; do if curl -fsS "http://127.0.0.1:${POORT}/healthz" >/dev/null 2>&1; then # Spatie na de dubbele punt toestaan: of json compact is, # hangt af van de Flask-versie. versie="$(curl -fsS "http://127.0.0.1:${POORT}/healthz" 2>/dev/null \ | sed -n 's/.*"version" *: *"\([^"]*\)".*/\1/p')" printf '\n' goed "Server Up${versie:+ }${versie:-} draait" return 0 fi # Een puntje per poging, zodat je ziet dat er nog iets gebeurt. printf '.' i=$((i + 1)) sleep 2 done printf '\n' waarschuw "Server Up reageerde niet binnen anderhalve minuut." log "Bekijk de logs met: $SUDO docker compose -f $DOEL/docker-compose.yml logs --tail=50" return 1 } afsluiting() { if [ "$BIND" = "0.0.0.0" ]; then ip="$(hostname -I 2>/dev/null | awk '{print $1}')" adres="http://${ip:-}:${POORT}" else adres="http://localhost:${POORT}" fi printf '\n%s────────────────────────────────────────────────────────%s\n' "$C_KOP" "$C_UIT" printf '%s Server Up draait op %s%s\n' "$C_OK" "$adres" "$C_UIT" printf '%s────────────────────────────────────────────────────────%s\n\n' "$C_KOP" "$C_UIT" printf ' %sMaak nu meteen een beheerdersaccount aan.%s\n' "$C_WAARSCHUW" "$C_UIT" printf ' Zolang dat niet gebeurd is, kan iedereen die deze pagina\n' printf ' bereikt het account claimen.\n\n' if [ "$BIND" = "127.0.0.1" ]; then printf ' Alleen bereikbaar vanaf deze server zelf. Van buitenaf erbij?\n' printf ' 1. Zet een reverse proxy met TLS ervoor\n' printf ' 2. sed -i "s/^BIND=.*/BIND=0.0.0.0/" %s/.env\n' "$DOEL" printf ' 3. %s docker compose -f %s/docker-compose.yml up -d\n\n' "${SUDO:-}" "$DOEL" else printf ' %sDe interface staat open op je netwerk. Server Up beheert%s\n' "$C_WAARSCHUW" "$C_UIT" printf ' Docker als root, dus zet er een reverse proxy met TLS voor.\n\n' fi printf ' Bijwerken: sh %s/install.sh --update\n' "$DOEL" printf ' Logs: %s docker compose -f %s/docker-compose.yml logs -f\n' "${SUDO:-}" "$DOEL" printf ' Documentatie: %s/README.md\n\n' "$DOEL" } verwijderen() { stap "Server Up verwijderen" [ -d "$DOEL" ] || fout "Niets gevonden in $DOEL" if ! vraag "Container stoppen en verwijderen? Je gegevens blijven staan."; then log "Afgebroken." exit 0 fi doe $SUDO docker compose -f "$DOEL/docker-compose.yml" --project-directory "$DOEL" \ down --remove-orphans goed "Container gestopt en verwijderd" printf '\n' log "Je gegevens staan nog in het docker-volume 'su-data' en in je stackmappen." log "Alles definitief weg? Dan:" log " $SUDO docker volume rm su-data" log " $SUDO rm -rf $DOEL" printf '\n' } # ── Hoofdprogramma ─────────────────────────────────────────────────────────── printf '\n%sServer Up — installatiescript %s%s\n' "$C_KOP" "$SCRIPT_VERSIE" "$C_UIT" [ "$DROOG" -eq 1 ] && waarschuw "Proefdraai: er wordt niets gewijzigd." case "$ACTIE" in uninstall) bepaal_rechten verwijderen ;; update) [ -d "$DOEL" ] || fout "Geen installatie gevonden in $DOEL. Installeer eerst zonder --update." controleer_systeem controleer_docker haal_broncode start wacht_op_gereed || true printf '\n' goed "Bijgewerkt." ;; install) controleer_systeem controleer_docker haal_broncode schrijf_env start wacht_op_gereed || true afsluiting ;; esac