Server (Website, Admin-Bereich, Player-API) und Player (raspotify + Controller) mit Install-Skripten für Debian-Systeme. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
147 lines
5.0 KiB
Bash
147 lines
5.0 KiB
Bash
# Audiofy – gemeinsame Hilfsfunktionen für die Install-Skripte (wird per "source" geladen)
|
||
# shellcheck shell=bash
|
||
|
||
AUDIOFY_DIR="${AUDIOFY_DIR:-/opt/audiofy}"
|
||
AUDIOFY_USER="${AUDIOFY_USER:-audiofy}"
|
||
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
VORLAGEN="$REPO/install/vorlagen"
|
||
|
||
if [ "$(id -u)" -eq 0 ]; then
|
||
SUDO=""
|
||
ALS_DIENST=(runuser -u "$AUDIOFY_USER" --)
|
||
else
|
||
SUDO="sudo"
|
||
ALS_DIENST=(sudo -u "$AUDIOFY_USER")
|
||
fi
|
||
|
||
# --- Ausgabe ------------------------------------------------------------------
|
||
|
||
schritt() { printf '\n\033[1m==> %s\033[0m\n' "$*"; }
|
||
info() { printf ' %s\n' "$*"; }
|
||
warnung() { printf '\033[33m ! %s\033[0m\n' "$*"; }
|
||
abbruch() { printf '\033[31m ✗ %s\033[0m\n' "$*" >&2; exit 1; }
|
||
|
||
# --- Abfragen -----------------------------------------------------------------
|
||
|
||
# frage VAR "Text" [Standard]
|
||
frage() {
|
||
local antwort
|
||
read -r -p " $2${3:+ [$3]}: " antwort
|
||
printf -v "$1" '%s' "${antwort:-${3:-}}"
|
||
}
|
||
|
||
# frage_geheim VAR "Text" – Eingabe wird nicht angezeigt
|
||
frage_geheim() {
|
||
local antwort
|
||
read -r -s -p " $2: " antwort
|
||
echo
|
||
printf -v "$1" '%s' "$antwort"
|
||
}
|
||
|
||
# ja_nein "Frage" [j|n] – Rückgabewert 0 = ja
|
||
ja_nein() {
|
||
local standard="${2:-j}" antwort
|
||
read -r -p " $1 [$([ "$standard" = j ] && echo J/n || echo j/N)]: " antwort
|
||
antwort="${antwort:-$standard}"
|
||
[[ "$antwort" =~ ^[jJyY] ]]
|
||
}
|
||
|
||
# --- System -------------------------------------------------------------------
|
||
|
||
pruefe_system() {
|
||
[ -f /etc/debian_version ] || abbruch "Nur für Debian und Debian-basierte Systeme (z. B. Raspberry Pi OS)."
|
||
command -v systemctl >/dev/null || abbruch "systemd wird benötigt."
|
||
command -v apt-get >/dev/null || abbruch "apt wird benötigt."
|
||
if [ -n "$SUDO" ]; then
|
||
command -v sudo >/dev/null || abbruch "Bitte als root ausführen oder sudo installieren."
|
||
info "Für die Einrichtung sind root-Rechte nötig – sudo fragt ggf. nach deinem Passwort."
|
||
sudo true || abbruch "sudo nicht möglich."
|
||
fi
|
||
python3 -c 'import sys; sys.exit(sys.version_info < (3, 11))' 2>/dev/null \
|
||
|| abbruch "Python 3.11 oder neuer wird benötigt (Debian 12 oder neuer)."
|
||
}
|
||
|
||
pakete() {
|
||
info "Installiere: $*"
|
||
$SUDO env DEBIAN_FRONTEND=noninteractive apt-get install -y -qq "$@" >/dev/null
|
||
}
|
||
|
||
dienst_benutzer() {
|
||
if ! id "$AUDIOFY_USER" >/dev/null 2>&1; then
|
||
$SUDO useradd --system --home-dir "$AUDIOFY_DIR" --no-create-home --shell /usr/sbin/nologin "$AUDIOFY_USER"
|
||
info "Dienst-Benutzer '$AUDIOFY_USER' angelegt (ohne Login, ohne sudo)"
|
||
fi
|
||
}
|
||
|
||
# code_kopieren QUELLE ZIEL – ersetzt den Inhalt von ZIEL (gehört root, Dienst darf nur lesen)
|
||
code_kopieren() {
|
||
local quelle="$1" ziel="$2"
|
||
$SUDO install -d -o root -g "$AUDIOFY_USER" -m 750 "$ziel"
|
||
$SUDO find "$ziel" -mindepth 1 -delete
|
||
tar -C "$quelle" --exclude='__pycache__' --exclude='.venv' --exclude='.pytest_cache' --exclude='tests' -cf - . \
|
||
| $SUDO tar -C "$ziel" --no-same-owner -xf -
|
||
$SUDO chown -R root:"$AUDIOFY_USER" "$ziel"
|
||
$SUDO find "$ziel" -type d -exec chmod 750 {} +
|
||
$SUDO find "$ziel" -type f -exec chmod 640 {} +
|
||
}
|
||
|
||
# venv_einrichten REQUIREMENTS
|
||
venv_einrichten() {
|
||
local venv="$AUDIOFY_DIR/.venv"
|
||
[ -x "$venv/bin/python" ] || $SUDO python3 -m venv "$venv"
|
||
$SUDO "$venv/bin/pip" install -q --upgrade pip
|
||
$SUDO "$venv/bin/pip" install -q -r "$1"
|
||
$SUDO chown -R root:"$AUDIOFY_USER" "$venv"
|
||
$SUDO chmod -R g+rX,o-rwx "$venv"
|
||
}
|
||
|
||
# vorlage DATEI ZIEL – ersetzt {{NAME}} durch den Wert der Shell-Variablen NAME
|
||
vorlage() {
|
||
local inhalt
|
||
inhalt="$(python3 - "$VORLAGEN/$1" <<'PY'
|
||
import os, re, sys
|
||
text = open(sys.argv[1]).read()
|
||
def ersetzen(m):
|
||
wert = os.environ.get(m.group(1))
|
||
if wert is None:
|
||
sys.exit(f"Vorlage {sys.argv[1]}: {m.group(1)} ist nicht gesetzt")
|
||
return wert
|
||
print(re.sub(r"\{\{([A-Z_]+)\}\}", ersetzen, text), end="")
|
||
PY
|
||
)" || abbruch "Vorlage $1 konnte nicht ausgefüllt werden"
|
||
printf '%s\n' "$inhalt" | $SUDO tee "$2" >/dev/null
|
||
}
|
||
|
||
# --- .env (gehört dem Dienst-Benutzer, Rechte 600) ------------------------------
|
||
|
||
# env_setzen DATEI KEY WERT – der Wert läuft über stdin (nicht in der Prozessliste sichtbar)
|
||
env_setzen() {
|
||
printf '%s' "$3" | "${ALS_DIENST[@]}" python3 -c '
|
||
import os, sys
|
||
pfad, key = sys.argv[1], sys.argv[2]
|
||
wert = sys.stdin.read()
|
||
zeilen = open(pfad).read().splitlines() if os.path.exists(pfad) else []
|
||
zeilen = [z for z in zeilen if not z.strip().startswith(key + "=")] + [f"{key}={wert}"]
|
||
fd = os.open(pfad, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
|
||
with os.fdopen(fd, "w") as f:
|
||
f.write("\n".join(zeilen) + "\n")
|
||
' "$1" "$2"
|
||
}
|
||
|
||
# env_wert DATEI KEY – gibt den Wert aus (leer, wenn nicht vorhanden)
|
||
env_wert() {
|
||
"${ALS_DIENST[@]}" sh -c 'grep -m1 "^$2=" "$1" 2>/dev/null | cut -d= -f2-' _ "$1" "$2" || true
|
||
}
|
||
|
||
# env_umbenennen DATEI ALT NEU – übernimmt alte Schlüsselnamen (für Updates)
|
||
env_umbenennen() {
|
||
local wert
|
||
if [ -z "$(env_wert "$1" "$3")" ]; then
|
||
wert="$(env_wert "$1" "$2")"
|
||
if [ -n "$wert" ]; then
|
||
env_setzen "$1" "$3" "$wert"
|
||
info "$2 → $3 übernommen"
|
||
fi
|
||
fi
|
||
}
|