KI-AGENT: Ergänze geführtes Selfhost Setup
This commit is contained in:
469
scripts/selfhost-setup.sh
Executable file
469
scripts/selfhost-setup.sh
Executable file
@@ -0,0 +1,469 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ENV_FILE="$ROOT_DIR/.env"
|
||||
ENV_EXAMPLE="$ROOT_DIR/.env.example"
|
||||
COMPOSE_FILE="$ROOT_DIR/docker-compose.selfhost.yml"
|
||||
|
||||
MODE=""
|
||||
START_STACK="ask"
|
||||
FORCE="false"
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
FEDEO Selfhost Setup
|
||||
|
||||
Nutzung:
|
||||
bash scripts/selfhost-setup.sh
|
||||
bash scripts/selfhost-setup.sh --simple
|
||||
bash scripts/selfhost-setup.sh --advanced
|
||||
bash scripts/selfhost-setup.sh --simple --start
|
||||
|
||||
Optionen:
|
||||
--simple Kurzer Assistent mit lokalen Diensten und sicheren Defaults
|
||||
--advanced Fragt zusätzliche Integrationen wie SMTP, OpenAI und DATEV-nahes Umfeld ab
|
||||
--start Startet den Stack nach dem Schreiben der Konfiguration
|
||||
--no-start Schreibt nur Konfiguration und Verzeichnisse
|
||||
--force Überschreibt eine vorhandene .env ohne Rückfrage
|
||||
USAGE
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--simple)
|
||||
MODE="simple"
|
||||
;;
|
||||
--advanced)
|
||||
MODE="advanced"
|
||||
;;
|
||||
--start)
|
||||
START_STACK="yes"
|
||||
;;
|
||||
--no-start)
|
||||
START_STACK="no"
|
||||
;;
|
||||
--force)
|
||||
FORCE="true"
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unbekannte Option: $1" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
need_file() {
|
||||
if [[ ! -f "$1" ]]; then
|
||||
echo "Fehlt: $1" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
random_secret() {
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
openssl rand -base64 48 | tr -d '\n'
|
||||
else
|
||||
LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c 64
|
||||
fi
|
||||
}
|
||||
|
||||
env_quote() {
|
||||
local value="$1"
|
||||
value="${value//\'/\'\\\'\'}"
|
||||
printf "'%s'" "$value"
|
||||
}
|
||||
|
||||
prompt() {
|
||||
local label="$1"
|
||||
local default_value="${2:-}"
|
||||
local value
|
||||
|
||||
if [[ -n "$default_value" ]]; then
|
||||
read -r -p "$label [$default_value]: " value
|
||||
echo "${value:-$default_value}"
|
||||
else
|
||||
read -r -p "$label: " value
|
||||
echo "$value"
|
||||
fi
|
||||
}
|
||||
|
||||
prompt_required() {
|
||||
local label="$1"
|
||||
local default_value="${2:-}"
|
||||
local value
|
||||
|
||||
while true; do
|
||||
value="$(prompt "$label" "$default_value")"
|
||||
if [[ -n "$value" ]]; then
|
||||
echo "$value"
|
||||
return
|
||||
fi
|
||||
echo "Bitte einen Wert eintragen." >&2
|
||||
done
|
||||
}
|
||||
|
||||
prompt_secret() {
|
||||
local label="$1"
|
||||
local default_value="${2:-}"
|
||||
local value
|
||||
|
||||
if [[ -n "$default_value" ]]; then
|
||||
read -r -s -p "$label [vorbelegt, Enter übernimmt]: " value
|
||||
echo >&2
|
||||
echo "${value:-$default_value}"
|
||||
else
|
||||
read -r -s -p "$label: " value
|
||||
echo >&2
|
||||
echo "$value"
|
||||
fi
|
||||
}
|
||||
|
||||
yes_no() {
|
||||
local label="$1"
|
||||
local default_value="${2:-n}"
|
||||
local answer
|
||||
|
||||
while true; do
|
||||
read -r -p "$label [$default_value]: " answer
|
||||
answer="${answer:-$default_value}"
|
||||
case "$answer" in
|
||||
y|Y|j|J|yes|Yes|ja|Ja)
|
||||
return 0
|
||||
;;
|
||||
n|N|no|No|nein|Nein)
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
echo "Bitte ja oder nein eingeben."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
choose_mode() {
|
||||
if [[ -n "$MODE" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Setup-Modus"
|
||||
echo " 1) einfach - Domain, Admin, lokale Datenbank, MinIO, Matrix"
|
||||
echo " 2) advanced - zusätzlich SMTP, externe Schlüssel und optionale Dienste"
|
||||
echo
|
||||
|
||||
local choice
|
||||
while true; do
|
||||
read -r -p "Modus wählen [1]: " choice
|
||||
case "${choice:-1}" in
|
||||
1|einfach|simple)
|
||||
MODE="simple"
|
||||
return
|
||||
;;
|
||||
2|advanced|erweitert)
|
||||
MODE="advanced"
|
||||
return
|
||||
;;
|
||||
*)
|
||||
echo "Bitte 1 oder 2 wählen."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
print_structure_guide() {
|
||||
cat <<EOF
|
||||
|
||||
FEDEO Selfhost Setup
|
||||
|
||||
Dieses Script führt dich durch die lokale Betriebsstruktur:
|
||||
|
||||
$ROOT_DIR/
|
||||
docker-compose.selfhost.yml Docker Stack für FEDEO, Traefik, PostgreSQL, MinIO und Matrix
|
||||
.env Zielkonfiguration, wird von diesem Script geschrieben
|
||||
postgres/ persistente FEDEO-Datenbank
|
||||
minio/ lokaler S3-kompatibler Dateispeicher
|
||||
matrix/postgres/ persistente Synapse-Datenbank
|
||||
matrix/synapse/ generierte Synapse-Konfiguration und Matrix-Daten
|
||||
traefik/letsencrypt/ Let's-Encrypt-Zertifikate
|
||||
traefik/logs/ Traefik-Logs
|
||||
|
||||
Öffentliche Endpunkte auf einer Domain:
|
||||
|
||||
https://DOMAIN/ FEDEO Frontend
|
||||
https://DOMAIN/backend FEDEO API
|
||||
https://DOMAIN/_matrix Matrix Homeserver
|
||||
https://DOMAIN/.well-known Matrix Discovery
|
||||
https://DOMAIN/livekit/sfu LiveKit
|
||||
https://DOMAIN/livekit/jwt LiveKit JWT-Service
|
||||
https://DOMAIN/element Element Web
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
write_env() {
|
||||
local domain="$1"
|
||||
local contact_email="$2"
|
||||
local db_password="$3"
|
||||
local minio_password="$4"
|
||||
local cookie_secret="$5"
|
||||
local jwt_secret="$6"
|
||||
local encryption_key="$7"
|
||||
local m2m_key="$8"
|
||||
local admin_email="$9"
|
||||
local admin_password="${10}"
|
||||
local admin_first_name="${11}"
|
||||
local admin_last_name="${12}"
|
||||
local tenant_name="${13}"
|
||||
local tenant_short="${14}"
|
||||
local matrix_db_password="${15}"
|
||||
local matrix_turn_secret="${16}"
|
||||
local matrix_registration_secret="${17}"
|
||||
local livekit_secret="${18}"
|
||||
local mailer_host="${19}"
|
||||
local mailer_port="${20}"
|
||||
local mailer_ssl="${21}"
|
||||
local mailer_user="${22}"
|
||||
local mailer_pass="${23}"
|
||||
local mailer_from="${24}"
|
||||
local web_push_public="${25}"
|
||||
local web_push_private="${26}"
|
||||
local pdf_license="${27}"
|
||||
local openai_key="${28}"
|
||||
local stirling_key="${29}"
|
||||
local gocardless_secret_id="${30}"
|
||||
local gocardless_secret_key="${31}"
|
||||
local dokubox_host="${32}"
|
||||
local dokubox_port="${33}"
|
||||
local dokubox_secure="${34}"
|
||||
local dokubox_user="${35}"
|
||||
local dokubox_password="${36}"
|
||||
|
||||
cat >"$ENV_FILE" <<EOF
|
||||
# FEDEO Selfhosting
|
||||
DOMAIN=$(env_quote "$domain")
|
||||
CONTACT_EMAIL=$(env_quote "$contact_email")
|
||||
|
||||
DB_NAME=$(env_quote "fedeo")
|
||||
DB_USER=$(env_quote "fedeo")
|
||||
DB_PASSWORD=$(env_quote "$db_password")
|
||||
DATABASE_URL=$(env_quote "postgres://fedeo:$db_password@db:5432/fedeo")
|
||||
|
||||
MINIO_ROOT_USER=$(env_quote "fedeo-minio")
|
||||
MINIO_ROOT_PASSWORD=$(env_quote "$minio_password")
|
||||
MINIO_BUCKET=$(env_quote "fedeo")
|
||||
|
||||
HOST=$(env_quote "0.0.0.0")
|
||||
PORT=$(env_quote "3100")
|
||||
FEDEO_RUN_MIGRATIONS=$(env_quote "true")
|
||||
COOKIE_SECRET=$(env_quote "$cookie_secret")
|
||||
JWT_SECRET=$(env_quote "$jwt_secret")
|
||||
ENCRYPTION_KEY=$(env_quote "$encryption_key")
|
||||
|
||||
MAILER_SMTP_HOST=$(env_quote "$mailer_host")
|
||||
MAILER_SMTP_PORT=$(env_quote "$mailer_port")
|
||||
MAILER_SMTP_SSL=$(env_quote "$mailer_ssl")
|
||||
MAILER_SMTP_USER=$(env_quote "$mailer_user")
|
||||
MAILER_SMTP_PASS=$(env_quote "$mailer_pass")
|
||||
MAILER_FROM=$(env_quote "$mailer_from")
|
||||
|
||||
WEB_PUSH_PUBLIC_KEY=$(env_quote "$web_push_public")
|
||||
WEB_PUSH_PRIVATE_KEY=$(env_quote "$web_push_private")
|
||||
WEB_PUSH_SUBJECT=$(env_quote "mailto:$contact_email")
|
||||
|
||||
S3_ENDPOINT=$(env_quote "http://minio:9000")
|
||||
S3_REGION=$(env_quote "eu-central-1")
|
||||
S3_ACCESS_KEY=$(env_quote "fedeo-minio")
|
||||
S3_SECRET_KEY=$(env_quote "$minio_password")
|
||||
S3_BUCKET=$(env_quote "fedeo")
|
||||
|
||||
M2M_API_KEY=$(env_quote "$m2m_key")
|
||||
API_BASE_URL=$(env_quote "https://$domain/backend")
|
||||
GOCARDLESS_BASE_URL=$(env_quote "https://api.gocardless.com")
|
||||
GOCARDLESS_SECRET_ID=$(env_quote "$gocardless_secret_id")
|
||||
GOCARDLESS_SECRET_KEY=$(env_quote "$gocardless_secret_key")
|
||||
|
||||
DOKUBOX_IMAP_HOST=$(env_quote "$dokubox_host")
|
||||
DOKUBOX_IMAP_PORT=$(env_quote "$dokubox_port")
|
||||
DOKUBOX_IMAP_SECURE=$(env_quote "$dokubox_secure")
|
||||
DOKUBOX_IMAP_USER=$(env_quote "$dokubox_user")
|
||||
DOKUBOX_IMAP_PASSWORD=$(env_quote "$dokubox_password")
|
||||
|
||||
OPENAI_API_KEY=$(env_quote "$openai_key")
|
||||
STIRLING_API_KEY=$(env_quote "$stirling_key")
|
||||
NUXT_PUBLIC_PDF_LICENSE=$(env_quote "$pdf_license")
|
||||
|
||||
FEDEO_BOOTSTRAP_ADMIN_EMAIL=$(env_quote "$admin_email")
|
||||
FEDEO_BOOTSTRAP_ADMIN_PASSWORD=$(env_quote "$admin_password")
|
||||
FEDEO_BOOTSTRAP_ADMIN_FIRST_NAME=$(env_quote "$admin_first_name")
|
||||
FEDEO_BOOTSTRAP_ADMIN_LAST_NAME=$(env_quote "$admin_last_name")
|
||||
FEDEO_BOOTSTRAP_TENANT_NAME=$(env_quote "$tenant_name")
|
||||
FEDEO_BOOTSTRAP_TENANT_SHORT=$(env_quote "$tenant_short")
|
||||
|
||||
MATRIX_SERVER_NAME=$(env_quote "$domain")
|
||||
MATRIX_POSTGRES_DB=$(env_quote "synapse")
|
||||
MATRIX_POSTGRES_USER=$(env_quote "synapse")
|
||||
MATRIX_POSTGRES_PASSWORD=$(env_quote "$matrix_db_password")
|
||||
MATRIX_TURN_SHARED_SECRET=$(env_quote "$matrix_turn_secret")
|
||||
LIVEKIT_KEY=$(env_quote "fedeo-livekit")
|
||||
LIVEKIT_SECRET=$(env_quote "$livekit_secret")
|
||||
MATRIX_HOMESERVER_URL=$(env_quote "http://matrix-synapse:8008")
|
||||
MATRIX_RTC_HOST=$(env_quote "$domain")
|
||||
MATRIX_RTC_JWT_URL=$(env_quote "https://$domain/livekit/jwt")
|
||||
MATRIX_LIVEKIT_URL=$(env_quote "wss://$domain/livekit/sfu")
|
||||
MATRIX_REGISTRATION_SHARED_SECRET=$(env_quote "$matrix_registration_secret")
|
||||
MATRIX_SERVICE_USER_LOCALPART=$(env_quote "fedeo_service")
|
||||
NUXT_PUBLIC_MATRIX_ELEMENT_URL=$(env_quote "https://$domain/element")
|
||||
EOF
|
||||
}
|
||||
|
||||
prepare_directories() {
|
||||
mkdir -p \
|
||||
"$ROOT_DIR/traefik/letsencrypt" \
|
||||
"$ROOT_DIR/traefik/logs" \
|
||||
"$ROOT_DIR/postgres" \
|
||||
"$ROOT_DIR/minio" \
|
||||
"$ROOT_DIR/matrix/postgres" \
|
||||
"$ROOT_DIR/matrix/synapse"
|
||||
|
||||
touch "$ROOT_DIR/traefik/letsencrypt/acme.json"
|
||||
chmod 600 "$ROOT_DIR/traefik/letsencrypt/acme.json"
|
||||
}
|
||||
|
||||
main() {
|
||||
need_file "$COMPOSE_FILE"
|
||||
need_file "$ENV_EXAMPLE"
|
||||
|
||||
print_structure_guide
|
||||
choose_mode
|
||||
|
||||
if [[ -f "$ENV_FILE" && "$FORCE" != "true" ]]; then
|
||||
if yes_no ".env existiert bereits. Backup erstellen und überschreiben?" "n"; then
|
||||
cp "$ENV_FILE" "$ENV_FILE.$(date +%Y%m%d-%H%M%S).bak"
|
||||
else
|
||||
echo "Abgebrochen, .env bleibt unverändert."
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Basis"
|
||||
local domain contact_email admin_email admin_password admin_first_name admin_last_name tenant_name tenant_short
|
||||
domain="$(prompt_required "Domain für FEDEO" "app.example.com")"
|
||||
contact_email="$(prompt_required "Kontakt-E-Mail für Let's Encrypt" "admin@example.com")"
|
||||
admin_email="$(prompt_required "Erster Admin-Login" "$contact_email")"
|
||||
admin_password="$(prompt_secret "Erstes Admin-Passwort" "$(random_secret)")"
|
||||
admin_first_name="$(prompt_required "Admin-Vorname" "Admin")"
|
||||
admin_last_name="$(prompt_required "Admin-Nachname" "Benutzer")"
|
||||
tenant_name="$(prompt_required "Name des ersten Mandanten" "Mein Unternehmen")"
|
||||
tenant_short="$(prompt_required "Kurzname des ersten Mandanten" "MEIN")"
|
||||
|
||||
echo
|
||||
echo "Secrets werden automatisch erzeugt."
|
||||
local db_password minio_password cookie_secret jwt_secret encryption_key m2m_key
|
||||
local matrix_db_password matrix_turn_secret matrix_registration_secret livekit_secret
|
||||
db_password="$(random_secret)"
|
||||
minio_password="$(random_secret)"
|
||||
cookie_secret="$(random_secret)"
|
||||
jwt_secret="$(random_secret)"
|
||||
encryption_key="$(random_secret)"
|
||||
m2m_key="$(random_secret)"
|
||||
matrix_db_password="$(random_secret)"
|
||||
matrix_turn_secret="$(random_secret)"
|
||||
matrix_registration_secret="$(random_secret)"
|
||||
livekit_secret="$(random_secret)"
|
||||
|
||||
local mailer_host="smtp.example.com"
|
||||
local mailer_port="587"
|
||||
local mailer_ssl="false"
|
||||
local mailer_user="mailer@example.com"
|
||||
local mailer_pass="change-this-mail-password"
|
||||
local mailer_from="FEDEO <no-reply@example.com>"
|
||||
local web_push_public="replace-this-web-push-public-key"
|
||||
local web_push_private="replace-this-web-push-private-key"
|
||||
local pdf_license="replace-with-your-pdf-license"
|
||||
local openai_key="replace-this"
|
||||
local stirling_key="replace-this"
|
||||
local gocardless_secret_id="replace-this"
|
||||
local gocardless_secret_key="replace-this"
|
||||
local dokubox_host="imap.example.com"
|
||||
local dokubox_port="993"
|
||||
local dokubox_secure="true"
|
||||
local dokubox_user="dokubox@example.com"
|
||||
local dokubox_password="change-this-imap-password"
|
||||
|
||||
if [[ "$MODE" == "advanced" ]]; then
|
||||
echo
|
||||
echo "Advanced: SMTP"
|
||||
mailer_host="$(prompt "SMTP Host" "$mailer_host")"
|
||||
mailer_port="$(prompt "SMTP Port" "$mailer_port")"
|
||||
mailer_ssl="$(prompt "SMTP SSL true/false" "$mailer_ssl")"
|
||||
mailer_user="$(prompt "SMTP Benutzer" "$mailer_user")"
|
||||
mailer_pass="$(prompt_secret "SMTP Passwort" "$mailer_pass")"
|
||||
mailer_from="$(prompt "Absender" "FEDEO <no-reply@$domain>")"
|
||||
|
||||
echo
|
||||
echo "Advanced: optionale Schlüssel"
|
||||
pdf_license="$(prompt "PDF-Lizenz" "$pdf_license")"
|
||||
openai_key="$(prompt_secret "OpenAI API Key" "$openai_key")"
|
||||
stirling_key="$(prompt_secret "Stirling API Key" "$stirling_key")"
|
||||
web_push_public="$(prompt "Web Push Public Key" "$web_push_public")"
|
||||
web_push_private="$(prompt_secret "Web Push Private Key" "$web_push_private")"
|
||||
|
||||
echo
|
||||
echo "Advanced: Banking und Dokubox"
|
||||
gocardless_secret_id="$(prompt "GoCardless Secret ID" "$gocardless_secret_id")"
|
||||
gocardless_secret_key="$(prompt_secret "GoCardless Secret Key" "$gocardless_secret_key")"
|
||||
dokubox_host="$(prompt "Dokubox IMAP Host" "$dokubox_host")"
|
||||
dokubox_port="$(prompt "Dokubox IMAP Port" "$dokubox_port")"
|
||||
dokubox_secure="$(prompt "Dokubox IMAP Secure true/false" "$dokubox_secure")"
|
||||
dokubox_user="$(prompt "Dokubox IMAP Benutzer" "$dokubox_user")"
|
||||
dokubox_password="$(prompt_secret "Dokubox IMAP Passwort" "$dokubox_password")"
|
||||
fi
|
||||
|
||||
write_env \
|
||||
"$domain" "$contact_email" "$db_password" "$minio_password" \
|
||||
"$cookie_secret" "$jwt_secret" "$encryption_key" "$m2m_key" \
|
||||
"$admin_email" "$admin_password" "$admin_first_name" "$admin_last_name" \
|
||||
"$tenant_name" "$tenant_short" "$matrix_db_password" "$matrix_turn_secret" \
|
||||
"$matrix_registration_secret" "$livekit_secret" "$mailer_host" "$mailer_port" \
|
||||
"$mailer_ssl" "$mailer_user" "$mailer_pass" "$mailer_from" "$web_push_public" \
|
||||
"$web_push_private" "$pdf_license" "$openai_key" "$stirling_key" \
|
||||
"$gocardless_secret_id" "$gocardless_secret_key" "$dokubox_host" \
|
||||
"$dokubox_port" "$dokubox_secure" "$dokubox_user" "$dokubox_password"
|
||||
|
||||
prepare_directories
|
||||
|
||||
echo
|
||||
echo "Konfiguration geschrieben:"
|
||||
echo " $ENV_FILE"
|
||||
echo
|
||||
echo "Vor dem Start prüfen:"
|
||||
echo " 1. DNS zeigt auf diesen Server: $domain"
|
||||
echo " 2. Ports 80, 443, 3478/tcp, 3478/udp, 5349/tcp, 49160-49200/udp sind offen"
|
||||
echo " 3. Platzhalter für optionale Dienste in .env bei Bedarf ersetzen"
|
||||
|
||||
if [[ "$START_STACK" == "ask" ]]; then
|
||||
if yes_no "Stack jetzt mit Docker Compose starten?" "n"; then
|
||||
START_STACK="yes"
|
||||
else
|
||||
START_STACK="no"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$START_STACK" == "yes" ]]; then
|
||||
docker compose -f "$COMPOSE_FILE" up -d
|
||||
else
|
||||
echo
|
||||
echo "Start später mit:"
|
||||
echo " docker compose -f docker-compose.selfhost.yml up -d"
|
||||
fi
|
||||
}
|
||||
|
||||
main
|
||||
Reference in New Issue
Block a user