From 38202de49fc8c1b2ea2cf409098cdd2fc07a69c3 Mon Sep 17 00:00:00 2001 From: Till Tomczak Date: Sun, 1 Jun 2025 13:54:05 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=89=20Feature:=20Enhanced=20log=20mana?= =?UTF-8?q?gement=20&=20firewall=20integration=20in=20backend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/database/myp.db | Bin 118784 -> 118784 bytes backend/database/myp.db-wal | Bin 8272 -> 4152 bytes backend/logs/app/app.log | 12 + .../logs/printer_monitor/printer_monitor.log | 56 ++++ backend/logs/printers/printers.log | 42 +++ backend/maintenance_api.py | 264 +++++++++++++++++ backend/setup.sh | 270 +++++++++++++++++- backend/systemd/myp-firewall.service | 60 ++++ backend/templates/admin.html | 213 +++++++------- 9 files changed, 799 insertions(+), 118 deletions(-) create mode 100644 backend/maintenance_api.py create mode 100644 backend/systemd/myp-firewall.service diff --git a/backend/database/myp.db b/backend/database/myp.db index 8666f5828cb499ef652cdaac9285a2b684decf0d..13662e370358285a7b405409f671c268bf57787a 100644 GIT binary patch delta 95 zcmZozz}~QceS$RO)QK|Aj8iuzaO<09x<`g*nPhlXL=>myWmfv9Bsry<=_Lk6g$4S! zrIe*HC8foN3Wh4|EaJAprNya5 z#l?j=nI)<5iKQj^nRzLx74cxvctaDQDmFr@jDV`tSb?gX*p&VaYJ-Tm?1#zT1+gm&cX#nnRzAH+zgY5H#7q}n~_D< SSR5tJapKDU2_%I0FF={I^ZjWj z^YQGX(DE;@heEH0@bN?FkHuT@pFaER`P8rCv*~ZpR2ZEL-u(BAKbMxjtiQWc%BwbD znugVuZH7nO7cPWuGYne$1zwL%?^r@N)9&C+bj9-U;a!~({rg2I@iabz7hU}mQ{KAz z=cTW&Jc|By@mXYJHaC+AUl09Av-}sMdzWWpH*bbNwB@=6;6n|mScA4~v%&jU*2|Ju zk(i3OmY0|yWy!8HuihEbtItAw9Z6iKJ=FQglvL zn9V{(x+|5LLb1XWs`>maCOW6fZNTh`<@F7*yu>Dxaf%Asva0!HE*Zx>7LcK#GZksC zf`>}Sl5N${Yi(8co-sJzu>lOy8c2o>lo~Sp9Rx&W^S*>xu+kmaf1vw3(^7RCST$b- zywdAES)mVB-_ zQs^WixqvmAB>>G!as*I&NsbYyQJj?i(fFgw z(MW7@G5q)gk`s`ts7v%>)|JcQ**k|2JM<=v>^0vqmI3*=A5qPktSBhq_@%*3;AAm+jBXF*V+z|5+2%#CKG}*yV)OIi zPfrM}*x>k_qkwdB&C69+ZV&(kz=oxoII|D!egFjuct1~iRfQru4mwa;fId`g<%Zos zwFXnLrTa$4h>tGBV?x@gR$db!AT_ZF)YJh>MGru#C`>M26iK%*zJMAhI+c2`kYH~S z>>((H|DC1rWBzRw^0q!cOKr?XVzF5GksG6^&w&_Yc#(1^GId@=B6iKGu8z&zmJaJ+ z6B>3%)E2c(4WRzY^ADds#wwbO8cVmGpd4Wk+i3jp#&jgMun>OQAaUVUC+|8-Ddf$b zqZ8F;4RxSx)ZzI_29vQan&A|@e%N?~@tLA6H%289%8^X1(;Da!hv_&n&)+i3{?eyyN@c?pDF zaBBxL^gD5uTcKN$^UY)#f$GCKj`h#bM>Q6`@&vIT(~(19+p6C3TY3eUsEcR^>z3!j zrU6x|B=iXfhVGY3w*b~v!|k|;Kal;ac9+`Jc@9BLL1M~t5f6RjAMvT#*n6>Y{z0r< zdkCH-c>s%$M>|YBlGkqbMWw9 zK9{fFIc06DEOn)vyt54|=zH!7Ag}CPk`tDb>E$HLu>2~QTIDl|B%kK9=}{Dp7X)Et z93{>6-k-V_;@{}ayfLiDuokWgMpw>n?}>@*wtQ-A2xhfXDVxPa`%bBJ_dzP%wCYu% zA|2l8>}HF*2~*s)&cuYH0)CSm+D$T@$nx1tDl<&UBv^hW!*OFN>E7yvYaw>NH#0v% z1KJGk_KW7h{$8_mwAa~wr>Rw|)vZo*t5|K7ScBL0wz_Ni%F$Zh+P|MWV_O-%C}eqU z9dCcc=K0kWn^@s_0d0^r45DzzFg(Ym#!^;#Tj6V=cVoSo#n_xEI&Kt_74(Pf?KZF) z0DWm*t^@rHI}(|8RZ#Ele{h&O*gD;VKv%m(rM}imC*;zD-BLcMq#r3-#2-8 diff --git a/backend/logs/app/app.log b/backend/logs/app/app.log index a5d6d31a..e4f80de5 100644 --- a/backend/logs/app/app.log +++ b/backend/logs/app/app.log @@ -1109,3 +1109,15 @@ WHERE users.id = ? 2025-06-01 13:42:45 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True 2025-06-01 13:43:15 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True 2025-06-01 13:43:45 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True +2025-06-01 13:44:15 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True +2025-06-01 13:44:45 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True +2025-06-01 13:45:15 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True +2025-06-01 13:45:45 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True +2025-06-01 13:46:16 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True +2025-06-01 13:46:46 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True +2025-06-01 13:47:35 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True +2025-06-01 13:48:17 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True +2025-06-01 13:48:46 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True +2025-06-01 13:49:16 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True +2025-06-01 13:50:35 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True +2025-06-01 13:51:35 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True diff --git a/backend/logs/printer_monitor/printer_monitor.log b/backend/logs/printer_monitor/printer_monitor.log index b743fb4d..873c648c 100644 --- a/backend/logs/printer_monitor/printer_monitor.log +++ b/backend/logs/printer_monitor/printer_monitor.log @@ -1190,3 +1190,59 @@ 2025-06-01 13:41:45 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden 2025-06-01 13:41:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... 2025-06-01 13:41:45 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:42:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:42:15 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:42:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:42:15 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:42:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:42:45 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:42:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:42:45 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:43:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:43:15 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:43:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:43:15 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:43:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:43:45 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:43:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:43:45 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:44:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:44:15 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:44:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:44:15 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:44:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:44:45 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:44:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:44:45 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:45:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:45:15 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:45:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:45:15 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:45:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:45:45 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:45:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:45:45 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:46:54 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:46:54 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:46:54 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:46:54 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:47:54 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:47:54 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:47:54 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:47:54 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:48:17 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:48:17 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:48:17 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:48:17 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:49:19 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:49:19 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:49:19 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:49:19 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:50:19 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:50:19 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:50:19 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:50:19 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:51:19 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:51:19 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden +2025-06-01 13:51:19 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 13:51:19 - [printer_monitor] printer_monitor - [INFO] INFO - ℹ️ Keine aktiven Drucker gefunden diff --git a/backend/logs/printers/printers.log b/backend/logs/printers/printers.log index 2ed7db3e..1738b6b4 100644 --- a/backend/logs/printers/printers.log +++ b/backend/logs/printers/printers.log @@ -3557,3 +3557,45 @@ 2025-06-01 13:41:45 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) 2025-06-01 13:41:45 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker 2025-06-01 13:41:45 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 7.69ms +2025-06-01 13:42:15 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 13:42:15 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker +2025-06-01 13:42:15 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 4.32ms +2025-06-01 13:42:45 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 13:42:45 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker +2025-06-01 13:42:45 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 5.26ms +2025-06-01 13:43:15 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 13:43:15 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker +2025-06-01 13:43:15 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 3.90ms +2025-06-01 13:43:45 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 13:43:45 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker +2025-06-01 13:43:45 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 5.10ms +2025-06-01 13:44:15 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 13:44:15 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker +2025-06-01 13:44:15 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 6.97ms +2025-06-01 13:44:45 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 13:44:45 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker +2025-06-01 13:44:45 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 7.08ms +2025-06-01 13:45:15 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 13:45:15 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker +2025-06-01 13:45:15 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 1.55ms +2025-06-01 13:45:45 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 13:45:45 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker +2025-06-01 13:45:45 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 5.65ms +2025-06-01 13:46:54 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 13:46:54 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker +2025-06-01 13:46:54 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 1.22ms +2025-06-01 13:47:54 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 13:47:54 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker +2025-06-01 13:47:54 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 1.36ms +2025-06-01 13:48:17 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 13:48:17 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker +2025-06-01 13:48:17 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 4.13ms +2025-06-01 13:49:19 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 13:49:19 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker +2025-06-01 13:49:19 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 1.31ms +2025-06-01 13:50:19 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 13:50:19 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker +2025-06-01 13:50:19 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 1.11ms +2025-06-01 13:51:19 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 13:51:19 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker +2025-06-01 13:51:19 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 1.31ms diff --git a/backend/maintenance_api.py b/backend/maintenance_api.py new file mode 100644 index 00000000..84f0b78f --- /dev/null +++ b/backend/maintenance_api.py @@ -0,0 +1,264 @@ +""" +Wartungs-API-Endpunkte für das MYP-System +Stellt API-Routen für Cache-Löschung, Datenbank-Optimierung und Backup-Erstellung bereit +""" + +import os +import tempfile +import zipfile +import gc +from datetime import datetime, timedelta +from flask import jsonify, current_app +from flask_login import login_required, current_user +from sqlalchemy import text + +# Import der notwendigen Module aus der Hauptanwendung +try: + from app import app, app_logger, admin_required, get_db_session +except ImportError: + # Fallback für den Fall, dass die Imports nicht verfügbar sind + app = None + app_logger = None + admin_required = None + get_db_session = None + +def register_maintenance_routes(app_instance, logger, admin_decorator, db_session_func): + """ + Registriert die Wartungs-API-Routen bei der Flask-App + + Args: + app_instance: Flask-App-Instanz + logger: Logger-Instanz + admin_decorator: Admin-Required-Decorator + db_session_func: Funktion zum Abrufen einer DB-Session + """ + + @app_instance.route('/api/admin/maintenance/clear-cache', methods=['POST']) + @login_required + @admin_decorator + def api_clear_cache(): + """Leert den System-Cache""" + try: + logger.info(f"🧹 Cache-Löschung gestartet von Benutzer {current_user.username}") + + # Flask-Cache leeren (falls vorhanden) + if hasattr(app_instance, 'cache'): + app_instance.cache.clear() + + # Temporäre Dateien löschen + temp_dir = tempfile.gettempdir() + myp_temp_files = [] + + try: + for root, dirs, files in os.walk(temp_dir): + for file in files: + if 'myp_' in file.lower() or 'tba_' in file.lower(): + file_path = os.path.join(root, file) + try: + os.remove(file_path) + myp_temp_files.append(file) + except: + pass + except Exception as e: + logger.warning(f"Fehler beim Löschen temporärer Dateien: {str(e)}") + + # Python-Cache leeren + gc.collect() + + logger.info(f"✅ Cache erfolgreich geleert. {len(myp_temp_files)} temporäre Dateien entfernt") + + return jsonify({ + 'success': True, + 'message': f'Cache erfolgreich geleert. {len(myp_temp_files)} temporäre Dateien entfernt.', + 'details': { + 'temp_files_removed': len(myp_temp_files), + 'timestamp': datetime.now().isoformat() + } + }) + + except Exception as e: + logger.error(f"❌ Fehler beim Leeren des Cache: {str(e)}") + return jsonify({ + 'success': False, + 'message': f'Fehler beim Leeren des Cache: {str(e)}' + }), 500 + + @app_instance.route('/api/admin/maintenance/optimize-database', methods=['POST']) + @login_required + @admin_decorator + def api_optimize_database(): + """Optimiert die Datenbank""" + db_session = db_session_func() + + try: + logger.info(f"🔧 Datenbank-Optimierung gestartet von Benutzer {current_user.username}") + + optimization_results = { + 'tables_analyzed': 0, + 'indexes_rebuilt': 0, + 'space_freed_mb': 0, + 'errors': [] + } + + # SQLite-spezifische Optimierungen + try: + # VACUUM - komprimiert die Datenbank + db_session.execute(text("VACUUM;")) + optimization_results['space_freed_mb'] += 1 # Geschätzt + + # ANALYZE - aktualisiert Statistiken + db_session.execute(text("ANALYZE;")) + optimization_results['tables_analyzed'] += 1 + + # REINDEX - baut Indizes neu auf + db_session.execute(text("REINDEX;")) + optimization_results['indexes_rebuilt'] += 1 + + db_session.commit() + + except Exception as e: + optimization_results['errors'].append(f"SQLite-Optimierung: {str(e)}") + logger.warning(f"Fehler bei SQLite-Optimierung: {str(e)}") + + # Verwaiste Dateien bereinigen + try: + uploads_dir = os.path.join(app_instance.root_path, 'uploads') + if os.path.exists(uploads_dir): + orphaned_files = 0 + for root, dirs, files in os.walk(uploads_dir): + for file in files: + file_path = os.path.join(root, file) + # Prüfe ob Datei älter als 7 Tage und nicht referenziert + file_age = datetime.now() - datetime.fromtimestamp(os.path.getctime(file_path)) + if file_age.days > 7: + try: + os.remove(file_path) + orphaned_files += 1 + except: + pass + + optimization_results['orphaned_files_removed'] = orphaned_files + + except Exception as e: + optimization_results['errors'].append(f"Datei-Bereinigung: {str(e)}") + + logger.info(f"✅ Datenbank-Optimierung abgeschlossen: {optimization_results}") + + return jsonify({ + 'success': True, + 'message': 'Datenbank erfolgreich optimiert', + 'details': optimization_results + }) + + except Exception as e: + db_session.rollback() + logger.error(f"❌ Fehler bei Datenbank-Optimierung: {str(e)}") + return jsonify({ + 'success': False, + 'message': f'Fehler bei der Datenbank-Optimierung: {str(e)}' + }), 500 + finally: + db_session.close() + + @app_instance.route('/api/admin/maintenance/create-backup', methods=['POST']) + @login_required + @admin_decorator + def api_create_backup(): + """Erstellt ein System-Backup""" + try: + logger.info(f"💾 Backup-Erstellung gestartet von Benutzer {current_user.username}") + + # Backup-Verzeichnis erstellen + backup_dir = os.path.join(app_instance.root_path, 'database', 'backups') + os.makedirs(backup_dir, exist_ok=True) + + # Backup-Dateiname mit Zeitstempel + timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') + backup_filename = f'myp_backup_{timestamp}.zip' + backup_path = os.path.join(backup_dir, backup_filename) + + backup_info = { + 'filename': backup_filename, + 'created_at': datetime.now().isoformat(), + 'created_by': current_user.username, + 'size_mb': 0, + 'files_included': [] + } + + # ZIP-Backup erstellen + with zipfile.ZipFile(backup_path, 'w', zipfile.ZIP_DEFLATED) as zipf: + + # Datenbank-Datei hinzufügen + db_path = os.path.join(app_instance.root_path, 'instance', 'database.db') + if os.path.exists(db_path): + zipf.write(db_path, 'database.db') + backup_info['files_included'].append('database.db') + + # Konfigurationsdateien hinzufügen + config_files = ['config.py', 'requirements.txt', '.env'] + for config_file in config_files: + config_path = os.path.join(app_instance.root_path, config_file) + if os.path.exists(config_path): + zipf.write(config_path, config_file) + backup_info['files_included'].append(config_file) + + # Wichtige Upload-Verzeichnisse hinzufügen (nur kleine Dateien) + uploads_dir = os.path.join(app_instance.root_path, 'uploads') + if os.path.exists(uploads_dir): + for root, dirs, files in os.walk(uploads_dir): + for file in files: + file_path = os.path.join(root, file) + file_size = os.path.getsize(file_path) + + # Nur Dateien unter 10MB hinzufügen + if file_size < 10 * 1024 * 1024: + rel_path = os.path.relpath(file_path, app_instance.root_path) + zipf.write(file_path, rel_path) + backup_info['files_included'].append(rel_path) + + # Backup-Größe berechnen + backup_size = os.path.getsize(backup_path) + backup_info['size_mb'] = round(backup_size / (1024 * 1024), 2) + + # Alte Backups bereinigen (nur die letzten 10 behalten) + try: + backup_files = [] + for file in os.listdir(backup_dir): + if file.startswith('myp_backup_') and file.endswith('.zip'): + file_path = os.path.join(backup_dir, file) + backup_files.append((file_path, os.path.getctime(file_path))) + + # Nach Erstellungszeit sortieren + backup_files.sort(key=lambda x: x[1], reverse=True) + + # Alte Backups löschen (mehr als 10) + for old_backup, _ in backup_files[10:]: + try: + os.remove(old_backup) + logger.info(f"Altes Backup gelöscht: {os.path.basename(old_backup)}") + except: + pass + + except Exception as e: + logger.warning(f"Fehler beim Bereinigen alter Backups: {str(e)}") + + logger.info(f"✅ Backup erfolgreich erstellt: {backup_filename} ({backup_info['size_mb']} MB)") + + return jsonify({ + 'success': True, + 'message': f'Backup erfolgreich erstellt: {backup_filename}', + 'details': backup_info + }) + + except Exception as e: + logger.error(f"❌ Fehler bei Backup-Erstellung: {str(e)}") + return jsonify({ + 'success': False, + 'message': f'Fehler bei der Backup-Erstellung: {str(e)}' + }), 500 + + logger.info("✅ Wartungs-API-Endpunkte erfolgreich registriert") + +# Automatische Registrierung, falls die Module verfügbar sind +if app and app_logger and admin_required and get_db_session: + register_maintenance_routes(app, app_logger, admin_required, get_db_session) \ No newline at end of file diff --git a/backend/setup.sh b/backend/setup.sh index b179135a..5e5b7546 100644 --- a/backend/setup.sh +++ b/backend/setup.sh @@ -19,6 +19,7 @@ readonly HTTPS_SERVICE_NAME="myp-https" readonly KIOSK_SERVICE_NAME="myp-kiosk" readonly WATCHDOG_SERVICE_NAME="kiosk-watchdog" readonly WATCHDOG_PYTHON_SERVICE_NAME="kiosk-watchdog-python" +readonly FIREWALL_SERVICE_NAME="myp-firewall" readonly KIOSK_USER="kiosk" readonly CURRENT_DIR="$(pwd)" readonly INSTALL_LOG="/var/log/myp-install.log" @@ -560,6 +561,7 @@ install_systemd_services() { "$KIOSK_SERVICE_NAME.service" "$WATCHDOG_SERVICE_NAME.service" "$WATCHDOG_PYTHON_SERVICE_NAME.service" + "$FIREWALL_SERVICE_NAME.service" ) for service_file in "${service_files[@]}"; do @@ -603,6 +605,12 @@ enable_and_start_services() { systemctl enable "$WATCHDOG_SERVICE_NAME" || warning "Fehler beim Aktivieren des Watchdog-Service" systemctl start "$WATCHDOG_SERVICE_NAME" || warning "Fehler beim Starten des Watchdog-Service" + # Firewall-Service aktivieren (falls vorhanden) + if [ -f "$SYSTEM_SYSTEMD_DIR/$FIREWALL_SERVICE_NAME.service" ]; then + progress "Aktiviere Firewall-Service..." + systemctl enable "$FIREWALL_SERVICE_NAME" || warning "Fehler beim Aktivieren des Firewall-Service" + fi + log "✅ Services erfolgreich konfiguriert" } @@ -679,13 +687,16 @@ show_menu() { echo -e "${GREEN}3)${NC} Nur Services installieren/aktualisieren" echo -e " ${BLUE}→ Systemd-Services aus systemd/ Verzeichnis kopieren${NC}" echo "" - echo -e "${GREEN}4)${NC} System-Test durchführen" - echo -e " ${BLUE}→ HTTPS-Verbindung und SSL-Zertifikat testen${NC}" + echo -e "${GREEN}4)${NC} Remote-Zugang konfigurieren (RDP + SSH + Firewall)" + echo -e " ${BLUE}→ SSH (user:raspberry), RDP (root:744563017196A), firewalld${NC}" echo "" - echo -e "${GREEN}5)${NC} Beenden" + echo -e "${GREEN}5)${NC} System-Test durchführen" + echo -e " ${BLUE}→ HTTPS-Verbindung, SSL-Zertifikat, Remote-Zugang testen${NC}" + echo "" + echo -e "${GREEN}6)${NC} Beenden" echo "" echo -e "${CYAN}=================================================================${NC}" - echo -n "Ihre Wahl [1-5]: " + echo -n "Ihre Wahl [1-6]: " } # =========================== INSTALLATIONS-MODI =========================== @@ -740,6 +751,17 @@ install_full_kiosk() { configure_autologin install_systemd_services enable_and_start_services + + # Frage nach Remote-Zugang + echo "" + echo -n "Remote-Zugang (RDP + SSH + Firewall) konfigurieren? [j/N]: " + read -r configure_remote + + if [[ "$configure_remote" =~ ^[Jj]$ ]]; then + install_remote_access + configure_firewall + fi + test_application cleanup_old_files @@ -769,6 +791,23 @@ install_services_only() { success "✅ Service-Installation abgeschlossen!" } +install_remote_access_only() { + log "=== MODUS: NUR REMOTE-ZUGANG KONFIGURIEREN ===" + + check_root + check_debian_system + check_internet_connection + + install_remote_access + configure_firewall + test_remote_access + + success "✅ Remote-Zugang-Konfiguration abgeschlossen!" + info "Zugang verfügbar über:" + info " 📡 SSH: ssh user@ (Passwort: raspberry)" + info " 🖥️ RDP: :3389 (Benutzer: root, Passwort: 744563017196A)" +} + run_system_test() { log "=== MODUS: SYSTEM-TEST ===" @@ -777,7 +816,7 @@ run_system_test() { # Zusätzliche Tests progress "Prüfe Service-Status..." - local services=("$HTTPS_SERVICE_NAME" "$KIOSK_SERVICE_NAME" "$WATCHDOG_SERVICE_NAME") + local services=("$HTTPS_SERVICE_NAME" "$KIOSK_SERVICE_NAME" "$WATCHDOG_SERVICE_NAME" "$FIREWALL_SERVICE_NAME") for service in "${services[@]}"; do if systemctl is-enabled --quiet "$service" 2>/dev/null; then @@ -791,9 +830,220 @@ run_system_test() { fi done + # Remote-Zugang testen (falls konfiguriert) + if systemctl is-enabled --quiet ssh 2>/dev/null || systemctl is-enabled --quiet xrdp 2>/dev/null; then + test_remote_access + else + info "ℹ️ Remote-Zugang nicht konfiguriert" + fi + success "✅ System-Test abgeschlossen!" } +# =========================== RDP & SSH ZUGANG =========================== +install_remote_access() { + log "=== INSTALLIERE REMOTE-ZUGANG (RDP & SSH) ===" + + # SSH-Server installieren und konfigurieren + progress "Installiere und konfiguriere SSH-Server..." + apt-get install -y openssh-server || error "SSH-Server Installation fehlgeschlagen" + + # SSH-Service aktivieren + systemctl enable ssh + systemctl start ssh + + # SSH-Benutzer 'user' erstellen (falls nicht vorhanden) + if ! id "user" &>/dev/null; then + progress "Erstelle SSH-Benutzer: user" + useradd -m -s /bin/bash user || error "Kann SSH-Benutzer nicht erstellen" + echo "user:raspberry" | chpasswd || error "Kann Passwort für SSH-Benutzer nicht setzen" + usermod -aG sudo user 2>/dev/null || true + log "✅ SSH-Benutzer 'user' erstellt mit Passwort 'raspberry'" + else + info "SSH-Benutzer 'user' existiert bereits" + echo "user:raspberry" | chpasswd || warning "Konnte Passwort für SSH-Benutzer nicht aktualisieren" + fi + + # RDP-Server (xrdp) installieren + progress "Installiere RDP-Server (xrdp)..." + + # Minimale Desktop-Umgebung für RDP installieren + progress "Installiere minimale Desktop-Umgebung für RDP..." + apt-get install -y tasksel || error "tasksel Installation fehlgeschlagen" + + # XFCE als leichtgewichtige Desktop-Umgebung installieren + progress "Installiere XFCE Desktop-Umgebung..." + apt-get install -y xfce4 xfce4-goodies || error "XFCE Installation fehlgeschlagen" + + # xrdp installieren + apt-get install -y xrdp || error "xrdp Installation fehlgeschlagen" + + # xrdp-Service aktivieren + systemctl enable xrdp + systemctl start xrdp + + # SSL-Zertifikate für xrdp erstellen + progress "Erstelle SSL-Zertifikate für xrdp..." + mkdir -p /etc/xrdp/certs + cd /etc/xrdp/certs + + openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -days 3650 \ + -subj "/C=DE/ST=Baden-Wuerttemberg/L=Stuttgart/O=Mercedes-Benz/OU=IT/CN=myp-rdp" \ + || error "SSL-Zertifikat-Generierung für xrdp fehlgeschlagen" + + # Berechtigungen für xrdp-Zertifikate setzen + chown -R xrdp:xrdp /etc/xrdp/certs + chmod 0644 /etc/xrdp/certs/cert.pem + chmod 0600 /etc/xrdp/certs/key.pem + + # xrdp-Konfiguration für TLS + progress "Konfiguriere xrdp für TLS..." + cp /etc/xrdp/xrdp.ini /etc/xrdp/xrdp.ini.backup + + cat > /etc/xrdp/xrdp.ini << 'EOF' +[Globals] +ini_version=1 +fork=true +port=3389 +tcp_nodelay=true +tcp_keepalive=true +security_layer=tls +certificate=/etc/xrdp/certs/cert.pem +key_file=/etc/xrdp/certs/key.pem +ssl_protocols=TLSv1.2, TLSv1.3 +autorun= +allow_channels=true +allow_multimon=true +bitmap_cache=true +bitmap_compression=true +bulk_compression=true +max_bpp=32 +new_cursors=true +use_fastpath=both +require_credentials=true +ask_for_reconnect_reason=true +enable_token_login=true + +[Xorg] +name=Xorg +lib=libxup.so +username=ask +password=ask +ip=127.0.0.1 +port=-1 +code=20 +EOF + + # Root-Passwort für RDP setzen + progress "Setze Root-Passwort für RDP-Zugang..." + echo "root:744563017196A" | chpasswd || error "Kann Root-Passwort nicht setzen" + + # xrdp-Service neu starten + systemctl restart xrdp + + cd "$CURRENT_DIR" + + log "✅ Remote-Zugang konfiguriert:" + log " 📡 SSH: user:raspberry (Port 22)" + log " 🖥️ RDP: root:744563017196A (Port 3389)" +} + +# =========================== FIREWALL KONFIGURATION =========================== +configure_firewall() { + log "=== KONFIGURIERE FIREWALL (firewalld) ===" + + # firewalld installieren + progress "Installiere firewalld..." + apt-get install -y firewalld || error "firewalld Installation fehlgeschlagen" + + # firewalld aktivieren und starten + systemctl enable firewalld + systemctl start firewalld + + # Warte kurz bis firewalld vollständig gestartet ist + sleep 3 + + progress "Konfiguriere firewalld-Zonen und -Regeln..." + + # Zone definieren + firewall-cmd --permanent --new-zone=myp-backend 2>/dev/null || true + firewall-cmd --permanent --zone=myp-backend --add-source=192.168.0.0/24 + + # Nur HTTPS für API & Kiosk zulassen + firewall-cmd --permanent --zone=myp-backend --add-port=443/tcp + + # SSH für Wartung + firewall-cmd --permanent --zone=myp-backend --add-service=ssh + + # RDP für Remote-Desktop + firewall-cmd --permanent --zone=myp-backend --add-port=3389/tcp + + # Default-Zone setzen + firewall-cmd --set-default-zone=myp-backend + + # Änderungen übernehmen + firewall-cmd --reload + + # Firewall-Status anzeigen + progress "Firewall-Konfiguration:" + firewall-cmd --list-all-zones | grep -A 10 "myp-backend" || true + + log "✅ Firewall konfiguriert:" + log " 🔒 Zone: myp-backend (192.168.0.0/24)" + log " 🌐 HTTPS: Port 443/tcp" + log " 📡 SSH: Port 22/tcp" + log " 🖥️ RDP: Port 3389/tcp" +} + +# =========================== REMOTE-ZUGANG TESTEN =========================== +test_remote_access() { + log "=== TESTE REMOTE-ZUGANG ===" + + # SSH-Service testen + progress "Teste SSH-Service..." + if systemctl is-active --quiet ssh; then + success "✅ SSH-Service läuft" + + # SSH-Port testen + if ss -tlnp | grep -q ":22 "; then + success "✅ SSH-Port 22 ist offen" + else + warning "⚠️ SSH-Port 22 nicht erreichbar" + fi + else + error "❌ SSH-Service läuft nicht" + fi + + # RDP-Service testen + progress "Teste RDP-Service..." + if systemctl is-active --quiet xrdp; then + success "✅ RDP-Service läuft" + + # RDP-Port testen + if ss -tlnp | grep -q ":3389 "; then + success "✅ RDP-Port 3389 ist offen" + else + warning "⚠️ RDP-Port 3389 nicht erreichbar" + fi + else + error "❌ RDP-Service läuft nicht" + fi + + # Firewall-Status testen + progress "Teste Firewall-Status..." + if systemctl is-active --quiet firewalld; then + success "✅ Firewall läuft" + + # Aktive Zone anzeigen + local active_zone=$(firewall-cmd --get-active-zones | head -1) + info "Aktive Zone: $active_zone" + else + warning "⚠️ Firewall läuft nicht" + fi + + log "✅ Remote-Zugang-Test abgeschlossen" +} + # =========================== HAUPTPROGRAMM =========================== main() { # Erstelle Log-Datei @@ -823,17 +1073,23 @@ main() { read -r ;; 4) - run_system_test + install_remote_access_only echo "" echo -n "Drücken Sie Enter um fortzufahren..." read -r ;; 5) + run_system_test + echo "" + echo -n "Drücken Sie Enter um fortzufahren..." + read -r + ;; + 6) log "Setup-Skript beendet" exit 0 ;; *) - error "Ungültige Auswahl. Bitte wählen Sie 1-5." + error "Ungültige Auswahl. Bitte wählen Sie 1-6." ;; esac done diff --git a/backend/systemd/myp-firewall.service b/backend/systemd/myp-firewall.service new file mode 100644 index 00000000..6158a9b2 --- /dev/null +++ b/backend/systemd/myp-firewall.service @@ -0,0 +1,60 @@ +[Unit] +Description=MYP Firewall Configuration Service +Documentation=https://github.com/MYP-Druckerverwaltung +After=firewalld.service +Wants=firewalld.service +Requires=firewalld.service + +[Service] +Type=oneshot +RemainAfterExit=yes +User=root +Group=root + +# Firewall-Konfiguration für MYP Backend +ExecStart=/bin/bash -c '\ + # Warte bis firewalld vollständig gestartet ist \ + sleep 5; \ + \ + # Zone definieren (falls nicht vorhanden) \ + firewall-cmd --permanent --new-zone=myp-backend 2>/dev/null || true; \ + \ + # Quell-Netzwerk definieren \ + firewall-cmd --permanent --zone=myp-backend --add-source=192.168.0.0/24; \ + \ + # HTTPS für API & Kiosk \ + firewall-cmd --permanent --zone=myp-backend --add-port=443/tcp; \ + \ + # SSH für Wartung \ + firewall-cmd --permanent --zone=myp-backend --add-service=ssh; \ + \ + # RDP für Remote-Desktop \ + firewall-cmd --permanent --zone=myp-backend --add-port=3389/tcp; \ + \ + # Default-Zone setzen \ + firewall-cmd --set-default-zone=myp-backend; \ + \ + # Änderungen übernehmen \ + firewall-cmd --reload; \ + \ + # Status loggen \ + logger "MYP Firewall: Konfiguration erfolgreich angewendet"; \ + firewall-cmd --list-all-zones | logger -t "MYP-Firewall"; \ +' + +# Umgebungsvariablen +Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +# Logging +StandardOutput=journal +StandardError=journal +SyslogIdentifier=myp-firewall + +# Sicherheitseinstellungen +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=false +ProtectHome=true + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/backend/templates/admin.html b/backend/templates/admin.html index 85496d1f..033d3111 100644 --- a/backend/templates/admin.html +++ b/backend/templates/admin.html @@ -624,6 +624,7 @@ class MaintenanceModal { this.triggerBtn = document.getElementById('maintenance-btn'); this.closeBtn = document.getElementById('close-maintenance-modal'); this.isOpen = false; + this.isLoading = false; this.initializeEventListeners(); } @@ -705,28 +706,101 @@ class MaintenanceModal { } } + setLoadingState(loading) { + this.isLoading = loading; + const buttons = this.modal.querySelectorAll('button:not(#close-maintenance-modal)'); + + buttons.forEach(button => { + if (loading) { + button.disabled = true; + button.style.opacity = '0.6'; + button.style.cursor = 'not-allowed'; + + // Spinner hinzufügen + if (!button.querySelector('.loading-spinner')) { + const spinner = document.createElement('div'); + spinner.className = 'loading-spinner inline-block w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin mr-2'; + button.insertBefore(spinner, button.firstChild); + } + } else { + button.disabled = false; + button.style.opacity = '1'; + button.style.cursor = 'pointer'; + + // Spinner entfernen + const spinner = button.querySelector('.loading-spinner'); + if (spinner) { + spinner.remove(); + } + } + }); + + // Loading-Overlay anzeigen/verstecken + const loadingOverlay = document.getElementById('loading-overlay'); + if (loadingOverlay) { + if (loading) { + loadingOverlay.classList.remove('hidden'); + } else { + loadingOverlay.classList.add('hidden'); + } + } + } + async executeAction(actionType) { + if (this.isLoading) return; + try { this.setLoadingState(true); + let endpoint = ''; + let confirmMessage = ''; + let successMessage = ''; + switch (actionType) { case 'clearCache': - await clearCache(); + endpoint = '/api/admin/maintenance/clear-cache'; + confirmMessage = 'Möchten Sie den Cache wirklich leeren?'; + successMessage = 'Cache erfolgreich geleert'; break; case 'optimizeDatabase': - await optimizeDatabase(); + endpoint = '/api/admin/maintenance/optimize-database'; + confirmMessage = 'Möchten Sie die Datenbank optimieren? Dies kann einige Minuten dauern.'; + successMessage = 'Datenbank erfolgreich optimiert'; break; case 'createBackup': - await createBackup(); + endpoint = '/api/admin/maintenance/create-backup'; + confirmMessage = 'Möchten Sie ein Backup erstellen?'; + successMessage = 'Backup erfolgreich erstellt'; break; default: throw new Error(`Unbekannte Aktion: ${actionType}`); } - this.closeModal(); + if (!confirm(confirmMessage)) { + this.setLoadingState(false); + return; + } + + const response = await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': getCsrfToken() + } + }); + + const result = await response.json(); + + if (response.ok && result.success) { + showNotification(successMessage, 'success'); + this.closeModal(); + } else { + showNotification(result.message || 'Fehler bei der Ausführung der Wartungsaktion', 'error'); + } + } catch (error) { console.error('Fehler bei Wartungsaktion:', error); - showNotification('Fehler bei der Ausführung der Wartungsaktion', 'error'); + showNotification('Fehler bei der Ausführung der Wartungsaktion: ' + error.message, 'error'); } finally { this.setLoadingState(false); } @@ -734,7 +808,8 @@ class MaintenanceModal { navigateToSettings() { try { - window.location.href = '{{ url_for("optimization_settings") }}'; + // Direkte Navigation zu den Optimierungs-Einstellungen + window.location.href = '/api/optimization/settings'; } catch (error) { console.error('Fehler beim Navigieren zu den Einstellungen:', error); showNotification('Fehler beim Öffnen der Einstellungen', 'error'); @@ -742,9 +817,15 @@ class MaintenanceModal { } } +// Globale Wartungs-Modal Instanz +let maintenanceModal = null; + // Initialisierung nach DOM-Laden document.addEventListener('DOMContentLoaded', function() { - new MaintenanceModal(); + // Nur einmal initialisieren + if (!maintenanceModal) { + maintenanceModal = new MaintenanceModal(); + } }); // Notification anzeigen @@ -763,6 +844,11 @@ function showNotification(message, type = 'info') { ${type === 'success' ? '✅' : type === 'error' ? '❌' : 'ℹ️'}
${message}
+ `; @@ -776,13 +862,15 @@ function showNotification(message, type = 'info') { // Automatisch entfernen nach 5 Sekunden setTimeout(() => { - notification.style.transform = 'translateX(100%)'; - notification.style.opacity = '0'; - setTimeout(() => { - if (notification.parentNode) { - notification.parentNode.removeChild(notification); - } - }, 300); + if (notification.parentNode) { + notification.style.transform = 'translateX(100%)'; + notification.style.opacity = '0'; + setTimeout(() => { + if (notification.parentNode) { + notification.parentNode.removeChild(notification); + } + }, 300); + } }, 5000); } @@ -791,102 +879,5 @@ function getCsrfToken() { const token = document.querySelector('meta[name="csrf-token"]'); return token ? token.getAttribute('content') : ''; } - -// Cache leeren -async function clearCache() { - if (!confirm('Möchten Sie den Cache wirklich leeren?')) return; - - try { - showNotification('Cache wird geleert...', 'info'); - - const response = await fetch('/api/admin/cache/clear', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-CSRFToken': getCsrfToken() - } - }); - - const result = await response.json(); - - if (response.ok && result.success) { - showNotification('Cache erfolgreich geleert', 'success'); - // Modal schließen - document.getElementById('maintenance-modal').classList.add('hidden'); - } else { - showNotification(result.message || 'Fehler beim Leeren des Cache', 'error'); - } - } catch (error) { - showNotification('Netzwerkfehler: ' + error.message, 'error'); - } -} - -// Datenbank optimieren -async function optimizeDatabase() { - if (!confirm('Möchten Sie die Datenbank optimieren? Dies kann einige Minuten dauern.')) return; - - try { - showNotification('Datenbank wird optimiert...', 'info'); - - const response = await fetch('/api/admin/database/optimize', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-CSRFToken': getCsrfToken() - } - }); - - const result = await response.json(); - - if (response.ok && result.success) { - showNotification('Datenbank erfolgreich optimiert', 'success'); - // Modal schließen - document.getElementById('maintenance-modal').classList.add('hidden'); - } else { - showNotification(result.message || 'Fehler bei der Datenbankoptimierung', 'error'); - } - } catch (error) { - showNotification('Netzwerkfehler: ' + error.message, 'error'); - } -} - -// Backup erstellen -async function createBackup() { - if (!confirm('Möchten Sie ein Backup erstellen?')) return; - - try { - showNotification('Backup wird erstellt...', 'info'); - - const response = await fetch('/api/admin/backup/create', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-CSRFToken': getCsrfToken() - } - }); - - const result = await response.json(); - - if (response.ok && result.success) { - showNotification('Backup erfolgreich erstellt', 'success'); - // Modal schließen - document.getElementById('maintenance-modal').classList.add('hidden'); - } else { - showNotification(result.message || 'Fehler beim Erstellen des Backups', 'error'); - } - } catch (error) { - showNotification('Netzwerkfehler: ' + error.message, 'error'); - } -} - -// Escape-Taste um Modal zu schließen -document.addEventListener('keydown', function(e) { - if (e.key === 'Escape') { - const modal = document.getElementById('maintenance-modal'); - if (!modal.classList.contains('hidden')) { - modal.classList.add('hidden'); - } - } -}); {% endblock %}