""" API-Blueprint für das 3D-Druck-Management-System Dieses Modul enthält allgemeine API-Endpunkte und WebSocket-Fallback-Funktionalität. """ import logging from datetime import datetime from flask import Blueprint, jsonify, request, session from flask_login import login_required, current_user from models import get_db_session, User, Notification from utils.logging_config import get_logger # Blueprint erstellen api_blueprint = Blueprint('api', __name__, url_prefix='/api') # Logger initialisieren api_logger = get_logger("api") @api_blueprint.route('/ws-fallback', methods=['GET']) @login_required def ws_fallback(): """WebSocket-Fallback für Browser ohne WebSocket-Unterstützung""" try: # Einfache Polling-Antwort für Clients ohne WebSocket return jsonify({ 'success': True, 'timestamp': datetime.now().isoformat(), 'user_id': current_user.id, 'message': 'WebSocket-Fallback aktiv' }) except Exception as e: api_logger.error(f"Fehler im WebSocket-Fallback: {str(e)}") return jsonify({'error': 'WebSocket-Fallback-Fehler'}), 500 @api_blueprint.route('/notifications', methods=['GET']) @login_required def get_notifications(): """Abrufen der Benutzer-Benachrichtigungen""" try: db_session = get_db_session() # Benutzer-spezifische Benachrichtigungen notifications = db_session.query(Notification).filter( Notification.user_id == current_user.id, Notification.is_read == False ).order_by(Notification.created_at.desc()).limit(20).all() notification_list = [] for notification in notifications: notification_list.append({ 'id': notification.id, 'title': notification.title, 'message': notification.message, 'type': notification.type, 'created_at': notification.created_at.isoformat(), 'is_read': notification.is_read }) db_session.close() return jsonify({ 'success': True, 'notifications': notification_list, 'count': len(notification_list) }) except Exception as e: api_logger.error(f"Fehler beim Abrufen der Benachrichtigungen: {str(e)}") return jsonify({'error': 'Fehler beim Laden der Benachrichtigungen'}), 500 @api_blueprint.route('/notifications//read', methods=['POST']) @login_required def mark_notification_read(notification_id): """Markiert eine Benachrichtigung als gelesen""" try: db_session = get_db_session() notification = db_session.query(Notification).filter( Notification.id == notification_id, Notification.user_id == current_user.id ).first() if not notification: db_session.close() return jsonify({'error': 'Benachrichtigung nicht gefunden'}), 404 notification.is_read = True notification.read_at = datetime.now() db_session.commit() db_session.close() api_logger.info(f"Benachrichtigung {notification_id} als gelesen markiert") return jsonify({ 'success': True, 'message': 'Benachrichtigung als gelesen markiert' }) except Exception as e: api_logger.error(f"Fehler beim Markieren der Benachrichtigung: {str(e)}") return jsonify({'error': 'Fehler beim Markieren der Benachrichtigung'}), 500 @api_blueprint.route('/system/status', methods=['GET']) @login_required def system_status(): """Gibt den System-Status zurück""" try: return jsonify({ 'success': True, 'status': 'online', 'timestamp': datetime.now().isoformat(), 'user': { 'id': current_user.id, 'username': current_user.username, 'is_admin': current_user.is_admin } }) except Exception as e: api_logger.error(f"Fehler beim Abrufen des System-Status: {str(e)}") return jsonify({'error': 'System-Status nicht verfügbar'}), 500 @api_blueprint.route('/heartbeat', methods=['POST']) @login_required def heartbeat(): """Heartbeat-Endpunkt für Frontend-Verbindungsmonitoring""" try: # Session-Aktivität NICHT in Cookie speichern # session['last_heartbeat'] = datetime.now().strftime('%H:%M:%S') # ENTFERNT session.permanent = True return jsonify({ 'success': True, 'timestamp': datetime.now().isoformat(), 'user_id': current_user.id }) except Exception as e: api_logger.error(f"Fehler im Heartbeat: {str(e)}") return jsonify({'error': 'Heartbeat-Fehler'}), 500 @api_blueprint.route('/session/status', methods=['GET']) def session_status(): """Gibt den aktuellen Session-Status zurück""" try: if current_user.is_authenticated: # Benutzer ist angemeldet from datetime import timedelta from backend.config.settings import SESSION_LIFETIME # Session-Informationen sammeln session_start = session.get('session_start') last_activity = session.get('last_activity', datetime.now().isoformat()) # Standard Session-Lifetime verwenden max_inactive_minutes = int(SESSION_LIFETIME.total_seconds() / 60) # Verbleibende Zeit berechnen if isinstance(last_activity, str): try: last_activity_dt = datetime.fromisoformat(last_activity.replace('Z', '+00:00')) except: last_activity_dt = datetime.now() else: last_activity_dt = datetime.now() time_since_activity = datetime.now() - last_activity_dt time_left_seconds = max(0, (SESSION_LIFETIME.total_seconds() - time_since_activity.total_seconds())) return jsonify({ 'success': True, 'user': { 'id': current_user.id, 'username': current_user.username, 'email': current_user.email, 'is_admin': current_user.is_admin }, 'session': { 'is_authenticated': True, 'max_inactive_minutes': max_inactive_minutes, 'time_left_seconds': int(time_left_seconds), 'last_activity': last_activity, 'session_start': session_start }, 'timestamp': datetime.now().isoformat() }) else: # Benutzer ist nicht angemeldet return jsonify({ 'success': True, 'user': None, 'session': { 'is_authenticated': False, 'max_inactive_minutes': 0, 'time_left_seconds': 0, 'last_activity': None, 'session_start': None }, 'timestamp': datetime.now().isoformat() }) except Exception as e: api_logger.error(f"Fehler beim Abrufen des Session-Status: {str(e)}") return jsonify({ 'success': False, 'error': 'Session-Status nicht verfügbar', 'message': str(e) }), 500 @api_blueprint.route('/session/heartbeat', methods=['POST']) @login_required def session_heartbeat(): """Session-Heartbeat für automatische Verlängerung""" try: # Letzte Aktivität NICHT in Cookie speichern (Cookie-Größe reduzieren) # session['last_activity'] = datetime.now().isoformat() # ENTFERNT # Session als permanent markieren für Verlängerung session.permanent = True # Verbleibende Session-Zeit berechnen from backend.config.settings import SESSION_LIFETIME time_left_seconds = int(SESSION_LIFETIME.total_seconds()) api_logger.debug(f"Session-Heartbeat von Benutzer {current_user.username}") return jsonify({ 'success': True, 'message': 'Session aktualisiert', 'time_left_seconds': time_left_seconds, 'timestamp': datetime.now().isoformat() }) except Exception as e: api_logger.error(f"Fehler beim Session-Heartbeat: {str(e)}") return jsonify({ 'success': False, 'error': 'Session-Heartbeat fehlgeschlagen', 'message': str(e) }), 500 @api_blueprint.route('/session/extend', methods=['POST']) @login_required def extend_session(): """Verlängert die aktuelle Session""" try: data = request.get_json() or {} extend_minutes = data.get('extend_minutes', 30) # Session verlängern durch Markierung als permanent session.permanent = True # Neue Aktivitätszeit NICHT in Cookie speichern # session['last_activity'] = datetime.now().isoformat() # ENTFERNT api_logger.info(f"Session für Benutzer {current_user.username} um {extend_minutes} Minuten verlängert") return jsonify({ 'success': True, 'message': f'Session um {extend_minutes} Minuten verlängert', 'extended_minutes': extend_minutes, 'timestamp': datetime.now().isoformat() }) except Exception as e: api_logger.error(f"Fehler beim Verlängern der Session: {str(e)}") return jsonify({ 'success': False, 'error': 'Session-Verlängerung fehlgeschlagen', 'message': str(e) }), 500