add debug admin API for logs and database management
This commit is contained in:
parent
b41fd980ce
commit
95960ab7a9
|
|
@ -128,6 +128,15 @@ SMTP_USE_TLS=true
|
||||||
# WICHTIG: Muss die URL sein, unter der das Frontend erreichbar ist
|
# WICHTIG: Muss die URL sein, unter der das Frontend erreichbar ist
|
||||||
FRONTEND_URL=https://coder.example.com
|
FRONTEND_URL=https://coder.example.com
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# DEBUG & ADMINISTRATION
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# Debug-Token fuer Admin-API (view-logs, delete-email, etc.)
|
||||||
|
# Generiere mit: python3 -c "import secrets; print(secrets.token_hex(32))"
|
||||||
|
# Nutze via: curl -H "X-Debug-Token: xxx" http://localhost:5000/api/admin/debug?action=view-logs
|
||||||
|
DEBUG_TOKEN=
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# PRODUKTION - Erweiterte Einstellungen
|
# PRODUKTION - Erweiterte Einstellungen
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
|
||||||
161
admin_api.py
161
admin_api.py
|
|
@ -322,3 +322,164 @@ def get_active_takeovers():
|
||||||
'sessions': sessions_list,
|
'sessions': sessions_list,
|
||||||
'total': len(sessions_list)
|
'total': len(sessions_list)
|
||||||
}), 200
|
}), 200
|
||||||
|
|
||||||
|
|
||||||
|
@admin_bp.route('/debug', methods=['GET', 'POST'])
|
||||||
|
def debug_management():
|
||||||
|
"""
|
||||||
|
Debug-Management Endpoint für Logs und Datenbank-Bereinigung
|
||||||
|
|
||||||
|
Authentifizierung via:
|
||||||
|
1. DEBUG_TOKEN Header: X-Debug-Token: <token>
|
||||||
|
2. Oder Admin JWT Token
|
||||||
|
|
||||||
|
Actions:
|
||||||
|
- view-logs: Zeigt letzte 100 Zeilen der Logs
|
||||||
|
- clear-logs: Löscht alle Logs
|
||||||
|
- delete-email: Entfernt User und alle zugehörigen Daten
|
||||||
|
Parameter: ?email=test@example.com
|
||||||
|
- delete-token: Entfernt Magic Link Tokens für Email
|
||||||
|
Parameter: ?email=test@example.com
|
||||||
|
"""
|
||||||
|
# Authentifizierung prüfen
|
||||||
|
debug_token = current_app.config.get('DEBUG_TOKEN')
|
||||||
|
provided_token = request.headers.get('X-Debug-Token')
|
||||||
|
|
||||||
|
# Versuch JWT-Auth
|
||||||
|
is_admin = False
|
||||||
|
try:
|
||||||
|
from flask_jwt_extended import verify_jwt_in_request, get_jwt_identity
|
||||||
|
verify_jwt_in_request(optional=True)
|
||||||
|
user_id = get_jwt_identity()
|
||||||
|
if user_id:
|
||||||
|
user = User.query.get(int(user_id))
|
||||||
|
is_admin = user and user.is_admin
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Authentifizierung validieren
|
||||||
|
if not (is_admin or (debug_token and provided_token == debug_token)):
|
||||||
|
return jsonify({'error': 'Authentifizierung erforderlich (JWT oder X-Debug-Token Header)'}), 403
|
||||||
|
|
||||||
|
action = request.args.get('action', '').lower()
|
||||||
|
|
||||||
|
# ===== view-logs =====
|
||||||
|
if action == 'view-logs':
|
||||||
|
log_file = current_app.config.get('LOG_FILE', '/app/logs/spawner.log')
|
||||||
|
try:
|
||||||
|
with open(log_file, 'r', encoding='utf-8') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
last_100 = lines[-100:] if len(lines) > 100 else lines
|
||||||
|
return jsonify({
|
||||||
|
'action': 'view-logs',
|
||||||
|
'lines': len(lines),
|
||||||
|
'last_100': ''.join(last_100)
|
||||||
|
}), 200
|
||||||
|
except FileNotFoundError:
|
||||||
|
return jsonify({'error': 'Log-Datei nicht gefunden'}), 404
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': f'Fehler beim Lesen der Logs: {str(e)}'}), 500
|
||||||
|
|
||||||
|
# ===== clear-logs =====
|
||||||
|
elif action == 'clear-logs':
|
||||||
|
log_file = current_app.config.get('LOG_FILE', '/app/logs/spawner.log')
|
||||||
|
try:
|
||||||
|
with open(log_file, 'w') as f:
|
||||||
|
f.write('')
|
||||||
|
current_app.logger.info('[DEBUG] Logs wurden gelöscht')
|
||||||
|
return jsonify({
|
||||||
|
'action': 'clear-logs',
|
||||||
|
'message': 'Logs wurden gelöscht'
|
||||||
|
}), 200
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': f'Fehler beim Löschen der Logs: {str(e)}'}), 500
|
||||||
|
|
||||||
|
# ===== delete-email =====
|
||||||
|
elif action == 'delete-email':
|
||||||
|
email = request.args.get('email', '').strip()
|
||||||
|
if not email:
|
||||||
|
return jsonify({'error': 'Parameter erforderlich: email'}), 400
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = User.query.filter_by(email=email).first()
|
||||||
|
if not user:
|
||||||
|
return jsonify({'error': f'User {email} nicht gefunden'}), 404
|
||||||
|
|
||||||
|
user_id = user.id
|
||||||
|
email_deleted = user.email
|
||||||
|
|
||||||
|
# Container löschen falls vorhanden
|
||||||
|
if user.container_id:
|
||||||
|
try:
|
||||||
|
container_mgr = ContainerManager()
|
||||||
|
container_mgr.stop_container(user.container_id)
|
||||||
|
container_mgr.remove_container(user.container_id)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# User und alle zugehörigen Daten löschen
|
||||||
|
db.session.delete(user)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
current_app.logger.info(f'[DEBUG] User {email_deleted} wurde gelöscht')
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'action': 'delete-email',
|
||||||
|
'message': f'User {email_deleted} wurde gelöscht',
|
||||||
|
'user_id': user_id
|
||||||
|
}), 200
|
||||||
|
except Exception as e:
|
||||||
|
db.session.rollback()
|
||||||
|
return jsonify({'error': f'Fehler beim Löschen: {str(e)}'}), 500
|
||||||
|
|
||||||
|
# ===== delete-token =====
|
||||||
|
elif action == 'delete-token':
|
||||||
|
email = request.args.get('email', '').strip()
|
||||||
|
if not email:
|
||||||
|
return jsonify({'error': 'Parameter erforderlich: email'}), 400
|
||||||
|
|
||||||
|
try:
|
||||||
|
from models import MagicLinkToken
|
||||||
|
user = User.query.filter_by(email=email).first()
|
||||||
|
if not user:
|
||||||
|
return jsonify({'error': f'User {email} nicht gefunden'}), 404
|
||||||
|
|
||||||
|
tokens = MagicLinkToken.query.filter_by(user_id=user.id).all()
|
||||||
|
count = len(tokens)
|
||||||
|
|
||||||
|
for token in tokens:
|
||||||
|
db.session.delete(token)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
current_app.logger.info(f'[DEBUG] {count} Magic Link Tokens für {email} wurden gelöscht')
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'action': 'delete-token',
|
||||||
|
'message': f'{count} Tokens für {email} gelöscht',
|
||||||
|
'tokens_deleted': count
|
||||||
|
}), 200
|
||||||
|
except Exception as e:
|
||||||
|
db.session.rollback()
|
||||||
|
return jsonify({'error': f'Fehler: {str(e)}'}), 500
|
||||||
|
|
||||||
|
# ===== info =====
|
||||||
|
elif action == 'info' or not action:
|
||||||
|
return jsonify({
|
||||||
|
'endpoint': '/api/admin/debug',
|
||||||
|
'auth': 'X-Debug-Token Header oder Admin JWT',
|
||||||
|
'actions': {
|
||||||
|
'view-logs': 'Zeigt letzte 100 Zeilen der Logs',
|
||||||
|
'clear-logs': 'Löscht alle Logs',
|
||||||
|
'delete-email': 'Löscht User (Parameter: email=...)',
|
||||||
|
'delete-token': 'Löscht Magic Link Tokens (Parameter: email=...)',
|
||||||
|
'info': 'Diese Hilfe'
|
||||||
|
},
|
||||||
|
'examples': [
|
||||||
|
'GET /api/admin/debug?action=view-logs -H "X-Debug-Token: xxx"',
|
||||||
|
'GET /api/admin/debug?action=delete-email&email=test@example.com',
|
||||||
|
'GET /api/admin/debug?action=delete-token&email=test@example.com'
|
||||||
|
]
|
||||||
|
}), 200
|
||||||
|
|
||||||
|
else:
|
||||||
|
return jsonify({'error': f'Unbekannte Action: {action}'}), 400
|
||||||
|
|
|
||||||
55
config.py
55
config.py
|
|
@ -99,35 +99,10 @@ class Config:
|
||||||
print(f"[CONFIG] Warnung: Fehler beim Laden von templates.json: {e}", file=sys.stderr)
|
print(f"[CONFIG] Warnung: Fehler beim Laden von templates.json: {e}", file=sys.stderr)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@staticmethod
|
# Temp-Variablen für Template-Loading (werden nach Klasse verarbeitet)
|
||||||
def _build_container_templates() -> dict:
|
TEMPLATE_IMAGES = None
|
||||||
"""
|
TEMPLATES_CONFIG = None
|
||||||
Baut CONTAINER_TEMPLATES Dictionary aus:
|
CONTAINER_TEMPLATES = None
|
||||||
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
|
# Traefik/Domain-Konfiguration
|
||||||
|
|
@ -186,6 +161,11 @@ class Config:
|
||||||
MAGIC_LINK_TOKEN_EXPIRY = int(os.getenv('MAGIC_LINK_TOKEN_EXPIRY', 900)) # 15 Minuten
|
MAGIC_LINK_TOKEN_EXPIRY = int(os.getenv('MAGIC_LINK_TOKEN_EXPIRY', 900)) # 15 Minuten
|
||||||
MAGIC_LINK_RATE_LIMIT = int(os.getenv('MAGIC_LINK_RATE_LIMIT', 3)) # Max 3 pro Stunde
|
MAGIC_LINK_RATE_LIMIT = int(os.getenv('MAGIC_LINK_RATE_LIMIT', 3)) # Max 3 pro Stunde
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# Debug & Administration
|
||||||
|
# ========================================
|
||||||
|
DEBUG_TOKEN = os.getenv('DEBUG_TOKEN', '') # Für Admin-Debug-API
|
||||||
|
|
||||||
|
|
||||||
class DevelopmentConfig(Config):
|
class DevelopmentConfig(Config):
|
||||||
"""Konfiguration für Entwicklung"""
|
"""Konfiguration für Entwicklung"""
|
||||||
|
|
@ -215,6 +195,23 @@ class TestingConfig(Config):
|
||||||
WTF_CSRF_ENABLED = False
|
WTF_CSRF_ENABLED = False
|
||||||
|
|
||||||
|
|
||||||
|
# Initialisiere Templates NACH Klassendefini tion
|
||||||
|
Config.TEMPLATE_IMAGES = Config._load_template_images()
|
||||||
|
Config.TEMPLATES_CONFIG = Config._load_templates_config()
|
||||||
|
|
||||||
|
# Baue CONTAINER_TEMPLATES aus Templates
|
||||||
|
templates = {}
|
||||||
|
for image in Config.TEMPLATE_IMAGES:
|
||||||
|
container_type = Config._extract_type_from_image(image)
|
||||||
|
config_meta = Config.TEMPLATES_CONFIG.get(container_type, {})
|
||||||
|
templates[container_type] = {
|
||||||
|
'image': image,
|
||||||
|
'display_name': config_meta.get('display_name', container_type.replace('-', ' ').title()),
|
||||||
|
'description': config_meta.get('description', f'Container basierend auf {image}')
|
||||||
|
}
|
||||||
|
Config.CONTAINER_TEMPLATES = templates
|
||||||
|
|
||||||
|
|
||||||
# Config-Dict für einfaches Laden
|
# Config-Dict für einfaches Laden
|
||||||
config = {
|
config = {
|
||||||
'development': DevelopmentConfig,
|
'development': DevelopmentConfig,
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,12 @@ services:
|
||||||
container_name: spawner
|
container_name: spawner
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
env_file:
|
|
||||||
- .env
|
|
||||||
|
|
||||||
ports:
|
ports:
|
||||||
- "5000:5000" # Optional: Direktzugriff für Debugging
|
- "5000:5000" # Optional: Direktzugriff für Debugging
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
# .env zur Laufzeit laden (wird bei restart neu gelesen!)
|
||||||
|
- .env:/app/.env:ro
|
||||||
# Docker-Socket für Container-Management
|
# Docker-Socket für Container-Management
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:rw
|
- /var/run/docker.sock:/var/run/docker.sock:rw
|
||||||
# Persistente Daten
|
# Persistente Daten
|
||||||
|
|
@ -21,39 +20,7 @@ services:
|
||||||
# Logs
|
# Logs
|
||||||
- ./logs:/app/logs
|
- ./logs:/app/logs
|
||||||
|
|
||||||
environment:
|
# .env wird als Volume gemountet - keine Duplication nötig!
|
||||||
# Aus .env-Datei - Sicherheit
|
|
||||||
- SECRET_KEY=${SECRET_KEY}
|
|
||||||
- JWT_SECRET_KEY=${JWT_SECRET_KEY:-${SECRET_KEY}}
|
|
||||||
# Domain & Routing
|
|
||||||
- BASE_DOMAIN=${BASE_DOMAIN}
|
|
||||||
- SPAWNER_SUBDOMAIN=${SPAWNER_SUBDOMAIN:-coder}
|
|
||||||
- CORS_ORIGINS=https://${SPAWNER_SUBDOMAIN:-coder}.${BASE_DOMAIN},http://localhost:3000
|
|
||||||
# Docker & Traefik
|
|
||||||
- TRAEFIK_NETWORK=${TRAEFIK_NETWORK}
|
|
||||||
- DOCKER_HOST=${DOCKER_HOST:-unix:///var/run/docker.sock}
|
|
||||||
# Templates (Dynamisches System)
|
|
||||||
- USER_TEMPLATE_IMAGE=${USER_TEMPLATE_IMAGE:-user-template-01:latest}
|
|
||||||
- USER_TEMPLATE_IMAGES=${USER_TEMPLATE_IMAGES:-"user-template-01:latest;user-template-02:latest;user-template-next:latest"}
|
|
||||||
# Traefik-Zertifikate
|
|
||||||
- TRAEFIK_CERTRESOLVER=${TRAEFIK_CERTRESOLVER:-lets-encrypt}
|
|
||||||
- TRAEFIK_ENTRYPOINT=${TRAEFIK_ENTRYPOINT:-websecure}
|
|
||||||
# SMTP & Email
|
|
||||||
- SMTP_HOST=${SMTP_HOST}
|
|
||||||
- SMTP_PORT=${SMTP_PORT}
|
|
||||||
- SMTP_USER=${SMTP_USER}
|
|
||||||
- SMTP_PASSWORD=${SMTP_PASSWORD}
|
|
||||||
- SMTP_FROM=${SMTP_FROM}
|
|
||||||
- SMTP_USE_TLS=${SMTP_USE_TLS:-true}
|
|
||||||
- FRONTEND_URL=${FRONTEND_URL}
|
|
||||||
# Magic Links
|
|
||||||
- MAGIC_LINK_TOKEN_EXPIRY=${MAGIC_LINK_TOKEN_EXPIRY:-900}
|
|
||||||
- MAGIC_LINK_RATE_LIMIT=${MAGIC_LINK_RATE_LIMIT:-3}
|
|
||||||
# JWT
|
|
||||||
- JWT_ACCESS_TOKEN_EXPIRES=${JWT_ACCESS_TOKEN_EXPIRES:-3600}
|
|
||||||
# Ressourcen-Limits
|
|
||||||
- DEFAULT_MEMORY_LIMIT=${DEFAULT_MEMORY_LIMIT:-512m}
|
|
||||||
- DEFAULT_CPU_QUOTA=${DEFAULT_CPU_QUOTA:-50000}
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
- web
|
- web
|
||||||
|
|
|
||||||
|
|
@ -402,6 +402,305 @@ docker exec spawner sqlite3 /app/data/users.db \
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## ⚙️ Häufige Konfigurationsänderungen nach Deployment
|
||||||
|
|
||||||
|
**WICHTIG:** Die `.env` Datei wird als **Volume in den Container gemountet**. Das bedeutet:
|
||||||
|
- Änderungen in `.env` werden **zur Laufzeit** gelesen
|
||||||
|
- Du brauchst **kein Docker-Rebuild** für Konfigurationsänderungen
|
||||||
|
- Nur `docker-compose down` + `docker-compose up -d` reicht
|
||||||
|
|
||||||
|
### SMTP/Email-Anmeldedaten ändern
|
||||||
|
|
||||||
|
Falls du die Email-Anmeldedaten später ändern musst (z.B. Passwort aktualisiert):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Bearbeite .env
|
||||||
|
nano .env
|
||||||
|
|
||||||
|
# Ändere diese Zeilen:
|
||||||
|
# SMTP_HOST=smtp.gmail.com
|
||||||
|
# SMTP_PORT=587
|
||||||
|
# SMTP_USER=deine-email@gmail.com
|
||||||
|
# SMTP_PASSWORD=neues-passwort
|
||||||
|
# SMTP_FROM=noreply@domain.com
|
||||||
|
|
||||||
|
# 2. Stoppe Container komplett und starte neu
|
||||||
|
docker-compose down
|
||||||
|
docker-compose up -d spawner
|
||||||
|
|
||||||
|
# 3. Überprüfe ob neue Credentials geladen wurden
|
||||||
|
docker exec spawner cat /app/.env | grep SMTP_HOST
|
||||||
|
```
|
||||||
|
|
||||||
|
**Hinweis:** `docker-compose restart spawner` reicht auch aus (schneller), aber `down`/`up` ist sicherer.
|
||||||
|
|
||||||
|
### Domain oder Base URL ändern
|
||||||
|
|
||||||
|
Falls du die Domain oder Subdomain ändern möchtest:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Bearbeite .env
|
||||||
|
nano .env
|
||||||
|
|
||||||
|
# Ändere diese Zeilen:
|
||||||
|
# BASE_DOMAIN=neudomain.com
|
||||||
|
# SPAWNER_SUBDOMAIN=coder (oder etwas anderes)
|
||||||
|
# FRONTEND_URL=https://coder.neudomain.com
|
||||||
|
|
||||||
|
# 2. Starte Services neu
|
||||||
|
docker-compose down
|
||||||
|
docker-compose up -d spawner frontend
|
||||||
|
|
||||||
|
# 3. Überprüfe Config (sollte neue Domain zeigen)
|
||||||
|
docker exec spawner cat /app/.env | grep BASE_DOMAIN
|
||||||
|
```
|
||||||
|
|
||||||
|
### Magic Link Token Expiration ändern
|
||||||
|
|
||||||
|
Standardmäßig haben Magic Links 15 Minuten Gültigkeitsdauer. Wenn du das ändern möchtest:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Bearbeite .env
|
||||||
|
nano .env
|
||||||
|
|
||||||
|
# Ändere diese Zeilen:
|
||||||
|
# MAGIC_LINK_TOKEN_EXPIRY=900 (in Sekunden, default: 15 Min)
|
||||||
|
# MAGIC_LINK_RATE_LIMIT=3 (max. 3 Links pro Stunde)
|
||||||
|
|
||||||
|
# 2. Starte Backend neu
|
||||||
|
docker-compose restart spawner
|
||||||
|
# oder sicherer:
|
||||||
|
# docker-compose down
|
||||||
|
# docker-compose up -d spawner
|
||||||
|
```
|
||||||
|
|
||||||
|
### JWT Token Expiration ändern
|
||||||
|
|
||||||
|
Standardmäßig verfallen JWT Tokens nach 1 Stunde:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Bearbeite .env
|
||||||
|
nano .env
|
||||||
|
|
||||||
|
# Ändere diese Zeile:
|
||||||
|
# JWT_ACCESS_TOKEN_EXPIRES=3600 (in Sekunden)
|
||||||
|
|
||||||
|
# 2. Starte Backend neu
|
||||||
|
docker-compose restart spawner
|
||||||
|
```
|
||||||
|
|
||||||
|
### Container-Resource-Limits ändern
|
||||||
|
|
||||||
|
Wenn deine Server-Hardware unterschiedlich ist, passe die Limits an:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Bearbeite .env
|
||||||
|
nano .env
|
||||||
|
|
||||||
|
# Ändere diese Zeilen:
|
||||||
|
# DEFAULT_MEMORY_LIMIT=512m (RAM pro Container)
|
||||||
|
# DEFAULT_CPU_QUOTA=50000 (CPU: 50000 = 0.5 CPU, 100000 = 1 CPU)
|
||||||
|
|
||||||
|
# 2. Starte Backend neu
|
||||||
|
docker-compose restart spawner
|
||||||
|
|
||||||
|
# Info: Neue Container verwenden sofort die neuen Limits
|
||||||
|
# Laufende Container behalten alte Limits (bis Neustart)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Logging Level ändern
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Bearbeite .env
|
||||||
|
nano .env
|
||||||
|
|
||||||
|
# Ändere diese Zeile:
|
||||||
|
# LOG_LEVEL=INFO (Options: DEBUG, INFO, WARNING, ERROR)
|
||||||
|
|
||||||
|
# 2. Starte Backend neu
|
||||||
|
docker-compose restart spawner
|
||||||
|
```
|
||||||
|
|
||||||
|
### Überprüfe welche Werte der Container tatsächlich nutzt
|
||||||
|
|
||||||
|
Falls du unsicher bist, ob die neuen Konfigurationswerte geladen wurden:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Zeige die .env Werte, die der Container sieht
|
||||||
|
docker exec spawner cat /app/.env | grep SMTP
|
||||||
|
|
||||||
|
# Alle SMTP-Einstellungen anzeigen:
|
||||||
|
docker exec spawner cat /app/.env | grep SMTP
|
||||||
|
|
||||||
|
# Überprüfe mit Python, ob die Werte korrekt geladen sind:
|
||||||
|
docker exec spawner python3 << 'EOF'
|
||||||
|
from config import Config
|
||||||
|
print(f"SMTP_HOST: {Config.SMTP_HOST}")
|
||||||
|
print(f"SMTP_USER: {Config.SMTP_USER}")
|
||||||
|
print(f"SMTP_PORT: {Config.SMTP_PORT}")
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wichtig:**
|
||||||
|
- `cat /app/.env | grep SMTP` zeigt die **aktuellen** Werte aus der `.env` Datei
|
||||||
|
- `docker exec spawner env` zeigt Shell-Variablen, nicht Python-Variablen!
|
||||||
|
- Python lädt die Werte mit `load_dotenv()` - überprüfe mit Python-Code ob sie korrekt geladen sind
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 Debug-API für Administratoren
|
||||||
|
|
||||||
|
**Neue Feature:** Admin-API zum Debuggen und Bereinigen von Logs und Datenbanken
|
||||||
|
|
||||||
|
### Vorbereitung: DEBUG_TOKEN generieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Token generieren
|
||||||
|
python3 -c "import secrets; print(secrets.token_hex(32))"
|
||||||
|
|
||||||
|
# Beispiel-Output:
|
||||||
|
# a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6
|
||||||
|
|
||||||
|
# 2. In .env eintragen
|
||||||
|
nano .env
|
||||||
|
# DEBUG_TOKEN=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6
|
||||||
|
|
||||||
|
# 3. Backend neustarten
|
||||||
|
docker-compose restart spawner
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug-API Endpoints
|
||||||
|
|
||||||
|
**Base:** `/api/admin/debug`
|
||||||
|
|
||||||
|
**Authentifizierung via Header:**
|
||||||
|
```bash
|
||||||
|
curl -H "X-Debug-Token: your-token-here" "http://localhost:5000/api/admin/debug?action=..."
|
||||||
|
```
|
||||||
|
|
||||||
|
Oder mit **Admin JWT Token:**
|
||||||
|
```bash
|
||||||
|
curl -H "Authorization: Bearer your-jwt-token" "http://localhost:5000/api/admin/debug?action=..."
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verfügbare Actions
|
||||||
|
|
||||||
|
#### 1. Logs anzeigen (view-logs)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "X-Debug-Token: xxx" \
|
||||||
|
"http://localhost:5000/api/admin/debug?action=view-logs"
|
||||||
|
```
|
||||||
|
|
||||||
|
Zeigt die **letzten 100 Zeilen** der Logs mit Zeilenanzahl.
|
||||||
|
|
||||||
|
#### 2. Logs löschen (clear-logs)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "X-Debug-Token: xxx" \
|
||||||
|
"http://localhost:5000/api/admin/debug?action=clear-logs"
|
||||||
|
```
|
||||||
|
|
||||||
|
Löscht **alle Logs** komplett. Danach ist die Log-Datei leer.
|
||||||
|
|
||||||
|
#### 3. User entfernen (delete-email)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "X-Debug-Token: xxx" \
|
||||||
|
"http://localhost:5000/api/admin/debug?action=delete-email&email=test@example.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
Entfernt einen **User komplett** aus der Datenbank:
|
||||||
|
- User-Profil gelöscht
|
||||||
|
- Container gelöscht (falls existiert)
|
||||||
|
- Alle Token gelöscht
|
||||||
|
- Alle Datenbank-Einträge entfernt
|
||||||
|
|
||||||
|
**WARNUNG:** Das ist **nicht rückgängig zu machen**!
|
||||||
|
|
||||||
|
#### 4. Magic Link Tokens entfernen (delete-token)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "X-Debug-Token: xxx" \
|
||||||
|
"http://localhost:5000/api/admin/debug?action=delete-token&email=test@example.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
Löscht **nur die Magic Link Tokens** für eine Email. Der User bleibt bestehen!
|
||||||
|
|
||||||
|
Nützlich wenn:
|
||||||
|
- Rate-Limiting blockiert den User
|
||||||
|
- Tokens sind fehlerhaft
|
||||||
|
- User neue Tokens anfordern soll
|
||||||
|
|
||||||
|
#### 5. Hilfe anzeigen (info)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "X-Debug-Token: xxx" \
|
||||||
|
"http://localhost:5000/api/admin/debug?action=info"
|
||||||
|
```
|
||||||
|
|
||||||
|
Zeigt alle verfügbaren Actions und Beispiele.
|
||||||
|
|
||||||
|
### Praktische Beispiele
|
||||||
|
|
||||||
|
**Problem: User kann sich nicht anmelden (Rate Limit)**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Lösche alle Tokens für diese Email
|
||||||
|
curl -H "X-Debug-Token: xxx" \
|
||||||
|
"http://localhost:5000/api/admin/debug?action=delete-token&email=user@example.com"
|
||||||
|
|
||||||
|
# 2. User kann jetzt neu anfragen
|
||||||
|
```
|
||||||
|
|
||||||
|
**Problem: Alte Test-User in der Datenbank**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Lösche kompletten User
|
||||||
|
curl -H "X-Debug-Token: xxx" \
|
||||||
|
"http://localhost:5000/api/admin/debug?action=delete-email&email=test@example.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Problem: Logs sind zu groß**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Lösche alle Logs
|
||||||
|
curl -H "X-Debug-Token: xxx" \
|
||||||
|
"http://localhost:5000/api/admin/debug?action=clear-logs"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Problem: Brauche die letzten Fehler zum Debuggen**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Hole letzten 100 Zeilen
|
||||||
|
curl -H "X-Debug-Token: xxx" \
|
||||||
|
"http://localhost:5000/api/admin/debug?action=view-logs"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bash-Alias für schnellen Zugriff
|
||||||
|
|
||||||
|
Füge dies in `~/.bashrc` ein für schnellere Befehle:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export SPAWNER_DEBUG_TOKEN="dein-token-hier"
|
||||||
|
export SPAWNER_URL="http://localhost:5000"
|
||||||
|
|
||||||
|
alias spawner-logs="curl -H 'X-Debug-Token: $SPAWNER_DEBUG_TOKEN' '$SPAWNER_URL/api/admin/debug?action=view-logs'"
|
||||||
|
alias spawner-clear-logs="curl -H 'X-Debug-Token: $SPAWNER_DEBUG_TOKEN' '$SPAWNER_URL/api/admin/debug?action=clear-logs'"
|
||||||
|
alias spawner-delete-email="curl -H 'X-Debug-Token: $SPAWNER_DEBUG_TOKEN' '$SPAWNER_URL/api/admin/debug?action=delete-email&email="
|
||||||
|
```
|
||||||
|
|
||||||
|
Dann:
|
||||||
|
```bash
|
||||||
|
# Logs anzeigen
|
||||||
|
spawner-logs
|
||||||
|
|
||||||
|
# User löschen
|
||||||
|
spawner-delete-email=test@example.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 🔐 Security Checklist
|
## 🔐 Security Checklist
|
||||||
|
|
||||||
- [ ] SECRET_KEY ist generiert und komplex
|
- [ ] SECRET_KEY ist generiert und komplex
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user