🎉 Improved backend functionality & documentation, added OTP documentation & JavaScript file. 🎨

This commit is contained in:
2025-06-01 00:15:47 +02:00
parent b2bdc2d123
commit 91548dfb0e
5 changed files with 984 additions and 155 deletions

View File

@@ -41,6 +41,29 @@ from utils.queue_manager import start_queue_manager, stop_queue_manager, get_que
from config.settings import SECRET_KEY, UPLOAD_FOLDER, ALLOWED_EXTENSIONS, ENVIRONMENT, SESSION_LIFETIME, SCHEDULER_ENABLED, SCHEDULER_INTERVAL, TAPO_USERNAME, TAPO_PASSWORD
from utils.file_manager import file_manager, save_job_file, save_guest_file, save_avatar_file, save_asset_file, save_log_file, save_backup_file, save_temp_file, delete_file as delete_file_safe
# ===== OFFLINE-MODUS KONFIGURATION =====
# System läuft im Offline-Modus ohne Internetverbindung
OFFLINE_MODE = True # Produktionseinstellung für Offline-Betrieb
# ===== BEDINGTE IMPORTS FÜR OFFLINE-MODUS =====
if not OFFLINE_MODE:
# Nur laden wenn Online-Modus
import requests
else:
# Offline-Mock für requests
class OfflineRequestsMock:
"""Mock-Klasse für requests im Offline-Modus"""
@staticmethod
def get(*args, **kwargs):
raise ConnectionError("System läuft im Offline-Modus - keine Internet-Verbindung verfügbar")
@staticmethod
def post(*args, **kwargs):
raise ConnectionError("System läuft im Offline-Modus - keine Internet-Verbindung verfügbar")
requests = OfflineRequestsMock()
# Datenbank-Engine für Kompatibilität mit init_simple_db.py
from models import engine as db_engine
@@ -311,7 +334,7 @@ def format_datetime_filter(value, format='%d.%m.%Y %H:%M'):
setup_logging()
log_startup_info()
# Logger für verschiedene Komponenten
# app_logger für verschiedene Komponenten
app_logger = get_logger("app")
auth_logger = get_logger("auth")
jobs_logger = get_logger("jobs")
@@ -1695,7 +1718,7 @@ def api_admin_system_health():
})
except Exception as e:
logger.error(f"Fehler beim System-Gesundheitscheck: {str(e)}")
app_logger.error(f"Fehler beim System-Gesundheitscheck: {str(e)}")
return jsonify({
"success": False,
"error": str(e)
@@ -1731,7 +1754,7 @@ def api_admin_system_health():
})
except Exception as e:
logger.error(f"Fehler beim System-Gesundheitscheck: {str(e)}")
app_logger.error(f"Fehler beim System-Gesundheitscheck: {str(e)}")
return jsonify({
"success": False,
"error": str(e)
@@ -3210,6 +3233,118 @@ def test_admin_guest_requests():
'user_is_admin': current_user.is_admin if current_user.is_authenticated else False
})
@app.route('/api/guest-status', methods=['POST'])
def get_guest_request_status():
"""
Öffentliche Route für Gäste um ihren Auftragsstatus mit OTP-Code zu prüfen.
Keine Authentifizierung erforderlich.
"""
try:
data = request.get_json()
if not data:
return jsonify({
'success': False,
'message': 'Keine Daten empfangen'
}), 400
otp_code = data.get('otp_code', '').strip()
email = data.get('email', '').strip() # Optional für zusätzliche Verifikation
if not otp_code:
return jsonify({
'success': False,
'message': 'OTP-Code ist erforderlich'
}), 400
db_session = get_db_session()
# Alle Gastaufträge finden, die den OTP-Code haben könnten
# Da OTP gehashed ist, müssen wir durch alle iterieren
guest_requests = db_session.query(GuestRequest).filter(
GuestRequest.otp_code.isnot(None)
).all()
found_request = None
for request_obj in guest_requests:
if request_obj.verify_otp(otp_code):
# Zusätzliche E-Mail-Verifikation falls angegeben
if email and request_obj.email.lower() != email.lower():
continue
found_request = request_obj
break
if not found_request:
db_session.close()
app_logger.warning(f"Ungültiger OTP-Code für Gast-Status-Abfrage: {otp_code[:4]}****")
return jsonify({
'success': False,
'message': 'Ungültiger Code oder E-Mail-Adresse'
}), 404
# Status-Informationen für den Gast zusammenstellen
status_info = {
'id': found_request.id,
'name': found_request.name,
'file_name': found_request.file_name,
'status': found_request.status,
'created_at': found_request.created_at.isoformat() if found_request.created_at else None,
'updated_at': found_request.updated_at.isoformat() if found_request.updated_at else None,
'duration_minutes': found_request.duration_minutes,
'copies': found_request.copies,
'reason': found_request.reason
}
# Status-spezifische Informationen hinzufügen
if found_request.status == 'approved':
status_info.update({
'approved_at': found_request.approved_at.isoformat() if found_request.approved_at else None,
'approval_notes': found_request.approval_notes,
'message': 'Ihr Auftrag wurde genehmigt! Sie können mit dem Drucken beginnen.'
})
elif found_request.status == 'rejected':
status_info.update({
'rejected_at': found_request.rejected_at.isoformat() if found_request.rejected_at else None,
'rejection_reason': found_request.rejection_reason,
'message': 'Ihr Auftrag wurde leider abgelehnt.'
})
elif found_request.status == 'pending':
# Berechne wie lange der Auftrag schon wartet
if found_request.created_at:
waiting_time = datetime.now() - found_request.created_at
hours_waiting = int(waiting_time.total_seconds() / 3600)
status_info.update({
'hours_waiting': hours_waiting,
'message': f'Ihr Auftrag wird bearbeitet. Wartezeit: {hours_waiting} Stunden.'
})
else:
status_info['message'] = 'Ihr Auftrag wird bearbeitet.'
db_session.commit() # OTP als verwendet markieren
db_session.close()
app_logger.info(f"Gast-Status-Abfrage erfolgreich für Request {found_request.id}")
return jsonify({
'success': True,
'request': status_info
})
except Exception as e:
app_logger.error(f"Fehler bei Gast-Status-Abfrage: {str(e)}")
return jsonify({
'success': False,
'message': 'Fehler beim Abrufen des Status'
}), 500
@app.route('/guest-status')
def guest_status_page():
"""
Öffentliche Seite für Gäste um ihren Auftragsstatus zu prüfen.
"""
return render_template('guest_status.html')
@app.route('/api/admin/guest-requests', methods=['GET'])
@admin_required
def get_admin_guest_requests():
@@ -3374,32 +3509,37 @@ def approve_guest_request(request_id):
if printer:
guest_request.assigned_printer_id = printer_id
# OTP-Code generieren für den Gast
import secrets
otp_code = ''.join([str(secrets.randbelow(10)) for _ in range(6)])
guest_request.otp_code = otp_code
guest_request.otp_expires_at = datetime.now() + timedelta(hours=24)
# OTP-Code generieren falls noch nicht vorhanden (nutze die Methode aus models.py)
otp_code = None
if not guest_request.otp_code:
otp_code = guest_request.generate_otp()
guest_request.otp_expires_at = datetime.now() + timedelta(hours=48) # 48h gültig
db_session.commit()
# Benachrichtigung an den Gast senden (falls E-Mail verfügbar)
if guest_request.email:
if guest_request.email and otp_code:
try:
# Hier würde normalerweise eine E-Mail gesendet werden
app_logger.info(f"E-Mail-Benachrichtigung würde an {guest_request.email} gesendet (OTP: {otp_code})")
app_logger.info(f"Genehmigungs-E-Mail würde an {guest_request.email} gesendet (OTP für Status-Abfrage verfügbar)")
except Exception as e:
app_logger.warning(f"Fehler beim Senden der E-Mail-Benachrichtigung: {str(e)}")
db_session.close()
app_logger.info(f"Gastauftrag {request_id} von Admin {current_user.id} genehmigt (OTP: {otp_code})")
app_logger.info(f"Gastauftrag {request_id} von Admin {current_user.id} genehmigt")
return jsonify({
response_data = {
'success': True,
'message': 'Gastauftrag erfolgreich genehmigt',
'otp_code': otp_code,
'expires_at': (datetime.now() + timedelta(hours=24)).isoformat()
})
'message': 'Gastauftrag erfolgreich genehmigt'
}
# OTP-Code nur zurückgeben wenn er neu generiert wurde (für Admin-Info)
if otp_code:
response_data['otp_code_generated'] = True
response_data['status_check_url'] = url_for('guest_status_page', _external=True)
return jsonify(response_data)
except Exception as e:
app_logger.error(f"Fehler beim Genehmigen des Gastauftrags {request_id}: {str(e)}")
@@ -4065,7 +4205,7 @@ def get_validation_js():
response.headers['Cache-Control'] = 'public, max-age=3600' # 1 Stunde Cache
return response
except Exception as e:
logger.error(f"Fehler beim Laden des Validierungs-JS: {str(e)}")
app_logger.error(f"Fehler beim Laden des Validierungs-JS: {str(e)}")
return "console.error('Validierungs-JavaScript konnte nicht geladen werden');", 500
@app.route('/api/validation/validate-form', methods=['POST'])
@@ -4099,7 +4239,7 @@ def validate_form_api():
})
except Exception as e:
logger.error(f"Fehler bei Formular-Validierung: {str(e)}")
app_logger.error(f"Fehler bei Formular-Validierung: {str(e)}")
return jsonify({'success': False, 'error': str(e)}), 500
# ===== REPORT GENERATOR API =====
@@ -4171,7 +4311,7 @@ def generate_report():
return jsonify({'error': 'Report-Generierung fehlgeschlagen'}), 500
except Exception as e:
logger.error(f"Fehler bei Report-Generierung: {str(e)}")
app_logger.error(f"Fehler bei Report-Generierung: {str(e)}")
return jsonify({'error': str(e)}), 500
# ===== REALTIME DASHBOARD API =====
@@ -4183,7 +4323,7 @@ def get_dashboard_config():
config = dashboard_manager.get_dashboard_config(current_user.id)
return jsonify(config)
except Exception as e:
logger.error(f"Fehler beim Laden der Dashboard-Konfiguration: {str(e)}")
app_logger.error(f"Fehler beim Laden der Dashboard-Konfiguration: {str(e)}")
return jsonify({'error': str(e)}), 500
@app.route('/api/dashboard/widgets/<widget_id>/data', methods=['GET'])
@@ -4198,7 +4338,7 @@ def get_widget_data(widget_id):
'timestamp': datetime.now().isoformat()
})
except Exception as e:
logger.error(f"Fehler beim Laden der Widget-Daten für {widget_id}: {str(e)}")
app_logger.error(f"Fehler beim Laden der Widget-Daten für {widget_id}: {str(e)}")
return jsonify({'error': str(e)}), 500
@app.route('/api/dashboard/emit-event', methods=['POST'])
@@ -4223,7 +4363,7 @@ def emit_dashboard_event():
return jsonify({'success': True})
except Exception as e:
logger.error(f"Fehler beim Senden des Dashboard-Ereignisses: {str(e)}")
app_logger.error(f"Fehler beim Senden des Dashboard-Ereignisses: {str(e)}")
return jsonify({'error': str(e)}), 500
@app.route('/api/dashboard/client-js', methods=['GET'])
@@ -4236,7 +4376,7 @@ def get_dashboard_js():
response.headers['Cache-Control'] = 'public, max-age=1800' # 30 Minuten Cache
return response
except Exception as e:
logger.error(f"Fehler beim Laden des Dashboard-JS: {str(e)}")
app_logger.error(f"Fehler beim Laden des Dashboard-JS: {str(e)}")
return "console.error('Dashboard-JavaScript konnte nicht geladen werden');", 500
# ===== DRAG & DROP API =====
@@ -4270,7 +4410,7 @@ def update_job_order():
return jsonify({'error': 'Fehler beim Aktualisieren der Job-Reihenfolge'}), 500
except Exception as e:
logger.error(f"Fehler beim Aktualisieren der Job-Reihenfolge: {str(e)}")
app_logger.error(f"Fehler beim Aktualisieren der Job-Reihenfolge: {str(e)}")
return jsonify({'error': str(e)}), 500
@app.route('/api/dragdrop/get-job-order/<int:printer_id>', methods=['GET'])
@@ -4300,7 +4440,7 @@ def get_job_order_api(printer_id):
})
except Exception as e:
logger.error(f"Fehler beim Abrufen der Job-Reihenfolge: {str(e)}")
app_logger.error(f"Fehler beim Abrufen der Job-Reihenfolge: {str(e)}")
return jsonify({'error': str(e)}), 500
@app.route('/api/dragdrop/upload-session', methods=['POST'])
@@ -4318,7 +4458,7 @@ def create_upload_session():
})
except Exception as e:
logger.error(f"Fehler beim Erstellen der Upload-Session: {str(e)}")
app_logger.error(f"Fehler beim Erstellen der Upload-Session: {str(e)}")
return jsonify({'error': str(e)}), 500
@app.route('/api/dragdrop/upload-progress/<session_id>', methods=['GET'])
@@ -4329,7 +4469,7 @@ def get_upload_progress(session_id):
progress = drag_drop_manager.get_session_progress(session_id)
return jsonify(progress)
except Exception as e:
logger.error(f"Fehler beim Abrufen des Upload-Progress: {str(e)}")
app_logger.error(f"Fehler beim Abrufen des Upload-Progress: {str(e)}")
return jsonify({'error': str(e)}), 500
@app.route('/api/dragdrop/client-js', methods=['GET'])
@@ -4342,7 +4482,7 @@ def get_dragdrop_js():
response.headers['Cache-Control'] = 'public, max-age=3600'
return response
except Exception as e:
logger.error(f"Fehler beim Laden des Drag & Drop JS: {str(e)}")
app_logger.error(f"Fehler beim Laden des Drag & Drop JS: {str(e)}")
return "console.error('Drag & Drop JavaScript konnte nicht geladen werden');", 500
@app.route('/api/dragdrop/client-css', methods=['GET'])
@@ -4355,7 +4495,7 @@ def get_dragdrop_css():
response.headers['Cache-Control'] = 'public, max-age=3600'
return response
except Exception as e:
logger.error(f"Fehler beim Laden des Drag & Drop CSS: {str(e)}")
app_logger.error(f"Fehler beim Laden des Drag & Drop CSS: {str(e)}")
return "/* Drag & Drop CSS konnte nicht geladen werden */", 500
# ===== ADVANCED TABLES API =====
@@ -4422,7 +4562,7 @@ def query_advanced_table():
return jsonify(result)
except Exception as e:
logger.error(f"Fehler bei erweiterte Tabellen-Abfrage: {str(e)}")
app_logger.error(f"Fehler bei erweiterte Tabellen-Abfrage: {str(e)}")
return jsonify({'error': str(e)}), 500
@app.route('/api/tables/export', methods=['POST'])
@@ -4435,31 +4575,137 @@ def export_table_data():
export_format = data.get('format', 'csv')
query_params = data.get('query', {})
# Hier würde die Export-Logik implementiert
# Für jetzt einfache CSV-Export-Simulation
# Vollständige Export-Logik implementierung
app_logger.info(f"📊 Starte Tabellen-Export: {table_type} als {export_format}")
# Tabellen-Konfiguration basierend auf Typ erstellen
if table_type == 'jobs':
config = create_table_config(
'jobs',
['id', 'filename', 'status', 'printer_name', 'user_name', 'created_at', 'completed_at'],
base_query='Job'
)
elif table_type == 'printers':
config = create_table_config(
'printers',
['id', 'name', 'ip_address', 'status', 'location', 'model'],
base_query='Printer'
)
elif table_type == 'users':
config = create_table_config(
'users',
['id', 'name', 'email', 'role', 'active', 'last_login'],
base_query='User'
)
else:
return jsonify({'error': 'Unbekannter Tabellen-Typ für Export'}), 400
# Erweiterte Abfrage für Export-Daten erstellen
query_builder = AdvancedTableQuery(config)
# Filter aus Query-Parametern anwenden
if 'filters' in query_params:
for filter_data in query_params['filters']:
query_builder.add_filter(
filter_data['column'],
filter_data['operator'],
filter_data['value']
)
# Sortierung anwenden
if 'sort' in query_params:
query_builder.set_sorting(
query_params['sort']['column'],
query_params['sort']['direction']
)
# Für Export: Alle Daten ohne Paginierung
query_builder.set_pagination(1, 10000) # Maximale Anzahl für Export
# Daten abrufen
result = query_builder.execute()
export_data = result.get('data', [])
if export_format == 'csv':
import csv
import io
# CSV-Export implementierung
output = io.StringIO()
writer = csv.writer(output)
writer = csv.writer(output, delimiter=';', quoting=csv.QUOTE_MINIMAL)
# Beispiel-Daten (würde durch echte Abfrage ersetzt)
writer.writerow(['ID', 'Name', 'Status', 'Erstellt'])
writer.writerow([1, 'Beispiel Job', 'Aktiv', '2025-01-07'])
# Header-Zeile schreiben
if export_data:
headers = list(export_data[0].keys())
writer.writerow(headers)
# Daten-Zeilen schreiben
for row in export_data:
# Werte für CSV formatieren
formatted_row = []
for value in row.values():
if value is None:
formatted_row.append('')
elif isinstance(value, datetime):
formatted_row.append(value.strftime('%d.%m.%Y %H:%M:%S'))
else:
formatted_row.append(str(value))
writer.writerow(formatted_row)
response = make_response(output.getvalue())
response.headers['Content-Type'] = 'text/csv'
response.headers['Content-Disposition'] = f'attachment; filename="{table_type}_export.csv"'
# Response erstellen
csv_content = output.getvalue()
output.close()
response = make_response(csv_content)
response.headers['Content-Type'] = 'text/csv; charset=utf-8'
response.headers['Content-Disposition'] = f'attachment; filename="{table_type}_export_{datetime.now().strftime("%Y%m%d_%H%M%S")}.csv"'
app_logger.info(f"✅ CSV-Export erfolgreich: {len(export_data)} Datensätze")
return response
return jsonify({'error': 'Export-Format nicht unterstützt'}), 400
elif export_format == 'json':
# JSON-Export implementierung
json_content = json.dumps(export_data, indent=2, default=str, ensure_ascii=False)
response = make_response(json_content)
response.headers['Content-Type'] = 'application/json; charset=utf-8'
response.headers['Content-Disposition'] = f'attachment; filename="{table_type}_export_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json"'
app_logger.info(f"✅ JSON-Export erfolgreich: {len(export_data)} Datensätze")
return response
elif export_format == 'excel':
# Excel-Export implementierung (falls openpyxl verfügbar)
try:
import openpyxl
from openpyxl.utils.dataframe import dataframe_to_rows
import pandas as pd
# DataFrame erstellen
df = pd.DataFrame(export_data)
# Excel-Datei in Memory erstellen
output = io.BytesIO()
with pd.ExcelWriter(output, engine='openpyxl') as writer:
df.to_excel(writer, sheet_name=table_type.capitalize(), index=False)
output.seek(0)
response = make_response(output.getvalue())
response.headers['Content-Type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
response.headers['Content-Disposition'] = f'attachment; filename="{table_type}_export_{datetime.now().strftime("%Y%m%d_%H%M%S")}.xlsx"'
app_logger.info(f"✅ Excel-Export erfolgreich: {len(export_data)} Datensätze")
return response
except ImportError:
app_logger.warning("⚠️ Excel-Export nicht verfügbar - openpyxl/pandas fehlt")
return jsonify({'error': 'Excel-Export nicht verfügbar - erforderliche Bibliotheken fehlen'}), 400
except Exception as e:
logger.error(f"Fehler beim Tabellen-Export: {str(e)}")
app_logger.error(f"Fehler beim Tabellen-Export: {str(e)}")
return jsonify({'error': str(e)}), 500
@app.route('/api/tables/client-js', methods=['GET'])
def get_tables_js():
"""Liefert Client-seitige Advanced Tables JavaScript"""
@@ -4470,7 +4716,7 @@ def get_tables_js():
response.headers['Cache-Control'] = 'public, max-age=3600'
return response
except Exception as e:
logger.error(f"Fehler beim Laden des Tables-JS: {str(e)}")
app_logger.error(f"Fehler beim Laden des Tables-JS: {str(e)}")
return "console.error('Advanced Tables JavaScript konnte nicht geladen werden');", 500
@app.route('/api/tables/client-css', methods=['GET'])
@@ -4483,7 +4729,7 @@ def get_tables_css():
response.headers['Cache-Control'] = 'public, max-age=3600'
return response
except Exception as e:
logger.error(f"Fehler beim Laden des Tables-CSS: {str(e)}")
app_logger.error(f"Fehler beim Laden des Tables-CSS: {str(e)}")
return "/* Advanced Tables CSS konnte nicht geladen werden */", 500
# ===== MAINTENANCE SYSTEM API =====
@@ -4508,7 +4754,7 @@ def maintenance_tasks():
})
except Exception as e:
logger.error(f"Fehler beim Abrufen der Wartungsaufgaben: {str(e)}")
app_logger.error(f"Fehler beim Abrufen der Wartungsaufgaben: {str(e)}")
return jsonify({'error': str(e)}), 500
elif request.method == 'POST':
@@ -4542,7 +4788,7 @@ def maintenance_tasks():
return jsonify({'error': 'Fehler beim Erstellen der Wartungsaufgabe'}), 500
except Exception as e:
logger.error(f"Fehler beim Erstellen der Wartungsaufgabe: {str(e)}")
app_logger.error(f"Fehler beim Erstellen der Wartungsaufgabe: {str(e)}")
return jsonify({'error': str(e)}), 500
@app.route('/api/maintenance/tasks/<int:task_id>/status', methods=['PUT'])
@@ -4570,7 +4816,7 @@ def update_maintenance_task_status(task_id):
return jsonify({'error': 'Fehler beim Aktualisieren des Status'}), 500
except Exception as e:
logger.error(f"Fehler beim Aktualisieren des Wartungsaufgaben-Status: {str(e)}")
app_logger.error(f"Fehler beim Aktualisieren des Wartungsaufgaben-Status: {str(e)}")
return jsonify({'error': str(e)}), 500
@app.route('/api/maintenance/overview', methods=['GET'])
@@ -4581,7 +4827,7 @@ def get_maintenance_overview():
overview = get_maintenance_overview()
return jsonify(overview)
except Exception as e:
logger.error(f"Fehler beim Abrufen der Wartungs-Übersicht: {str(e)}")
app_logger.error(f"Fehler beim Abrufen der Wartungs-Übersicht: {str(e)}")
return jsonify({'error': str(e)}), 500
@app.route('/api/maintenance/schedule', methods=['POST'])
@@ -4609,7 +4855,7 @@ def schedule_maintenance_api():
return jsonify({'error': 'Fehler beim Erstellen des Wartungsplans'}), 500
except Exception as e:
logger.error(f"Fehler beim Planen der Wartung: {str(e)}")
app_logger.error(f"Fehler beim Planen der Wartung: {str(e)}")
return jsonify({'error': str(e)}), 500
# ===== MULTI-LOCATION SYSTEM API =====
@@ -4631,7 +4877,7 @@ def locations():
})
except Exception as e:
logger.error(f"Fehler beim Abrufen der Standorte: {str(e)}")
app_logger.error(f"Fehler beim Abrufen der Standorte: {str(e)}")
return jsonify({'error': str(e)}), 500
elif request.method == 'POST':
@@ -4657,7 +4903,7 @@ def locations():
return jsonify({'error': 'Fehler beim Erstellen des Standorts'}), 500
except Exception as e:
logger.error(f"Fehler beim Erstellen des Standorts: {str(e)}")
app_logger.error(f"Fehler beim Erstellen des Standorts: {str(e)}")
return jsonify({'error': str(e)}), 500
@app.route('/api/locations/<int:location_id>/users', methods=['GET', 'POST'])
@@ -4675,7 +4921,7 @@ def location_users(location_id):
})
except Exception as e:
logger.error(f"Fehler beim Abrufen der Standort-Benutzer: {str(e)}")
app_logger.error(f"Fehler beim Abrufen der Standort-Benutzer: {str(e)}")
return jsonify({'error': str(e)}), 500
elif request.method == 'POST':
@@ -4698,7 +4944,7 @@ def location_users(location_id):
return jsonify({'error': 'Fehler bei der Benutzer-Zuweisung'}), 500
except Exception as e:
logger.error(f"Fehler bei der Benutzer-Zuweisung: {str(e)}")
app_logger.error(f"Fehler bei der Benutzer-Zuweisung: {str(e)}")
return jsonify({'error': str(e)}), 500
@app.route('/api/locations/user/<int:user_id>', methods=['GET'])
@@ -4718,7 +4964,7 @@ def get_user_locations_api(user_id):
})
except Exception as e:
logger.error(f"Fehler beim Abrufen der Benutzer-Standorte: {str(e)}")
app_logger.error(f"Fehler beim Abrufen der Benutzer-Standorte: {str(e)}")
return jsonify({'error': str(e)}), 500
@app.route('/api/locations/distance', methods=['POST'])
@@ -4741,7 +4987,7 @@ def calculate_distance_api():
})
except Exception as e:
logger.error(f"Fehler bei Entfernungsberechnung: {str(e)}")
app_logger.error(f"Fehler bei Entfernungsberechnung: {str(e)}")
return jsonify({'error': str(e)}), 500
@app.route('/api/locations/nearest', methods=['POST'])
@@ -4776,10 +5022,47 @@ def find_nearest_location_api():
})
except Exception as e:
logger.error(f"Fehler bei der Suche nach nächstem Standort: {str(e)}")
app_logger.error(f"Fehler bei der Suche nach nächstem Standort: {str(e)}")
return jsonify({'error': str(e)}), 500
# ===== GASTANTRÄGE API-ROUTEN =====
def setup_database_with_migrations():
"""
Datenbank initialisieren und alle erforderlichen Tabellen erstellen.
Führt Migrationen für neue Tabellen wie JobOrder durch.
"""
try:
app_logger.info("🔄 Starte Datenbank-Setup und Migrationen...")
# Standard-Datenbank-Initialisierung
init_database()
# Explizite Migration für JobOrder-Tabelle
engine = get_engine()
# Erstelle alle Tabellen (nur neue werden tatsächlich erstellt)
Base.metadata.create_all(engine)
# Prüfe ob JobOrder-Tabelle existiert
from sqlalchemy import inspect
inspector = inspect(engine)
existing_tables = inspector.get_table_names()
if 'job_orders' in existing_tables:
app_logger.info("✅ JobOrder-Tabelle bereits vorhanden")
else:
# Tabelle manuell erstellen
JobOrder.__table__.create(engine, checkfirst=True)
app_logger.info("✅ JobOrder-Tabelle erfolgreich erstellt")
# Initial-Admin erstellen falls nicht vorhanden
create_initial_admin()
app_logger.info("✅ Datenbank-Setup und Migrationen erfolgreich abgeschlossen")
except Exception as e:
app_logger.error(f"❌ Fehler bei Datenbank-Setup: {str(e)}")
raise e
# ===== STARTUP UND MAIN =====
if __name__ == "__main__":
@@ -5008,100 +5291,3 @@ if __name__ == "__main__":
except:
pass
sys.exit(1)
def setup_database_with_migrations():
"""
Datenbank initialisieren und alle erforderlichen Tabellen erstellen.
Führt Migrationen für neue Tabellen wie JobOrder durch.
"""
try:
app_logger.info("🔄 Starte Datenbank-Setup und Migrationen...")
# Standard-Datenbank-Initialisierung
init_database()
# Explizite Migration für JobOrder-Tabelle
engine = get_engine()
# Erstelle alle Tabellen (nur neue werden tatsächlich erstellt)
Base.metadata.create_all(engine)
# Prüfe ob JobOrder-Tabelle existiert
from sqlalchemy import inspect
inspector = inspect(engine)
existing_tables = inspector.get_table_names()
if 'job_orders' in existing_tables:
app_logger.info("✅ JobOrder-Tabelle bereits vorhanden")
else:
# Tabelle manuell erstellen
JobOrder.__table__.create(engine, checkfirst=True)
app_logger.info("✅ JobOrder-Tabelle erfolgreich erstellt")
# Initial-Admin erstellen falls nicht vorhanden
create_initial_admin()
app_logger.info("✅ Datenbank-Setup und Migrationen erfolgreich abgeschlossen")
except Exception as e:
app_logger.error(f"❌ Fehler bei Datenbank-Setup: {str(e)}")
raise e
@app.route("/admin/printers/<int:printer_id>/settings")
@login_required
def admin_printer_settings_page(printer_id):
"""Zeigt die Drucker-Einstellungsseite an."""
if not current_user.is_admin:
flash("Sie haben keine Berechtigung für den Admin-Bereich.", "error")
return redirect(url_for("index"))
db_session = get_db_session()
try:
printer = db_session.get(Printer, printer_id)
if not printer:
flash("Drucker nicht gefunden.", "error")
return redirect(url_for("admin_page"))
printer_data = {
"id": printer.id,
"name": printer.name,
"model": printer.model or 'Unbekanntes Modell',
"location": printer.location or 'Unbekannter Standort',
"mac_address": printer.mac_address,
"plug_ip": printer.plug_ip,
"status": printer.status or "offline",
"active": printer.active if hasattr(printer, 'active') else True,
"created_at": printer.created_at.isoformat() if printer.created_at else datetime.now().isoformat()
}
db_session.close()
return render_template("admin_printer_settings.html", printer=printer_data)
except Exception as e:
db_session.close()
app_logger.error(f"Fehler beim Laden der Drucker-Einstellungen: {str(e)}")
flash("Fehler beim Laden der Drucker-Daten.", "error")
return redirect(url_for("admin_page"))
# Erstelle alle Tabellen (nur neue werden tatsächlich erstellt)
Base.metadata.create_all(engine)
# Prüfe ob JobOrder-Tabelle existiert
from sqlalchemy import inspect
inspector = inspect(engine)
existing_tables = inspector.get_table_names()
if 'job_orders' in existing_tables:
app_logger.info("✅ JobOrder-Tabelle bereits vorhanden")
else:
# Tabelle manuell erstellen
JobOrder.__table__.create(engine, checkfirst=True)
app_logger.info("✅ JobOrder-Tabelle erfolgreich erstellt")
# Initial-Admin erstellen falls nicht vorhanden
create_initial_admin()
app_logger.info("✅ Datenbank-Setup und Migrationen erfolgreich abgeschlossen")
except Exception as e:
app_logger.error(f"❌ Fehler bei Datenbank-Setup: {str(e)}")
raise e