feat(deploy): Installations-Skript + Anleitung für natives YunoHost-SSO (jamulix.net)
Alternative zum Authelia-Target (voice.jamulix.de): eigenständiges Ein-Maschinen-Setup, das YunoHosts Portal-Cookie (yunohost.portal) direkt nutzt statt Authelia. deploy/install_va_on_yunohost.sh automatisiert Code- Deploy, venv, systemd-Service, YunoHost-Domain und nginx-Reverse-Proxy inkl. WebSocket-Support; INSTALLATION_AUF_YUNOHOST_SERVER.md dokumentiert Ablauf, Sicherheitsmodell und offene manuelle Schritte. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
55d07187bb
commit
6ab24977f1
2 changed files with 336 additions and 0 deletions
195
deploy/install_va_on_yunohost.sh
Executable file
195
deploy/install_va_on_yunohost.sh
Executable file
|
|
@ -0,0 +1,195 @@
|
|||
#!/usr/bin/env bash
|
||||
# Installiert den Voice-Assistant-Gateway auf einem YunoHost-Server und bindet
|
||||
# die Identitaet ueber YunoHosts eigenes Portal-SSO an (Cookie "yunohost.portal"),
|
||||
# analog zur bereits produktiven va.linix.de-Installation (siehe deploy/README.md),
|
||||
# aber als Ein-Maschinen-Variante OHNE Authelia - nur natives YunoHost-Login.
|
||||
#
|
||||
# Voraussetzungen (manuell, VOR dem Ausfuehren):
|
||||
# - SSH-Login auf dem YunoHost-Server als root (oder sudo-faehiger User).
|
||||
# - DNS: A-Record der Subdomain zeigt bereits auf die Server-IP.
|
||||
# - Du kennst deinen YunoHost-Benutzernamen (Authelia/LDAP-Konto in YunoHost),
|
||||
# z. B. "dschlueter".
|
||||
#
|
||||
# Aufruf:
|
||||
# sudo bash install_va_on_yunohost.sh <subdomain> <yunohost-user[,user2,...]>
|
||||
#
|
||||
# Beispiel:
|
||||
# sudo bash install_va_on_yunohost.sh voice.jamulix.net dschlueter
|
||||
#
|
||||
# Nach dem Lauf noch manuell noetig (siehe Ausgabe am Ende):
|
||||
# - Zertifikat: yunohost domain cert-install <subdomain>
|
||||
# - OPENROUTER_API_KEY nachtragen, falls beim Lauf leer gelassen.
|
||||
# - Einmal ueber https://<domain>/yunohost/sso/ einloggen, dann die App oeffnen.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DOMAIN="${1:?Nutzung: $0 <subdomain, z.B. voice.jamulix.net> <yunohost-user[,user2,...]>}"
|
||||
ADMIN_USERS="${2:?Nutzung: $0 <subdomain> <yunohost-user[,user2,...]>}"
|
||||
|
||||
REPO_URL="https://kitux.de/forgejo/dschlueter/my_voice_assistant_v3_jamulix.git"
|
||||
APP_DIR="/opt/voice-assistant"
|
||||
APP_USER="voice"
|
||||
PORT="8003"
|
||||
ENV_DIR="/etc/voice-assistant"
|
||||
ENV_FILE="$ENV_DIR/voice-assistant.env"
|
||||
DB_DIR="/var/lib/voice-assistant"
|
||||
COOKIE_SECRET_FILE="/etc/yunohost/.ssowat_cookie_secret"
|
||||
|
||||
# --- Vorbedingungen ----------------------------------------------------------
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "Bitte als root ausfuehren (sudo bash $0 ...)" >&2
|
||||
exit 1
|
||||
fi
|
||||
command -v yunohost >/dev/null 2>&1 || {
|
||||
echo "yunohost-CLI nicht gefunden - laeuft das hier wirklich auf dem YunoHost-Server?" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "== 1/8: System-Pakete =="
|
||||
apt-get update -qq
|
||||
apt-get install -y git python3-venv python3-pip python3-dev build-essential >/dev/null
|
||||
|
||||
PYVER="$(python3 -c 'import sys; print("%d.%d" % sys.version_info[:2])')"
|
||||
if [ "$(printf '%s\n' "3.11" "$PYVER" | sort -V | head -n1)" != "3.11" ]; then
|
||||
echo "WARNUNG: python3 ist $PYVER, das Projekt braucht >=3.11." >&2
|
||||
echo " Pruefe 'apt list -a python3.11' bzw. dein Debian-Release; danach das venv" >&2
|
||||
echo " in Schritt 4 manuell mit 'python3.11 -m venv .venv' anlegen." >&2
|
||||
fi
|
||||
|
||||
echo "== 2/8: System-User '$APP_USER' =="
|
||||
id -u "$APP_USER" >/dev/null 2>&1 || useradd --system --create-home --shell /usr/sbin/nologin "$APP_USER"
|
||||
|
||||
echo "== 3/8: Code holen ($REPO_URL) =="
|
||||
if [ -d "$APP_DIR/.git" ]; then
|
||||
sudo -u "$APP_USER" git -C "$APP_DIR" pull --ff-only
|
||||
else
|
||||
mkdir -p "$APP_DIR"
|
||||
chown "$APP_USER:$APP_USER" "$APP_DIR"
|
||||
sudo -u "$APP_USER" git clone "$REPO_URL" "$APP_DIR"
|
||||
fi
|
||||
|
||||
echo "== 4/8: Python-venv + Abhaengigkeiten (Cloud-Profil, kein lokales STT/TTS) =="
|
||||
VENV_SETUP=/tmp/va_venv_setup.sh
|
||||
cat > "$VENV_SETUP" <<'INNER'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$1"
|
||||
python3 -m venv .venv
|
||||
.venv/bin/pip install -U pip
|
||||
.venv/bin/pip install -e '.'
|
||||
INNER
|
||||
chmod +x "$VENV_SETUP"
|
||||
sudo -u "$APP_USER" "$VENV_SETUP" "$APP_DIR"
|
||||
rm -f "$VENV_SETUP"
|
||||
|
||||
echo "== 5/8: Konfiguration ($ENV_FILE) =="
|
||||
mkdir -p "$ENV_DIR" "$DB_DIR"
|
||||
chown "$APP_USER:$APP_USER" "$DB_DIR"
|
||||
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
ADMIN_API_KEY="$(openssl rand -hex 32)"
|
||||
read -srp "OpenRouter API Key (OPENROUTER_API_KEY, Enter = spaeter manuell nachtragen): " OPENROUTER_API_KEY
|
||||
echo
|
||||
COOKIE_SECRET=""
|
||||
if [ -r "$COOKIE_SECRET_FILE" ]; then
|
||||
COOKIE_SECRET="$(cat "$COOKIE_SECRET_FILE")"
|
||||
else
|
||||
echo " WARNUNG: $COOKIE_SECRET_FILE nicht lesbar - TRUSTED_AUTH_JWT_SECRET bleibt leer" >&2
|
||||
echo " (Cookie wird dann ungeprueft gelesen, siehe deploy/README.md Abschnitt 4)." >&2
|
||||
fi
|
||||
cat > "$ENV_FILE" <<EOF
|
||||
# Erzeugt von install_va_on_yunohost.sh am $(date -Is)
|
||||
HOST=127.0.0.1
|
||||
PORT=$PORT
|
||||
OPENROUTER_API_KEY=$OPENROUTER_API_KEY
|
||||
VA_PROFILE=cloud
|
||||
AUTH_ENABLED=true
|
||||
ADMIN_API_KEY=$ADMIN_API_KEY
|
||||
|
||||
# Identitaet: natives YunoHost-Portal-SSO (Cookie "yunohost.portal"), KEIN Authelia.
|
||||
# nginx und Gateway laufen auf derselben Maschine -> Quell-IP ist immer 127.0.0.1.
|
||||
TRUSTED_AUTH_COOKIE=yunohost.portal
|
||||
TRUSTED_AUTH_COOKIE_CLAIM=user
|
||||
TRUSTED_AUTH_JWT_SECRET=$COOKIE_SECRET
|
||||
TRUSTED_PROXY_IPS=127.0.0.1
|
||||
ADMIN_USERS=$ADMIN_USERS
|
||||
SSO_LOGOUT_URL=https://$DOMAIN/yunohost/sso/?action=logout
|
||||
|
||||
DB_PATH=$DB_DIR/voice-assistant.db
|
||||
EOF
|
||||
chown root:"$APP_USER" "$ENV_FILE"
|
||||
chmod 640 "$ENV_FILE"
|
||||
else
|
||||
echo " $ENV_FILE existiert bereits - unveraendert gelassen."
|
||||
fi
|
||||
|
||||
echo "== 6/8: systemd-Service =="
|
||||
cat > /etc/systemd/system/voice-assistant.service <<EOF
|
||||
[Unit]
|
||||
Description=Voice Assistant Gateway
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=$APP_USER
|
||||
WorkingDirectory=$APP_DIR
|
||||
EnvironmentFile=$ENV_FILE
|
||||
ExecStart=$APP_DIR/.venv/bin/uvicorn app.main:app --host \${HOST} --port \${PORT}
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now voice-assistant.service
|
||||
|
||||
echo "== 7/8: YunoHost-Domain '$DOMAIN' =="
|
||||
if yunohost domain list 2>/dev/null | grep -qxF " - $DOMAIN"; then
|
||||
echo " Domain ist bereits registriert."
|
||||
else
|
||||
yunohost domain add "$DOMAIN"
|
||||
fi
|
||||
|
||||
echo "== 8/8: nginx-Reverse-Proxy =="
|
||||
mkdir -p "/etc/nginx/conf.d/$DOMAIN.d"
|
||||
if [ ! -f /etc/nginx/conf.d/websocket-upgrade.conf ]; then
|
||||
cat > /etc/nginx/conf.d/websocket-upgrade.conf <<'EOF'
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
cat > "/etc/nginx/conf.d/$DOMAIN.d/voice-assistant.conf" <<EOF
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:$PORT;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
|
||||
# WebSocket-Upgrade (zwingend fuer /ws/voice, /ws/chat - Mikrofon-Button):
|
||||
proxy_set_header Upgrade \$http_upgrade;
|
||||
proxy_set_header Connection \$connection_upgrade;
|
||||
proxy_read_timeout 3600s;
|
||||
proxy_send_timeout 3600s;
|
||||
|
||||
# Identitaet: KEIN eigener Header noetig, das Cookie "yunohost.portal" wird
|
||||
# ohnehin durchgereicht; das Gateway liest/prueft es selbst (siehe .env).
|
||||
}
|
||||
EOF
|
||||
nginx -t && systemctl reload nginx
|
||||
|
||||
echo
|
||||
echo "=================================================================="
|
||||
echo "Fertig. Manuell noch offen:"
|
||||
echo " 1) DNS pruefen: dig +short $DOMAIN (muss auf diese Server-IP zeigen)"
|
||||
echo " 2) Zertifikat: yunohost domain cert-install $DOMAIN"
|
||||
echo " 3) Login testen: https://$DOMAIN/yunohost/sso/ (einloggen als $ADMIN_USERS),"
|
||||
echo " danach https://$DOMAIN/ oeffnen - sollte dich erkennen."
|
||||
echo " 4) Falls OPENROUTER_API_KEY leer gelassen wurde: in $ENV_FILE nachtragen,"
|
||||
echo " dann: systemctl restart voice-assistant"
|
||||
echo " Logs: journalctl -u voice-assistant -f"
|
||||
echo "=================================================================="
|
||||
Loading…
Add table
Add a link
Reference in a new issue