All checks were successful
Deploy server-up (dev) / deploy (push) Successful in 16m26s
- tools/backup_rondgang.py draait backup en terugzetten tegen een echte Docker-daemon in de CI: postgres-sidecar vullen, backuppen, alles weggooien, terugzetten, en controleren dat de database daarna nog kan schrijven. Alle bestaande backuptests vervangen Docker door een nep, dus die keten was nooit bewezen - daardoor gevonden: het terugzetten gaf de appdata de verkeerde eigenaar. Het archief bevat de echte uid (postgres draait als 70 en zegt dat nergens in de metadata); die gaat nu voor op PUID en op onze eigen uid - een mislukte --update rolt terug naar de commit van ervoor en start die opnieuw, in plaats van een stilstaande server en een rijtje commando's - met een registry-image (SU_IMAGE) wordt er gepulld in plaats van gebouwd - nieuw --reconfigure: bind, poort, account of hoofdmap wijzigen zonder de broncode aan te raken - nieuw --uninstall --purge: ook het volume, de gegevensmap en het account, per onderdeel gevraagd. Zonder --purge somt --uninstall nu op wat blijft - --dir wordt op een volledig pad gecontroleerd, het tijdelijke bestand voor het beheerdersaccount is niet meer voorspelbaar, en er gaat nog een rondgang naar de docker-daemon in plaats van twee Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q9eqpADJSRs49SoGGr4NAy
1365 lines
57 KiB
Bash
Executable file
1365 lines
57 KiB
Bash
Executable file
#!/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.2"
|
||
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"
|
||
BEHEERDER=""
|
||
BEHEERDER_WW=""
|
||
BEHEERDER_WW_BESTAND=""
|
||
# Expliciet meegegeven waarden overschrijven wél een bestaande .env; de rest
|
||
# blijft staan, zodat een --update je instellingen niet terugzet.
|
||
BIND_EXPLICIET=0
|
||
POORT_EXPLICIET=0
|
||
# Onder welk account Server Up draait. Leeg = nog niet gekozen; 0 = root.
|
||
GEBRUIKER=""
|
||
GEBRUIKER_EXPLICIET=0
|
||
SU_UID=""
|
||
SU_GID=""
|
||
SU_NAAM=""
|
||
BASE_DIR="${BASE_DIR:-}"
|
||
BASE_DIR_EXPLICIET=0
|
||
# Heeft de server na het starten daadwerkelijk geantwoord? Bepaalt het slot en
|
||
# de exitcode; bij --dry-run is er niets om op te wachten.
|
||
GEREED=1
|
||
# De commit waar een --update vandaan kwam, zodat terugrollen kan als de nieuwe
|
||
# versie niet opkomt.
|
||
VORIGE_COMMIT=""
|
||
# Verwijdert --uninstall ook je gegevens? Standaard niet.
|
||
PURGE=0
|
||
|
||
# ── 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
|
||
}
|
||
|
||
# Is er een terminal om iets aan te vragen? Bewust /dev/tty en niet stdin: bij
|
||
# `curl … | sh` — de manier die de README noemt — is stdin de pipe, terwijl de
|
||
# terminal gewoon bereikbaar is. Op stdin testen betekende dat élke vraag
|
||
# ongesteld bleef en zichzelf met "ja" beantwoordde: Docker werd van
|
||
# get.docker.com gehaald zonder dat het gevraagd was, en --uninstall brak je
|
||
# installatie af zonder bevestiging.
|
||
#
|
||
# Bestaan is niet genoeg: in een container zonder terminal bestaat /dev/tty wel
|
||
# maar levert openen "No such device or address".
|
||
# De openpoging staat in een subshell, niet tussen accolades: ':' is een
|
||
# special builtin, en een mislukte redirect daarop beëindigt volgens POSIX de
|
||
# hele shell. In een subshell sneuvelt alleen die subshell.
|
||
terminal_beschikbaar() {
|
||
[ -e /dev/tty ] || return 1
|
||
( : </dev/tty ) 2>/dev/null || return 1
|
||
return 0
|
||
}
|
||
|
||
vraag() {
|
||
# vraag "Vraag?" -> 0 bij ja. Met --yes of zonder terminal: altijd ja.
|
||
[ "$JA" -eq 1 ] && return 0
|
||
terminal_beschikbaar || return 0
|
||
printf ' %s [J/n] ' "$1"
|
||
read -r antwoord </dev/tty || return 0
|
||
case "$antwoord" in [nN]*) return 1 ;; *) return 0 ;; esac
|
||
}
|
||
|
||
gebruik() {
|
||
cat <<UITLEG
|
||
Server Up installatiescript ${SCRIPT_VERSIE}
|
||
|
||
Gebruik: install.sh [opties]
|
||
|
||
--dir PAD Installatiemap (standaard: ${DOEL})
|
||
--port POORT Poort voor de webinterface (standaard: ${POORT})
|
||
--bind ADRES Waarop de poort luistert (standaard: ${BIND})
|
||
Gebruik 0.0.0.0 om er van buiten de server bij te kunnen,
|
||
maar zet er dan wel een reverse proxy met TLS voor.
|
||
--branch NAAM Branch om te installeren (standaard: ${BRANCH})
|
||
--token TOKEN Toegangstoken, als je git-repo niet openbaar is
|
||
--base-dir PAD Hoofdmap voor stacks, appdata en backups
|
||
(standaard: /opt/serverup). Later wijzigen kan via
|
||
Instellingen → Paden, die de data meeverhuist.
|
||
--user NAAM Account waaronder Server Up draait ('serverup' wordt zo
|
||
nodig aangemaakt; 'root' is de oude situatie). Zonder deze
|
||
optie wordt ernaar gevraagd.
|
||
--admin NAAM Maak meteen een beheerdersaccount met deze naam
|
||
--admin-password-file PAD
|
||
Lees het wachtwoord uit een bestand. Kan ook via de
|
||
omgevingsvariabele SU_ADMIN_PASSWORD. Geef je geen van
|
||
beide, dan wordt ernaar gevraagd; kan dat niet (geen
|
||
terminal), dan wordt er één aangemaakt en getoond.
|
||
--create-admin Alleen het beheerdersaccount aanmaken, bij een installatie
|
||
die al draait. Werkt zolang er nog geen account bestaat.
|
||
--update Bijwerken naar de nieuwste versie
|
||
--doctor Een bestaande installatie doorlichten
|
||
--uninstall Stoppen en verwijderen (je gegevens blijven staan)
|
||
--purge Bij --uninstall: ook het volume, je gegevensmap en het
|
||
aangemaakte account weg. Vraagt per onderdeel.
|
||
--reconfigure Bind, poort, account of hoofdmap wijzigen zonder
|
||
opnieuw te installeren
|
||
--yes Niets vragen, overal ja op antwoorden
|
||
--dry-run Alleen tonen wat er zou gebeuren
|
||
--help Deze uitleg
|
||
|
||
Het wachtwoord staat bewust niet als optie op de opdrachtregel: die is voor
|
||
iedereen op de server te zien met 'ps' en blijft in je shell-geschiedenis staan.
|
||
|
||
Voorbeelden:
|
||
sh install.sh
|
||
sh install.sh --bind 0.0.0.0 --port 8080
|
||
sh install.sh --admin ramon
|
||
SU_ADMIN_PASSWORD=... sh install.sh --admin ramon --yes
|
||
sh install.sh --update
|
||
UITLEG
|
||
}
|
||
|
||
# ── Argumenten ───────────────────────────────────────────────────────────────
|
||
while [ $# -gt 0 ]; do
|
||
case "$1" in
|
||
--dir) DOEL="${2:?--dir vereist een pad}"; shift 2 ;;
|
||
--port) POORT="${2:?--port vereist een nummer}"; POORT_EXPLICIET=1; shift 2 ;;
|
||
--bind) BIND="${2:?--bind vereist een adres}"; BIND_EXPLICIET=1; shift 2 ;;
|
||
--branch) BRANCH="${2:?--branch vereist een naam}"; shift 2 ;;
|
||
--token) TOKEN="${2:?--token vereist een waarde}"; shift 2 ;;
|
||
--user) GEBRUIKER="${2:?--user vereist een accountnaam}"
|
||
GEBRUIKER_EXPLICIET=1; shift 2 ;;
|
||
--base-dir) BASE_DIR="${2:?--base-dir vereist een pad}"
|
||
BASE_DIR_EXPLICIET=1; shift 2 ;;
|
||
--admin) BEHEERDER="${2:?--admin vereist een gebruikersnaam}"; shift 2 ;;
|
||
--admin-password-file)
|
||
BEHEERDER_WW_BESTAND="${2:?--admin-password-file vereist een pad}"; shift 2 ;;
|
||
--admin-password)
|
||
fout "Gebruik --admin-password-file of SU_ADMIN_PASSWORD; een wachtwoord op de opdrachtregel is voor iedereen zichtbaar met 'ps'." ;;
|
||
--update) ACTIE="update"; shift ;;
|
||
--doctor) ACTIE="doctor"; shift ;;
|
||
--reconfigure) ACTIE="reconfigure"; shift ;;
|
||
--purge) PURGE=1; shift ;;
|
||
--uninstall) ACTIE="uninstall"; shift ;;
|
||
--create-admin) ACTIE="create-admin"; shift ;;
|
||
--yes|-y) JA=1; shift ;;
|
||
--dry-run) DROOG=1; shift ;;
|
||
--help|-h) gebruik; exit 0 ;;
|
||
*) fout "Onbekende optie: $1 (gebruik --help)" ;;
|
||
esac
|
||
done
|
||
|
||
case "$POORT" in ''|*[!0-9]*) fout "Ongeldige poort: $POORT" ;; esac
|
||
[ "$POORT" -ge 1 ] && [ "$POORT" -le 65535 ] || fout "Poort buiten bereik: $POORT"
|
||
|
||
# Docker publiceert op een adres, niet op een hostnaam. Alleen cijfers, punten
|
||
# en dubbele punten (IPv6) toestaan, zodat een typefout hier opvalt en niet pas
|
||
# als 'docker compose up' er een onbegrijpelijke fout over geeft.
|
||
geldig_bind() {
|
||
case "$1" in
|
||
''|*[!0-9.:a-fA-F]*) return 1 ;;
|
||
*) return 0 ;;
|
||
esac
|
||
}
|
||
geldig_bind "$BIND" || fout "Ongeldig bind-adres: $BIND (verwacht een IP-adres, bijvoorbeeld 127.0.0.1 of 0.0.0.0)"
|
||
|
||
# Een relatief pad hangt af van waar je toevallig stond toen je het script
|
||
# aanriep, en met `curl … | sh` is dat je thuismap. De compose-aanroepen
|
||
# gebruiken dit pad letterlijk.
|
||
case "$DOEL" in
|
||
/*) DOEL="${DOEL%/}" ;;
|
||
*) fout "--dir vereist een volledig pad, beginnend met / (kreeg: $DOEL)" ;;
|
||
esac
|
||
|
||
# BASE_DIR wordt in de container op zichzelf gemount, dus een relatief pad zou
|
||
# binnen de container ergens anders uitkomen dan op de host. En een handvol
|
||
# systeemmappen wil je hier niet per ongeluk vullen met appdata.
|
||
if [ -n "$BASE_DIR" ]; then
|
||
case "$BASE_DIR" in
|
||
/*) ;;
|
||
*) fout "--base-dir vereist een volledig pad, beginnend met / (kreeg: $BASE_DIR)" ;;
|
||
esac
|
||
BASE_DIR="${BASE_DIR%/}"
|
||
case "$BASE_DIR" in
|
||
''|/|/etc|/bin|/sbin|/usr|/var|/tmp|/root|/boot|/dev|/proc|/sys)
|
||
fout "--base-dir mag niet $BASE_DIR zijn; kies een eigen map, bijvoorbeeld /srv/serverup." ;;
|
||
esac
|
||
fi
|
||
|
||
# Waar bereiken wíj de server na het starten? Compose publiceert op $BIND, dus
|
||
# met een specifiek adres luistert er niets op 127.0.0.1. Daar toch naartoe
|
||
# vragen leverde "reageerde niet binnen anderhalve minuut" en een overgeslagen
|
||
# beheerdersaccount op, terwijl alles gewoon draaide — precies de installatie
|
||
# die daarna zonder account open staat.
|
||
probe_adres() {
|
||
case "$BIND" in
|
||
''|0.0.0.0) printf '127.0.0.1' ;;
|
||
::|'[::]') printf '[::1]' ;;
|
||
*:*) printf '[%s]' "$BIND" ;; # IPv6 hoort tussen blokhaken
|
||
*) printf '%s' "$BIND" ;;
|
||
esac
|
||
}
|
||
|
||
# ── Rechten ──────────────────────────────────────────────────────────────────
|
||
# Alleen sudo gebruiken waar het echt nodig is: kan de gebruiker zelf al in de
|
||
# doelmap schrijven en docker aanroepen, dan blijft alles onder zijn eigen account.
|
||
SUDO=""
|
||
bepaal_rechten() {
|
||
if [ "$(id -u)" -eq 0 ]; then
|
||
return 0
|
||
fi
|
||
bovenliggend="$(dirname "$DOEL")"
|
||
if [ -w "$bovenliggend" ] || { [ -d "$DOEL" ] && [ -w "$DOEL" ]; }; then
|
||
if docker info >/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() {
|
||
# Bij een proefdraai niets aanroepen wat via sudo loopt: dan staat er
|
||
# ineens een wachtwoordvraag op het scherm terwijl we net beloofd hebben
|
||
# dat er niets gebeurt — en zonder terminal blijft hij daarop hangen.
|
||
if [ "$DROOG" -eq 1 ]; then
|
||
if heeft docker; then
|
||
goed "Docker aanwezig (de daemon wordt bij een proefdraai niet aangesproken)"
|
||
else
|
||
waarschuw "Docker is niet geïnstalleerd; het script zou aanbieden dat te doen."
|
||
fi
|
||
return 0
|
||
fi
|
||
|
||
if heeft docker; then
|
||
# Eén vraag aan de daemon in plaats van twee: `docker version` heeft de
|
||
# daemon net zo goed nodig als `docker info`, en levert meteen iets om
|
||
# te tonen. Lukt het niet, dan reageert hij niet.
|
||
_dv="$($SUDO docker version --format '{{.Server.Version}}' 2>/dev/null || true)"
|
||
if [ -n "$_dv" ]; then
|
||
goed "Docker draait ($_dv)"
|
||
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 ─────────────────────────────────────────────────────────
|
||
# De token gaat nooit in de URL en nooit in een argument. In de URL schrijft git
|
||
# hem verbatim in .git/config, waar hij blijft staan; als argument staat hij in
|
||
# /proc/<pid>/cmdline en is hij voor elke gebruiker op de server te lezen met
|
||
# 'ps'. Dat is precies waarom dit script --admin-password weigert.
|
||
#
|
||
# In plaats daarvan: een bestand met modus 0600 dat na afloop weer weg is. Git
|
||
# leest het via een credential-helper, curl via --config; in beide gevallen
|
||
# staat alleen het pád op de opdrachtregel.
|
||
bron_url() { printf 'https://%s/%s.git' "$GIT_HOST" "$GIT_PAD"; }
|
||
|
||
TOKEN_BESTAND=""
|
||
CURL_BESTAND=""
|
||
|
||
opruimen() {
|
||
[ -n "$TOKEN_BESTAND" ] && rm -f "$TOKEN_BESTAND"
|
||
[ -n "$CURL_BESTAND" ] && rm -f "$CURL_BESTAND"
|
||
TOKEN_BESTAND=""; CURL_BESTAND=""
|
||
}
|
||
trap opruimen EXIT HUP INT TERM
|
||
|
||
zet_token_klaar() {
|
||
[ -n "$TOKEN" ] || return 0
|
||
[ -n "$TOKEN_BESTAND" ] && return 0
|
||
TOKEN_BESTAND="$(mktemp "${TMPDIR:-/tmp}/su-token.XXXXXX")"
|
||
CURL_BESTAND="$(mktemp "${TMPDIR:-/tmp}/su-curl.XXXXXX")"
|
||
chmod 600 "$TOKEN_BESTAND" "$CURL_BESTAND"
|
||
# Schema en host uit bron_url halen in plaats van https hard in te vullen:
|
||
# git zoekt de opgeslagen inloggegevens op exact schema + host, dus een
|
||
# verschil daarin betekent stilzwijgend geen inloggegevens.
|
||
_url="$(bron_url)"
|
||
_schema="${_url%%://*}"
|
||
_rest="${_url#*://}"
|
||
_host="${_rest%%/*}"
|
||
# Formaat van `git credential store`: één URL per regel met de gegevens erin.
|
||
printf '%s://%s:x-oauth-basic@%s\n' "$_schema" "$TOKEN" "$_host" > "$TOKEN_BESTAND"
|
||
printf 'header = "Authorization: token %s"\n' "$TOKEN" > "$CURL_BESTAND"
|
||
}
|
||
|
||
git_doe() {
|
||
# git_doe <argumenten> — met de token uit het bestand als die er is.
|
||
if [ -n "$TOKEN_BESTAND" ]; then
|
||
doe $SUDO git -c "credential.helper=store --file=$TOKEN_BESTAND" "$@"
|
||
else
|
||
doe $SUDO git "$@"
|
||
fi
|
||
}
|
||
|
||
haal_broncode() {
|
||
stap "Broncode ophalen"
|
||
zet_token_klaar
|
||
if [ -d "$DOEL/.git" ]; then
|
||
log "Bestaande installatie bijwerken in $DOEL"
|
||
# Onthouden waar we vandaan komen, zodat we terug kunnen als de nieuwe
|
||
# versie niet opkomt. Voor de update pas ophalen: bij een verse
|
||
# installatie is er niets om naar terug te gaan.
|
||
VORIGE_COMMIT="$($SUDO git -C "$DOEL" rev-parse HEAD 2>/dev/null || echo '')"
|
||
[ -n "$VORIGE_COMMIT" ] && log "Huidige versie: $(printf '%.9s' "$VORIGE_COMMIT")"
|
||
git_doe -C "$DOEL" fetch --depth 1 origin "$BRANCH"
|
||
# FETCH_HEAD en niet origin/$BRANCH: een tag krijgt geen origin/-ref,
|
||
# dus `--branch v0.7.92-beta` installeerde prima maar kon daarna nooit
|
||
# meer bijgewerkt worden ("fatal: ambiguous argument"). FETCH_HEAD wijst
|
||
# na een fetch altijd naar wat er net opgehaald is, tak of tag.
|
||
git_doe -C "$DOEL" reset --hard FETCH_HEAD
|
||
goed "Bijgewerkt naar $BRANCH"
|
||
opruimen
|
||
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 git_doe clone --depth 1 --branch "$BRANCH" "$(bron_url)" "$DOEL"; then
|
||
goed "Gekloond naar $DOEL"
|
||
opruimen
|
||
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 ${CURL_BESTAND:+--config "$CURL_BESTAND"} "$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
|
||
opruimen
|
||
goed "Uitgepakt naar $DOEL"
|
||
}
|
||
|
||
# ── Instellingen ─────────────────────────────────────────────────────────────
|
||
server_ip() { hostname -I 2>/dev/null | awk '{print $1}'; }
|
||
|
||
kies_bind() {
|
||
# Alleen vragen als er nog iets te kiezen valt: met --bind, met --yes of
|
||
# zonder terminal is de keuze al gemaakt.
|
||
[ "$BIND_EXPLICIET" -eq 1 ] && return 0
|
||
[ "$JA" -eq 1 ] && return 0
|
||
[ "$DROOG" -eq 1 ] && return 0
|
||
terminal_beschikbaar || return 0
|
||
|
||
# Installeer je opnieuw over een bestaande map, dan is je huidige keuze het
|
||
# uitgangspunt — anders zet enter je server ongemerkt terug op loopback.
|
||
huidig=""
|
||
if [ -f "$DOEL/.env" ]; then
|
||
lees_env_terug "$DOEL/.env"
|
||
huidig="$BIND"
|
||
fi
|
||
|
||
ip="$(server_ip)"
|
||
stap "Bereikbaarheid"
|
||
printf ' Waarop moet de webinterface luisteren?\n\n'
|
||
printf ' 1) Alleen deze server (127.0.0.1) — veiligst\n'
|
||
printf ' Erbij via een SSH-tunnel of een reverse proxy op deze machine.\n'
|
||
printf ' 2) Het hele netwerk (0.0.0.0)\n'
|
||
printf ' Direct bereikbaar op http://%s:%s — zet er wel TLS voor.\n' "${ip:-<server-ip>}" "$POORT"
|
||
printf ' 3) Een specifiek adres\n\n'
|
||
if [ -n "$huidig" ]; then
|
||
printf ' Nu ingesteld: %s\n' "$huidig"
|
||
printf ' Keuze [enter = zo laten]: '
|
||
else
|
||
printf ' Keuze [1]: '
|
||
fi
|
||
read -r keuze </dev/tty || keuze=""
|
||
case "$keuze" in
|
||
1) BIND="127.0.0.1" ;;
|
||
2) BIND="0.0.0.0" ;;
|
||
3) printf ' Adres: '
|
||
read -r adres </dev/tty || adres=""
|
||
geldig_bind "$adres" || fout "Ongeldig bind-adres: $adres"
|
||
BIND="$adres" ;;
|
||
'') BIND="${huidig:-127.0.0.1}" ;;
|
||
*) fout "Onbekende keuze: $keuze" ;;
|
||
esac
|
||
BIND_EXPLICIET=1
|
||
goed "Luistert op $BIND"
|
||
}
|
||
|
||
env_waarde() {
|
||
# env_waarde SLEUTEL — de waarde uit .env van de installatie, of leeg.
|
||
[ -f "$DOEL/.env" ] || return 0
|
||
sed -n "s/^$1=//p" "$DOEL/.env" 2>/dev/null | tail -1
|
||
}
|
||
|
||
zet_env_regel() {
|
||
# zet_env_regel BESTAND SLEUTEL WAARDE — vervangt de regel of voegt hem toe.
|
||
# Waarden zijn hier altijd al gevalideerd (cijfers, punten, dubbele punten).
|
||
$SUDO sh -c "sed -i '/^$2=/d' '$1' && printf '%s=%s\n' '$2' '$3' >> '$1'"
|
||
}
|
||
|
||
# ── Onder welk account draait Server Up? ─────────────────────────────────────
|
||
# Server Up start als root — dat moet, want het docker-volume is van root — en
|
||
# zakt daarna af naar dit account. Zie docs/beveiliging.md voor wat dat wel en
|
||
# niet oplevert.
|
||
|
||
uid_van() { getent passwd "$1" 2>/dev/null | cut -d: -f3; }
|
||
gid_van() { getent passwd "$1" 2>/dev/null | cut -d: -f4; }
|
||
|
||
ingelogde_gebruiker() {
|
||
# Wie de installatie aanstuurt. Onder sudo is dat niet 'root' maar degene
|
||
# die het aanriep; zonder sudo valt logname terug op de terminal.
|
||
naam="${SUDO_USER:-}"
|
||
[ -n "$naam" ] || naam="$(logname 2>/dev/null || echo '')"
|
||
[ "$naam" = "root" ] && naam=""
|
||
printf '%s' "$naam"
|
||
}
|
||
|
||
bruikbare_gebruikers() {
|
||
# Gewone accounts: uid 1000–60000 en een echte shell. Systeemaccounts
|
||
# (daemon, www-data, nobody) horen hier niet tussen — die hebben eigen
|
||
# rechten en zouden verwarrend zijn om uit te kiezen.
|
||
awk -F: '$3 >= 1000 && $3 < 60000 &&
|
||
$7 !~ /(nologin|false|sync)$/ { print $1 " (uid " $3 ")" }' \
|
||
/etc/passwd 2>/dev/null
|
||
}
|
||
|
||
maak_serverup_account() {
|
||
# Systeemaccount: geen shell, geen wachtwoord, geen thuismap. Het bestaat
|
||
# alleen om eigenaar te zijn van bestanden.
|
||
if [ -n "$(uid_van serverup)" ]; then
|
||
log "Account 'serverup' bestaat al"
|
||
return 0
|
||
fi
|
||
if [ "$DROOG" -eq 1 ]; then
|
||
printf ' [droog] account serverup aanmaken (systeemaccount, geen shell)\n'
|
||
return 0
|
||
fi
|
||
if heeft useradd; then
|
||
$SUDO useradd --system --no-create-home --shell /usr/sbin/nologin serverup \
|
||
|| fout "Kon het account 'serverup' niet aanmaken."
|
||
elif heeft adduser; then
|
||
$SUDO adduser --system --no-create-home --shell /usr/sbin/nologin serverup \
|
||
|| fout "Kon het account 'serverup' niet aanmaken."
|
||
else
|
||
fout "Geen useradd of adduser gevonden. Maak zelf een account en geef het op met --user NAAM."
|
||
fi
|
||
goed "Account 'serverup' aangemaakt"
|
||
}
|
||
|
||
zet_gebruiker() {
|
||
# zet_gebruiker NAAM — vertaal een naam naar SU_UID/SU_GID.
|
||
SU_NAAM="$1"
|
||
if [ "$1" = "root" ]; then
|
||
SU_UID=0; SU_GID=0
|
||
waarschuw "Server Up draait als root; dat is de oude situatie."
|
||
return 0
|
||
fi
|
||
_uid="$(uid_van "$1")"
|
||
if [ -z "$_uid" ]; then
|
||
if [ "$1" = "serverup" ]; then
|
||
maak_serverup_account
|
||
_uid="$(uid_van serverup)"
|
||
fi
|
||
if [ -z "$_uid" ] && [ "$DROOG" -eq 1 ] && [ "$1" = "serverup" ]; then
|
||
# Alleen 'serverup' zouden we zélf hebben aangemaakt; dat het nu
|
||
# nog ontbreekt is dus geen fout. Een andere naam die niet bestaat
|
||
# is een typefout, en die hoor je juist bij een proefdraai te horen.
|
||
SU_UID=""; SU_GID=""
|
||
log "Zou draaien als 'serverup' (uid wordt bij het aanmaken toegekend)"
|
||
return 0
|
||
fi
|
||
[ -n "$_uid" ] || fout "Account '$1' bestaat niet. Maak het aan of kies een ander."
|
||
fi
|
||
SU_UID="$_uid"
|
||
SU_GID="$(gid_van "$1")"
|
||
SU_GID="${SU_GID:-$SU_UID}"
|
||
goed "Server Up draait als $1 ($SU_UID:$SU_GID)"
|
||
# Bewust géén lidmaatschap van de docker-groep: wie daarin zit kan een
|
||
# container starten die / mount, en is dus root op deze machine. De
|
||
# container leest de gid van de socket zelf uit en heeft dit niet nodig.
|
||
log "Niet toegevoegd aan de docker-groep — dat zou root-toegang geven en is niet nodig."
|
||
}
|
||
|
||
kies_gebruiker() {
|
||
[ "$GEBRUIKER_EXPLICIET" -eq 1 ] && { zet_gebruiker "$GEBRUIKER"; return 0; }
|
||
# Wat er al staat wint: bij --update wordt kies_bind niet aangeroepen, dus
|
||
# zonder dit zou een bestaande installatie elke keer opnieuw vragen.
|
||
lees_env_terug "$DOEL/.env"
|
||
[ -n "$SU_UID" ] && return 0
|
||
if [ "$JA" -eq 1 ] || ! terminal_beschikbaar; then
|
||
zet_gebruiker serverup
|
||
return 0
|
||
fi
|
||
|
||
_ingelogd="$(ingelogde_gebruiker)"
|
||
stap "Account"
|
||
printf ' Onder welk account moet Server Up draaien?\n'
|
||
printf ' Het draait nu nog als root; dat hoeft niet voor alles.\n\n'
|
||
printf " 1) Een nieuw account 'serverup' aanmaken — aanbevolen\n"
|
||
printf ' Systeemaccount zonder shell en zonder wachtwoord.\n'
|
||
if [ -n "$_ingelogd" ]; then
|
||
printf ' 2) %s — het account waarmee je nu werkt\n' "$_ingelogd"
|
||
else
|
||
printf ' 2) (geen ingelogd account gevonden)\n'
|
||
fi
|
||
printf ' 3) Een bestaand account kiezen\n'
|
||
printf ' 4) Als root draaien — zoals voorheen\n\n'
|
||
printf ' Keuze [1]: '
|
||
read -r keuze </dev/tty || keuze=""
|
||
case "$keuze" in
|
||
''|1) zet_gebruiker serverup ;;
|
||
2) [ -n "$_ingelogd" ] || fout "Er is geen ingelogd account gevonden; kies 1, 3 of 4."
|
||
zet_gebruiker "$_ingelogd" ;;
|
||
3) printf '\n Bruikbare accounts:\n'
|
||
bruikbare_gebruikers | sed 's/^/ /'
|
||
printf '\n Naam: '
|
||
read -r naam </dev/tty || naam=""
|
||
naam="$(printf '%s' "$naam" | tr -d '[:space:]')"
|
||
[ -n "$naam" ] || fout "Geen naam opgegeven."
|
||
zet_gebruiker "$naam" ;;
|
||
4) zet_gebruiker root ;;
|
||
*) fout "Onbekende keuze: $keuze" ;;
|
||
esac
|
||
}
|
||
|
||
zet_mappen_klaar() {
|
||
# De hoofdmap en de twee mappen die Server Up zélf beheert alvast op de
|
||
# juiste eigenaar zetten, zodat de eerste start niet op rechten struikelt.
|
||
#
|
||
# appdata blijft er bewust buiten: die mappen zijn van de apps (postgres
|
||
# draait als 999, een linuxserver-image als jouw PUID). Er een chown
|
||
# overheen halen breekt precies die containers.
|
||
[ "${SU_UID:-0}" = "0" ] && [ "$DROOG" -eq 0 ] && return 0
|
||
[ "${SU_NAAM:-root}" = "root" ] && return 0
|
||
_basis="${BASE_DIR:-/opt/serverup}"
|
||
if [ "$DROOG" -eq 1 ]; then
|
||
printf ' [droog] %s, %s/stacks en %s/backups op %s zetten (appdata blijft ongemoeid)\n' \
|
||
"$_basis" "$_basis" "$_basis" "$SU_NAAM"
|
||
return 0
|
||
fi
|
||
$SUDO mkdir -p "$_basis/stacks" "$_basis/backups" "$_basis/appdata"
|
||
$SUDO chown "$SU_UID:$SU_GID" "$_basis"
|
||
$SUDO chown -R "$SU_UID:$SU_GID" "$_basis/stacks" "$_basis/backups"
|
||
goed "Mappen klaargezet onder $_basis"
|
||
}
|
||
|
||
lees_env_terug() {
|
||
# Wat er in .env staat is de waarheid, niet wat dit script als standaard
|
||
# heeft. Zonder dit klopt bij --update de wachtlus en het slotadres niet
|
||
# meer zodra je ooit een andere poort hebt gekozen.
|
||
[ -f "$1" ] || return 0
|
||
# Lezen kan meestal zonder sudo; alleen als het bestand van root is en
|
||
# afgeschermd, is de omweg nodig.
|
||
if [ -r "$1" ]; then
|
||
_lees="cat"
|
||
elif [ -n "$SUDO" ]; then
|
||
_lees="$SUDO cat"
|
||
elif heeft sudo; then
|
||
_lees="sudo cat"
|
||
else
|
||
_lees="cat"
|
||
fi
|
||
_inhoud="$($_lees "$1" 2>/dev/null || true)"
|
||
_poort="$(printf '%s\n' "$_inhoud" | sed -n 's/^PORT=//p' | tail -1)"
|
||
_bind="$(printf '%s\n' "$_inhoud" | sed -n 's/^BIND=//p' | tail -1)"
|
||
# SU_UID/SU_GID en BASE_DIR overnemen, zodat --update niet opnieuw vraagt
|
||
# onder welk account het moet draaien en niet de verkeerde mappen chownt.
|
||
if [ -z "$SU_UID" ]; then
|
||
_uid="$(printf '%s\n' "$_inhoud" | sed -n 's/^SU_UID=//p' | tail -1)"
|
||
case "$_uid" in ''|*[!0-9]*) ;; *) SU_UID="$_uid"
|
||
_gid="$(printf '%s\n' "$_inhoud" | sed -n 's/^SU_GID=//p' | tail -1)"
|
||
case "$_gid" in ''|*[!0-9]*) SU_GID="$_uid" ;; *) SU_GID="$_gid" ;; esac ;;
|
||
esac
|
||
fi
|
||
if [ -z "$BASE_DIR" ] && [ "$BASE_DIR_EXPLICIET" -eq 0 ]; then
|
||
_basis="$(printf '%s\n' "$_inhoud" | sed -n 's/^BASE_DIR=//p' | tail -1)"
|
||
case "$_basis" in /*) BASE_DIR="$_basis" ;; esac
|
||
fi
|
||
if [ "$POORT_EXPLICIET" -eq 0 ]; then
|
||
case "$_poort" in ''|*[!0-9]*) ;; *) POORT="$_poort" ;; esac
|
||
fi
|
||
if [ "$BIND_EXPLICIET" -eq 0 ] && [ -n "$_bind" ] && geldig_bind "$_bind"; then
|
||
BIND="$_bind"
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
schrijf_env() {
|
||
stap "Instellingen"
|
||
env_bestand="$DOEL/.env"
|
||
|
||
if [ -f "$env_bestand" ]; then
|
||
# Een bestaande .env bevat jouw instellingen; die blijven staan. Alleen
|
||
# wat je nu expliciet meegeeft wordt doorgevoerd — dat is precies het
|
||
# verschil dat eerder ontbrak, waardoor --bind niets deed zodra er al
|
||
# een .env stond.
|
||
gewijzigd=0
|
||
if [ "$BIND_EXPLICIET" -eq 1 ]; then
|
||
if [ "$DROOG" -eq 1 ]; then
|
||
printf ' [droog] BIND=%s in bestaande .env zetten\n' "$BIND"
|
||
else
|
||
zet_env_regel "$env_bestand" BIND "$BIND"
|
||
fi
|
||
gewijzigd=1
|
||
fi
|
||
if [ "$POORT_EXPLICIET" -eq 1 ]; then
|
||
if [ "$DROOG" -eq 1 ]; then
|
||
printf ' [droog] PORT=%s in bestaande .env zetten\n' "$POORT"
|
||
else
|
||
zet_env_regel "$env_bestand" PORT "$POORT"
|
||
fi
|
||
gewijzigd=1
|
||
fi
|
||
# Het account is een keuze die je net gemaakt hebt; die hoort er altijd
|
||
# in te komen, ook bij een --update van een installatie die nog als root
|
||
# draaide.
|
||
if [ -n "$SU_UID" ]; then
|
||
if [ "$DROOG" -eq 1 ]; then
|
||
printf ' [droog] SU_UID=%s SU_GID=%s in bestaande .env zetten\n' \
|
||
"$SU_UID" "$SU_GID"
|
||
else
|
||
zet_env_regel "$env_bestand" SU_UID "$SU_UID"
|
||
zet_env_regel "$env_bestand" SU_GID "$SU_GID"
|
||
fi
|
||
gewijzigd=1
|
||
fi
|
||
# Alleen als je er expliciet om vroeg: anders zou een --update de
|
||
# hoofdmap terugzetten terwijl je data ergens anders staat.
|
||
if [ "$BASE_DIR_EXPLICIET" -eq 1 ] && [ -n "$BASE_DIR" ]; then
|
||
if [ "$DROOG" -eq 1 ]; then
|
||
printf ' [droog] BASE_DIR=%s in bestaande .env zetten\n' "$BASE_DIR"
|
||
else
|
||
zet_env_regel "$env_bestand" BASE_DIR "$BASE_DIR"
|
||
fi
|
||
waarschuw "BASE_DIR gewijzigd; bestaande data verhuist hier niet mee."
|
||
log "Verplaatsen doe je vanuit Instellingen → Paden."
|
||
gewijzigd=1
|
||
fi
|
||
if [ "$gewijzigd" -eq 1 ]; then
|
||
goed "Bestaande .env bijgewerkt (BIND=$BIND, PORT=$POORT)"
|
||
else
|
||
lees_env_terug "$env_bestand"
|
||
goed "Bestaande .env blijft ongemoeid (BIND=$BIND, PORT=$POORT)"
|
||
fi
|
||
return 0
|
||
fi
|
||
|
||
if [ "$DROOG" -eq 1 ]; then
|
||
printf ' [droog] .env aanmaken met BIND=%s PORT=%s BASE_DIR=%s, draaiend als %s\n' \
|
||
"$BIND" "$POORT" "${BASE_DIR:-/opt/serverup}" "${SU_NAAM:-root}"
|
||
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'"
|
||
if [ -n "$SU_UID" ]; then
|
||
zet_env_regel "$env_bestand" SU_UID "$SU_UID"
|
||
zet_env_regel "$env_bestand" SU_GID "$SU_GID"
|
||
fi
|
||
[ -n "$BASE_DIR" ] && zet_env_regel "$env_bestand" BASE_DIR "$BASE_DIR"
|
||
goed "Aangemaakt: $env_bestand (BIND=$BIND, PORT=$POORT)"
|
||
}
|
||
|
||
# ── Starten ──────────────────────────────────────────────────────────────────
|
||
# Draait deze installatie vanaf een kant-en-klaar image uit een registry? Dan
|
||
# scheelt ophalen minuten ten opzichte van zelf bouwen. `SU_IMAGE` is het teken:
|
||
# staat er een registry-pad (met een schuine streep) in, dan valt er iets te
|
||
# halen. Zonder blijft het bouwen, want dan bestaat het image nergens anders.
|
||
uit_registry() {
|
||
_img="$(sed -n 's/^SU_IMAGE=//p' "$DOEL/.env" 2>/dev/null | tail -1)"
|
||
case "$_img" in
|
||
*/*) return 0 ;;
|
||
*) return 1 ;;
|
||
esac
|
||
}
|
||
|
||
start() {
|
||
_compose="$SUDO docker compose -f $DOEL/docker-compose.yml --project-directory $DOEL"
|
||
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
|
||
|
||
if uit_registry; then
|
||
stap "Ophalen en starten"
|
||
log "Deze installatie draait vanaf een registry-image."
|
||
# shellcheck disable=SC2086
|
||
if doe $_compose pull; then
|
||
# shellcheck disable=SC2086
|
||
doe $_compose up -d --remove-orphans
|
||
goed "Container gestart"
|
||
return 0
|
||
fi
|
||
waarschuw "Ophalen mislukte; ik bouw het image zelf."
|
||
fi
|
||
|
||
stap "Bouwen en starten"
|
||
log "Dit duurt de eerste keer enkele minuten."
|
||
# shellcheck disable=SC2086
|
||
doe $_compose up -d --build --remove-orphans
|
||
goed "Container gestart"
|
||
}
|
||
|
||
wacht_op_gereed() {
|
||
[ "$DROOG" -eq 1 ] && return 0
|
||
# Aantal pogingen is instelbaar: op een trage machine duurt de eerste start
|
||
# langer dan anderhalve minuut, en de tests willen niet zo lang wachten.
|
||
pogingen="${SU_WACHT_POGINGEN:-45}"
|
||
basis="http://$(probe_adres):${POORT}"
|
||
stap "Wachten tot Server Up reageert"
|
||
printf ' '
|
||
i=0
|
||
# Zonder tijdslimiet blijft curl hangen op een adres waar niets naartoe
|
||
# routeert, en betekent het aantal pogingen niets meer.
|
||
tijden="--connect-timeout 2 --max-time 5"
|
||
while [ "$i" -lt "$pogingen" ]; do
|
||
if curl -fsS $tijden "${basis}/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 $tijden "${basis}/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 op ${basis}."
|
||
log "Bekijk de logs met: $SUDO docker compose -f $DOEL/docker-compose.yml logs --tail=50"
|
||
return 1
|
||
}
|
||
|
||
# ── Beheerdersaccount ────────────────────────────────────────────────────────
|
||
# Tussen "container draait" en "eerste account" ligt een venster waarin iedereen
|
||
# die de pagina bereikt zichzelf beheerder kan maken. Dat venster hoort zo kort
|
||
# mogelijk te zijn, dus kan het script het account meteen aanmaken.
|
||
WW_GEGENEREERD=0
|
||
BEHEERDER_WW_TOON=""
|
||
|
||
json_tekst() {
|
||
# Escapet een waarde voor gebruik tussen JSON-aanhalingstekens.
|
||
printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
|
||
}
|
||
|
||
genereer_wachtwoord() {
|
||
LC_ALL=C tr -dc 'A-Za-z0-9' < /dev/urandom 2>/dev/null | head -c 24
|
||
}
|
||
|
||
vraag_wachtwoord() {
|
||
# Twee keer intypen, met de echo uit. Mislukt dat, dan valt hij terug op
|
||
# een gegenereerd wachtwoord in plaats van er een half account van te maken.
|
||
oude_stty=""
|
||
if stty -g </dev/tty >/dev/null 2>&1; then oude_stty="$(stty -g </dev/tty)"; fi
|
||
poging=0
|
||
while [ "$poging" -lt 3 ]; do
|
||
poging=$((poging + 1))
|
||
if [ -n "$oude_stty" ]; then stty -echo </dev/tty; fi
|
||
printf ' Wachtwoord voor %s (minstens 10 tekens): ' "$BEHEERDER"
|
||
read -r ww1 </dev/tty || ww1=""
|
||
printf '\n Nogmaals: '
|
||
read -r ww2 </dev/tty || ww2=""
|
||
if [ -n "$oude_stty" ]; then stty "$oude_stty" </dev/tty; fi
|
||
printf '\n'
|
||
if [ "$ww1" != "$ww2" ]; then
|
||
waarschuw "De twee invoeren verschillen."
|
||
elif [ "${#ww1}" -lt 10 ]; then
|
||
waarschuw "Minstens 10 tekens, dat is ook wat de server eist."
|
||
else
|
||
BEHEERDER_WW="$ww1"
|
||
return 0
|
||
fi
|
||
done
|
||
waarschuw "Geen bruikbaar wachtwoord ingevoerd; er wordt er een aangemaakt."
|
||
return 1
|
||
}
|
||
|
||
bepaal_wachtwoord() {
|
||
# Volgorde: bestand, omgevingsvariabele, vragen, anders zelf aanmaken.
|
||
[ -n "$BEHEERDER_WW" ] && return 0
|
||
if [ -n "$BEHEERDER_WW_BESTAND" ]; then
|
||
[ -r "$BEHEERDER_WW_BESTAND" ] || fout "Kan $BEHEERDER_WW_BESTAND niet lezen."
|
||
BEHEERDER_WW="$(head -n 1 "$BEHEERDER_WW_BESTAND")"
|
||
[ -n "$BEHEERDER_WW" ] || fout "$BEHEERDER_WW_BESTAND is leeg."
|
||
return 0
|
||
fi
|
||
if [ -n "${SU_ADMIN_PASSWORD:-}" ]; then
|
||
BEHEERDER_WW="$SU_ADMIN_PASSWORD"
|
||
return 0
|
||
fi
|
||
if [ "$JA" -eq 0 ] && terminal_beschikbaar; then
|
||
vraag_wachtwoord && return 0
|
||
fi
|
||
BEHEERDER_WW="$(genereer_wachtwoord)"
|
||
[ -n "$BEHEERDER_WW" ] || fout "Kon geen wachtwoord aanmaken (/dev/urandom niet leesbaar). Gebruik --admin-password-file."
|
||
WW_GEGENEREERD=1
|
||
return 0
|
||
}
|
||
|
||
vraag_beheerder() {
|
||
# Niets meegegeven, maar wel een terminal? Dan hier aanbieden, want dit is
|
||
# het moment waarop het uitmaakt.
|
||
[ -n "$BEHEERDER" ] && return 0
|
||
[ "$JA" -eq 1 ] && return 0
|
||
[ "$DROOG" -eq 1 ] && return 0
|
||
terminal_beschikbaar || return 0
|
||
|
||
stap "Beheerdersaccount"
|
||
if ! vraag "Nu meteen een beheerdersaccount aanmaken?"; then
|
||
return 0
|
||
fi
|
||
printf ' Gebruikersnaam: '
|
||
read -r naam </dev/tty || naam=""
|
||
naam="$(printf '%s' "$naam" | tr -d '[:space:]')"
|
||
if [ -z "$naam" ]; then
|
||
waarschuw "Geen naam opgegeven; overgeslagen."
|
||
return 0
|
||
fi
|
||
BEHEERDER="$naam"
|
||
return 0
|
||
}
|
||
|
||
beheerder_vooraf() {
|
||
# Alles wat om invoer vraagt gebeurt vóór het bouwen, zodat je daarna kunt
|
||
# weglopen terwijl het image gebouwd wordt.
|
||
[ "$DROOG" -eq 1 ] && return 0
|
||
vraag_beheerder
|
||
[ -n "$BEHEERDER" ] || return 0
|
||
bepaal_wachtwoord
|
||
return 0
|
||
}
|
||
|
||
maak_beheerder() {
|
||
[ -n "$BEHEERDER" ] || return 0
|
||
|
||
if [ "$DROOG" -eq 1 ]; then
|
||
stap "Beheerdersaccount"
|
||
printf ' [droog] account %s aanmaken via POST /api/auth/setup\n' "$BEHEERDER"
|
||
return 0
|
||
fi
|
||
|
||
bepaal_wachtwoord
|
||
|
||
# Het wachtwoord gaat via stdin naar curl, niet via de opdrachtregel:
|
||
# argumenten zijn voor elke gebruiker op de server zichtbaar in 'ps'.
|
||
# Geen voorspelbaar pad als terugval: /tmp is voor iedereen schrijfbaar,
|
||
# dus een ander kan er alvast een symlink neerleggen. Lukt mktemp niet,
|
||
# dan is er iets grondig mis en stoppen we liever.
|
||
antwoord="$(mktemp "${TMPDIR:-/tmp}/su-setup.XXXXXX")" \
|
||
|| fout "Kon geen tijdelijk bestand aanmaken."
|
||
chmod 600 "$antwoord"
|
||
code="$(printf '{"username":"%s","password":"%s"}' \
|
||
"$(json_tekst "$BEHEERDER")" "$(json_tekst "$BEHEERDER_WW")" \
|
||
| curl -sS -o "$antwoord" -w '%{http_code}' \
|
||
--connect-timeout 5 --max-time 20 \
|
||
-X POST -H 'Content-Type: application/json' \
|
||
--data-binary @- \
|
||
"http://$(probe_adres):${POORT}/api/auth/setup" 2>/dev/null || true)"
|
||
# Bij een mislukte verbinding schrijft curl zelf al "000"; kan curl helemaal
|
||
# niet draaien, dan blijft het leeg. Beide gevallen zijn hetzelfde verhaal.
|
||
case "$code" in ''|*[!0-9]*) code="000" ;; esac
|
||
|
||
case "$code" in
|
||
200) goed "Beheerder '$BEHEERDER' aangemaakt"
|
||
# Alleen een zelf aangemaakt wachtwoord moet nog getoond worden;
|
||
# een ingetypt of meegegeven wachtwoord kent de gebruiker al.
|
||
if [ "$WW_GEGENEREERD" -eq 1 ]; then
|
||
BEHEERDER_WW_TOON="$BEHEERDER_WW"
|
||
fi ;;
|
||
409) waarschuw "Er bestond al een account; er is niets gewijzigd."
|
||
BEHEERDER=""; WW_GEGENEREERD=0 ;;
|
||
000) waarschuw "Server Up was niet bereikbaar; account niet aangemaakt."
|
||
BEHEERDER=""; WW_GEGENEREERD=0 ;;
|
||
*) melding="$(sed -n 's/.*"msg" *: *"\([^"]*\)".*/\1/p' "$antwoord" 2>/dev/null)"
|
||
waarschuw "Aanmaken mislukt (HTTP $code)${melding:+: }${melding:-}"
|
||
BEHEERDER=""; WW_GEGENEREERD=0 ;;
|
||
esac
|
||
rm -f "$antwoord"
|
||
BEHEERDER_WW=""
|
||
}
|
||
|
||
afsluiting() {
|
||
if [ "$BIND" = "0.0.0.0" ]; then
|
||
ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
|
||
adres="http://${ip:-<server-ip>}:${POORT}"
|
||
else
|
||
adres="http://localhost:${POORT}"
|
||
fi
|
||
|
||
printf '\n%s────────────────────────────────────────────────────────%s\n' "$C_KOP" "$C_UIT"
|
||
if [ "$GEREED" -eq 1 ]; then
|
||
printf '%s Server Up draait op %s%s\n' "$C_OK" "$adres" "$C_UIT"
|
||
else
|
||
printf '%s Server Up antwoordde niet op %s%s\n' "$C_WAARSCHUW" "$adres" "$C_UIT"
|
||
fi
|
||
printf '%s────────────────────────────────────────────────────────%s\n\n' "$C_KOP" "$C_UIT"
|
||
|
||
if [ "$GEREED" -eq 0 ]; then
|
||
printf ' De container is aangemaakt, maar reageerde nog niet. Kijk wat\n'
|
||
printf ' er aan de hand is met:\n'
|
||
printf ' %s docker compose -f %s/docker-compose.yml logs --tail=50\n\n' "${SUDO:-}" "$DOEL"
|
||
fi
|
||
|
||
if [ -n "$BEHEERDER" ]; then
|
||
printf ' %s✔%s Inloggen als: %s%s%s\n' "$C_OK" "$C_UIT" "$C_KOP" "$BEHEERDER" "$C_UIT"
|
||
if [ "$WW_GEGENEREERD" -eq 1 ]; then
|
||
printf ' Wachtwoord: %s%s%s\n' "$C_KOP" "$BEHEERDER_WW_TOON" "$C_UIT"
|
||
printf ' %sSchrijf dit nu over — het staat nergens anders.%s\n' "$C_WAARSCHUW" "$C_UIT"
|
||
fi
|
||
printf '\n'
|
||
else
|
||
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'
|
||
fi
|
||
|
||
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.%s Server Up beheert de\n' "$C_WAARSCHUW" "$C_UIT"
|
||
printf ' Docker-daemon, dus wie erbij kan, kan alles op deze server.\n'
|
||
printf ' Zet er een reverse proxy met TLS voor.\n\n'
|
||
fi
|
||
|
||
printf ' Draait als: %s\n' "${SU_NAAM:-root}"
|
||
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"
|
||
}
|
||
|
||
# ── Terugrollen ──────────────────────────────────────────────────────────────
|
||
# Een update die niet opkomt liet je met een stilstaande server zitten en een
|
||
# rijtje commando's om zelf uit te voeren. De vorige commit is bekend, dus dat
|
||
# kan het script net zo goed zelf doen.
|
||
terugrollen() {
|
||
[ -n "$VORIGE_COMMIT" ] || return 1
|
||
stap "Terugrollen"
|
||
log "Terug naar $(printf '%.9s' "$VORIGE_COMMIT")…"
|
||
if ! $SUDO git -C "$DOEL" reset --hard "$VORIGE_COMMIT" >/dev/null 2>&1; then
|
||
waarschuw "Kon niet terug naar de vorige versie."
|
||
return 1
|
||
fi
|
||
start
|
||
if wacht_op_gereed; then
|
||
goed "Terug op de vorige versie; die draait weer."
|
||
return 0
|
||
fi
|
||
waarschuw "Ook de vorige versie komt niet op; er is meer aan de hand."
|
||
return 1
|
||
}
|
||
|
||
# ── Doorlichten ──────────────────────────────────────────────────────────────
|
||
# De fouten die dit script had, waren allemaal van dezelfde soort: iets klopte
|
||
# niet en niemand merkte het, omdat er nergens werd nagekeken. Dit is het
|
||
# tegengif — het controleert de aannames in plaats van ze te doen.
|
||
DOK_FOUTEN=0
|
||
DOK_WAARSCHUWINGEN=0
|
||
|
||
dok_ok() { goed "$1"; }
|
||
dok_waar() { waarschuw "$1"; DOK_WAARSCHUWINGEN=$((DOK_WAARSCHUWINGEN + 1)); }
|
||
dok_fout() { printf ' %s✖%s %s\n' "$C_FOUT" "$C_UIT" "$1"; DOK_FOUTEN=$((DOK_FOUTEN + 1)); }
|
||
|
||
_dok_inspect() {
|
||
# _dok_inspect FORMAAT — docker inspect op de container, of leeg.
|
||
$SUDO docker inspect server-up --format "$1" 2>/dev/null || true
|
||
}
|
||
|
||
doorlichten() {
|
||
stap "Installatie"
|
||
if [ -d "$DOEL" ] && [ -f "$DOEL/docker-compose.yml" ]; then
|
||
dok_ok "Gevonden in $DOEL"
|
||
else
|
||
dok_fout "Geen installatie in $DOEL (geen docker-compose.yml)"
|
||
printf '\n'
|
||
return 1
|
||
fi
|
||
if [ -f "$DOEL/.env" ]; then
|
||
_rechten="$(stat -c '%a' "$DOEL/.env" 2>/dev/null || echo '?')"
|
||
case "$_rechten" in
|
||
600|400) dok_ok ".env staat op $_rechten" ;;
|
||
'?') dok_waar "Rechten van .env niet te bepalen" ;;
|
||
*) dok_waar ".env staat op $_rechten; er staan tokens en wachtwoorden in. Zet hem op 600." ;;
|
||
esac
|
||
else
|
||
dok_waar "Geen .env in $DOEL; alles draait op de standaardwaarden"
|
||
fi
|
||
[ -f "$DOEL/VERSION" ] && log "Versie in de map: $(cat "$DOEL/VERSION" 2>/dev/null)"
|
||
|
||
stap "Docker"
|
||
if $SUDO docker info >/dev/null 2>&1; then
|
||
dok_ok "Daemon reageert ($($SUDO docker version --format '{{.Server.Version}}' 2>/dev/null || echo '?'))"
|
||
else
|
||
dok_fout "De Docker-daemon reageert niet"
|
||
printf '\n'
|
||
return 1
|
||
fi
|
||
|
||
stap "Container"
|
||
_staat="$(_dok_inspect '{{.State.Status}}')"
|
||
if [ -z "$_staat" ]; then
|
||
dok_fout "Er is geen container 'server-up'. Start hem met: $SUDO docker compose -f $DOEL/docker-compose.yml up -d"
|
||
elif [ "$_staat" = "running" ]; then
|
||
_gezond="$(_dok_inspect '{{if .State.Health}}{{.State.Health.Status}}{{end}}')"
|
||
dok_ok "Draait${_gezond:+ (}${_gezond:-}${_gezond:+)}"
|
||
[ "${_gezond:-healthy}" = "healthy" ] || dok_waar "De healthcheck staat op '$_gezond'"
|
||
else
|
||
dok_fout "De container staat op '$_staat'"
|
||
fi
|
||
_img="$(_dok_inspect '{{.Config.Image}}')"
|
||
[ -n "$_img" ] && log "Image: $_img"
|
||
|
||
stap "Bereikbaarheid"
|
||
_poort="$(env_waarde PORT)"; _poort="${_poort:-$POORT}"
|
||
_bind="$(env_waarde BIND)"
|
||
[ -n "$_bind" ] && geldig_bind "$_bind" && BIND="$_bind"
|
||
POORT="$_poort"
|
||
_adres="http://$(probe_adres):${POORT}"
|
||
if curl -fsS --connect-timeout 3 --max-time 8 "${_adres}/healthz" >/dev/null 2>&1; then
|
||
dok_ok "Antwoordt op $_adres"
|
||
else
|
||
dok_fout "Geen antwoord op $_adres"
|
||
log "Dit is het adres waarop compose publiceert (BIND=$BIND)."
|
||
fi
|
||
|
||
stap "Account"
|
||
_uid_env="$(env_waarde SU_UID)"
|
||
_uid_echt="$($SUDO docker exec server-up id -u 2>/dev/null || echo '')"
|
||
if [ -z "$_uid_echt" ]; then
|
||
dok_waar "Kon niet in de container kijken onder welk account hij draait"
|
||
elif [ "$_uid_echt" = "0" ]; then
|
||
if [ -n "$_uid_env" ] && [ "$_uid_env" != "0" ]; then
|
||
dok_fout "SU_UID=$_uid_env staat in .env maar hij draait als root. Hercreëer de container: $SUDO docker compose -f $DOEL/docker-compose.yml up -d"
|
||
else
|
||
dok_waar "Draait als root. Zie docs/beveiliging.md; sh $DOEL/install.sh --update laat je kiezen."
|
||
fi
|
||
else
|
||
dok_ok "Draait als uid $_uid_echt"
|
||
[ -n "$_uid_env" ] && [ "$_uid_env" != "$_uid_echt" ] \
|
||
&& dok_waar "In .env staat SU_UID=$_uid_env; hercreëer de container om dat door te voeren."
|
||
fi
|
||
|
||
stap "Mappen"
|
||
_basis="$(env_waarde BASE_DIR)"; _basis="${_basis:-/opt/serverup}"
|
||
log "BASE_DIR: $_basis"
|
||
if $SUDO docker exec server-up test -d "$_basis" 2>/dev/null; then
|
||
# Bestaan is niet genoeg: zonder mount schrijft hij in zijn eigen
|
||
# overlay en zie je op de host niets terug.
|
||
if $SUDO docker exec server-up sh -c "grep -q ' $_basis ' /proc/self/mountinfo" 2>/dev/null; then
|
||
dok_ok "$_basis is in de container gekoppeld"
|
||
else
|
||
dok_fout "$_basis bestaat in de container maar is niet gekoppeld; wat hij daar schrijft komt niet op de host terecht"
|
||
fi
|
||
else
|
||
dok_waar "Kon $_basis niet in de container nakijken"
|
||
fi
|
||
for _sub in stacks appdata backups; do
|
||
if [ -d "$_basis/$_sub" ]; then
|
||
log " $_sub: $(find "$_basis/$_sub" -maxdepth 1 -mindepth 1 2>/dev/null | wc -l) items, eigenaar $(stat -c '%U:%G' "$_basis/$_sub" 2>/dev/null || echo '?')"
|
||
else
|
||
dok_waar " $_sub ontbreekt nog ($_basis/$_sub)"
|
||
fi
|
||
done
|
||
if heeft df; then
|
||
_vrij="$(df -h "$_basis" 2>/dev/null | awk 'NR>1 {print $4; exit}')"
|
||
[ -n "$_vrij" ] && log "Vrije ruimte: $_vrij"
|
||
fi
|
||
|
||
stap "Bijwerken vanuit de interface"
|
||
_werkmap="$(_dok_inspect '{{index .Config.Labels "com.docker.compose.project.working_dir"}}')"
|
||
if [ -z "$_werkmap" ]; then
|
||
dok_waar "Geen compose-labels; bijwerken vanuit de interface kan niet"
|
||
else
|
||
log "Werkmap volgens de container: $_werkmap"
|
||
case "$_img" in
|
||
*/*) dok_ok "Registry-image, dus de knop in de interface werkt" ;;
|
||
*) log "Zelfgebouwd image; bijwerken loopt via $DOEL/install.sh --update" ;;
|
||
esac
|
||
fi
|
||
|
||
printf '\n%s────────────────────────────────────────────────────────%s\n' "$C_KOP" "$C_UIT"
|
||
if [ "$DOK_FOUTEN" -gt 0 ]; then
|
||
printf '%s %s fout(en), %s waarschuwing(en)%s\n' "$C_FOUT" "$DOK_FOUTEN" "$DOK_WAARSCHUWINGEN" "$C_UIT"
|
||
elif [ "$DOK_WAARSCHUWINGEN" -gt 0 ]; then
|
||
printf '%s Geen fouten, %s waarschuwing(en)%s\n' "$C_WAARSCHUW" "$DOK_WAARSCHUWINGEN" "$C_UIT"
|
||
else
|
||
printf '%s Alles in orde%s\n' "$C_OK" "$C_UIT"
|
||
fi
|
||
printf '%s────────────────────────────────────────────────────────%s\n\n' "$C_KOP" "$C_UIT"
|
||
[ "$DOK_FOUTEN" -eq 0 ]
|
||
}
|
||
|
||
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"
|
||
|
||
_basis="$(env_waarde BASE_DIR)"; _basis="${_basis:-/opt/serverup}"
|
||
_naam="$(getent passwd "$(env_waarde SU_UID)" 2>/dev/null | cut -d: -f1)"
|
||
|
||
if [ "$PURGE" -eq 0 ]; then
|
||
printf '\n'
|
||
log "Je gegevens staan nog op deze plekken:"
|
||
log " docker-volume 'su-data' — instellingen, accounts, auditlog"
|
||
log " $_basis — je stacks, appdata en backups"
|
||
log " $DOEL — de installatie zelf"
|
||
[ -n "$_naam" ] && [ "$_naam" != "root" ] \
|
||
&& log " het account '$_naam', dat bij de installatie is aangemaakt"
|
||
printf '\n'
|
||
log "Alles definitief weg? Draai dan: sh $DOEL/install.sh --uninstall --purge"
|
||
printf '\n'
|
||
return 0
|
||
fi
|
||
|
||
# ── Opruimen ─────────────────────────────────────────────────────────────
|
||
# Per onderdeel apart vragen. Dit gooit gegevens weg die nergens anders meer
|
||
# staan, dus "ja op alles" is hier geen dienst.
|
||
stap "Opruimen"
|
||
# Let op de vorm: `doe … >/dev/null` zou ook de [droog]-regel wegslikken,
|
||
# waardoor een proefdraai niet meer laat zien wat hij zou doen.
|
||
if vraag "Het docker-volume 'su-data' verwijderen (instellingen, accounts, auditlog)?"; then
|
||
if doe $SUDO docker volume rm su-data; then
|
||
goed "Volume su-data verwijderd"
|
||
else
|
||
waarschuw "Volume su-data niet gevonden of nog in gebruik"
|
||
fi
|
||
fi
|
||
|
||
if [ -d "$_basis" ]; then
|
||
_omvang="$(du -sh "$_basis" 2>/dev/null | cut -f1)"
|
||
waarschuw "$_basis bevat je stacks, appdata en backups (${_omvang:-?})."
|
||
waarschuw "Dit is de map met de gegevens van al je apps."
|
||
if vraag "Deze map écht verwijderen?"; then
|
||
doe $SUDO rm -rf "$_basis" && goed "$_basis verwijderd"
|
||
else
|
||
log "$_basis blijft staan."
|
||
fi
|
||
fi
|
||
|
||
if [ -n "$_naam" ] && [ "$_naam" != "root" ]; then
|
||
if vraag "Het account '$_naam' verwijderen?"; then
|
||
if doe $SUDO userdel "$_naam"; then
|
||
goed "Account $_naam verwijderd"
|
||
else
|
||
waarschuw "Kon $_naam niet verwijderen; misschien bezit het nog bestanden."
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# De installatiemap als laatste: daar staat dit script zelf in.
|
||
if vraag "De installatiemap $DOEL verwijderen?"; then
|
||
doe $SUDO rm -rf "$DOEL" && goed "$DOEL verwijderd"
|
||
fi
|
||
printf '\n'
|
||
}
|
||
|
||
# ── Hoofdprogramma ───────────────────────────────────────────────────────────
|
||
# De testsuite laadt dit bestand om losse functies te kunnen aanroepen — vooral
|
||
# de vragen die bepalen waarop de server straks luistert. Zonder deze uitgang
|
||
# zou het script bij het inladen meteen een hele installatie beginnen.
|
||
if [ -n "${SU_INSTALL_ALLEEN_FUNCTIES:-}" ]; then
|
||
return 0 2>/dev/null || exit 0
|
||
fi
|
||
|
||
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
|
||
;;
|
||
reconfigure)
|
||
# Instellingen wijzigen zonder de broncode aan te raken. Handig als je
|
||
# de server op je netwerk wilt zetten, een andere poort wilt, of alsnog
|
||
# van root af wilt — dingen waarvoor je anders --update draaide en dus
|
||
# ongewild ook nieuwe code binnenhaalde.
|
||
[ -f "$DOEL/docker-compose.yml" ] || fout "Geen installatie gevonden in $DOEL."
|
||
bepaal_rechten
|
||
controleer_docker
|
||
lees_env_terug "$DOEL/.env"
|
||
printf '\n Nu ingesteld: BIND=%s PORT=%s BASE_DIR=%s, draaiend als %s\n' \
|
||
"$BIND" "$POORT" "${BASE_DIR:-/opt/serverup}" \
|
||
"$(getent passwd "$(env_waarde SU_UID)" 2>/dev/null | cut -d: -f1 || echo root)"
|
||
kies_bind
|
||
# Zonder --user opnieuw vragen: dat is meestal juist waarvoor je hier bent.
|
||
[ "$GEBRUIKER_EXPLICIET" -eq 1 ] || SU_UID=""
|
||
kies_gebruiker
|
||
schrijf_env
|
||
zet_mappen_klaar
|
||
start
|
||
if wacht_op_gereed; then
|
||
printf '\n'
|
||
goed "Instellingen bijgewerkt."
|
||
afsluiting
|
||
else
|
||
fout "De container kwam niet op met de nieuwe instellingen. Bekijk de logs."
|
||
fi
|
||
;;
|
||
doctor)
|
||
# Alleen kijken, nooit wijzigen. Daarom geen bepaal_rechten: die kan
|
||
# afbreken omdat je niet kunt schrijven, terwijl lezen prima gaat.
|
||
if [ "$(id -u)" -ne 0 ] && ! docker info >/dev/null 2>&1 && heeft sudo; then
|
||
SUDO="sudo"
|
||
fi
|
||
doorlichten
|
||
;;
|
||
update)
|
||
[ -d "$DOEL" ] || fout "Geen installatie gevonden in $DOEL. Installeer eerst zonder --update."
|
||
controleer_systeem
|
||
controleer_docker
|
||
haal_broncode
|
||
# Bestaande installaties draaien nog als root. Eén keer vragen of dat zo
|
||
# moet blijven; wie al gekozen heeft, wordt niet opnieuw lastiggevallen
|
||
# (kies_gebruiker ziet SU_UID uit .env staan).
|
||
kies_gebruiker
|
||
# Ook bij bijwerken: expliciete --bind/--port doorvoeren, en anders
|
||
# overnemen wat er al staat.
|
||
schrijf_env
|
||
zet_mappen_klaar
|
||
start
|
||
# Niet wegkijken als hij niet opkomt: "Bijgewerkt." bij een kapotte
|
||
# update zag er precies zo uit als een geslaagde, en een script dat dit
|
||
# aanroept zag exitcode 0.
|
||
if wacht_op_gereed; then
|
||
printf '\n'
|
||
goed "Bijgewerkt."
|
||
else
|
||
printf '\n'
|
||
waarschuw "De nieuwe versie kwam niet omhoog."
|
||
if terugrollen; then
|
||
printf '\n'
|
||
fout "Bijwerken mislukt; je draait weer op de vorige versie. Bekijk de logs voordat je het opnieuw probeert."
|
||
fi
|
||
log "Bekijk de logs met:"
|
||
log " $SUDO docker compose -f $DOEL/docker-compose.yml logs --tail=50"
|
||
fout "Bijwerken mislukt en terugrollen lukte niet."
|
||
fi
|
||
;;
|
||
create-admin)
|
||
# Losse actie voor een installatie die al draait: handig als het
|
||
# aanmaken tijdens de installatie is misgegaan, of overgeslagen.
|
||
# Docker en schrijfrechten zijn hier niet nodig — alleen .env lezen
|
||
# en één verzoek naar de draaiende server — dus geen bepaal_rechten.
|
||
lees_env_terug "$DOEL/.env"
|
||
if [ -z "$BEHEERDER" ]; then
|
||
if terminal_beschikbaar && [ "$JA" -eq 0 ]; then
|
||
printf '\n Gebruikersnaam: '
|
||
read -r naam </dev/tty || naam=""
|
||
BEHEERDER="$(printf '%s' "$naam" | tr -d '[:space:]')"
|
||
fi
|
||
[ -n "$BEHEERDER" ] || fout "Geef de gebruikersnaam op met --admin NAAM."
|
||
fi
|
||
stap "Beheerdersaccount"
|
||
maak_beheerder
|
||
if [ -n "$BEHEERDER" ] && [ "$WW_GEGENEREERD" -eq 1 ]; then
|
||
printf '\n Wachtwoord: %s%s%s\n' "$C_KOP" "$BEHEERDER_WW_TOON" "$C_UIT"
|
||
printf ' %sSchrijf dit nu over — het staat nergens anders.%s\n\n' "$C_WAARSCHUW" "$C_UIT"
|
||
fi
|
||
;;
|
||
install)
|
||
controleer_systeem
|
||
controleer_docker
|
||
kies_bind
|
||
kies_gebruiker
|
||
beheerder_vooraf
|
||
haal_broncode
|
||
schrijf_env
|
||
zet_mappen_klaar
|
||
start
|
||
# Hier wél doorgaan: de aanwijzingen aan het eind zijn juist dán nuttig.
|
||
# Maar het slot mag niet doen alsof alles goed ging.
|
||
if wacht_op_gereed; then GEREED=1; else GEREED=0; fi
|
||
maak_beheerder
|
||
afsluiting
|
||
[ "$GEREED" -eq 1 ] || exit 1
|
||
;;
|
||
esac
|