from flask import Blueprint, jsonify, request, session, current_app from datetime import datetime, timedelta import logging import uuid import json import os from models import User, Job, Printer, SystemLog from database.db_manager import DatabaseManager from flask_login import login_required, current_user api_bp = Blueprint('api', __name__, url_prefix='/api') logger = logging.getLogger(__name__) @api_bp.route('/jobs') def get_jobs(): """Get jobs with optional filtering""" try: db = DatabaseManager() # Get query parameters status = request.args.get('status') limit = request.args.get('limit', type=int) # Use a session for proper relationship handling session = db.get_session() try: from sqlalchemy.orm import joinedload # Get jobs based on filters with eager loading of relationships if status == 'active': jobs_query = session.query(Job).options( joinedload(Job.user), joinedload(Job.printer) ).filter(Job.status == 'running') jobs = jobs_query.all() or [] # Also include pending jobs pending_jobs_query = session.query(Job).options( joinedload(Job.user), joinedload(Job.printer) ).filter(Job.status == 'pending') pending_jobs = pending_jobs_query.all() or [] jobs.extend(pending_jobs) else: jobs_query = session.query(Job).options( joinedload(Job.user), joinedload(Job.printer) ) jobs = jobs_query.all() or [] # Apply limit if limit: jobs = jobs[:limit] # Format jobs for API response - convert all data while session is open formatted_jobs = [] for job in jobs: if hasattr(job, '__dict__'): # Access all relationship data here while the session is still open printer_name = job.printer.name if job.printer else 'Kein Drucker' user_name = job.user.name if job.user else 'Kein Benutzer' job_data = { 'id': getattr(job, 'id', None), 'name': getattr(job, 'name', 'Unbenannter Auftrag'), 'status': getattr(job, 'status', 'unknown'), 'progress': getattr(job, 'progress', 0), 'printer_name': printer_name, 'user_name': user_name, 'created_at': getattr(job, 'created_at', datetime.now()).isoformat() if hasattr(job, 'created_at') else datetime.now().isoformat(), 'estimated_time': getattr(job, 'estimated_time', 0), 'print_time': getattr(job, 'print_time', 0) } formatted_jobs.append(job_data) finally: # Always close the session session.close() return jsonify(formatted_jobs) except Exception as e: logger.error(f"Error getting jobs: {str(e)}") return jsonify([]) @api_bp.route('/printers', methods=['GET']) def get_printers(): """Get all printers""" try: db = DatabaseManager() session = db.get_session() try: printers = session.query(Printer).all() or [] # Format printers for API response formatted_printers = [] for printer in printers: printer_data = { 'id': printer.id, 'name': printer.name, 'model': printer.model, 'location': printer.location, 'mac_address': printer.mac_address, 'plug_ip': printer.plug_ip, 'status': printer.status, 'created_at': printer.created_at.isoformat() if printer.created_at else datetime.now().isoformat() } formatted_printers.append(printer_data) return jsonify({'printers': formatted_printers}) finally: session.close() except Exception as e: logger.error(f"Error getting printers: {str(e)}") return jsonify({'printers': []}) @api_bp.route('/printers', methods=['POST']) @login_required def add_printer(): """Add a new printer""" # Überprüfe, ob der Benutzer Admin-Rechte hat if not current_user.is_admin: return jsonify({'error': 'Nur Administratoren können Drucker hinzufügen'}), 403 try: # Hole die Daten aus dem Request data = request.json if not data: return jsonify({'error': 'Keine Daten erhalten'}), 400 # Überprüfe, ob alle notwendigen Felder vorhanden sind required_fields = ['name', 'model', 'location', 'mac_address', 'plug_ip'] for field in required_fields: if field not in data: return jsonify({'error': f'Feld {field} fehlt'}), 400 # Erstelle ein neues Printer-Objekt new_printer = Printer( name=data['name'], model=data['model'], location=data['location'], mac_address=data['mac_address'], plug_ip=data['plug_ip'], plug_username='admin', # Default-Werte für Plug-Credentials plug_password='admin', # In einer Produktionsumgebung sollten diese sicher gespeichert werden status='available' ) # Speichere den Drucker in der Datenbank db = DatabaseManager() session = db.get_session() try: session.add(new_printer) session.commit() # Gib die Drucker-Daten zurück printer_data = { 'id': new_printer.id, 'name': new_printer.name, 'model': new_printer.model, 'location': new_printer.location, 'mac_address': new_printer.mac_address, 'plug_ip': new_printer.plug_ip, 'status': new_printer.status, 'created_at': new_printer.created_at.isoformat() if new_printer.created_at else datetime.now().isoformat() } return jsonify({'printer': printer_data, 'message': 'Drucker erfolgreich hinzugefügt'}), 201 except Exception as e: session.rollback() logger.error(f"Error adding printer: {str(e)}") return jsonify({'error': f'Fehler beim Hinzufügen des Druckers: {str(e)}'}), 500 finally: session.close() except Exception as e: logger.error(f"Error adding printer: {str(e)}") return jsonify({'error': f'Fehler beim Hinzufügen des Druckers: {str(e)}'}), 500 @api_bp.route('/printers/', methods=['DELETE']) @login_required def delete_printer(printer_id): """Delete a printer by ID""" # Überprüfe, ob der Benutzer Admin-Rechte hat if not current_user.is_admin: return jsonify({'error': 'Nur Administratoren können Drucker löschen'}), 403 try: db = DatabaseManager() session = db.get_session() try: # Finde den Drucker printer = session.query(Printer).filter(Printer.id == printer_id).first() if not printer: return jsonify({'error': f'Drucker mit ID {printer_id} nicht gefunden'}), 404 # Lösche den Drucker session.delete(printer) session.commit() return jsonify({'message': 'Drucker erfolgreich gelöscht'}), 200 except Exception as e: session.rollback() logger.error(f"Error deleting printer: {str(e)}") return jsonify({'error': f'Fehler beim Löschen des Druckers: {str(e)}'}), 500 finally: session.close() except Exception as e: logger.error(f"Error deleting printer: {str(e)}") return jsonify({'error': f'Fehler beim Löschen des Druckers: {str(e)}'}), 500 # API-Route für Admin-Statistiken @api_bp.route('/admin/stats', methods=['GET']) @login_required def get_admin_stats(): if not current_user.is_admin: return jsonify({"error": "Unauthorized"}), 403 # Statistikdaten sammeln total_users = User.query.count() total_printers = Printer.query.count() active_jobs = Job.query.filter_by(status='running').count() # Erfolgsrate berechnen total_completed_jobs = Job.query.filter_by(status='completed').count() total_jobs = Job.query.filter(Job.status.in_(['completed', 'failed'])).count() success_rate = "0%" if total_jobs > 0: success_rate = f"{int((total_completed_jobs / total_jobs) * 100)}%" # Scheduler-Status (Beispiel) scheduler_status = True # In einer echten Anwendung würde hier der tatsächliche Status geprüft # Systeminformationen system_stats = { "cpu_usage": "25%", "memory_usage": "40%", "disk_usage": "30%", "uptime": "3d 12h 45m" } # Drucker-Status printer_status = { "available": Printer.query.filter_by(status='available').count(), "busy": Printer.query.filter_by(status='busy').count(), "offline": Printer.query.filter_by(status='offline').count(), "maintenance": Printer.query.filter_by(status='maintenance').count() } return jsonify({ "total_users": total_users, "total_printers": total_printers, "active_jobs": active_jobs, "success_rate": success_rate, "scheduler_status": scheduler_status, "system_stats": system_stats, "printer_status": printer_status, "last_updated": datetime.now().isoformat() }) # API-Route für Protokolle @api_bp.route('/logs', methods=['GET']) @login_required def get_logs(): if not current_user.is_admin: return jsonify({"error": "Unauthorized"}), 403 # Logs aus der Datenbank abrufen logs = SystemLog.query.order_by(SystemLog.timestamp.desc()).limit(100).all() log_data = [] for log in logs: log_data.append({ "id": log.id, "timestamp": log.timestamp.isoformat(), "level": log.level, "source": log.source, "message": log.message }) return jsonify({"logs": log_data}) # Scheduler-Status abrufen @api_bp.route('/scheduler/status', methods=['GET']) @login_required def get_scheduler_status(): if not current_user.is_admin: return jsonify({"error": "Unauthorized"}), 403 # In einer echten Anwendung würde hier der tatsächliche Status geprüft werden scheduler_active = True # Aktuelle Aufgaben (Beispiel) tasks = [ { "id": 1, "name": "Druckaufträge verarbeiten", "status": "running", "last_run": datetime.now().isoformat(), "next_run": datetime.now().isoformat() }, { "id": 2, "name": "Status-Updates sammeln", "status": "idle", "last_run": datetime.now().isoformat(), "next_run": datetime.now().isoformat() } ] return jsonify({ "active": scheduler_active, "tasks": tasks, "last_updated": datetime.now().isoformat() }) # Scheduler starten @api_bp.route('/scheduler/start', methods=['POST']) @login_required def start_scheduler(): if not current_user.is_admin: return jsonify({"error": "Unauthorized"}), 403 # In einer echten Anwendung würde hier der Scheduler gestartet werden return jsonify({ "success": True, "message": "Scheduler started successfully", "active": True }) # Scheduler stoppen @api_bp.route('/scheduler/stop', methods=['POST']) @login_required def stop_scheduler(): if not current_user.is_admin: return jsonify({"error": "Unauthorized"}), 403 # In einer echten Anwendung würde hier der Scheduler gestoppt werden return jsonify({ "success": True, "message": "Scheduler stopped successfully", "active": False }) # Benutzer abrufen @api_bp.route('/users', methods=['GET']) @login_required def get_users(): if not current_user.is_admin: return jsonify({"error": "Unauthorized"}), 403 users = User.query.all() result = [] for user in users: result.append({ 'id': user.id, 'name': user.name, 'email': user.email, 'is_admin': user.is_admin, 'is_active': user.is_active, 'created_at': user.created_at.isoformat() }) return jsonify({"users": result}) # Neuen Benutzer hinzufügen @api_bp.route('/users', methods=['POST']) @login_required def add_user(): if not current_user.is_admin: return jsonify({"error": "Unauthorized"}), 403 data = request.json if not data: return jsonify({"error": "No data provided"}), 400 required_fields = ['name', 'email', 'password'] for field in required_fields: if field not in data: return jsonify({"error": f"Missing required field: {field}"}), 400 # Prüfen, ob E-Mail bereits existiert existing_user = User.query.filter_by(email=data['email']).first() if existing_user: return jsonify({"error": "Email already exists"}), 400 # Neuen Benutzer erstellen user = User( name=data['name'], email=data['email'], is_admin=data.get('role') == 'admin', is_active=data.get('status') == 'active' ) user.set_password(data['password']) try: db = DatabaseManager() session = db.get_session() session.add(user) session.commit() return jsonify({ "success": True, "message": "User added successfully", "user": { "id": user.id, "name": user.name, "email": user.email, "is_admin": user.is_admin, "is_active": user.is_active, "created_at": user.created_at.isoformat() } }), 201 except Exception as e: db.get_session().rollback() return jsonify({"error": str(e)}), 500 # Benutzer löschen @api_bp.route('/users/', methods=['DELETE']) @login_required def delete_user(user_id): if not current_user.is_admin: return jsonify({"error": "Unauthorized"}), 403 # Benutzer kann sich nicht selbst löschen if current_user.id == user_id: return jsonify({"error": "Cannot delete yourself"}), 400 user = User.query.get(user_id) if not user: return jsonify({"error": "User not found"}), 404 try: db = DatabaseManager() session = db.get_session() session.delete(user) session.commit() return jsonify({"success": True, "message": "User deleted successfully"}) except Exception as e: session.rollback() return jsonify({"error": str(e)}), 500