feat: Remove outdated documentation files and update SSL certificate paths in installer scripts
This commit is contained in:
parent
b379cdf4c9
commit
c2ea6c34ea
131
backend/COMMON_ERRORS.md
Normal file
131
backend/COMMON_ERRORS.md
Normal file
@ -0,0 +1,131 @@
|
||||
# Häufige Fehler und Lösungen
|
||||
|
||||
## API-Route-Fehler
|
||||
|
||||
### 1. Blueprint nicht registriert
|
||||
**Problem:** API-Blueprint wird nicht registriert, führt zu 404-Fehlern
|
||||
**Lösung:** Blueprint in app.py importieren und registrieren:
|
||||
```python
|
||||
from blueprints.api import api_bp
|
||||
app.register_blueprint(api_bp)
|
||||
```
|
||||
|
||||
### 2. Fehlende CSRF-Token bei POST-Requests
|
||||
**Problem:** CSRF-Validierung schlägt fehl
|
||||
**Lösung:** CSRF-Token in Templates einbinden oder API-Routen von CSRF befreien
|
||||
|
||||
### 3. Database Session nicht geschlossen
|
||||
**Problem:** Database connections leak
|
||||
**Lösung:** Immer try/finally verwenden:
|
||||
```python
|
||||
db_session = get_db_session()
|
||||
try:
|
||||
# Database operations
|
||||
pass
|
||||
finally:
|
||||
db_session.close()
|
||||
```
|
||||
|
||||
### 4. Fehlende Authentifizierung
|
||||
**Problem:** @login_required decorator fehlt
|
||||
**Lösung:** Alle geschützten Routen mit @login_required versehen
|
||||
|
||||
### 5. Falsche JSON-Response-Struktur
|
||||
**Problem:** Frontend erwartet andere Datenstruktur
|
||||
**Lösung:** Konsistente API-Response-Struktur verwenden:
|
||||
```python
|
||||
return jsonify({
|
||||
"success": True/False,
|
||||
"data": {...},
|
||||
"error": "error message" # nur bei Fehlern
|
||||
})
|
||||
```
|
||||
|
||||
## Datenbankfehler
|
||||
|
||||
### 1. Relationship not loaded
|
||||
**Problem:** Lazy loading von Relationships
|
||||
**Lösung:** Eager loading verwenden:
|
||||
```python
|
||||
from sqlalchemy.orm import joinedload
|
||||
jobs = session.query(Job).options(joinedload(Job.user)).all()
|
||||
```
|
||||
|
||||
### 2. Session closed before accessing relationships
|
||||
**Problem:** Zugriff auf Relationships nach Session.close()
|
||||
**Lösung:** Alle benötigten Daten vor Session.close() laden
|
||||
|
||||
## Logging-Fehler
|
||||
|
||||
### 1. Logger nicht initialisiert
|
||||
**Problem:** Logging funktioniert nicht
|
||||
**Lösung:** Logger korrekt initialisieren:
|
||||
```python
|
||||
from utils.logging_config import get_logger
|
||||
logger = get_logger("component_name")
|
||||
```
|
||||
|
||||
## File-Upload-Fehler
|
||||
|
||||
### 1. Upload-Ordner existiert nicht
|
||||
**Problem:** FileNotFoundError beim Upload
|
||||
**Lösung:** Ordner erstellen:
|
||||
```python
|
||||
os.makedirs(upload_folder, exist_ok=True)
|
||||
```
|
||||
|
||||
### 2. Unsichere Dateinamen
|
||||
**Problem:** Path traversal vulnerability
|
||||
**Lösung:** secure_filename() verwenden:
|
||||
```python
|
||||
from werkzeug.utils import secure_filename
|
||||
filename = secure_filename(file.filename)
|
||||
```
|
||||
|
||||
## Frontend-Integration-Fehler
|
||||
|
||||
### 1. CORS-Probleme
|
||||
**Problem:** Cross-Origin-Requests werden blockiert
|
||||
**Lösung:** CORS-Headers setzen oder Flask-CORS verwenden
|
||||
|
||||
### 2. Inkonsistente API-Endpunkte
|
||||
**Problem:** Frontend ruft nicht existierende Endpunkte auf
|
||||
**Lösung:** Systematische Überprüfung aller Frontend-API-Calls
|
||||
|
||||
### 3. Fehlende Error-Handling
|
||||
**Problem:** Frontend kann Fehler nicht verarbeiten
|
||||
**Lösung:** Konsistente Error-Response-Struktur implementieren
|
||||
|
||||
### 4. Admin-Dashboard-Fehler
|
||||
**Problem:** Admin-Dashboard API-Routen fehlen
|
||||
**Lösung:** Vollständige Admin-API implementieren:
|
||||
```python
|
||||
@app.route("/api/admin/users/create", methods=["POST"])
|
||||
@login_required
|
||||
def api_admin_create_user():
|
||||
if not current_user.is_admin:
|
||||
return jsonify({"error": "Keine Berechtigung"}), 403
|
||||
# Implementation...
|
||||
```
|
||||
|
||||
### 5. Fehlende Berechtigungsprüfung
|
||||
**Problem:** Admin-Routen ohne Berechtigungsprüfung
|
||||
**Lösung:** Immer Admin-Check einbauen:
|
||||
```python
|
||||
if not current_user.is_admin:
|
||||
return jsonify({"error": "Keine Berechtigung"}), 403
|
||||
```
|
||||
|
||||
## Performance-Probleme
|
||||
|
||||
### 1. N+1 Query Problem
|
||||
**Problem:** Zu viele Datenbankabfragen
|
||||
**Lösung:** Eager loading oder batch loading verwenden
|
||||
|
||||
### 2. Fehlende Indizes
|
||||
**Problem:** Langsame Datenbankabfragen
|
||||
**Lösung:** Indizes auf häufig abgefragte Spalten erstellen
|
||||
|
||||
### 3. Große Response-Größen
|
||||
**Problem:** Langsame API-Responses
|
||||
**Lösung:** Pagination und Feldfilterung implementieren
|
@ -29,6 +29,29 @@ MYP V2 ist ein 3D-Drucker-Management-System mit automatischer Smart Plug-Steueru
|
||||
- **Benutzer-Management**: Admin-Funktionen
|
||||
- **Statistiken**: Systemmetriken und Berichte
|
||||
- **Scheduler-Steuerung**: Start/Stop/Status des Job-Monitors
|
||||
- **✅ API-Blueprint-Registrierung**: API-Blueprint wurde in app.py registriert
|
||||
- **✅ Fehlende Frontend-API-Routen**: Alle vom Frontend benötigten API-Endpunkte implementiert
|
||||
- `/api/dashboard` - Dashboard-Daten
|
||||
- `/api/jobs/recent` - Letzte Jobs
|
||||
- `/api/files/upload` - Datei-Upload
|
||||
- `/api/files/download` - Datei-Download
|
||||
- `/api/stats/*` - Verschiedene Statistik-Endpunkte
|
||||
- `/api/user/*` - Benutzer-spezifische API-Endpunkte
|
||||
- `/api/job/{id}/remaining-time` - Verbleibende Zeit für Jobs
|
||||
- `/api/test` - Debug-Server Test-Endpunkt
|
||||
- `/api/status` - System-Status-Überwachung
|
||||
- `/api/schedule` - Scheduler-Informationen
|
||||
- **✅ Admin-Dashboard-API-Routen**: Vollständige API-Integration für Admin-Panel
|
||||
- `/api/admin/users/create` - Benutzer erstellen
|
||||
- `/api/admin/users/{id}/edit` - Benutzer bearbeiten
|
||||
- `/api/admin/users/{id}/toggle` - Benutzer aktivieren/deaktivieren
|
||||
- `/api/admin/printers/create` - Drucker erstellen
|
||||
- `/api/admin/printers/{id}/edit` - Drucker bearbeiten
|
||||
- `/api/admin/printers/{id}/toggle` - Drucker aktivieren/deaktivieren
|
||||
- `/api/admin/jobs/cancel/{id}` - Jobs abbrechen
|
||||
- `/api/admin/system/info` - Erweiterte System-Informationen
|
||||
- `/api/admin/logs/download` - Log-Download als ZIP
|
||||
- `/api/admin/maintenance/run` - Wartungsroutinen ausführen
|
||||
|
||||
#### Smart Plug-Integration
|
||||
- **TP-Link Tapo P110** Steuerung über PyP100
|
||||
|
@ -33,6 +33,7 @@ from utils.job_scheduler import scheduler
|
||||
from utils.template_helpers import register_template_helpers
|
||||
from blueprints.auth import auth_bp
|
||||
from blueprints.user import user_bp
|
||||
from blueprints.api import api_bp
|
||||
|
||||
# Flask-App initialisieren
|
||||
app = Flask(__name__)
|
||||
@ -93,6 +94,14 @@ print("Auth-Blueprint erfolgreich geladen")
|
||||
app.register_blueprint(user_bp)
|
||||
print("User-Blueprint erfolgreich geladen")
|
||||
|
||||
# API-Blueprint registrieren
|
||||
try:
|
||||
from blueprints.api import api_bp
|
||||
app.register_blueprint(api_bp)
|
||||
print("API-Blueprint erfolgreich geladen")
|
||||
except ImportError as e:
|
||||
print(f"API-Blueprint konnte nicht geladen werden: {e}")
|
||||
|
||||
# Logging initialisieren
|
||||
setup_logging()
|
||||
log_startup_info()
|
||||
@ -2419,3 +2428,917 @@ def admin_settings():
|
||||
return redirect(url_for("index"))
|
||||
|
||||
return render_template("admin_settings.html")
|
||||
|
||||
@app.route("/api/dashboard", methods=["GET"])
|
||||
@login_required
|
||||
def api_dashboard():
|
||||
"""Dashboard-Daten für API-Aufrufe"""
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
|
||||
# Grundlegende Statistiken sammeln
|
||||
total_jobs = db_session.query(Job).count()
|
||||
active_jobs = db_session.query(Job).filter(Job.status.in_(['running', 'pending'])).count()
|
||||
total_printers = db_session.query(Printer).count()
|
||||
available_printers = db_session.query(Printer).filter(Printer.status == 'available').count()
|
||||
|
||||
dashboard_data = {
|
||||
"total_jobs": total_jobs,
|
||||
"active_jobs": active_jobs,
|
||||
"total_printers": total_printers,
|
||||
"available_printers": available_printers,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
db_session.close()
|
||||
return jsonify(dashboard_data)
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler beim Laden der Dashboard-Daten: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Laden der Dashboard-Daten"}), 500
|
||||
|
||||
@app.route("/api/jobs/recent", methods=["GET"])
|
||||
@login_required
|
||||
def api_jobs_recent():
|
||||
"""Letzte Jobs für Dashboard"""
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
limit = request.args.get('limit', 10, type=int)
|
||||
|
||||
recent_jobs = db_session.query(Job).order_by(Job.created_at.desc()).limit(limit).all()
|
||||
|
||||
jobs_data = []
|
||||
for job in recent_jobs:
|
||||
jobs_data.append({
|
||||
"id": job.id,
|
||||
"name": job.name,
|
||||
"status": job.status,
|
||||
"user_name": job.user.name if job.user else "Unbekannt",
|
||||
"printer_name": job.printer.name if job.printer else "Kein Drucker",
|
||||
"created_at": job.created_at.isoformat() if job.created_at else None
|
||||
})
|
||||
|
||||
db_session.close()
|
||||
return jsonify(jobs_data)
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler beim Laden der letzten Jobs: {str(e)}")
|
||||
return jsonify([]), 500
|
||||
|
||||
@app.route("/api/files/upload", methods=["POST"])
|
||||
@login_required
|
||||
def api_files_upload():
|
||||
"""Datei-Upload für Druckaufträge"""
|
||||
try:
|
||||
if 'file' not in request.files:
|
||||
return jsonify({"error": "Keine Datei hochgeladen"}), 400
|
||||
|
||||
file = request.files['file']
|
||||
if file.filename == '':
|
||||
return jsonify({"error": "Keine Datei ausgewählt"}), 400
|
||||
|
||||
# Sicherer Dateiname erstellen
|
||||
filename = secure_filename(file.filename)
|
||||
|
||||
# Upload-Ordner erstellen falls nicht vorhanden
|
||||
upload_folder = os.path.join(app.root_path, 'uploads')
|
||||
os.makedirs(upload_folder, exist_ok=True)
|
||||
|
||||
# Eindeutigen Dateinamen erstellen
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
unique_filename = f"{timestamp}_{filename}"
|
||||
file_path = os.path.join(upload_folder, unique_filename)
|
||||
|
||||
# Datei speichern
|
||||
file.save(file_path)
|
||||
|
||||
# Relative Pfad für Datenbank
|
||||
relative_path = os.path.join('uploads', unique_filename)
|
||||
|
||||
jobs_logger.info(f"Datei hochgeladen: {unique_filename} von Benutzer {current_user.username}")
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"file_path": relative_path,
|
||||
"filename": unique_filename,
|
||||
"original_filename": filename
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
jobs_logger.error(f"Fehler beim Datei-Upload: {str(e)}")
|
||||
return jsonify({"error": f"Fehler beim Datei-Upload: {str(e)}"}), 500
|
||||
|
||||
@app.route("/api/files/download", methods=["GET"])
|
||||
@login_required
|
||||
def api_files_download():
|
||||
"""Datei-Download"""
|
||||
try:
|
||||
file_path = request.args.get('path')
|
||||
if not file_path:
|
||||
return jsonify({"error": "Kein Dateipfad angegeben"}), 400
|
||||
|
||||
# Sicherheitscheck: Nur Dateien im uploads-Ordner erlauben
|
||||
if not file_path.startswith('uploads/'):
|
||||
return jsonify({"error": "Ungültiger Dateipfad"}), 403
|
||||
|
||||
full_path = os.path.join(app.root_path, file_path)
|
||||
|
||||
if not os.path.exists(full_path):
|
||||
return jsonify({"error": "Datei nicht gefunden"}), 404
|
||||
|
||||
return send_file(full_path, as_attachment=True)
|
||||
|
||||
except Exception as e:
|
||||
jobs_logger.error(f"Fehler beim Datei-Download: {str(e)}")
|
||||
return jsonify({"error": f"Fehler beim Datei-Download: {str(e)}"}), 500
|
||||
|
||||
@app.route("/api/stats/total-jobs", methods=["GET"])
|
||||
@login_required
|
||||
def api_stats_total_jobs():
|
||||
"""Gesamtzahl der Jobs"""
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
total = db_session.query(Job).count()
|
||||
db_session.close()
|
||||
return jsonify({"value": total})
|
||||
except Exception as e:
|
||||
return jsonify({"value": 0})
|
||||
|
||||
@app.route("/api/stats/completed-jobs", methods=["GET"])
|
||||
@login_required
|
||||
def api_stats_completed_jobs():
|
||||
"""Anzahl abgeschlossener Jobs"""
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
completed = db_session.query(Job).filter(Job.status == 'completed').count()
|
||||
db_session.close()
|
||||
return jsonify({"value": completed})
|
||||
except Exception as e:
|
||||
return jsonify({"value": 0})
|
||||
|
||||
@app.route("/api/stats/active-printers", methods=["GET"])
|
||||
@login_required
|
||||
def api_stats_active_printers():
|
||||
"""Anzahl aktiver Drucker"""
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
active = db_session.query(Printer).filter(Printer.status.in_(['available', 'busy'])).count()
|
||||
db_session.close()
|
||||
return jsonify({"value": active})
|
||||
except Exception as e:
|
||||
return jsonify({"value": 0})
|
||||
|
||||
@app.route("/api/stats/job-status", methods=["GET"])
|
||||
@login_required
|
||||
def api_stats_job_status():
|
||||
"""Job-Status-Verteilung für Charts"""
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
from sqlalchemy import func
|
||||
|
||||
status_counts = db_session.query(
|
||||
Job.status,
|
||||
func.count(Job.id).label('count')
|
||||
).group_by(Job.status).all()
|
||||
|
||||
data = [{"status": status, "count": count} for status, count in status_counts]
|
||||
db_session.close()
|
||||
return jsonify(data)
|
||||
except Exception as e:
|
||||
return jsonify([])
|
||||
|
||||
@app.route("/api/stats/printer-usage", methods=["GET"])
|
||||
@login_required
|
||||
def api_stats_printer_usage():
|
||||
"""Drucker-Nutzungsstatistiken für Charts"""
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
from sqlalchemy import func
|
||||
|
||||
usage_stats = db_session.query(
|
||||
Printer.name,
|
||||
func.count(Job.id).label('job_count')
|
||||
).join(Job, Printer.id == Job.printer_id, isouter=True)\
|
||||
.group_by(Printer.name).all()
|
||||
|
||||
data = [{"printer": name, "jobs": count} for name, count in usage_stats]
|
||||
db_session.close()
|
||||
return jsonify(data)
|
||||
except Exception as e:
|
||||
return jsonify([])
|
||||
|
||||
@app.route("/api/stats/activity", methods=["GET"])
|
||||
@login_required
|
||||
def api_stats_activity():
|
||||
"""Aktivitätsliste für Statistiken"""
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
limit = request.args.get('limit', 10, type=int)
|
||||
|
||||
recent_activity = db_session.query(Job)\
|
||||
.order_by(Job.updated_at.desc())\
|
||||
.limit(limit).all()
|
||||
|
||||
activity_data = []
|
||||
for job in recent_activity:
|
||||
activity_data.append({
|
||||
"id": job.id,
|
||||
"name": job.name,
|
||||
"action": f"Status: {job.status}",
|
||||
"user": job.user.name if job.user else "Unbekannt",
|
||||
"timestamp": job.updated_at.isoformat() if job.updated_at else None
|
||||
})
|
||||
|
||||
db_session.close()
|
||||
return jsonify(activity_data)
|
||||
except Exception as e:
|
||||
return jsonify([])
|
||||
|
||||
@app.route("/api/stats/job-duration", methods=["GET"])
|
||||
@login_required
|
||||
def api_stats_job_duration():
|
||||
"""Durchschnittliche Job-Dauer"""
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
from sqlalchemy import func
|
||||
|
||||
avg_duration = db_session.query(
|
||||
func.avg(Job.print_time)
|
||||
).filter(Job.status == 'completed').scalar()
|
||||
|
||||
db_session.close()
|
||||
return jsonify({"value": round(avg_duration or 0, 2)})
|
||||
except Exception as e:
|
||||
return jsonify({"value": 0})
|
||||
|
||||
@app.route("/api/user/password", methods=["POST"])
|
||||
@login_required
|
||||
def api_user_password():
|
||||
"""Passwort ändern über API"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
current_password = data.get('current_password')
|
||||
new_password = data.get('new_password')
|
||||
|
||||
if not current_password or not new_password:
|
||||
return jsonify({"error": "Aktuelles und neues Passwort erforderlich"}), 400
|
||||
|
||||
db_session = get_db_session()
|
||||
user = db_session.query(User).filter(User.id == current_user.id).first()
|
||||
|
||||
if not user or not user.check_password(current_password):
|
||||
db_session.close()
|
||||
return jsonify({"error": "Aktuelles Passwort ist falsch"}), 401
|
||||
|
||||
user.set_password(new_password)
|
||||
user.updated_at = datetime.now()
|
||||
db_session.commit()
|
||||
db_session.close()
|
||||
|
||||
user_logger.info(f"Benutzer {current_user.username} hat sein Passwort geändert")
|
||||
return jsonify({"success": True, "message": "Passwort erfolgreich geändert"})
|
||||
|
||||
except Exception as e:
|
||||
user_logger.error(f"Fehler beim Passwort ändern: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Passwort ändern"}), 500
|
||||
|
||||
@app.route("/api/user/stats", methods=["GET"])
|
||||
@login_required
|
||||
def api_user_stats():
|
||||
"""Benutzerstatistiken"""
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
|
||||
user_jobs = db_session.query(Job).filter(Job.user_id == current_user.id).count()
|
||||
completed_jobs = db_session.query(Job).filter(
|
||||
Job.user_id == current_user.id,
|
||||
Job.status == 'completed'
|
||||
).count()
|
||||
|
||||
stats = {
|
||||
"total_jobs": user_jobs,
|
||||
"completed_jobs": completed_jobs,
|
||||
"success_rate": round((completed_jobs / user_jobs * 100) if user_jobs > 0 else 0, 1)
|
||||
}
|
||||
|
||||
db_session.close()
|
||||
return jsonify(stats)
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({"total_jobs": 0, "completed_jobs": 0, "success_rate": 0})
|
||||
|
||||
@app.route("/api/system/stats", methods=["GET"])
|
||||
@login_required
|
||||
def api_system_stats():
|
||||
"""System-Statistiken für Admin-Panel"""
|
||||
try:
|
||||
# Basis-Systemstatistiken
|
||||
uptime_days = get_system_uptime_days()
|
||||
|
||||
db_session = get_db_session()
|
||||
total_users = db_session.query(User).count()
|
||||
total_printers = db_session.query(Printer).count()
|
||||
total_jobs = db_session.query(Job).count()
|
||||
db_session.close()
|
||||
|
||||
stats = {
|
||||
"uptime_days": uptime_days,
|
||||
"total_users": total_users,
|
||||
"total_printers": total_printers,
|
||||
"total_jobs": total_jobs,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
return jsonify(stats)
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler beim Laden der System-Statistiken: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Laden der System-Statistiken"}), 500
|
||||
|
||||
@app.route("/api/job/<int:job_id>/remaining-time", methods=["GET"])
|
||||
@login_required
|
||||
@job_owner_required
|
||||
def api_job_remaining_time(job_id):
|
||||
"""Verbleibende Zeit für einen Job berechnen"""
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
job = db_session.query(Job).filter(Job.id == job_id).first()
|
||||
|
||||
if not job:
|
||||
db_session.close()
|
||||
return jsonify({"error": "Job nicht gefunden"}), 404
|
||||
|
||||
# Verbleibende Zeit berechnen
|
||||
if job.status == 'running' and job.start_time:
|
||||
# Berechne geschätzte Endzeit
|
||||
estimated_end_time = job.start_time + timedelta(minutes=job.estimated_time)
|
||||
now = datetime.now()
|
||||
|
||||
if now < estimated_end_time:
|
||||
remaining_seconds = int((estimated_end_time - now).total_seconds())
|
||||
remaining_time = max(0, remaining_seconds * 1000) # Frontend erwartet Millisekunden
|
||||
else:
|
||||
remaining_time = 0
|
||||
else:
|
||||
remaining_time = 0
|
||||
|
||||
db_session.close()
|
||||
return jsonify({
|
||||
"id": job_id,
|
||||
"remainingTime": remaining_time,
|
||||
"status": job.status
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
jobs_logger.error(f"Fehler beim Berechnen der verbleibenden Zeit für Job {job_id}: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Berechnen der verbleibenden Zeit"}), 500
|
||||
|
||||
# Debug-Server API-Routen (nur für Entwicklung)
|
||||
@app.route("/api/test", methods=["GET"])
|
||||
def api_test():
|
||||
"""Test-Endpunkt für Debug-Server"""
|
||||
return jsonify({
|
||||
"status": "ok",
|
||||
"message": "Flask Backend ist erreichbar",
|
||||
"timestamp": datetime.now().isoformat()
|
||||
})
|
||||
|
||||
@app.route("/api/schedule", methods=["GET"])
|
||||
@login_required
|
||||
def api_schedule():
|
||||
"""Scheduler-Informationen"""
|
||||
try:
|
||||
from utils.job_scheduler import scheduler
|
||||
|
||||
schedule_info = {
|
||||
"enabled": SCHEDULER_ENABLED,
|
||||
"interval": SCHEDULER_INTERVAL,
|
||||
"is_running": scheduler.is_running() if hasattr(scheduler, 'is_running') else False,
|
||||
"next_run": None # Könnte erweitert werden
|
||||
}
|
||||
|
||||
return jsonify(schedule_info)
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler beim Laden der Scheduler-Informationen: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Laden der Scheduler-Informationen"}), 500
|
||||
|
||||
@app.route("/api/status", methods=["GET"])
|
||||
def api_status():
|
||||
"""System-Status für externe Überwachung"""
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
|
||||
# Grundlegende Gesundheitsprüfung der Datenbank
|
||||
try:
|
||||
db_session.execute("SELECT 1")
|
||||
db_healthy = True
|
||||
except Exception:
|
||||
db_healthy = False
|
||||
finally:
|
||||
db_session.close()
|
||||
|
||||
status = {
|
||||
"status": "healthy" if db_healthy else "unhealthy",
|
||||
"database": "connected" if db_healthy else "disconnected",
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"version": "1.0.0" # Könnte aus einer Konfiguration gelesen werden
|
||||
}
|
||||
|
||||
return jsonify(status), 200 if db_healthy else 503
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler bei der Status-Prüfung: {str(e)}")
|
||||
return jsonify({
|
||||
"status": "error",
|
||||
"error": str(e),
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}), 500
|
||||
|
||||
# Admin-Dashboard API-Routen für Frontend-Integration
|
||||
@app.route("/api/admin/users/create", methods=["POST"])
|
||||
@login_required
|
||||
def api_admin_create_user():
|
||||
"""API-Endpunkt zum Erstellen eines neuen Benutzers (Admin only)"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({"error": "Keine Berechtigung"}), 403
|
||||
|
||||
try:
|
||||
data = request.get_json()
|
||||
|
||||
# Pflichtfelder prüfen
|
||||
required_fields = ["email", "password", "username"]
|
||||
for field in required_fields:
|
||||
if field not in data or not data[field]:
|
||||
return jsonify({"error": f"Feld '{field}' ist erforderlich"}), 400
|
||||
|
||||
db_session = get_db_session()
|
||||
|
||||
# Prüfen, ob Benutzer bereits existiert
|
||||
existing_user = db_session.query(User).filter(
|
||||
sqlalchemy.or_(User.email == data["email"], User.username == data["username"])
|
||||
).first()
|
||||
|
||||
if existing_user:
|
||||
db_session.close()
|
||||
return jsonify({"error": "Benutzer mit dieser E-Mail oder diesem Benutzernamen existiert bereits"}), 400
|
||||
|
||||
# Neuen Benutzer erstellen
|
||||
new_user = User(
|
||||
email=data["email"],
|
||||
username=data["username"],
|
||||
name=data.get("name", ""),
|
||||
role=data.get("role", "user"),
|
||||
active=data.get("active", True),
|
||||
created_at=datetime.now()
|
||||
)
|
||||
|
||||
new_user.set_password(data["password"])
|
||||
|
||||
db_session.add(new_user)
|
||||
db_session.commit()
|
||||
|
||||
user_dict = new_user.to_dict()
|
||||
db_session.close()
|
||||
|
||||
auth_logger.info(f"Neuer Benutzer {data['email']} wurde von Admin {current_user.username} erstellt")
|
||||
return jsonify({"success": True, "user": user_dict}), 201
|
||||
|
||||
except Exception as e:
|
||||
auth_logger.error(f"Fehler beim Erstellen des Benutzers: {str(e)}")
|
||||
return jsonify({"error": f"Fehler beim Erstellen des Benutzers: {str(e)}"}), 500
|
||||
|
||||
@app.route("/api/admin/users/<int:user_id>/edit", methods=["PUT"])
|
||||
@login_required
|
||||
def api_admin_edit_user(user_id):
|
||||
"""API-Endpunkt zum Bearbeiten eines Benutzers (Admin only)"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({"error": "Keine Berechtigung"}), 403
|
||||
|
||||
try:
|
||||
data = request.get_json()
|
||||
|
||||
db_session = get_db_session()
|
||||
user = db_session.query(User).filter(User.id == user_id).first()
|
||||
|
||||
if not user:
|
||||
db_session.close()
|
||||
return jsonify({"error": "Benutzer nicht gefunden"}), 404
|
||||
|
||||
# Benutzerdaten aktualisieren
|
||||
if "email" in data:
|
||||
user.email = data["email"]
|
||||
if "username" in data:
|
||||
user.username = data["username"]
|
||||
if "name" in data:
|
||||
user.name = data["name"]
|
||||
if "role" in data:
|
||||
user.role = data["role"]
|
||||
if "active" in data:
|
||||
user.active = data["active"]
|
||||
if "password" in data and data["password"]:
|
||||
user.set_password(data["password"])
|
||||
|
||||
user.updated_at = datetime.now()
|
||||
|
||||
db_session.commit()
|
||||
|
||||
user_dict = user.to_dict()
|
||||
db_session.close()
|
||||
|
||||
auth_logger.info(f"Benutzer {user.email} wurde von Admin {current_user.username} bearbeitet")
|
||||
return jsonify({"success": True, "user": user_dict})
|
||||
|
||||
except Exception as e:
|
||||
auth_logger.error(f"Fehler beim Bearbeiten des Benutzers {user_id}: {str(e)}")
|
||||
return jsonify({"error": f"Fehler beim Bearbeiten des Benutzers: {str(e)}"}), 500
|
||||
|
||||
@app.route("/api/admin/users/<int:user_id>/toggle", methods=["POST"])
|
||||
@login_required
|
||||
def api_admin_toggle_user(user_id):
|
||||
"""API-Endpunkt zum Aktivieren/Deaktivieren eines Benutzers (Admin only)"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({"error": "Keine Berechtigung"}), 403
|
||||
|
||||
# Verhindern, dass Admin sich selbst deaktiviert
|
||||
if user_id == current_user.id:
|
||||
return jsonify({"error": "Sie können sich nicht selbst deaktivieren"}), 400
|
||||
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
user = db_session.query(User).filter(User.id == user_id).first()
|
||||
|
||||
if not user:
|
||||
db_session.close()
|
||||
return jsonify({"error": "Benutzer nicht gefunden"}), 404
|
||||
|
||||
# Status umschalten
|
||||
user.active = not user.active
|
||||
user.updated_at = datetime.now()
|
||||
|
||||
db_session.commit()
|
||||
|
||||
user_dict = user.to_dict()
|
||||
db_session.close()
|
||||
|
||||
status = "aktiviert" if user.active else "deaktiviert"
|
||||
auth_logger.info(f"Benutzer {user.email} wurde von Admin {current_user.username} {status}")
|
||||
|
||||
return jsonify({"success": True, "user": user_dict, "message": f"Benutzer wurde {status}"})
|
||||
|
||||
except Exception as e:
|
||||
auth_logger.error(f"Fehler beim Umschalten des Benutzerstatus {user_id}: {str(e)}")
|
||||
return jsonify({"error": f"Fehler beim Umschalten des Benutzerstatus: {str(e)}"}), 500
|
||||
|
||||
@app.route("/api/admin/printers/create", methods=["POST"])
|
||||
@login_required
|
||||
def api_admin_create_printer():
|
||||
"""API-Endpunkt zum Erstellen eines neuen Druckers (Admin only)"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({"error": "Keine Berechtigung"}), 403
|
||||
|
||||
try:
|
||||
data = request.get_json()
|
||||
|
||||
# Pflichtfelder prüfen
|
||||
required_fields = ["name", "model", "location", "ip_address"]
|
||||
for field in required_fields:
|
||||
if field not in data or not data[field]:
|
||||
return jsonify({"error": f"Feld '{field}' ist erforderlich"}), 400
|
||||
|
||||
db_session = get_db_session()
|
||||
|
||||
# Prüfen, ob Drucker bereits existiert
|
||||
existing_printer = db_session.query(Printer).filter(
|
||||
sqlalchemy.or_(Printer.name == data["name"], Printer.ip_address == data["ip_address"])
|
||||
).first()
|
||||
|
||||
if existing_printer:
|
||||
db_session.close()
|
||||
return jsonify({"error": "Drucker mit diesem Namen oder dieser IP-Adresse existiert bereits"}), 400
|
||||
|
||||
# Neuen Drucker erstellen
|
||||
new_printer = Printer(
|
||||
name=data["name"],
|
||||
model=data["model"],
|
||||
location=data["location"],
|
||||
ip_address=data["ip_address"],
|
||||
plug_ip=data.get("plug_ip"),
|
||||
plug_username=data.get("plug_username"),
|
||||
plug_password=data.get("plug_password"),
|
||||
mac_address=data.get("mac_address"),
|
||||
status="offline",
|
||||
active=data.get("active", True),
|
||||
created_at=datetime.now()
|
||||
)
|
||||
|
||||
db_session.add(new_printer)
|
||||
db_session.commit()
|
||||
|
||||
printer_dict = new_printer.to_dict()
|
||||
db_session.close()
|
||||
|
||||
printers_logger.info(f"Neuer Drucker {data['name']} wurde von Admin {current_user.username} erstellt")
|
||||
return jsonify({"success": True, "printer": printer_dict}), 201
|
||||
|
||||
except Exception as e:
|
||||
printers_logger.error(f"Fehler beim Erstellen des Druckers: {str(e)}")
|
||||
return jsonify({"error": f"Fehler beim Erstellen des Druckers: {str(e)}"}), 500
|
||||
|
||||
@app.route("/api/admin/printers/<int:printer_id>/edit", methods=["PUT"])
|
||||
@login_required
|
||||
def api_admin_edit_printer(printer_id):
|
||||
"""API-Endpunkt zum Bearbeiten eines Druckers (Admin only)"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({"error": "Keine Berechtigung"}), 403
|
||||
|
||||
try:
|
||||
data = request.get_json()
|
||||
|
||||
db_session = get_db_session()
|
||||
printer = db_session.query(Printer).filter(Printer.id == printer_id).first()
|
||||
|
||||
if not printer:
|
||||
db_session.close()
|
||||
return jsonify({"error": "Drucker nicht gefunden"}), 404
|
||||
|
||||
# Druckerdaten aktualisieren
|
||||
if "name" in data:
|
||||
printer.name = data["name"]
|
||||
if "model" in data:
|
||||
printer.model = data["model"]
|
||||
if "location" in data:
|
||||
printer.location = data["location"]
|
||||
if "ip_address" in data:
|
||||
printer.ip_address = data["ip_address"]
|
||||
if "plug_ip" in data:
|
||||
printer.plug_ip = data["plug_ip"]
|
||||
if "plug_username" in data:
|
||||
printer.plug_username = data["plug_username"]
|
||||
if "plug_password" in data:
|
||||
printer.plug_password = data["plug_password"]
|
||||
if "mac_address" in data:
|
||||
printer.mac_address = data["mac_address"]
|
||||
if "active" in data:
|
||||
printer.active = data["active"]
|
||||
|
||||
printer.updated_at = datetime.now()
|
||||
|
||||
db_session.commit()
|
||||
|
||||
printer_dict = printer.to_dict()
|
||||
db_session.close()
|
||||
|
||||
printers_logger.info(f"Drucker {printer.name} wurde von Admin {current_user.username} bearbeitet")
|
||||
return jsonify({"success": True, "printer": printer_dict})
|
||||
|
||||
except Exception as e:
|
||||
printers_logger.error(f"Fehler beim Bearbeiten des Druckers {printer_id}: {str(e)}")
|
||||
return jsonify({"error": f"Fehler beim Bearbeiten des Druckers: {str(e)}"}), 500
|
||||
|
||||
@app.route("/api/admin/printers/<int:printer_id>/toggle", methods=["POST"])
|
||||
@login_required
|
||||
def api_admin_toggle_printer(printer_id):
|
||||
"""API-Endpunkt zum Aktivieren/Deaktivieren eines Druckers (Admin only)"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({"error": "Keine Berechtigung"}), 403
|
||||
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
printer = db_session.query(Printer).filter(Printer.id == printer_id).first()
|
||||
|
||||
if not printer:
|
||||
db_session.close()
|
||||
return jsonify({"error": "Drucker nicht gefunden"}), 404
|
||||
|
||||
# Status umschalten
|
||||
printer.active = not printer.active
|
||||
printer.updated_at = datetime.now()
|
||||
|
||||
db_session.commit()
|
||||
|
||||
printer_dict = printer.to_dict()
|
||||
db_session.close()
|
||||
|
||||
status = "aktiviert" if printer.active else "deaktiviert"
|
||||
printers_logger.info(f"Drucker {printer.name} wurde von Admin {current_user.username} {status}")
|
||||
|
||||
return jsonify({"success": True, "printer": printer_dict, "message": f"Drucker wurde {status}"})
|
||||
|
||||
except Exception as e:
|
||||
printers_logger.error(f"Fehler beim Umschalten des Druckerstatus {printer_id}: {str(e)}")
|
||||
return jsonify({"error": f"Fehler beim Umschalten des Druckerstatus: {str(e)}"}), 500
|
||||
|
||||
@app.route("/api/admin/jobs/cancel/<int:job_id>", methods=["POST"])
|
||||
@login_required
|
||||
def api_admin_cancel_job(job_id):
|
||||
"""API-Endpunkt zum Abbrechen eines Jobs (Admin only)"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({"error": "Keine Berechtigung"}), 403
|
||||
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
job = db_session.query(Job).filter(Job.id == job_id).first()
|
||||
|
||||
if not job:
|
||||
db_session.close()
|
||||
return jsonify({"error": "Job nicht gefunden"}), 404
|
||||
|
||||
# Nur laufende oder geplante Jobs können abgebrochen werden
|
||||
if job.status not in ["scheduled", "running"]:
|
||||
db_session.close()
|
||||
return jsonify({"error": f"Job im Status '{job.status}' kann nicht abgebrochen werden"}), 400
|
||||
|
||||
# Job abbrechen
|
||||
job.status = "cancelled"
|
||||
job.actual_end_time = datetime.now()
|
||||
|
||||
# Steckdose ausschalten falls Job lief
|
||||
if job.status == "running":
|
||||
from utils.job_scheduler import toggle_plug
|
||||
toggle_plug(job.printer_id, False)
|
||||
|
||||
db_session.commit()
|
||||
|
||||
job_dict = job.to_dict()
|
||||
db_session.close()
|
||||
|
||||
jobs_logger.info(f"Job {job_id} wurde von Admin {current_user.username} abgebrochen")
|
||||
return jsonify({"success": True, "job": job_dict, "message": "Job wurde abgebrochen"})
|
||||
|
||||
except Exception as e:
|
||||
jobs_logger.error(f"Fehler beim Abbrechen des Jobs {job_id}: {str(e)}")
|
||||
return jsonify({"error": f"Fehler beim Abbrechen des Jobs: {str(e)}"}), 500
|
||||
|
||||
@app.route("/api/admin/system/info", methods=["GET"])
|
||||
@login_required
|
||||
def api_admin_system_info():
|
||||
"""Erweiterte System-Informationen für Admin-Dashboard"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({"error": "Keine Berechtigung"}), 403
|
||||
|
||||
try:
|
||||
import psutil
|
||||
import platform
|
||||
|
||||
# System-Grundinformationen
|
||||
system_info = {
|
||||
"platform": platform.platform(),
|
||||
"python_version": platform.python_version(),
|
||||
"architecture": platform.architecture()[0],
|
||||
"processor": platform.processor(),
|
||||
"hostname": platform.node(),
|
||||
"uptime": get_system_uptime_days()
|
||||
}
|
||||
|
||||
# Hardware-Informationen
|
||||
hardware_info = {
|
||||
"cpu_count": psutil.cpu_count(),
|
||||
"cpu_freq": psutil.cpu_freq()._asdict() if psutil.cpu_freq() else None,
|
||||
"memory_total": psutil.virtual_memory().total,
|
||||
"memory_available": psutil.virtual_memory().available,
|
||||
"disk_total": psutil.disk_usage('/').total,
|
||||
"disk_free": psutil.disk_usage('/').free
|
||||
}
|
||||
|
||||
# Datenbank-Informationen
|
||||
db_session = get_db_session()
|
||||
db_info = {
|
||||
"users_count": db_session.query(User).count(),
|
||||
"printers_count": db_session.query(Printer).count(),
|
||||
"jobs_count": db_session.query(Job).count(),
|
||||
"active_jobs": db_session.query(Job).filter(Job.status.in_(["scheduled", "running"])).count()
|
||||
}
|
||||
db_session.close()
|
||||
|
||||
# Anwendungs-Informationen
|
||||
app_info = {
|
||||
"version": "3.0.0", # Könnte aus einer Konfiguration gelesen werden
|
||||
"debug_mode": app.debug,
|
||||
"flask_env": os.environ.get('FLASK_ENV', 'production'),
|
||||
"scheduler_enabled": SCHEDULER_ENABLED,
|
||||
"ssl_enabled": SSL_ENABLED
|
||||
}
|
||||
|
||||
return jsonify({
|
||||
"system": system_info,
|
||||
"hardware": hardware_info,
|
||||
"database": db_info,
|
||||
"application": app_info,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler beim Abrufen der System-Informationen: {str(e)}")
|
||||
return jsonify({"error": f"Fehler beim Abrufen der System-Informationen: {str(e)}"}), 500
|
||||
|
||||
@app.route("/api/admin/logs/download", methods=["GET"])
|
||||
@login_required
|
||||
def api_admin_download_logs():
|
||||
"""Download der System-Logs als ZIP-Datei (Admin only)"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({"error": "Keine Berechtigung"}), 403
|
||||
|
||||
try:
|
||||
import zipfile
|
||||
import tempfile
|
||||
|
||||
# Temporäre ZIP-Datei erstellen
|
||||
temp_zip = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
|
||||
|
||||
with zipfile.ZipFile(temp_zip.name, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
||||
log_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logs')
|
||||
|
||||
# Alle Log-Dateien zur ZIP hinzufügen
|
||||
for root, dirs, files in os.walk(log_dir):
|
||||
for file in files:
|
||||
if file.endswith('.log'):
|
||||
file_path = os.path.join(root, file)
|
||||
arcname = os.path.relpath(file_path, log_dir)
|
||||
zipf.write(file_path, arcname)
|
||||
|
||||
# ZIP-Datei als Download senden
|
||||
return send_file(
|
||||
temp_zip.name,
|
||||
as_attachment=True,
|
||||
download_name=f"system_logs_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip",
|
||||
mimetype='application/zip'
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler beim Download der Logs: {str(e)}")
|
||||
return jsonify({"error": f"Fehler beim Download der Logs: {str(e)}"}), 500
|
||||
|
||||
@app.route("/api/admin/maintenance/run", methods=["POST"])
|
||||
@login_required
|
||||
def api_admin_run_maintenance():
|
||||
"""Führt Wartungsroutinen aus (Admin only)"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({"error": "Keine Berechtigung"}), 403
|
||||
|
||||
try:
|
||||
maintenance_results = []
|
||||
|
||||
# Cache leeren
|
||||
try:
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
cache_dir = os.path.join(tempfile.gettempdir(), 'flask_cache')
|
||||
if os.path.exists(cache_dir):
|
||||
shutil.rmtree(cache_dir)
|
||||
maintenance_results.append("Cache geleert")
|
||||
except Exception as e:
|
||||
maintenance_results.append(f"Cache-Fehler: {str(e)}")
|
||||
|
||||
# Datenbank optimieren
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
db_session.execute(sqlalchemy.text("VACUUM"))
|
||||
db_session.execute(sqlalchemy.text("ANALYZE"))
|
||||
db_session.commit()
|
||||
db_session.close()
|
||||
maintenance_results.append("Datenbank optimiert")
|
||||
except Exception as e:
|
||||
maintenance_results.append(f"DB-Fehler: {str(e)}")
|
||||
|
||||
# Alte Log-Dateien komprimieren (älter als 7 Tage)
|
||||
try:
|
||||
import gzip
|
||||
log_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logs')
|
||||
seven_days_ago = datetime.now() - timedelta(days=7)
|
||||
|
||||
compressed_files = 0
|
||||
for root, dirs, files in os.walk(log_dir):
|
||||
for file in files:
|
||||
if file.endswith('.log'):
|
||||
file_path = os.path.join(root, file)
|
||||
file_stat = os.stat(file_path)
|
||||
file_age = datetime.fromtimestamp(file_stat.st_mtime)
|
||||
|
||||
if file_age < seven_days_ago:
|
||||
# Datei komprimieren
|
||||
with open(file_path, 'rb') as f_in:
|
||||
with gzip.open(f"{file_path}.gz", 'wb') as f_out:
|
||||
shutil.copyfileobj(f_in, f_out)
|
||||
os.remove(file_path)
|
||||
compressed_files += 1
|
||||
|
||||
if compressed_files > 0:
|
||||
maintenance_results.append(f"{compressed_files} Log-Dateien komprimiert")
|
||||
else:
|
||||
maintenance_results.append("Keine Log-Dateien zu komprimieren")
|
||||
except Exception as e:
|
||||
maintenance_results.append(f"Log-Komprimierung-Fehler: {str(e)}")
|
||||
|
||||
app_logger.info(f"Wartung wurde von Admin {current_user.username} durchgeführt: {', '.join(maintenance_results)}")
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": "Wartung erfolgreich durchgeführt",
|
||||
"results": maintenance_results
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler bei der Wartung: {str(e)}")
|
||||
return jsonify({"error": f"Fehler bei der Wartung: {str(e)}"}), 500
|
@ -86,3 +86,67 @@ def logout():
|
||||
return jsonify({"success": True, "redirect_url": url_for("auth.login")})
|
||||
else:
|
||||
return redirect(url_for("auth.login"))
|
||||
|
||||
@auth_bp.route("/api/login", methods=["POST"])
|
||||
def api_login():
|
||||
"""API-Login-Endpunkt für Frontend"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"error": "Keine Daten erhalten"}), 400
|
||||
|
||||
username = data.get("username")
|
||||
password = data.get("password")
|
||||
remember_me = data.get("remember_me", False)
|
||||
|
||||
if not username or not password:
|
||||
return jsonify({"error": "Benutzername und Passwort müssen angegeben werden"}), 400
|
||||
|
||||
db_session = get_db_session()
|
||||
user = db_session.query(User).filter(
|
||||
(User.username == username) | (User.email == username)
|
||||
).first()
|
||||
|
||||
if user and user.check_password(password):
|
||||
login_user(user, remember=remember_me)
|
||||
auth_logger.info(f"API-Login erfolgreich für Benutzer {username}")
|
||||
|
||||
user_data = {
|
||||
"id": user.id,
|
||||
"username": user.username,
|
||||
"name": user.name,
|
||||
"email": user.email,
|
||||
"is_admin": user.is_admin
|
||||
}
|
||||
|
||||
db_session.close()
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"user": user_data,
|
||||
"redirect_url": url_for("index")
|
||||
})
|
||||
else:
|
||||
auth_logger.warning(f"Fehlgeschlagener API-Login für Benutzer {username}")
|
||||
db_session.close()
|
||||
return jsonify({"error": "Ungültiger Benutzername oder Passwort"}), 401
|
||||
|
||||
except Exception as e:
|
||||
auth_logger.error(f"Fehler beim API-Login: {str(e)}")
|
||||
return jsonify({"error": "Anmeldefehler. Bitte versuchen Sie es später erneut"}), 500
|
||||
|
||||
@auth_bp.route("/api/callback", methods=["GET", "POST"])
|
||||
def api_callback():
|
||||
"""OAuth-Callback-Endpunkt für externe Authentifizierung"""
|
||||
try:
|
||||
# Dieser Endpunkt würde für OAuth-Integration verwendet werden
|
||||
# Hier könnte GitHub/OAuth-Code verarbeitet werden
|
||||
|
||||
# Placeholder für OAuth-Integration
|
||||
return jsonify({
|
||||
"message": "OAuth-Callback noch nicht implementiert",
|
||||
"redirect_url": url_for("auth.login")
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
auth_logger.error(f"Fehler im OAuth-Callback: {str(e)}")
|
||||
return jsonify({"error": "OAuth-Callback-Fehler"}), 500
|
@ -111,7 +111,7 @@ mkdir -p "$APP_DIR/logs/jobs"
|
||||
mkdir -p "$APP_DIR/logs/printers"
|
||||
mkdir -p "$APP_DIR/logs/scheduler"
|
||||
mkdir -p "$APP_DIR/logs/errors"
|
||||
mkdir -p "$BACKEND_DIR/certs"
|
||||
mkdir -p "$APP_DIR/certs"
|
||||
mkdir -p "$PROJECT_DIR/frontend/ssl"
|
||||
|
||||
# Berechtigungen setzen
|
||||
@ -119,7 +119,7 @@ log "8. Berechtigungen setzen..."
|
||||
chown -R $USER:$USER "$PROJECT_DIR"
|
||||
chmod -R 755 "$PROJECT_DIR"
|
||||
chmod -R 700 "$APP_DIR/logs"
|
||||
chmod -R 700 "$BACKEND_DIR/certs"
|
||||
chmod -R 700 "$APP_DIR/certs"
|
||||
|
||||
# Datenbank initialisieren
|
||||
log "9. Datenbank initialisieren..."
|
||||
@ -196,8 +196,8 @@ server {
|
||||
server_name raspberrypi localhost;
|
||||
|
||||
# SSL-Konfiguration
|
||||
ssl_certificate $BACKEND_DIR/certs/myp.crt;
|
||||
ssl_certificate_key $BACKEND_DIR/certs/myp.key;
|
||||
ssl_certificate $APP_DIR/certs/myp.crt;
|
||||
ssl_certificate_key $APP_DIR/certs/myp.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
@ -313,7 +313,7 @@ log "📁 Wichtige Verzeichnisse:"
|
||||
log " • Anwendung: $APP_DIR"
|
||||
log " • Logs: $APP_DIR/logs"
|
||||
log " • Datenbank: $APP_DIR/database/myp.db"
|
||||
log " • SSL-Zertifikate: $BACKEND_DIR/certs"
|
||||
log " • SSL-Zertifikate: $APP_DIR/certs"
|
||||
log ""
|
||||
log "⚠️ Hinweise:"
|
||||
log " • Das SSL-Zertifikat ist selbstsigniert"
|
||||
|
@ -23,7 +23,7 @@ done
|
||||
|
||||
# Execute docker compose
|
||||
echo "Running docker compose..."
|
||||
docker compose -f "docker/compose.yml" up -d
|
||||
docker compose -f "compose.yml" up -d
|
||||
|
||||
# Check if the operation was successful
|
||||
if [ $? -eq 0 ]; then
|
||||
|
@ -476,8 +476,8 @@ function Show-SSLStatus {
|
||||
Show-Header "SSL-Zertifikat-Status"
|
||||
|
||||
$certPaths = @(
|
||||
"backend\instance\ssl\myp.crt",
|
||||
"backend\instance\ssl\myp.key",
|
||||
"backend\app\certs\myp.crt",
|
||||
"backend\app\certs\myp.key",
|
||||
"frontend\ssl\myp.crt",
|
||||
"frontend\ssl\myp.key"
|
||||
)
|
||||
@ -610,7 +610,7 @@ function Create-SSLCertificates {
|
||||
Show-Header "SSL-Zertifikat-Generator"
|
||||
|
||||
# Parameter definieren
|
||||
$certDir = "./backend/instance/ssl"
|
||||
$certDir = "./backend/app/certs"
|
||||
$backendCertFile = "$certDir/myp.crt"
|
||||
$backendKeyFile = "$certDir/myp.key"
|
||||
$frontendCertFile = "$certDir/frontend.crt"
|
||||
|
1477
myp_installer.sh
1477
myp_installer.sh
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user