Files
teOS/install.sh
Flexomatic81 0e8d5aef85 feat: add Docker deployment, web installer, and local test environment
- Multi-stage Dockerfiles for API (NestJS) and Web (Next.js standalone)
- docker-compose.prod.yml: full production stack (postgres, redis, keycloak,
  api, web) with optional Caddy/Let's Encrypt via --profile ssl
- docker-compose.local.yml: identical local test stack, all ports exposed
- docker/postgres/init.sql: auto-creates tos_app DB on first start
- Caddyfile: reverse proxy for app domain + auth subdomain
- install.sh: interactive installer (domain, SSL mode, secret generation)
- NestJS SetupModule: @Public() endpoints for /setup/status, /setup/admin,
  /setup/branding, /setup/complete with setup-token guard
- Web installer: 4-step flow (system check, admin creation, branding, complete)
  at /[locale]/setup/* with public middleware bypass
- i18n: installer namespace added to de.json and en.json
- CORS: x-setup-token header allowed in main.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 21:17:34 +01:00

233 lines
7.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# =============================================================================
# tOS Installation Script
# =============================================================================
# Interaktiver Installationsassistent fuer das tOS Production Deployment.
#
# Ausfuehren mit: chmod +x install.sh && ./install.sh
#
# Das Script:
# 1. Prueft Voraussetzungen (Docker, Docker Compose, openssl)
# 2. Sammelt Konfiguration (Domain, SSL-Modus)
# 3. Generiert kryptographische Secrets
# 4. Startet alle Services
# 5. Wartet auf Bereitschaft und zeigt Setup-URL an
# =============================================================================
set -euo pipefail
# ANSI Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
BOLD='\033[1m'
NC='\033[0m' # No Color
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
banner() {
echo -e "${BLUE}${BOLD}"
echo " ████████╗ ██████╗ ███████╗"
echo " ██╔══╝██╔═══██╗██╔════╝"
echo " ██║ ██║ ██║███████╗"
echo " ██║ ██║ ██║╚════██║"
echo " ██║ ╚██████╔╝███████║"
echo " ╚═╝ ╚═════╝ ╚══════╝"
echo -e "${NC}"
echo -e "${BOLD} tOS - Enterprise Web Operating System${NC}"
echo -e " Installationsassistent"
echo ""
}
check_prerequisites() {
echo -e "${BOLD}[1/4] Voraussetzungen pruefen...${NC}"
if ! command -v docker &> /dev/null; then
echo -e "${RED}[FEHLER] Docker nicht gefunden. Bitte installiere Docker: https://docs.docker.com/get-docker/${NC}"
exit 1
fi
echo -e "${GREEN}[OK] Docker gefunden: $(docker --version)${NC}"
if ! docker compose version &> /dev/null; then
echo -e "${RED}[FEHLER] Docker Compose Plugin nicht gefunden. Bitte aktualisiere Docker.${NC}"
exit 1
fi
echo -e "${GREEN}[OK] Docker Compose gefunden: $(docker compose version --short)${NC}"
if ! command -v openssl &> /dev/null; then
echo -e "${RED}[FEHLER] openssl nicht gefunden. Bitte installiere openssl.${NC}"
exit 1
fi
echo -e "${GREEN}[OK] openssl gefunden${NC}"
echo ""
}
collect_configuration() {
echo -e "${BOLD}[2/4] Konfiguration${NC}"
echo -e "Domain fuer tOS (z.B. tos.meinefirma.de):"
read -r -p " Domain: " APP_DOMAIN
APP_DOMAIN="${APP_DOMAIN:-tos.example.com}"
echo ""
echo -e "SSL-Konfiguration:"
echo " [1] Let's Encrypt (Caddy - empfohlen fuer oeffentliche Domain)"
echo " [2] Externer Reverse Proxy (nginx, Apache, Cloudflare etc.)"
read -r -p " Auswahl [1/2]: " SSL_CHOICE
SSL_MODE="external"
LETSENCRYPT_EMAIL=""
if [[ "${SSL_CHOICE}" == "1" ]]; then
SSL_MODE="letsencrypt"
read -r -p " E-Mail fuer Let's Encrypt: " LETSENCRYPT_EMAIL
fi
echo ""
}
generate_secrets() {
echo -e "${BOLD}[3/4] Secrets generieren...${NC}"
POSTGRES_PASSWORD=$(openssl rand -hex 32)
JWT_SECRET=$(openssl rand -hex 32)
ENCRYPTION_KEY=$(openssl rand -hex 32)
NEXTAUTH_SECRET=$(openssl rand -hex 32)
KEYCLOAK_ADMIN_PASSWORD=$(openssl rand -base64 24 | tr -d '/+=' | head -c 32)
# Generate SETUP_TOKEN
if command -v uuidgen &> /dev/null; then
SETUP_TOKEN=$(uuidgen | tr '[:upper:]' '[:lower:]')
else
SETUP_TOKEN=$(cat /proc/sys/kernel/random/uuid 2>/dev/null || openssl rand -hex 16)
fi
echo -e "${GREEN}[OK] Secrets generiert${NC}"
echo ""
# docker/.env erstellen
cat > "${SCRIPT_DIR}/docker/.env" << EOF
# =============================================================================
# tOS Production Configuration - Generated by install.sh on $(date)
# =============================================================================
# ---- Domain -----------------------------------------------------------------
APP_DOMAIN=${APP_DOMAIN}
LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL}
# ---- PostgreSQL -------------------------------------------------------------
POSTGRES_USER=tos_user
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
POSTGRES_DB=tos_db
POSTGRES_PORT=5432
# ---- Redis ------------------------------------------------------------------
REDIS_PORT=6379
# ---- Keycloak ---------------------------------------------------------------
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=${KEYCLOAK_ADMIN_PASSWORD}
KEYCLOAK_PORT=8080
KEYCLOAK_REALM=tOS
# ---- Application Secrets ---------------------------------------------------
JWT_SECRET=${JWT_SECRET}
ENCRYPTION_KEY=${ENCRYPTION_KEY}
NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
# ---- Keycloak OAuth Clients ------------------------------------------------
KEYCLOAK_CLIENT_ID=tos-backend
KEYCLOAK_CLIENT_SECRET=
NEXTAUTH_KEYCLOAK_CLIENT_ID=tos-nextauth
NEXTAUTH_KEYCLOAK_CLIENT_SECRET=
# ---- Setup Token (nach Einrichtung entfernen) -------------------------------
SETUP_TOKEN=${SETUP_TOKEN}
EOF
echo -e "${GREEN}[OK] Konfigurationsdatei erstellt: docker/.env${NC}"
echo ""
}
start_services() {
echo -e "${BOLD}[4/4] Dienste starten...${NC}"
cd "${SCRIPT_DIR}/docker"
if [[ "${SSL_MODE}" == "letsencrypt" ]]; then
echo -e "${YELLOW}Starte alle Dienste mit Let's Encrypt (SSL)...${NC}"
docker compose -f docker-compose.prod.yml --profile ssl up -d --build
else
echo -e "${YELLOW}Starte alle Dienste ohne SSL (externer Reverse Proxy)...${NC}"
docker compose -f docker-compose.prod.yml up -d --build
echo -e "${YELLOW}Hinweis: Konfiguriere deinen Reverse Proxy fuer Ports 3000 (Web), 3001 (API) und 8080 (Keycloak).${NC}"
fi
echo ""
echo -e "${YELLOW}Warte auf API-Bereitschaft (max. 3 Minuten)...${NC}"
API_URL="http://localhost:3001/api/v1/health/liveness"
for i in $(seq 1 36); do
if curl -sf "${API_URL}" > /dev/null 2>&1; then
echo ""
echo -e "${GREEN}[OK] API ist bereit!${NC}"
break
fi
if [[ $i -eq 36 ]]; then
echo ""
echo -e "${RED}[WARNUNG] API hat nicht innerhalb von 3 Minuten geantwortet.${NC}"
echo -e " Pruefe die Logs mit: docker compose -f docker/docker-compose.prod.yml logs api"
echo -e " Die Dienste laufen moeglicherweise noch hoch. Pruefe den Status mit:"
echo -e " docker compose -f docker/docker-compose.prod.yml ps"
break
fi
echo -n "."
sleep 5
done
echo ""
cd "${SCRIPT_DIR}"
}
print_completion() {
local SETUP_URL
if [[ "${SSL_MODE}" == "letsencrypt" ]]; then
SETUP_URL="https://${APP_DOMAIN}/setup?token=${SETUP_TOKEN}"
else
SETUP_URL="http://localhost:3000/setup?token=${SETUP_TOKEN}"
fi
echo ""
echo -e "${GREEN}${BOLD}================================================================${NC}"
echo -e "${GREEN}${BOLD} Infrastruktur erfolgreich gestartet!${NC}"
echo -e "${GREEN}${BOLD}================================================================${NC}"
echo ""
echo -e " Fahre jetzt mit der Einrichtung im Browser fort:"
echo ""
echo -e " ${BOLD}${BLUE}${SETUP_URL}${NC}"
echo ""
echo -e "${YELLOW} WICHTIG: Notiere dir diesen Setup-Token:${NC}"
echo -e " ${BOLD}${SETUP_TOKEN}${NC}"
echo ""
echo -e " Der Token wird nur einmal angezeigt und ist in"
echo -e " docker/.env gespeichert (SETUP_TOKEN=...)."
echo ""
echo -e " Nach der Einrichtung kannst du SETUP_TOKEN aus"
echo -e " docker/.env entfernen."
echo ""
echo -e " Nuetzliche Befehle:"
echo -e " ${BOLD}docker compose -f docker/docker-compose.prod.yml logs -f${NC} # Alle Logs"
echo -e " ${BOLD}docker compose -f docker/docker-compose.prod.yml ps${NC} # Service Status"
echo -e " ${BOLD}docker compose -f docker/docker-compose.prod.yml down${NC} # Stoppen"
echo -e " ${BOLD}docker compose -f docker/docker-compose.prod.yml up -d${NC} # Starten"
echo ""
}
main() {
banner
check_prerequisites
collect_configuration
generate_secrets
start_services
print_completion
}
main "$@"