" feat: Integrate calendar functionality into blueprint system "
This commit is contained in:
280
backend/app/blueprints/calendar.py
Normal file
280
backend/app/blueprints/calendar.py
Normal file
@@ -0,0 +1,280 @@
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from flask import Blueprint, render_template, request, jsonify, redirect, url_for, abort
|
||||
from flask_login import current_user, login_required
|
||||
from sqlalchemy import and_, or_
|
||||
|
||||
from models import Job, Printer, User, UserPermission, get_cached_session
|
||||
from utils.logging_config import get_logger
|
||||
|
||||
calendar_blueprint = Blueprint('calendar', __name__)
|
||||
logger = get_logger("calendar")
|
||||
|
||||
def can_edit_events(user):
|
||||
"""Prüft, ob ein Benutzer Kalendereinträge bearbeiten darf."""
|
||||
if user.is_admin:
|
||||
return True
|
||||
|
||||
with get_cached_session() as db_session:
|
||||
permission = db_session.query(UserPermission).filter_by(user_id=user.id).first()
|
||||
if not permission:
|
||||
return False
|
||||
return permission.can_approve_jobs
|
||||
|
||||
@calendar_blueprint.route('/calendar', methods=['GET'])
|
||||
@login_required
|
||||
def calendar_view():
|
||||
"""Kalender-Ansicht anzeigen."""
|
||||
can_edit = can_edit_events(current_user)
|
||||
|
||||
with get_cached_session() as db_session:
|
||||
printers = db_session.query(Printer).filter_by(active=True).all()
|
||||
|
||||
return render_template('calendar.html',
|
||||
printers=printers,
|
||||
can_edit=can_edit)
|
||||
|
||||
@calendar_blueprint.route('/api/calendar', methods=['GET'])
|
||||
@login_required
|
||||
def api_get_calendar_events():
|
||||
"""Kalendereinträge als JSON für FullCalendar zurückgeben."""
|
||||
try:
|
||||
# Datumsbereich aus Anfrage
|
||||
start_str = request.args.get('from')
|
||||
end_str = request.args.get('to')
|
||||
|
||||
if not start_str or not end_str:
|
||||
# Standardmäßig eine Woche anzeigen
|
||||
start_date = datetime.now()
|
||||
end_date = start_date + timedelta(days=7)
|
||||
else:
|
||||
try:
|
||||
start_date = datetime.fromisoformat(start_str)
|
||||
end_date = datetime.fromisoformat(end_str)
|
||||
except ValueError:
|
||||
return jsonify({"error": "Ungültiges Datumsformat"}), 400
|
||||
|
||||
# Optional: Filter nach Druckern
|
||||
printer_id = request.args.get('printer_id')
|
||||
|
||||
with get_cached_session() as db_session:
|
||||
# Jobs im angegebenen Zeitraum abfragen
|
||||
query = db_session.query(Job).filter(
|
||||
or_(
|
||||
# Jobs, die im Zeitraum beginnen
|
||||
and_(Job.start_at >= start_date, Job.start_at <= end_date),
|
||||
# Jobs, die im Zeitraum enden
|
||||
and_(Job.end_at >= start_date, Job.end_at <= end_date),
|
||||
# Jobs, die den Zeitraum komplett umfassen
|
||||
and_(Job.start_at <= start_date, Job.end_at >= end_date)
|
||||
)
|
||||
)
|
||||
|
||||
if printer_id:
|
||||
query = query.filter(Job.printer_id == printer_id)
|
||||
|
||||
jobs = query.all()
|
||||
|
||||
# Jobs in FullCalendar-Event-Format umwandeln
|
||||
events = []
|
||||
for job in jobs:
|
||||
# Farbe basierend auf Status bestimmen
|
||||
color = "#6B7280" # Grau für pending
|
||||
if job.status == "running":
|
||||
color = "#3B82F6" # Blau für running
|
||||
elif job.status == "finished":
|
||||
color = "#10B981" # Grün für finished
|
||||
elif job.status == "scheduled":
|
||||
color = "#10B981" # Grün für approved
|
||||
elif job.status == "cancelled" or job.status == "failed":
|
||||
color = "#EF4444" # Rot für abgebrochen/fehlgeschlagen
|
||||
|
||||
# Benutzerinformationen laden
|
||||
user = db_session.query(User).filter_by(id=job.user_id).first()
|
||||
user_name = user.name if user else "Unbekannt"
|
||||
|
||||
# Druckerinformationen laden
|
||||
printer = db_session.query(Printer).filter_by(id=job.printer_id).first()
|
||||
printer_name = printer.name if printer else "Unbekannt"
|
||||
|
||||
event = {
|
||||
"id": job.id,
|
||||
"title": job.name,
|
||||
"start": job.start_at.isoformat(),
|
||||
"end": job.end_at.isoformat(),
|
||||
"color": color,
|
||||
"extendedProps": {
|
||||
"status": job.status,
|
||||
"description": job.description,
|
||||
"userName": user_name,
|
||||
"printerId": job.printer_id,
|
||||
"printerName": printer_name
|
||||
}
|
||||
}
|
||||
|
||||
events.append(event)
|
||||
|
||||
return jsonify(events)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Abrufen der Kalendereinträge: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
||||
|
||||
@calendar_blueprint.route('/api/calendar/event', methods=['POST'])
|
||||
@login_required
|
||||
def api_create_calendar_event():
|
||||
"""Neuen Kalendereintrag (Job) erstellen."""
|
||||
# Nur Admins und Benutzer mit can_approve_jobs dürfen Einträge erstellen
|
||||
if not can_edit_events(current_user):
|
||||
return jsonify({"error": "Keine Berechtigung zum Erstellen von Kalendereinträgen"}), 403
|
||||
|
||||
try:
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"error": "Keine Daten erhalten"}), 400
|
||||
|
||||
# Pflichtfelder prüfen
|
||||
title = data.get('title')
|
||||
start = data.get('start')
|
||||
end = data.get('end')
|
||||
printer_id = data.get('printerId')
|
||||
|
||||
if not all([title, start, end, printer_id]):
|
||||
return jsonify({"error": "Titel, Start, Ende und Drucker sind erforderlich"}), 400
|
||||
|
||||
# Datumsfelder konvertieren
|
||||
try:
|
||||
start_date = datetime.fromisoformat(start)
|
||||
end_date = datetime.fromisoformat(end)
|
||||
except ValueError:
|
||||
return jsonify({"error": "Ungültiges Datumsformat"}), 400
|
||||
|
||||
# Dauer in Minuten berechnen
|
||||
duration_minutes = int((end_date - start_date).total_seconds() / 60)
|
||||
|
||||
with get_cached_session() as db_session:
|
||||
# Drucker prüfen
|
||||
printer = db_session.query(Printer).filter_by(id=printer_id).first()
|
||||
if not printer:
|
||||
return jsonify({"error": "Drucker nicht gefunden"}), 404
|
||||
|
||||
# Neuen Job erstellen
|
||||
job = Job(
|
||||
name=title,
|
||||
description=data.get('description', ''),
|
||||
user_id=current_user.id,
|
||||
printer_id=printer_id,
|
||||
start_at=start_date,
|
||||
end_at=end_date,
|
||||
status="scheduled",
|
||||
duration_minutes=duration_minutes,
|
||||
owner_id=current_user.id
|
||||
)
|
||||
|
||||
db_session.add(job)
|
||||
db_session.commit()
|
||||
|
||||
logger.info(f"Neuer Kalendereintrag erstellt: ID {job.id}, Name: {title}")
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"id": job.id,
|
||||
"title": job.name,
|
||||
"start": job.start_at.isoformat(),
|
||||
"end": job.end_at.isoformat(),
|
||||
"status": job.status
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Erstellen des Kalendereintrags: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
||||
|
||||
@calendar_blueprint.route('/api/calendar/event/<int:event_id>', methods=['PUT'])
|
||||
@login_required
|
||||
def api_update_calendar_event(event_id):
|
||||
"""Kalendereintrag (Job) aktualisieren."""
|
||||
# Nur Admins und Benutzer mit can_approve_jobs dürfen Einträge bearbeiten
|
||||
if not can_edit_events(current_user):
|
||||
return jsonify({"error": "Keine Berechtigung zum Bearbeiten von Kalendereinträgen"}), 403
|
||||
|
||||
try:
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"error": "Keine Daten erhalten"}), 400
|
||||
|
||||
with get_cached_session() as db_session:
|
||||
job = db_session.query(Job).filter_by(id=event_id).first()
|
||||
if not job:
|
||||
return jsonify({"error": "Kalendereintrag nicht gefunden"}), 404
|
||||
|
||||
# Felder aktualisieren, die im Request enthalten sind
|
||||
if 'title' in data:
|
||||
job.name = data['title']
|
||||
|
||||
if 'description' in data:
|
||||
job.description = data['description']
|
||||
|
||||
if 'start' in data and 'end' in data:
|
||||
try:
|
||||
start_date = datetime.fromisoformat(data['start'])
|
||||
end_date = datetime.fromisoformat(data['end'])
|
||||
|
||||
job.start_at = start_date
|
||||
job.end_at = end_date
|
||||
job.duration_minutes = int((end_date - start_date).total_seconds() / 60)
|
||||
except ValueError:
|
||||
return jsonify({"error": "Ungültiges Datumsformat"}), 400
|
||||
|
||||
if 'printerId' in data:
|
||||
printer = db_session.query(Printer).filter_by(id=data['printerId']).first()
|
||||
if not printer:
|
||||
return jsonify({"error": "Drucker nicht gefunden"}), 404
|
||||
job.printer_id = data['printerId']
|
||||
|
||||
if 'status' in data:
|
||||
# Status nur ändern, wenn er gültig ist
|
||||
valid_statuses = ["scheduled", "running", "finished", "cancelled"]
|
||||
if data['status'] in valid_statuses:
|
||||
job.status = data['status']
|
||||
|
||||
db_session.commit()
|
||||
|
||||
logger.info(f"Kalendereintrag aktualisiert: ID {job.id}")
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"id": job.id,
|
||||
"title": job.name,
|
||||
"start": job.start_at.isoformat(),
|
||||
"end": job.end_at.isoformat(),
|
||||
"status": job.status
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Aktualisieren des Kalendereintrags: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
||||
|
||||
@calendar_blueprint.route('/api/calendar/event/<int:event_id>', methods=['DELETE'])
|
||||
@login_required
|
||||
def api_delete_calendar_event(event_id):
|
||||
"""Kalendereintrag (Job) löschen."""
|
||||
# Nur Admins und Benutzer mit can_approve_jobs dürfen Einträge löschen
|
||||
if not can_edit_events(current_user):
|
||||
return jsonify({"error": "Keine Berechtigung zum Löschen von Kalendereinträgen"}), 403
|
||||
|
||||
try:
|
||||
with get_cached_session() as db_session:
|
||||
job = db_session.query(Job).filter_by(id=event_id).first()
|
||||
if not job:
|
||||
return jsonify({"error": "Kalendereintrag nicht gefunden"}), 404
|
||||
|
||||
db_session.delete(job)
|
||||
db_session.commit()
|
||||
|
||||
logger.info(f"Kalendereintrag gelöscht: ID {event_id}")
|
||||
|
||||
return jsonify({"success": True})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Löschen des Kalendereintrags: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
||||
349
backend/app/blueprints/guest.py
Normal file
349
backend/app/blueprints/guest.py
Normal file
@@ -0,0 +1,349 @@
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from flask import Blueprint, render_template, request, jsonify, redirect, url_for, abort, session, flash
|
||||
from flask_login import current_user, login_required
|
||||
from functools import wraps
|
||||
from sqlalchemy import desc
|
||||
|
||||
from models import GuestRequest, Job, Printer, User, UserPermission, Notification, get_cached_session
|
||||
from utils.logging_config import get_logger
|
||||
|
||||
guest_blueprint = Blueprint('guest', __name__)
|
||||
logger = get_logger("guest")
|
||||
|
||||
# Hilfsfunktionen
|
||||
def can_approve_jobs(user_id):
|
||||
"""Prüft, ob ein Benutzer Anfragen genehmigen darf."""
|
||||
with get_cached_session() as db_session:
|
||||
permission = db_session.query(UserPermission).filter_by(user_id=user_id).first()
|
||||
if not permission:
|
||||
return False
|
||||
return permission.can_approve_jobs
|
||||
|
||||
def approver_required(f):
|
||||
"""Decorator zur Prüfung der Genehmigungsberechtigung."""
|
||||
@wraps(f)
|
||||
@login_required
|
||||
def decorated_function(*args, **kwargs):
|
||||
if not can_approve_jobs(current_user.id):
|
||||
abort(403, "Keine Berechtigung zum Genehmigen von Anfragen")
|
||||
return f(*args, **kwargs)
|
||||
return decorated_function
|
||||
|
||||
# Gast-Routen
|
||||
@guest_blueprint.route('/request', methods=['GET'])
|
||||
def guest_request_form():
|
||||
"""Formular für Gastanfragen anzeigen."""
|
||||
with get_cached_session() as db_session:
|
||||
printers = db_session.query(Printer).filter_by(active=True).all()
|
||||
return render_template('guest_request.html', printers=printers)
|
||||
|
||||
@guest_blueprint.route('/request/<int:request_id>', methods=['GET'])
|
||||
def guest_request_status(request_id):
|
||||
"""Status einer Gastanfrage anzeigen."""
|
||||
with get_cached_session() as db_session:
|
||||
guest_request = db_session.query(GuestRequest).filter_by(id=request_id).first()
|
||||
if not guest_request:
|
||||
abort(404, "Anfrage nicht gefunden")
|
||||
|
||||
# Nur wenn Status "approved" ist, OTP generieren und anzeigen
|
||||
otp_code = None
|
||||
if guest_request.status == "approved" and not guest_request.otp_code:
|
||||
otp_code = guest_request.generate_otp()
|
||||
db_session.commit()
|
||||
|
||||
# Zugehörigen Job laden, falls vorhanden
|
||||
job = None
|
||||
if guest_request.job_id:
|
||||
job = db_session.query(Job).filter_by(id=guest_request.job_id).first()
|
||||
|
||||
return render_template('guest_status.html',
|
||||
request=guest_request,
|
||||
job=job,
|
||||
otp_code=otp_code)
|
||||
|
||||
# API-Endpunkte
|
||||
@guest_blueprint.route('/api/guest/requests', methods=['POST'])
|
||||
def api_create_guest_request():
|
||||
"""Neue Gastanfrage erstellen."""
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"error": "Keine Daten erhalten"}), 400
|
||||
|
||||
# Pflichtfelder prüfen
|
||||
name = data.get('name')
|
||||
if not name:
|
||||
return jsonify({"error": "Name ist erforderlich"}), 400
|
||||
|
||||
# Optionale Felder
|
||||
email = data.get('email')
|
||||
reason = data.get('reason')
|
||||
duration_min = data.get('duration_min', 60) # Standard: 1 Stunde
|
||||
printer_id = data.get('printer_id')
|
||||
|
||||
# IP-Adresse erfassen
|
||||
author_ip = request.remote_addr
|
||||
|
||||
try:
|
||||
with get_cached_session() as db_session:
|
||||
# Drucker prüfen
|
||||
if printer_id:
|
||||
printer = db_session.query(Printer).filter_by(id=printer_id, active=True).first()
|
||||
if not printer:
|
||||
return jsonify({"error": "Ungültiger Drucker ausgewählt"}), 400
|
||||
|
||||
# Neue Anfrage erstellen
|
||||
guest_request = GuestRequest(
|
||||
name=name,
|
||||
email=email,
|
||||
reason=reason,
|
||||
duration_min=duration_min,
|
||||
printer_id=printer_id,
|
||||
author_ip=author_ip
|
||||
)
|
||||
|
||||
db_session.add(guest_request)
|
||||
db_session.commit()
|
||||
|
||||
# Benachrichtigung für Genehmiger erstellen
|
||||
Notification.create_for_approvers(
|
||||
notification_type="guest_request",
|
||||
payload={
|
||||
"request_id": guest_request.id,
|
||||
"name": guest_request.name,
|
||||
"created_at": guest_request.created_at.isoformat(),
|
||||
"status": guest_request.status
|
||||
}
|
||||
)
|
||||
|
||||
logger.info(f"Neue Gastanfrage erstellt: ID {guest_request.id}, Name: {name}")
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"request_id": guest_request.id,
|
||||
"status": guest_request.status,
|
||||
"redirect_url": url_for('guest.guest_request_status', request_id=guest_request.id)
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Erstellen der Gastanfrage: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
||||
|
||||
@guest_blueprint.route('/api/guest/requests/<int:request_id>', methods=['GET'])
|
||||
def api_get_guest_request(request_id):
|
||||
"""Status einer Gastanfrage abrufen."""
|
||||
try:
|
||||
with get_cached_session() as db_session:
|
||||
guest_request = db_session.query(GuestRequest).filter_by(id=request_id).first()
|
||||
if not guest_request:
|
||||
return jsonify({"error": "Anfrage nicht gefunden"}), 404
|
||||
|
||||
# OTP wird nie über die API zurückgegeben
|
||||
response_data = guest_request.to_dict()
|
||||
response_data.pop("otp_code", None)
|
||||
|
||||
return jsonify(response_data)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Abrufen der Gastanfrage: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
||||
|
||||
@guest_blueprint.route('/api/requests/<int:request_id>/approve', methods=['POST'])
|
||||
@approver_required
|
||||
def api_approve_request(request_id):
|
||||
"""Gastanfrage genehmigen."""
|
||||
try:
|
||||
with get_cached_session() as db_session:
|
||||
guest_request = db_session.query(GuestRequest).filter_by(id=request_id).first()
|
||||
if not guest_request:
|
||||
return jsonify({"error": "Anfrage nicht gefunden"}), 404
|
||||
|
||||
if guest_request.status != "pending":
|
||||
return jsonify({"error": "Anfrage wurde bereits bearbeitet"}), 400
|
||||
|
||||
# Anfrage genehmigen
|
||||
guest_request.status = "approved"
|
||||
|
||||
# OTP generieren
|
||||
otp_plain = guest_request.generate_otp()
|
||||
|
||||
# Zugehörigen Job erstellen
|
||||
start_time = datetime.now() + timedelta(minutes=5) # Start in 5 Minuten
|
||||
end_time = start_time + timedelta(minutes=guest_request.duration_min)
|
||||
|
||||
# Admin-Benutzer als Eigentümer verwenden
|
||||
admin_user = db_session.query(User).filter_by(role="admin").first()
|
||||
|
||||
job = Job(
|
||||
name=f"Gastauftrag: {guest_request.name}",
|
||||
description=guest_request.reason or "Gastauftrag",
|
||||
user_id=admin_user.id,
|
||||
printer_id=guest_request.printer_id,
|
||||
start_at=start_time,
|
||||
end_at=end_time,
|
||||
status="scheduled",
|
||||
duration_minutes=guest_request.duration_min,
|
||||
owner_id=admin_user.id
|
||||
)
|
||||
|
||||
db_session.add(job)
|
||||
db_session.flush() # ID generieren
|
||||
|
||||
# Job-ID in Gastanfrage speichern
|
||||
guest_request.job_id = job.id
|
||||
|
||||
db_session.commit()
|
||||
|
||||
logger.info(f"Gastanfrage {request_id} genehmigt von Benutzer {current_user.id}")
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"status": "approved",
|
||||
"job_id": job.id,
|
||||
"otp": otp_plain # Nur in dieser Antwort wird der OTP-Klartext zurückgegeben
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Genehmigen der Gastanfrage: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
||||
|
||||
@guest_blueprint.route('/api/requests/<int:request_id>/deny', methods=['POST'])
|
||||
@approver_required
|
||||
def api_deny_request(request_id):
|
||||
"""Gastanfrage ablehnen."""
|
||||
try:
|
||||
data = request.get_json() or {}
|
||||
reason = data.get('reason', '')
|
||||
|
||||
with get_cached_session() as db_session:
|
||||
guest_request = db_session.query(GuestRequest).filter_by(id=request_id).first()
|
||||
if not guest_request:
|
||||
return jsonify({"error": "Anfrage nicht gefunden"}), 404
|
||||
|
||||
if guest_request.status != "pending":
|
||||
return jsonify({"error": "Anfrage wurde bereits bearbeitet"}), 400
|
||||
|
||||
# Anfrage ablehnen
|
||||
guest_request.status = "denied"
|
||||
db_session.commit()
|
||||
|
||||
logger.info(f"Gastanfrage {request_id} abgelehnt von Benutzer {current_user.id}")
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"status": "denied"
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Ablehnen der Gastanfrage: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
||||
|
||||
@guest_blueprint.route('/api/jobs/start/<string:otp>', methods=['POST'])
|
||||
def api_start_job_with_otp(otp):
|
||||
"""Job mit OTP-Code starten."""
|
||||
if not otp:
|
||||
return jsonify({"error": "Kein OTP-Code angegeben"}), 400
|
||||
|
||||
try:
|
||||
with get_cached_session() as db_session:
|
||||
# Alle Gastanfragen mit approved-Status durchsuchen
|
||||
guest_requests = db_session.query(GuestRequest).filter_by(status="approved").all()
|
||||
|
||||
valid_request = None
|
||||
for req in guest_requests:
|
||||
if req.verify_otp(otp):
|
||||
valid_request = req
|
||||
break
|
||||
|
||||
if not valid_request:
|
||||
return jsonify({"error": "Ungültiger OTP-Code"}), 400
|
||||
|
||||
# Zugehörigen Job laden
|
||||
job = db_session.query(Job).filter_by(id=valid_request.job_id).first()
|
||||
if not job:
|
||||
return jsonify({"error": "Kein Job für diese Anfrage gefunden"}), 404
|
||||
|
||||
# Grace-Period prüfen (5 Minuten nach geplantem Start)
|
||||
now = datetime.now()
|
||||
grace_end = job.start_at + timedelta(minutes=5)
|
||||
|
||||
if now > job.end_at:
|
||||
return jsonify({"error": "Der Job ist bereits abgelaufen"}), 400
|
||||
|
||||
# Job starten
|
||||
job.status = "running"
|
||||
|
||||
# OTP-Code nach Verwendung löschen
|
||||
valid_request.otp_code = None
|
||||
|
||||
db_session.commit()
|
||||
|
||||
logger.info(f"Job {job.id} mit OTP-Code gestartet")
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"job_id": job.id,
|
||||
"status": "running"
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Starten des Jobs mit OTP: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
||||
|
||||
@guest_blueprint.route('/api/notifications', methods=['GET'])
|
||||
@login_required
|
||||
def api_get_notifications():
|
||||
"""Benachrichtigungen für den aktuellen Benutzer abrufen."""
|
||||
try:
|
||||
# Zeitstempel für Filter (nur neue Benachrichtigungen)
|
||||
since = request.args.get('since')
|
||||
if since:
|
||||
try:
|
||||
since_date = datetime.fromisoformat(since)
|
||||
except ValueError:
|
||||
return jsonify({"error": "Ungültiges Datumsformat"}), 400
|
||||
else:
|
||||
since_date = None
|
||||
|
||||
with get_cached_session() as db_session:
|
||||
query = db_session.query(Notification).filter_by(
|
||||
user_id=current_user.id,
|
||||
read=False
|
||||
)
|
||||
|
||||
if since_date:
|
||||
query = query.filter(Notification.created_at > since_date)
|
||||
|
||||
notifications = query.order_by(desc(Notification.created_at)).all()
|
||||
|
||||
return jsonify({
|
||||
"count": len(notifications),
|
||||
"notifications": [n.to_dict() for n in notifications]
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Abrufen der Benachrichtigungen: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
||||
|
||||
@guest_blueprint.route('/api/notifications/<int:notification_id>/read', methods=['POST'])
|
||||
@login_required
|
||||
def api_mark_notification_read(notification_id):
|
||||
"""Benachrichtigung als gelesen markieren."""
|
||||
try:
|
||||
with get_cached_session() as db_session:
|
||||
notification = db_session.query(Notification).filter_by(
|
||||
id=notification_id,
|
||||
user_id=current_user.id
|
||||
).first()
|
||||
|
||||
if not notification:
|
||||
return jsonify({"error": "Benachrichtigung nicht gefunden"}), 404
|
||||
|
||||
notification.read = True
|
||||
db_session.commit()
|
||||
|
||||
return jsonify({"success": True})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Markieren der Benachrichtigung als gelesen: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
||||
1
backend/app/blueprints/users.py
Normal file
1
backend/app/blueprints/users.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
Reference in New Issue
Block a user