- 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>
233 lines
7.8 KiB
Bash
Executable File
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 "$@"
|