#!/usr/bin/env bash # # OpenNIT – vollautomatischer Installer für Debian/Ubuntu. # # Richtet einen frischen Server komplett ein: Systempakete, PHP, Apache (mod_php), # Datenbank mit korrekten Rechten, lädt OpenNIT vom Update-Server, installiert die # PHP-Abhängigkeiten, legt den VirtualHost an und führt anschließend die # Grundeinrichtung (Datenbankschema, Konfiguration, Administrator-Konto) durch. # Danach ist OpenNIT direkt anmeldebereit – der Browser-Assistent ist nicht mehr # nötig. # # Aufruf (als root), nachdem das Skript auf dem Server liegt: # sudo bash install.sh # # Ein erneuter Lauf nach einem Abbruch ist möglich; eine bereits abgeschlossene # Installation wird erkannt und zum Schutz der Daten (.env/APP_KEY) nicht # überschrieben. Alle nötigen Angaben werden interaktiv abgefragt. set -euo pipefail TOTAL_STEPS=7 # --------------------------------------------------------------------------- # Bezugsquelle: der Haupt-Kanal des Updaters # # Identisch mit dem Kanal "stable" in UpdateManager::CHANNELS. Damit installiert # das Skript immer exakt den Stand, den eine bestehende Installation per Update # beziehen würde – ohne eigene, nachzupflegende Versions- oder Release-Angabe. # Alle Endpunkte werden aus dieser einen Adresse abgeleitet. # Für Testzwecke überschreibbar: OPENNIT_UPDATE_BASE=… bash install.sh # --------------------------------------------------------------------------- UPDATE_BASE="${OPENNIT_UPDATE_BASE:-https://update.loheide.cloud/opennit}" UPDATE_BASE="${UPDATE_BASE%/}" ZIP_URL="$UPDATE_BASE/zip" VERSION_URL="$UPDATE_BASE/version" # --------------------------------------------------------------------------- # Farben & Ausgabe-Helfer # --------------------------------------------------------------------------- if [ -t 1 ] && [ "${NO_COLOR:-}" = "" ]; then BLUE=$'\033[38;5;33m'; GREEN=$'\033[38;5;35m'; YELLOW=$'\033[38;5;214m' RED=$'\033[38;5;203m'; GREY=$'\033[38;5;245m'; BOLD=$'\033[1m'; R=$'\033[0m' else BLUE=""; GREEN=""; YELLOW=""; RED=""; GREY=""; BOLD=""; R="" fi info() { printf ' %s›%s %s\n' "$BLUE" "$R" "$*"; } ok() { printf ' %s✔%s %s\n' "$GREEN" "$R" "$*"; } warn() { printf ' %s▲%s %s\n' "$YELLOW" "$R" "$*"; } die() { printf '\n %s✗%s %s%s%s\n\n' "$RED" "$R" "$BOLD" "$*" "$R" >&2; exit 1; } # step – Abschnittsüberschrift mit Fortschrittsbalken step() { local n="$1" title="$2" width=30 i filled bar="" filled=$(( n * width / TOTAL_STEPS )) for ((i=0; i – entfernt projektinterne Hilfsdateien, die # nicht auf Produktivinstallationen gehören (identische Liste wie der eingebaute # Updater). Idempotent: fehlende Pfade werden übersprungen. prune_dev_artifacts() { local base="$1" p [ -n "$base" ] && [ -d "$base" ] || return 0 for p in \ "CLAUDE.md" ".claude" "AGENTS.md" ".cursor" ".cursorrules" \ ".aider.conf.yml" ".github/copilot-instructions.md"; do # ${base:?} bricht bei leerer Variable ab – nie versehentlich unter / löschen. rm -rf -- "${base:?}/$p" done } # run – führt einen Befehl aus, zeigt ✔/✗ run() { local desc="$1"; shift printf ' %s…%s %s' "$GREY" "$R" "$desc" if "$@" >/tmp/opennit-install.log 2>&1; then printf '\r %s✔%s %s\n' "$GREEN" "$R" "$desc" else printf '\r %s✗%s %s\n' "$RED" "$R" "$desc" printf '%s' "$GREY"; tail -n 6 /tmp/opennit-install.log; printf '%s' "$R" die "Abgebrochen bei: $desc" fi } # --------------------------------------------------------------------------- # Vorbedingungen # --------------------------------------------------------------------------- [ "$(id -u)" -eq 0 ] || die "Bitte als root ausführen (sudo bash install.sh)." command -v apt-get >/dev/null 2>&1 || die "Dieses Skript unterstützt nur Debian/Ubuntu (apt)." # Logo: "Open" blau, "NIT" grün – ohne Box (variable ASCII-Breite bliebe Ausrichtung nicht) printf '\n' printf ' %s ___ %s _ _ ___ _____%s\n' "$BOLD$BLUE" "$BOLD$GREEN" "$R" printf ' %s/ _ \\ _ __ ___ _ _ %s| \\| |_ _|_ _|%s\n' "$BOLD$BLUE" "$BOLD$GREEN" "$R" printf ' %s| (_) | '"'"'_ \\/ -_) '"'"' \\%s| .`|| | | |%s\n' "$BOLD$BLUE" "$BOLD$GREEN" "$R" printf ' %s\\___/| .__/\\___|_||_%s|_|\\_|___| |_|%s\n' "$BOLD$BLUE" "$BOLD$GREEN" "$R" printf ' %s |_|%s %sServer-Installer%s\n' "$BOLD$BLUE" "$R" "$GREY" "$R" printf ' %s────────────────────────────────────────────%s\n' "$GREY" "$R" printf ' %sRevisionssichere IT-Dokumentation · self-hosted%s\n' "$GREY" "$R" # --------------------------------------------------------------------------- # Angaben abfragen # --------------------------------------------------------------------------- printf '\n%s Bitte ein paar Angaben – Vorgaben in [Klammern] mit Enter übernehmen.%s\n\n' "$BOLD" "$R" ask() { # ask local __var="$1" __q="$2" __def="${3:-}" __ans if [ -n "$__def" ]; then read -r -p "$(printf ' %s?%s %s %s[%s]%s: ' "$GREEN" "$R" "$__q" "$GREY" "$__def" "$R")" __ans printf -v "$__var" '%s' "${__ans:-$__def}" else read -r -p "$(printf ' %s?%s %s: ' "$GREEN" "$R" "$__q")" __ans printf -v "$__var" '%s' "$__ans" fi } ask DOMAIN "Domain / ServerName" "$(hostname -f 2>/dev/null || hostname)" ask INSTALL_DIR "Installationsverzeichnis" "/var/www/opennit" ask DB_NAME "Datenbankname" "opennit" ask DB_USER "Datenbank-Benutzer" "opennit_user" ask DB_PASS "Datenbank-Passwort (leer = automatisch)" "" if [ -z "$DB_PASS" ]; then DB_PASS="$(head -c 18 /dev/urandom | base64 | tr -d '/+=' | head -c 24)" DB_PASS_GENERATED=1 fi # Erster Administrator (für den lokalen Login nach der Installation) ask ADMIN_EMAIL "Administrator-E-Mail" "" while [ -z "$ADMIN_EMAIL" ]; do warn "Die Administrator-E-Mail ist erforderlich." ask ADMIN_EMAIL "Administrator-E-Mail" "" done ask ADMIN_PASS "Administrator-Passwort (min. 10 Zeichen, leer = automatisch)" "" if [ -z "$ADMIN_PASS" ]; then ADMIN_PASS="$(head -c 18 /dev/urandom | base64 | tr -d '/+=' | head -c 20)Aa1" ADMIN_PASS_GENERATED=1 else while [ "${#ADMIN_PASS}" -lt 10 ]; do warn "Das Passwort muss mindestens 10 Zeichen lang sein." ask ADMIN_PASS "Administrator-Passwort (min. 10 Zeichen)" "" done fi printf ' %s?%s TLS-Zertifikat per Let'"'"'s Encrypt? %s(Domain muss öffentlich auf diesen Server zeigen)%s\n' "$GREEN" "$R" "$GREY" "$R" printf ' %sn%s = kein TLS (HTTP) %sj%s = Ja, produktiv %ss%s = Staging/Test-Zertifikat\n' "$BOLD" "$R" "$BOLD" "$R" "$BOLD" "$R" printf ' %sStaging umgeht die Let'"'"'s-Encrypt-Limits (5 Zertifikate/Domain/7 Tage) –%s\n' "$GREY" "$R" printf ' %sideal fürs wiederholte Testen; das Zertifikat ist im Browser aber nicht vertrauenswürdig.%s\n' "$GREY" "$R" read -r -p "$(printf ' %s→%s Auswahl %s[n/j/s, Standard n]%s: ' "$BLUE" "$R" "$GREY" "$R")" _tls case "${_tls,,}" in j|ja|y|yes) USE_CERTBOT="prod" ;; s|staging|test) USE_CERTBOT="staging" ;; *) USE_CERTBOT="no" ;; esac case "$USE_CERTBOT" in prod) TLS_LABEL="Let's Encrypt (produktiv)" ;; staging) TLS_LABEL="Let's Encrypt Staging (Test-Zertifikat)" ;; *) TLS_LABEL="kein TLS (HTTP)" ;; esac WEBROOT="$INSTALL_DIR/public" printf '\n %sZusammenfassung%s\n' "$BOLD" "$R" printf ' %s────────────────────────────────────────────────────────%s\n' "$GREY" "$R" printf ' Domain %s%s%s\n' "$BOLD" "$DOMAIN" "$R" printf ' Verzeichnis %s\n' "$INSTALL_DIR" printf ' Quelle %s\n' "$UPDATE_BASE" printf ' Datenbank %s\n' "$DB_NAME / $DB_USER" printf ' Administrator %s\n' "$ADMIN_EMAIL" printf ' TLS %s\n' "$TLS_LABEL" printf '\n' read -r -p "$(printf ' %s→%s Fortfahren? %s[J/n]%s: ' "$BLUE" "$R" "$GREY" "$R")" _go [[ "${_go:-j}" =~ ^(n|no|nein)$ ]] && die "Abgebrochen." # Bestehende, abgeschlossene Installation schützen: Ein erneuter Lauf würde in # Schritt 3 die .env löschen, während die Grundeinrichtung durch die # "bereits installiert"-Sperre blockiert – die Instanz bliebe ohne .env zurück # (Fehler 500). Ein Abbruch nach einem gescheiterten Erstlauf ist weiterhin # möglich (dann existiert weder Marker noch APP_KEY). if [ -f "$INSTALL_DIR/storage/.installed" ] \ || grep -qsE '^[[:space:]]*APP_KEY[[:space:]]*=[[:space:]]*\S' "$INSTALL_DIR/.env"; then warn "In $INSTALL_DIR ist bereits eine abgeschlossene OpenNIT-Installation vorhanden." info "Für eine Neuinstallation zuerst Verzeichnis und Datenbank entfernen:" info " sudo rm -rf $INSTALL_DIR" info " sudo mysql -e \"DROP DATABASE IF EXISTS $DB_NAME; DROP USER IF EXISTS '$DB_USER'@'localhost';\"" die "Abgebrochen, um vorhandene Daten (.env/APP_KEY) nicht zu zerstören." fi # --------------------------------------------------------------------------- # 1. Systempakete # --------------------------------------------------------------------------- step 1 "Systempakete" export DEBIAN_FRONTEND=noninteractive run "Paketquellen aktualisieren" apt-get update -qq run "Apache, PHP, MariaDB, Composer …" apt-get install -y -qq \ apache2 libapache2-mod-php \ php php-mysql php-mbstring php-xml php-curl php-zip php-gd php-intl \ mariadb-server composer unzip curl PHP_VER="$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')" ok "Installiert – PHP $PHP_VER, Apache, MariaDB." # --------------------------------------------------------------------------- # 2. OpenNIT laden # --------------------------------------------------------------------------- step 2 "OpenNIT laden" mkdir -p "$INSTALL_DIR" if [ -f "$WEBROOT/index.php" ]; then ok "OpenNIT liegt bereits in $INSTALL_DIR – Download übersprungen." else # Stand des Kanals vor dem Download festhalten: derselbe Commit-Hash, den # der Updater in storage/.version erwartet. Schlägt die Abfrage fehl, wird # weiterhin installiert – der Updater ermittelt den Stand dann selbst. REMOTE_SHA="$(curl -fsSL -A "OpenNIT-Installer" "$VERSION_URL" 2>/dev/null \ | sed -n 's/.*"sha"[[:space:]]*:[[:space:]]*"\([0-9a-fA-F]\{7,40\}\)".*/\1/p' | head -n1 || true)" TMP_ZIP="$(mktemp --suffix=.zip)"; TMP_DIR="$(mktemp -d)" run "Aktuelles Paket vom Update-Server holen" \ curl -fSL -A "OpenNIT-Installer" "$ZIP_URL" -o "$TMP_ZIP" run "Entpacken" unzip -q "$TMP_ZIP" -d "$TMP_DIR" SRC="$TMP_DIR" if [ "$(find "$TMP_DIR" -maxdepth 1 -mindepth 1 -type d | wc -l)" -eq 1 ] \ && [ ! -f "$TMP_DIR/composer.json" ]; then SRC="$(find "$TMP_DIR" -maxdepth 1 -mindepth 1 -type d)" fi cp -a "$SRC"/. "$INSTALL_DIR"/ rm -rf "$TMP_ZIP" "$TMP_DIR" [ -f "$WEBROOT/index.php" ] || die "Entpacktes Paket enthält kein public/index.php." # Installierten Stand vermerken, damit die Update-Prüfung im Backend direkt # hier anschließt und nicht sofort ein vermeintliches Update meldet. if [ -n "$REMOTE_SHA" ]; then mkdir -p "$INSTALL_DIR/storage" printf '%s' "$REMOTE_SHA" > "$INSTALL_DIR/storage/.version" fi INSTALLED_VERSION="$(cat "$INSTALL_DIR/VERSION" 2>/dev/null || true)" ok "OpenNIT${INSTALLED_VERSION:+ $INSTALLED_VERSION} nach $INSTALL_DIR entpackt." fi prune_dev_artifacts "$INSTALL_DIR" # --------------------------------------------------------------------------- # 3. PHP-Abhängigkeiten # --------------------------------------------------------------------------- step 3 "PHP-Abhängigkeiten" if [ -f "$INSTALL_DIR/vendor/autoload.php" ]; then ok "vendor/ bereits vorhanden." else run "composer install (das dauert einen Moment)" \ bash -c "cd '$INSTALL_DIR' && COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev --optimize-autoloader --no-interaction" ok "Abhängigkeiten installiert." fi rm -f "$INSTALL_DIR/.env" # --------------------------------------------------------------------------- # 4. Datenbank # --------------------------------------------------------------------------- step 4 "Datenbank" systemctl enable --now mariadb >/dev/null 2>&1 || systemctl enable --now mysql >/dev/null 2>&1 || true run "Datenbank & Benutzer anlegen, Rechte vergeben" bash -c "mysql <<'SQL' CREATE DATABASE IF NOT EXISTS \`${DB_NAME}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER IF NOT EXISTS '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}'; ALTER USER '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}'; GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX, DROP, REFERENCES, CREATE TEMPORARY TABLES, LOCK TABLES, CREATE VIEW, SHOW VIEW, TRIGGER ON \`${DB_NAME}\`.* TO '${DB_USER}'@'localhost'; SET GLOBAL log_bin_trust_function_creators = 1; FLUSH PRIVILEGES; SQL" # log_bin_trust_function_creators dauerhaft setzen (Trigger bei aktivem Binlog). # Ins erste vorhandene conf.d-Verzeichnis schreiben – je nach MySQL-/MariaDB-Paket # unterschiedlich benannt; nur existierende Verzeichnisse ansteuern. for _cnf_dir in /etc/mysql/mariadb.conf.d /etc/mysql/mysql.conf.d /etc/mysql/conf.d; do if [ -d "$_cnf_dir" ]; then printf '[mysqld]\nlog_bin_trust_function_creators = 1\n' > "$_cnf_dir/99-opennit.cnf" 2>/dev/null || true break fi done ok "Datenbank '$DB_NAME' bereit (inkl. View-/Trigger-Rechten)." # --------------------------------------------------------------------------- # 5. Berechtigungen & Apache # --------------------------------------------------------------------------- step 5 "Berechtigungen & Apache" chown -R www-data:www-data "$INSTALL_DIR" chmod -R 755 "$INSTALL_DIR" chmod -R 775 "$INSTALL_DIR/storage" 2>/dev/null || true ok "Dateirechte gesetzt (Webserver-Benutzer www-data)." # write_http_vhost – schreibt den sauberen Port-80-vHost und aktiviert ihn. # Wird sowohl bei der Ersteinrichtung als auch zur Wiederherstellung nach einem # fehlgeschlagenen Certbot-Lauf genutzt (überschreibt einen evtl. von Certbot # eingefügten HTTP→HTTPS-Redirect). write_http_vhost() { cat > /etc/apache2/sites-available/opennit.conf < ServerAdmin webmaster@localhost ServerName ${DOMAIN} DocumentRoot ${WEBROOT} AllowOverride All Require all granted Options -Indexes -MultiViews +FollowSymLinks DirectoryIndex index.php ErrorLog \${APACHE_LOG_DIR}/opennit-error.log CustomLog \${APACHE_LOG_DIR}/opennit-access.log combined CONF a2ensite opennit >/dev/null 2>&1 a2dissite 000-default >/dev/null 2>&1 || true } a2enmod rewrite headers >/dev/null 2>&1 write_http_vhost run "Apache-Konfiguration prüfen & neu laden" \ bash -c "apache2ctl configtest && systemctl reload apache2" ok "VirtualHost aktiv (Port 80)." # --------------------------------------------------------------------------- # 6. TLS (optional) # --------------------------------------------------------------------------- step 6 "TLS / HTTPS" SCHEME="http" if [ "$USE_CERTBOT" != "no" ]; then run "Certbot installieren" apt-get install -y -qq certbot python3-certbot-apache CERTBOT_ARGS=(--apache -d "$DOMAIN" --non-interactive --agree-tos \ --register-unsafely-without-email --redirect) # Staging: eigene, praktisch unbegrenzte Limits – Zertifikat ist nicht # vertrauenswürdig, aber ideal für wiederholte Testinstallationen. [ "$USE_CERTBOT" = "staging" ] && CERTBOT_ARGS+=(--staging) if certbot "${CERTBOT_ARGS[@]}" >/tmp/opennit-install.log 2>&1; then SCHEME="https" if [ "$USE_CERTBOT" = "staging" ]; then ok "Staging-Zertifikat eingerichtet (Browser-Warnung ist normal). HTTP→HTTPS aktiv." else ok "Let's-Encrypt-Zertifikat eingerichtet, HTTP→HTTPS aktiv." fi else warn "Certbot fehlgeschlagen. Server läuft weiter über HTTP." info "Ursache prüfen: tail -n30 /var/log/letsencrypt/letsencrypt.log" info "Häufig: Domain zeigt nicht auf diesen Server / Port 80 zu, oder Let's-Encrypt-Limit" info "(5 Zertifikate/Domain/7 Tage) – dann die Option 's' (Staging) nutzen." # Certbot hinterlässt bei einem Fehlschlag oft einen HTTP→HTTPS-Redirect # und einen unvollständigen SSL-vHost – die Seite wäre dann über http nicht # erreichbar. Sauberen Port-80-vHost wiederherstellen und SSL-Reste entfernen. a2dissite opennit-le-ssl >/dev/null 2>&1 || true rm -f /etc/apache2/sites-available/opennit-le-ssl.conf \ /etc/apache2/sites-enabled/opennit-le-ssl.conf 2>/dev/null || true write_http_vhost apache2ctl configtest >/dev/null 2>&1 && systemctl reload apache2 >/dev/null 2>&1 || true info "Sauberer HTTP-vHost wiederhergestellt – Seite über http:// erreichbar." fi else info "TLS übersprungen. Für interne Instanzen ein eigenes/Wildcard-Zertifikat einrichten." fi # --------------------------------------------------------------------------- # 7. Grundeinrichtung (Datenbankschema, Konfiguration, Administrator) # --------------------------------------------------------------------------- step 7 "Grundeinrichtung" APP_URL="${SCHEME}://${DOMAIN}" CLI_DONE=0 if grep -q "run_cli_installer" "$WEBROOT/install.php" 2>/dev/null; then # Passwörter über die Umgebung übergeben – nicht über die Prozessliste sichtbar. export OPENNIT_DB_PASSWORD="$DB_PASS" OPENNIT_ADMIN_PASSWORD="$ADMIN_PASS" run "Schema, Konfiguration & Administrator anlegen" \ php "$WEBROOT/install.php" --cli \ --db-host=localhost --db-port=3306 \ --db-name="$DB_NAME" --db-user="$DB_USER" \ --app-url="$APP_URL" --admin-email="$ADMIN_EMAIL" unset OPENNIT_DB_PASSWORD OPENNIT_ADMIN_PASSWORD # Session-Cookie an das Schema koppeln: Ohne HTTPS muss SESSION_SECURE=false, # sonst sendet der Browser das Cookie nicht und der Login scheitert stumm. if [ "$SCHEME" = "http" ] && ! grep -q '^SESSION_SECURE=' "$INSTALL_DIR/.env" 2>/dev/null; then # Sicherstellen, dass die .env mit einem Zeilenumbruch endet, bevor angehängt # wird – sonst klebt SESSION_SECURE an der letzten Zeile (dotenv-Parsefehler). if [ -s "$INSTALL_DIR/.env" ] && [ -n "$(tail -c1 "$INSTALL_DIR/.env")" ]; then printf '\n' >> "$INSTALL_DIR/.env" fi printf 'SESSION_SECURE=false\n' >> "$INSTALL_DIR/.env" fi chown -R www-data:www-data "$INSTALL_DIR" CLI_DONE=1 ok "OpenNIT eingerichtet – Anmeldung direkt möglich." else warn "Dieses Paket unterstützt die automatische Einrichtung noch nicht." info "Bitte das Setup einmalig im Browser abschließen (siehe unten)." fi # --------------------------------------------------------------------------- # Fertig # --------------------------------------------------------------------------- if [ "$CLI_DONE" = "1" ]; then printf '\n %s╭────────────────────────────────────────────╮%s\n' "$GREEN" "$R" printf ' %s│%s %s✔ Installation abgeschlossen%s %s│%s\n' "$GREEN" "$R" "$BOLD$GREEN" "$R" "$GREEN" "$R" printf ' %s╰────────────────────────────────────────────╯%s\n\n' "$GREEN" "$R" printf ' %sOpenNIT ist eingerichtet und anmeldebereit:%s\n\n' "$BOLD" "$R" printf ' %s%s://%s/%s\n\n' "$BLUE" "$SCHEME" "$DOMAIN" "$R" printf ' %sAnmeldung (Administrator)%s\n' "$BOLD" "$R" printf ' E-Mail %s%s%s\n' "$GREEN" "$ADMIN_EMAIL" "$R" printf ' Passwort %s%s%s\n\n' "$GREEN" "$ADMIN_PASS" "$R" if [ "${ADMIN_PASS_GENERATED:-0}" = "1" ]; then warn "Das Administrator-Passwort wurde automatisch erzeugt – jetzt notieren!" printf '\n' fi if [ "${DB_PASS_GENERATED:-0}" = "1" ]; then info "DB-Passwort (nur zur Info, steht bereits in der .env): $DB_PASS" fi ok "install.php wurde nach der Einrichtung automatisch entfernt." if [ "$USE_CERTBOT" = "staging" ]; then printf '\n' warn "Test-/Staging-Zertifikat aktiv: der Browser zeigt eine Zertifikatswarnung – das" warn "ist beim Testen normal. Für den Produktivbetrieb später mit produktivem Zertifikat neu ausstellen." fi if [ "$SCHEME" = "http" ]; then printf '\n' warn "Ohne HTTPS nur für Tests geeignet. SESSION_SECURE=false ist gesetzt; für den" warn "Produktivbetrieb ein Zertifikat einrichten und in der .env auf https/true umstellen." fi printf '\n' else # Fallback für Pakete ohne CLI-Installer: klassischer Web-Assistent. printf '\n %s╭────────────────────────────────────────────╮%s\n' "$GREEN" "$R" printf ' %s│%s %s✔ Server-Vorbereitung abgeschlossen%s %s│%s\n' "$GREEN" "$R" "$BOLD$GREEN" "$R" "$GREEN" "$R" printf ' %s╰────────────────────────────────────────────╯%s\n\n' "$GREEN" "$R" printf ' %sWeiter im Browser mit dem Setup-Assistenten:%s\n\n' "$BOLD" "$R" printf ' %s%s://%s/install.php%s\n\n' "$BLUE" "$SCHEME" "$DOMAIN" "$R" printf ' Im Schritt %s„Datenbank"%s eintragen:\n' "$BOLD" "$R" printf ' Host %slocalhost%s\n' "$GREEN" "$R" printf ' Datenbank %s%s%s\n' "$GREEN" "$DB_NAME" "$R" printf ' Benutzer %s%s%s\n' "$GREEN" "$DB_USER" "$R" printf ' Passwort %s%s%s\n\n' "$GREEN" "$DB_PASS" "$R" if [ "${DB_PASS_GENERATED:-0}" = "1" ]; then warn "Das DB-Passwort wurde automatisch erzeugt – jetzt notieren!" printf '\n' fi printf ' %sNach dem Web-Installer:%s\n' "$BOLD" "$R" printf ' %s•%s install.php entfernt sich nach Abschluss selbst – zur Kontrolle:\n' "$GREEN" "$R" printf ' ls %s/install.php %s(sollte „No such file" melden)%s\n' "$WEBROOT" "$GREY" "$R" printf ' %s•%s .env schützen: chmod 640 %s/.env\n\n' "$GREEN" "$R" "$INSTALL_DIR" fi