#!/usr/bin/env bash # Audiofy – Installation der Rolle "Server": Website für Gäste, Admin-Bereich, Player-API. # # install/install-server.sh Neuinstallation (fragt alles Nötige ab) # install/install-server.sh --update nur Code, Python-Umgebung und Dienst aktualisieren # # Voraussetzungen: Debian 12+ (oder abgeleitet), eine (Sub-)Domain, deren A/AAAA-Record # auf diesen Rechner zeigt, und offene Ports 80/443 (auch in einer Cloud-Firewall). set -euo pipefail source "$(dirname "$0")/lib.sh" UPDATE=0 for arg in "$@"; do case "$arg" in --update) UPDATE=1 ;; -h|--help) sed -n '2,9p' "$0"; exit 0 ;; *) abbruch "Unbekannte Option: $arg" ;; esac done ENV_DATEI="$AUDIOFY_DIR/.env" export AUDIOFY_DIR AUDIOFY_USER export APP_PORT="${APP_PORT:-8000}" schritt "Audiofy-Server: $([ $UPDATE = 1 ] && echo Update || echo Neuinstallation)" pruefe_system if [ $UPDATE = 1 ] && ! $SUDO test -f "$ENV_DATEI"; then abbruch "Keine bestehende Installation in $AUDIOFY_DIR gefunden – ohne --update ausführen." fi # ------------------------------------------------------------------ Abfragen if [ $UPDATE = 0 ]; then schritt "Angaben" frage DOMAIN "Domain der Website (z. B. musik.example.org)" [ -n "$DOMAIN" ] || abbruch "Ohne Domain geht es nicht (HTTPS über Let's Encrypt)." frage ACME_EMAIL "E-Mail für Let's Encrypt (Ablaufwarnungen)" info "Spotify-App (developer.spotify.com): Redirect-URI http://127.0.0.1:8888/callback eintragen." frage SPOTIFY_CLIENT_ID "Spotify Client ID" frage_geheim SPOTIFY_CLIENT_SECRET "Spotify Client Secret (verdeckt)" frage PLAYLIST "Grund-Playlist (Link oder ID, später im Admin änderbar)" PLAYLIST_ID="$(sed -E 's#.*playlist[/:]([A-Za-z0-9]{22}).*#\1#' <<<"$PLAYLIST")" if [ -n "$PLAYLIST_ID" ] && ! [[ "$PLAYLIST_ID" =~ ^[A-Za-z0-9]{22}$ ]]; then warnung "Das sieht nicht nach einer Playlist aus – bitte später im Admin-Bereich eintragen." PLAYLIST_ID="" fi # alle Ports, auf denen SSH lauscht, plus den der aktuellen Sitzung – damit sich niemand aussperrt SSH_PORTS="$( { ${SUDO} sshd -T 2>/dev/null | awk '/^port /{print $2}'; [ -n "${SSH_CONNECTION:-}" ] && awk '{print $4}' <<<"$SSH_CONNECTION"; } | sort -un | tr '\n' ' ')" SSH_PORTS="${SSH_PORTS:-22}" SSH_PORT="${SSH_PORTS%% *}" # für fail2ban: erster Port ja_nein "Firewall (ufw) einrichten? Erlaubt SSH (Port ${SSH_PORTS% }), 80 und 443" j && FIREWALL=1 || FIREWALL=0 ja_nein "fail2ban für SSH einrichten?" j && FAIL2BAN=1 || FAIL2BAN=0 ja_nein "Automatische Sicherheitsupdates aktivieren?" j && AUTOUPDATE=1 || AUTOUPDATE=0 export DOMAIN ACME_EMAIL SSH_PORT fi # ------------------------------------------------------------------ Pakete schritt "Pakete" $SUDO apt-get update -qq PAKETE=(python3-venv sqlite3 caddy curl) if [ $UPDATE = 0 ]; then [ $FIREWALL = 1 ] && PAKETE+=(ufw) [ $FAIL2BAN = 1 ] && PAKETE+=(fail2ban python3-systemd) [ $AUTOUPDATE = 1 ] && PAKETE+=(unattended-upgrades) fi pakete "${PAKETE[@]}" # ------------------------------------------------------------------ Benutzer, Ordner, Code schritt "Dienst-Benutzer, Ordner und Code" dienst_benutzer $SUDO install -d -o root -g "$AUDIOFY_USER" -m 750 "$AUDIOFY_DIR" $SUDO install -d -o "$AUDIOFY_USER" -g "$AUDIOFY_USER" -m 700 "$AUDIOFY_DIR/data" code_kopieren "$REPO/server" "$AUDIOFY_DIR/app" $SUDO install -o root -g "$AUDIOFY_USER" -m 640 "$REPO/player/auth.py" "$AUDIOFY_DIR/app/auth.py" venv_einrichten "$AUDIOFY_DIR/app/requirements.txt" info "Code in $AUDIOFY_DIR/app, Python-Umgebung in $AUDIOFY_DIR/.venv" # ------------------------------------------------------------------ Konfiguration schritt "Konfiguration ($ENV_DATEI)" $SUDO test -f "$ENV_DATEI" || $SUDO install -o "$AUDIOFY_USER" -g "$AUDIOFY_USER" -m 600 /dev/null "$ENV_DATEI" VERWALTUNG=("${ALS_DIENST[@]}" "$AUDIOFY_DIR/.venv/bin/python" "$AUDIOFY_DIR/app/verwaltung.py") "${VERWALTUNG[@]}" geheimnisse --env "$ENV_DATEI" | sed 's/^/ /' if [ $UPDATE = 0 ]; then env_setzen "$ENV_DATEI" DB_PATH "$AUDIOFY_DIR/data/audiofy.db" env_setzen "$ENV_DATEI" PUBLIC_URL "https://$DOMAIN" env_setzen "$ENV_DATEI" SPOTIFY_CLIENT_ID "$SPOTIFY_CLIENT_ID" env_setzen "$ENV_DATEI" SPOTIFY_CLIENT_SECRET "$SPOTIFY_CLIENT_SECRET" [ -n "$PLAYLIST_ID" ] && env_setzen "$ENV_DATEI" SPOTIFY_PLAYLIST_ID "$PLAYLIST_ID" unset SPOTIFY_CLIENT_SECRET schritt "Spotify-Login für die Suche" info "Öffne gleich die angezeigte Adresse im Browser, melde dich an und füge danach die" info "Adresse aus der Adresszeile (http://127.0.0.1:8888/callback?...) hier ein." if ja_nein "Jetzt anmelden?" j; then "${ALS_DIENST[@]}" "$AUDIOFY_DIR/.venv/bin/python" "$AUDIOFY_DIR/app/auth.py" --profil server --env "$ENV_DATEI" \ || warnung "Login fehlgeschlagen – später nachholen (siehe README)." fi schritt "Admin-Passwort" "${VERWALTUNG[@]}" passwort --env "$ENV_DATEI" || warnung "Kein Passwort gesetzt – später nachholen (siehe README)." if ja_nein "Cloudflare Turnstile (Bot-Schutz für Login und Wünsche) einrichten?" n; then "${VERWALTUNG[@]}" turnstile --env "$ENV_DATEI" || warnung "Turnstile nicht eingerichtet." fi fi # ------------------------------------------------------------------ Dienst schritt "Dienst audiofy-web" vorlage audiofy-web.service /etc/systemd/system/audiofy-web.service $SUDO systemctl daemon-reload $SUDO systemctl enable audiofy-web >/dev/null 2>&1 $SUDO systemctl restart audiofy-web sleep 3 systemctl is-active --quiet audiofy-web || abbruch "audiofy-web läuft nicht – siehe: journalctl -u audiofy-web" curl -fsS "http://127.0.0.1:$APP_PORT/healthz" >/dev/null && info "audiofy-web läuft" # ------------------------------------------------------------------ Caddy & Schutz (nur Neuinstallation) if [ $UPDATE = 0 ]; then schritt "Caddy (HTTPS)" if $SUDO grep -q "Audiofy" /etc/caddy/Caddyfile 2>/dev/null || $SUDO grep -q "/usr/share/caddy" /etc/caddy/Caddyfile 2>/dev/null; then vorlage Caddyfile /etc/caddy/Caddyfile $SUDO caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile >/dev/null 2>&1 || abbruch "Caddyfile ungültig" $SUDO systemctl enable caddy >/dev/null 2>&1 $SUDO systemctl reload-or-restart caddy info "Caddy holt das Zertifikat für $DOMAIN (dauert ein paar Sekunden)." else warnung "/etc/caddy/Caddyfile enthält schon eine eigene Konfiguration – nicht verändert." warnung "Bitte einen Block für $DOMAIN mit 'reverse_proxy 127.0.0.1:$APP_PORT' ergänzen (Vorlage: install/vorlagen/Caddyfile)." fi if [ $FIREWALL = 1 ]; then schritt "Firewall (ufw)" $SUDO ufw default deny incoming >/dev/null $SUDO ufw default allow outgoing >/dev/null for p in $SSH_PORTS; do $SUDO ufw allow "$p/tcp" comment "SSH" >/dev/null; done $SUDO ufw allow 80/tcp comment "HTTP (Caddy/ACME)" >/dev/null $SUDO ufw allow 443/tcp comment "HTTPS (Caddy)" >/dev/null $SUDO ufw --force enable >/dev/null info "ufw aktiv: SSH ${SSH_PORTS% }, 80, 443" fi if [ $FAIL2BAN = 1 ]; then schritt "fail2ban" vorlage fail2ban-audiofy.conf /etc/fail2ban/jail.d/audiofy.conf $SUDO systemctl enable fail2ban >/dev/null 2>&1 $SUDO systemctl restart fail2ban info "SSH-Schutz auf Port $SSH_PORT aktiv" fi if [ $AUTOUPDATE = 1 ]; then schritt "Automatische Sicherheitsupdates" vorlage 20auto-upgrades /etc/apt/apt.conf.d/20auto-upgrades info "aktiviert" fi schritt "Logs begrenzen" $SUDO install -d /etc/systemd/journald.conf.d vorlage journald-10-audiofy.conf /etc/systemd/journald.conf.d/10-audiofy.conf $SUDO systemctl restart systemd-journald info "journald max. 200 MB" fi # ------------------------------------------------------------------ Zusammenfassung schritt "Fertig" URL="$(env_wert "$ENV_DATEI" PUBLIC_URL)" info "Website: $URL (Gäste nur mit Einladungslink/QR-Code)" info "Admin: $URL/admin" info "Logs: journalctl -u audiofy-web -f" if [ $UPDATE = 0 ]; then info "" info "Für die Player-Installation brauchst du:" info " Server-Adresse: $URL" info " Player-Token: sudo -u $AUDIOFY_USER grep PLAYER_API_TOKEN $ENV_DATEI" info "Gastzugang ist anfangs aus – im Admin unter „Gäste“ einschalten." fi