diff --git a/.env.example b/.env.example index 3563d0b..97a22aa 100644 --- a/.env.example +++ b/.env.example @@ -31,7 +31,7 @@ TRAEFIK_CERTRESOLVER=lets-encrypt TRAEFIK_ENTRYPOINT=websecure # ============================================================ -# DOCKER - Normalerweise keine Aenderung noetig +# DOCKER - Template-Konfiguration (Dynamisches System) # ============================================================ # Docker Socket Pfad (Standard fuer Linux) @@ -39,18 +39,25 @@ TRAEFIK_ENTRYPOINT=websecure # Fuer TCP: tcp://localhost:2375 DOCKER_HOST=unix:///var/run/docker.sock -# Docker-Image fuer User-Container (LEGACY - wird noch verwendet) -# Verfuegbare Templates: -# - user-service-template:latest (nginx, einfache Willkommensseite) -# - user-template-next:latest (Next.js, moderne React-App) -USER_TEMPLATE_IMAGE=user-service-template:latest +# LEGACY Variable (wird noch für alte spawn_container() verwendet) +# Nutze diese nur für Fallback, nutze stattdessen USER_TEMPLATE_IMAGES +USER_TEMPLATE_IMAGE=user-template-01:latest -# Multi-Container Templates (MVP) -# Development Container Image -USER_TEMPLATE_IMAGE_DEV=user-service-template:latest - -# Production Container Image (Next.js mit Shadcn/UI) -USER_TEMPLATE_IMAGE_PROD=user-template-next:latest +# Dynamische Template-Liste (Semikolon-getrennt) +# Format: image1:tag;image2:tag;image3:tag +# Die Template-Metadaten (Namen, Beschreibungen) werden aus templates.json geladen +# +# Beispiele: +# - user-template-01:latest (Nginx Basic) +# - user-template-02:latest (Nginx Advanced) +# - user-template-next:latest (Next.js Production) +# +# WICHTIG: +# - Alle Images müssen gebaut sein (siehe install.sh) +# - Für Metadaten (Display-Namen, Beschreibungen) siehe templates.json +# - Container-Typen werden automatisch aus Image-Namen extrahiert +# Beispiel: user-template-01:latest → Typ: template-01 +USER_TEMPLATE_IMAGES=user-template-01:latest;user-template-02:latest;user-template-next:latest # ============================================================ # RESSOURCEN - Container-Limits diff --git a/config.py b/config.py index a2c590d..e95c7f4 100644 --- a/config.py +++ b/config.py @@ -1,4 +1,6 @@ import os +import json +from pathlib import Path from dotenv import load_dotenv load_dotenv() @@ -35,24 +37,97 @@ class Config: SQLALCHEMY_TRACK_MODIFICATIONS = False # ======================================== - # Docker-Konfiguration + # Docker-Konfiguration - DYNAMISCHES TEMPLATE-SYSTEM # ======================================== DOCKER_SOCKET = os.getenv('DOCKER_SOCKET', 'unix://var/run/docker.sock') - USER_TEMPLATE_IMAGE = os.getenv('USER_TEMPLATE_IMAGE', 'user-service-template:latest') - # Multi-Container Templates - CONTAINER_TEMPLATES = { - 'dev': { - 'image': os.getenv('USER_TEMPLATE_IMAGE_DEV', 'user-service-template:latest'), - 'display_name': 'Development Container', - 'description': 'Nginx-basierter Development Container' - }, - 'prod': { - 'image': os.getenv('USER_TEMPLATE_IMAGE_PROD', 'user-template-next:latest'), - 'display_name': 'Production Container', - 'description': 'Next.js Production Build' - } - } + # LEGACY: Wird noch für alte spawn_container() verwendet + USER_TEMPLATE_IMAGE = os.getenv('USER_TEMPLATE_IMAGE', 'user-template-01:latest') + + # ======================================== + # Dynamisches Template-Loading + # ======================================== + + @staticmethod + def _extract_type_from_image(image_name: str) -> str: + """ + Extrahiert Container-Typ aus Image-Namen + + Examples: + 'user-template-01:latest' → 'template-01' + 'user-template-next:latest' → 'template-next' + 'custom-nginx:v1.0' → 'custom-nginx' + """ + # Entferne Tag (:latest, :v1.0, etc.) + base_name = image_name.split(':')[0] + + # Entferne 'user-' Prefix falls vorhanden + if base_name.startswith('user-'): + base_name = base_name[5:] # 'user-template-01' → 'template-01' + + return base_name + + @staticmethod + def _load_template_images() -> list: + """Lädt Template-Image-Liste aus USER_TEMPLATE_IMAGES (semikolon-getrennt)""" + raw_images = os.getenv('USER_TEMPLATE_IMAGES', '') + if not raw_images: + # Fallback für Kompatibilität + return ['user-template-01:latest'] + + return [img.strip() for img in raw_images.split(';') if img.strip()] + + @staticmethod + def _load_templates_config() -> dict: + """Lädt Template-Konfiguration aus templates.json""" + config_path = Path(__file__).parent / 'templates.json' + + if not config_path.exists(): + return {} + + try: + with open(config_path, 'r', encoding='utf-8') as f: + data = json.load(f) + + # Konvertiere Array zu Dictionary (key=type) + return { + template['type']: template + for template in data.get('templates', []) + } + except (json.JSONDecodeError, KeyError) as e: + import sys + print(f"[CONFIG] Warnung: Fehler beim Laden von templates.json: {e}", file=sys.stderr) + return {} + + @staticmethod + def _build_container_templates() -> dict: + """ + Baut CONTAINER_TEMPLATES Dictionary aus: + 1. TEMPLATE_IMAGES (Liste der verfügbaren Images) + 2. TEMPLATES_CONFIG (Metadaten aus templates.json) + """ + templates = {} + + for image in Config.TEMPLATE_IMAGES: + # Extrahiere Typ aus Image-Namen + container_type = Config._extract_type_from_image(image) + + # Hole Metadaten aus JSON (falls vorhanden) + config = Config.TEMPLATES_CONFIG.get(container_type, {}) + + # Verwende JSON-Metadaten oder Fallback + templates[container_type] = { + 'image': image, + 'display_name': config.get('display_name', container_type.replace('-', ' ').title()), + 'description': config.get('description', f'Container basierend auf {image}') + } + + return templates + + # Dynamisches Template-Loading initialisieren + TEMPLATE_IMAGES = _load_template_images.__func__() + TEMPLATES_CONFIG = _load_templates_config.__func__() + CONTAINER_TEMPLATES = _build_container_templates.__func__() # ======================================== # Traefik/Domain-Konfiguration diff --git a/MVP_DEPLOYMENT_GUIDE.md b/docs/install/DEPLOYMENT_GUIDE.md similarity index 86% rename from MVP_DEPLOYMENT_GUIDE.md rename to docs/install/DEPLOYMENT_GUIDE.md index 69ad1ca..aa7b009 100644 --- a/MVP_DEPLOYMENT_GUIDE.md +++ b/docs/install/DEPLOYMENT_GUIDE.md @@ -1,12 +1,17 @@ -# Multi-Container MVP - Deployment Guide +# Container Spawner - Deployment Guide ## 🎯 Überblick -Das Multi-Container MVP implementiert Unterstützung für 2 Container-Typen pro User: -- **Development (dev)**: Nginx mit einfacher Willkommensseite -- **Production (prod)**: Next.js mit Shadcn/UI +Das System unterstützt **beliebig viele User-Templates** über ein dynamisches Konfigurationssystem: +- Templates werden in `.env` definiert (semikolon-getrennt) +- Metadaten (Namen, Beschreibungen) kommen aus `templates.json` +- `install.sh` baut automatisch alle `user-template-*` Verzeichnisse +- Jeder Benutzer kann beliebig viele Container verschiedener Typen erstellen -Jeder Benutzer kann beide Container independent verwalten über das Dashboard. +**Standardtemplates (können beliebig erweitert werden):** +- **template-01**: Nginx Basic - Einfacher Nginx-Server mit statischen Dateien +- **template-02**: Nginx Advanced - Nginx mit erweiterten Features +- **template-next**: Next.js Production - React-App mit Shadcn/UI --- @@ -40,23 +45,22 @@ rm -f spawner.db rm -rf logs/* ``` -#### 1.3 Template-Images bauen +#### 1.3 Templates werden automatisch gebaut! ```bash -# Development Template (Nginx) -docker build -t user-service-template:latest user-template/ +# install.sh erkennt AUTOMATISCH alle user-template-* Verzeichnisse und baut sie: +# - user-template-01/ +# - user-template-02/ +# - user-template-next/ +# etc. -# Production Template (Next.js) -docker build -t user-template-next:latest user-template-next/ +# Überprüfung der verfügbaren Template-Verzeichnisse: +ls -d user-template* -# Überprüfung -docker images | grep user- +# Expected output: +# user-template-01 user-template-02 user-template-next ``` -**Erwartet Output:** -``` -user-service-template latest abc123... 5 minutes ago 100MB -user-template-next latest def456... 3 minutes ago 250MB -``` +**WICHTIG:** Das `install.sh`-Script baut automatisch alle Templates - **keine manuellen Docker-Builds nötig**! #### 1.4 Environment konfigurieren ```bash @@ -69,7 +73,7 @@ nano .env **Erforderliche Änderungen in .env:** ```bash -# Neue Zeilen hinzufügen oder bestehende aktualisieren: +# Pflichtfelder SECRET_KEY= BASE_DOMAIN=yourdomain.com SPAWNER_SUBDOMAIN=coder @@ -77,9 +81,10 @@ TRAEFIK_NETWORK=web TRAEFIK_CERTRESOLVER=lets-encrypt TRAEFIK_ENTRYPOINT=websecure -# Multi-Container Templates (NEU!) -USER_TEMPLATE_IMAGE_DEV=user-service-template:latest -USER_TEMPLATE_IMAGE_PROD=user-template-next:latest +# Dynamische Template-Konfiguration (Semikolon-getrennt) +# Liste aller verfügbaren Container-Images +# Metadaten (Namen, Beschreibungen) werden aus templates.json geladen +USER_TEMPLATE_IMAGES=user-template-01:latest;user-template-02:latest;user-template-next:latest # Optional: SMTP für Magic Links SMTP_HOST=smtp.example.com @@ -90,6 +95,8 @@ SMTP_FROM=noreply@example.com FRONTEND_URL=https://coder.yourdomain.com ``` +**Hinweis:** Behalte alle anderen Einstellungen aus `.env.example` (CORS, JWT, etc.) + ### Phase 2: Services starten (10 Minuten) #### 2.1 Docker Compose starten @@ -153,8 +160,10 @@ https://coder.yourdomain.com 6. Du wirst zum Dashboard weitergeleitet #### 3.3 Dashboard überprüfen -- [ ] 2 Container-Cards sichtbar (Dev und Prod) -- [ ] Beide haben Status "Noch nicht erstellt" +- [ ] N Container-Cards sichtbar (entsprechend USER_TEMPLATE_IMAGES) + - Standardmäßig: template-01, template-02, template-next +- [ ] Alle haben Status "Noch nicht erstellt" +- [ ] Jede Card zeigt Display-Namen und Beschreibung aus `templates.json` - [ ] Buttons zeigen "Erstellen & Öffnen" ### Phase 4: Teste beide Container (10 Minuten) @@ -506,5 +515,5 @@ EOF **Version**: 1.0.0 (MVP) **Deployment Date**: 2025-01-31 -**Last Updated**: 2025-01-31 +**Last Updated**: 2025-02-01 **Status**: ✅ Ready for Production diff --git a/docs/install/README.md b/docs/install/README.md index d9b7a71..a1e45bc 100644 --- a/docs/install/README.md +++ b/docs/install/README.md @@ -2,8 +2,18 @@ Anleitung zur Installation und Aktualisierung des Container Spawner. +## 🚀 Quick Links + +- **[📋 Deployment Guide](./DEPLOYMENT_GUIDE.md)** - Vollständige Step-by-Step Anleitung für Deployment + - Phase 1: Vorbereitung (Daten bereinigen, Environment konfigurieren) + - Phase 2: Services starten + - Phase 3: Erste Registrierung + - Phase 4: Testing + - Troubleshooting, Monitoring, Security Checklist + ## Inhaltsverzeichnis +- [Quick Links](#quick-links) - [Voraussetzungen](#voraussetzungen) - [Neuinstallation](#neuinstallation) - [Update/Upgrade](#updateupgrade) @@ -183,21 +193,39 @@ docker-compose up -d **Hinweis**: Die Email-Konfiguration wird fuer die Email-Verifizierung bei der Registrierung und fuer Passwort-Reset-Emails benoetigt. -### User-Templates +### User-Templates (Dynamisches System) -Es stehen zwei Templates fuer User-Container zur Verfuegung: +Das System unterstützt **beliebig viele User-Templates** durch ein dynamisches Konfigurationssystem: + +**Verfügbare Standard-Templates:** | Image | Verzeichnis | Beschreibung | |-------|-------------|--------------| -| `user-service-template:latest` | `user-template/` | Einfache nginx-Willkommensseite (Standard) | -| `user-template-next:latest` | `user-template-next/` | Moderne Next.js React-Anwendung | +| `user-template-01:latest` | `user-template-01/` | Nginx Basic - Einfacher Nginx-Server | +| `user-template-02:latest` | `user-template-02/` | Nginx Advanced - Nginx mit erweiterten Features | +| `user-template-next:latest` | `user-template-next/` | Next.js Production - React-App mit Shadcn/UI | -Um ein anderes Template zu verwenden, aendere `USER_TEMPLATE_IMAGE` in `.env`: +**Konfiguration:** + +Templates werden in `.env` als semikolon-getrennte Liste definiert: ```bash -USER_TEMPLATE_IMAGE=user-template-next:latest +USER_TEMPLATE_IMAGES=user-template-01:latest;user-template-02:latest;user-template-next:latest ``` +**Automatisches Bauen:** + +Das `install.sh` Script erkennt automatisch alle `user-template-*` Verzeichnisse und baut sie. + +**Neues Template hinzufügen:** + +1. Erstelle `user-template-xyz/Dockerfile` +2. Füge zu `.env` ein: `USER_TEMPLATE_IMAGES=...;user-template-xyz:latest` +3. Füge Metadaten zu `templates.json` ein +4. Führe `bash install.sh` aus + +Siehe auch: [CLAUDE.md - Dynamisches Template-System](../../CLAUDE.md#dynamisches-template-system) + ### Produktions-Variablen | Variable | Beschreibung | diff --git a/install.sh b/install.sh index 0bc51fc..be0bc24 100644 --- a/install.sh +++ b/install.sh @@ -375,81 +375,75 @@ fi # 7. Docker-Images bauen # ============================================================ echo "" -echo "Baue Docker-Images fuer Multi-Container MVP..." -echo " - user-service-template (Development)" -echo " - user-template-next (Production)" -echo " - spawner-api (Backend)" -echo " - spawner-frontend (Frontend)" +echo "Baue Docker-Images (Dynamisches Template-System)..." echo "" # Stoppe laufende Container ${COMPOSE_CMD} down 2>/dev/null || true -# Zaehle aktive Builds fuer Fortschrittsanzeige +# Zähle Templates für Fortschrittsanzeige +TEMPLATE_DIRS=$(find "${INSTALL_DIR}" -maxdepth 1 -type d -name "user-template*" 2>/dev/null | wc -l) +TOTAL_BUILDS=$((2 + TEMPLATE_DIRS)) # spawner-api + frontend + templates BUILD_STEP=1 -TOTAL_BUILDS=3 # user-service-template + spawner-api + frontend -# Multi-Container Support: Baue auch user-template-next wenn Verzeichnis existiert -[ -d "${INSTALL_DIR}/user-template-next" ] && TOTAL_BUILDS=$((TOTAL_BUILDS + 1)) +# Auto-detect und baue alle user-template-* Verzeichnisse +echo " Auto-Detecting Template-Verzeichnisse..." +BUILT_TEMPLATES=0 + +for template_dir in "${INSTALL_DIR}"/user-template*; do + # Prüfe ob Verzeichnis existiert + [ -d "$template_dir" ] || continue + + # Extrahiere Template-Namen (z.B. user-template-01) + template_name=$(basename "$template_dir") + + # Image-Name = Verzeichnis-Name + :latest + image_name="${template_name}:latest" + + echo " [$BUILD_STEP/$TOTAL_BUILDS] Baue ${template_name}..." + + # Special handling für Next.js Templates (längere Build-Zeit) + if [[ "$template_name" == *"next"* ]]; then + echo -e " ${BLUE}Dies kann 2-5 Minuten dauern (npm install + build)...${NC}" + fi -# User-Template Image bauen (fuer User-Container) -if [ -d "${INSTALL_DIR}/user-template" ]; then - echo " [$BUILD_STEP/$TOTAL_BUILDS] Baue user-service-template (User-Container)..." echo "" - # Build ausfuehren und Output in Datei speichern BUILD_LOG="${LOG_FILE}" echo "" >> "${LOG_FILE}" - echo "=== Build: user-service-template ===" >> "${LOG_FILE}" - docker build --no-cache -t user-service-template:latest "${INSTALL_DIR}/user-template/" >> "${BUILD_LOG}" 2>&1 + echo "=== Build: ${template_name} ===" >> "${LOG_FILE}" + + docker build --no-cache -t "${image_name}" "${template_dir}/" >> "${BUILD_LOG}" 2>&1 BUILD_EXIT=$? # Gefilterten Output anzeigen grep -E "(Step |#[0-9]+ |Successfully|ERROR|error:|COPY|RUN|FROM)" "${BUILD_LOG}" 2>/dev/null | sed 's/^/ /' || true # Pruefe ob Build erfolgreich UND Image existiert - if [ $BUILD_EXIT -eq 0 ] && docker image inspect user-service-template:latest >/dev/null 2>&1; then + if [ $BUILD_EXIT -eq 0 ] && docker image inspect "${image_name}" >/dev/null 2>&1; then echo "" - echo -e " user-service-template: ${GREEN}OK${NC}" + echo -e " ${template_name}: ${GREEN}OK${NC}" + BUILT_TEMPLATES=$((BUILT_TEMPLATES + 1)) else echo "" - echo -e " user-service-template: ${RED}FEHLER${NC}" + echo -e " ${template_name}: ${RED}FEHLER${NC}" echo " Siehe Build-Log: ${LOG_FILE}" echo " Letzte 50 Zeilen:" tail -50 "${BUILD_LOG}" exit 1 fi + BUILD_STEP=$((BUILD_STEP + 1)) +done + +if [ $BUILT_TEMPLATES -eq 0 ]; then + echo -e "${RED}FEHLER: Keine Template-Verzeichnisse gefunden!${NC}" + echo "Erwartete Verzeichnisse: user-template*, z.B. user-template-01, user-template-next" + exit 1 fi -# User-Template-Next Image bauen (Production Template fuer Multi-Container MVP) -# Wird automatisch gebaut wenn Verzeichnis existiert -if [ -d "${INSTALL_DIR}/user-template-next" ]; then - echo " [$BUILD_STEP/$TOTAL_BUILDS] Baue user-template-next (Next.js Template - Production)..." - echo -e " ${BLUE}Dies kann 2-5 Minuten dauern (npm install + build)...${NC}" - echo "" - - BUILD_LOG="${LOG_FILE}" - echo "" >> "${LOG_FILE}" - echo "=== Build: user-template-next ===" >> "${LOG_FILE}" - docker build --no-cache -t user-template-next:latest "${INSTALL_DIR}/user-template-next/" >> "${BUILD_LOG}" 2>&1 - BUILD_EXIT=$? - - grep -E "(Step |#[0-9]+ |Successfully|ERROR|error:|COPY|RUN|FROM)" "${BUILD_LOG}" 2>/dev/null | sed 's/^/ /' || true - - if [ $BUILD_EXIT -eq 0 ] && docker image inspect user-template-next:latest >/dev/null 2>&1; then - echo "" - echo -e " user-template-next: ${GREEN}OK${NC}" - else - echo "" - echo -e " user-template-next: ${RED}FEHLER${NC}" - echo " Siehe Build-Log: ${LOG_FILE}" - echo " Letzte 50 Zeilen:" - tail -50 "${BUILD_LOG}" - exit 1 - fi - BUILD_STEP=$((BUILD_STEP + 1)) -fi +echo "" +echo -e "${GREEN}Alle ${BUILT_TEMPLATES} Template(s) erfolgreich gebaut.${NC}" # Spawner Backend Image bauen echo " [$BUILD_STEP/$TOTAL_BUILDS] Baue Spawner API (Flask Backend)..." diff --git a/templates.json b/templates.json new file mode 100644 index 0000000..2c4e567 --- /dev/null +++ b/templates.json @@ -0,0 +1,22 @@ +{ + "templates": [ + { + "type": "template-01", + "image": "user-template-01:latest", + "display_name": "Nginx Basic", + "description": "Einfacher Nginx-Server mit statischen Dateien und Willkommensseite" + }, + { + "type": "template-02", + "image": "user-template-02:latest", + "display_name": "Nginx Advanced", + "description": "Nginx mit erweiterten Features und Konfigurationen" + }, + { + "type": "template-next", + "image": "user-template-next:latest", + "display_name": "Next.js Production", + "description": "React-Anwendung mit Shadcn/UI, TypeScript und modernem Build-Setup" + } + ] +} diff --git a/user-template/Dockerfile b/user-template-01/Dockerfile similarity index 100% rename from user-template/Dockerfile rename to user-template-01/Dockerfile diff --git a/user-template/entrypoint.sh b/user-template-01/entrypoint.sh similarity index 100% rename from user-template/entrypoint.sh rename to user-template-01/entrypoint.sh diff --git a/user-template/index.html b/user-template-01/index.html similarity index 100% rename from user-template/index.html rename to user-template-01/index.html diff --git a/user-template/nginx.conf b/user-template-01/nginx.conf similarity index 100% rename from user-template/nginx.conf rename to user-template-01/nginx.conf diff --git a/user-template-02/Dockerfile b/user-template-02/Dockerfile new file mode 100644 index 0000000..df61e0f --- /dev/null +++ b/user-template-02/Dockerfile @@ -0,0 +1,23 @@ +FROM nginxinc/nginx-unprivileged:alpine + +# Wechsel zu root um Pakete zu installieren +USER root + +# gettext für envsubst installieren +RUN apk add --no-cache gettext + +# HTML-Template kopieren +COPY index.html /usr/share/nginx/html/index.html.template + +# Nginx-Konfiguration kopieren +COPY nginx.conf /etc/nginx/conf.d/default.conf + +# Entrypoint Script kopieren und executable machen +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh && \ + chmod 777 /usr/share/nginx/html + +EXPOSE 8080 + +# Entrypoint Script starten (läuft als root, startet Nginx) +ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file diff --git a/user-template-02/entrypoint.sh b/user-template-02/entrypoint.sh new file mode 100644 index 0000000..cd340b0 --- /dev/null +++ b/user-template-02/entrypoint.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -e + +# Umgebungsvariablen in die HTML-Datei einfügen +envsubst < /usr/share/nginx/html/index.html.template > /usr/share/nginx/html/index.html + +# Nginx starten +exec nginx -g "daemon off;" diff --git a/user-template-02/index.html b/user-template-02/index.html new file mode 100644 index 0000000..635ef99 --- /dev/null +++ b/user-template-02/index.html @@ -0,0 +1,284 @@ + + + + + + Dein persönlicher Service | SPAWNER + + + +
+ + ● Online + +

Willkommen bei SPAWNER!

+

Dies ist dein persönlicher Container-Service

+ +
+
+ User-ID + Wird geladen... +
+
+ Username + Wird geladen... +
+
+ Container gestartet + Wird geladen... +
+
+ Container-ID + Wird geladen... +
+
+ +
+

🎯 Deine Container-Features

+
+
+
🔒
+
Isolierte Umgebung
+
+
+
+
Schnelle Performance
+
+
+
🔄
+
Auto-Restart
+
+
+
📊
+
Resource-Limits
+
+
+
+ + +
+ + + + diff --git a/user-template-02/nginx.conf b/user-template-02/nginx.conf new file mode 100644 index 0000000..4d8915b --- /dev/null +++ b/user-template-02/nginx.conf @@ -0,0 +1,15 @@ +server { + listen 8080; + server_name _; + + # Root-Verzeichnis + root /usr/share/nginx/html; + + # Alle Anfragen einfach auf root Location + location / { + try_files $uri $uri/ /index.html; + } + + # Error pages + error_page 404 /index.html; +}