From 83b8f43b9ede3156d334abdf0d5d287471836745 Mon Sep 17 00:00:00 2001 From: scayac Date: Tue, 25 Aug 2026 15:24:02 +0200 Subject: [PATCH] Ajout gestion reconnexion automatique --- .gitignore | 2 +- README.md | 12 ++++++ data/css/styles.css.gz | Bin 1631 -> 0 bytes data/index.html.gz | Bin 674 -> 0 bytes data/js/api.js.gz | Bin 736 -> 0 bytes data/js/app.js.gz | Bin 982 -> 0 bytes data/js/crypto-js.min.js.gz | Bin 16587 -> 0 bytes data/login.html.gz | Bin 929 -> 0 bytes data/settings.html.gz | Bin 1790 -> 0 bytes data/update.html.gz | Bin 2649 -> 0 bytes platformio.ini | 24 ++++++++++- src/main.cpp | 80 ++++++++++++++++++++++++++++++------ src/server.cpp | 46 ++++++++++++++++++++- src/server.h | 2 + 14 files changed, 149 insertions(+), 17 deletions(-) delete mode 100644 data/css/styles.css.gz delete mode 100644 data/index.html.gz delete mode 100644 data/js/api.js.gz delete mode 100644 data/js/app.js.gz delete mode 100644 data/js/crypto-js.min.js.gz delete mode 100644 data/login.html.gz delete mode 100644 data/settings.html.gz delete mode 100644 data/update.html.gz diff --git a/.gitignore b/.gitignore index f7f92e3..3e594b0 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,4 @@ Thumbs.db # Build artifacts *.bin *.elf -*.map +*.map \ No newline at end of file diff --git a/README.md b/README.md index 3b70e74..cf80be1 100644 --- a/README.md +++ b/README.md @@ -258,6 +258,18 @@ Procédure recommandée (sans secret en clair dans les fichiers): 2. En mode AP, l'adresse est toujours `192.168.4.1` 3. En mode Station, trouvez l'IP dans les logs du moniteur série 4. Désactivez le cache du navigateur (Ctrl+F5) +5. En réseau entreprise (PEAP), un filtrage « client isolation » peut bloquer ping/HTTP entre clients WiFi +6. Utilisez l'AP de secours `ESP32-Webapp-setup` (ou `-setup`) avec l'IP `192.168.4.1` + +### Diagnostic rapide réseau (2 minutes) + +1. Ouvrez le moniteur série et vérifiez `Serveur web démarré sur le port 80` +2. Depuis votre PC, testez l'endpoint de santé: + - en STA: `curl -v http:///api/health` + - en AP de secours: `curl -v http://192.168.4.1/api/health` +3. Vérifiez les logs série: + - si vous voyez `HTTP GET /api/health depuis ...`, le trafic arrive bien + - si rien n'apparaît, le réseau filtre la communication vers l'ESP32 ### Erreur lors de la mise à jour OTA diff --git a/data/css/styles.css.gz b/data/css/styles.css.gz deleted file mode 100644 index abef4f78cbf4ec68a75e7d763d795bc831bc5e71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1631 zcmV-l2B7&LiwFo}B7JEB|8sPCY-MvUV{>x=%~;!x+cpq=&sPvx6j?7UN0KeuaZsR5 z(6oI>5M_+4f&NyPGi03?d`1|TuC7m`Rf_MAI~c!-%|PL zB_b1YMXr2*5vS?v-Ylc?uAYqM@wA;RArU!qH_wwrv|x6=N)jkbI~y+PVivWtjOJ^Q zc9JF2sGGc}GUxf)&ZcP+_=|Ry=c%w$!GebIvYpEKdZW5+%k))BJBjknPEBW1I`gf( z4OdXh(Le2IEF?n}C1t!SS7cc{bTbcLxuHzluZT~=;(^TYU#??%?T?B1n*^hFy^=I9 zx#A*U5t?Nmj2q z4%Mf|f<2l;Qjse!rCXjou82pAA_K2{EER0WXz$?gWZsM# zXY8oFcHLuOi)nJVmZHj;x3|#o@u41K>1jyA?Ms|c->=p$i3Fe0dA zWpg}!JENov`jhc{0;juhaLCozfs2(A+iptZU8}kY_mWz-(4KHIKHGC9=5}aJZ!E|l zMM~>5T#Agd)_(UQIht2=-k&CHK*VM`NacZ3dp7w!K$hy zxjTva0h*ETpDQqWl6hWK>NoXRz_-seeV%_FADu5~S>7XEPp!$TZ4C0%ij-&Y{IfB( zy%c=^n}b_$5%q3BTxYY~6=UqesB-Dph0OUYvv0>L6-iYZ14N~CiRWJAM)ui~YrmG( zzf-4{4#Me}gi+Mi(+%I=pgfT`T#HrSrwq^QdedP}AJ4-`>?1NvsG-zyrl4t^c7V)z zS0$A!HF$;Kj;PbFI-ryG9b(jC|Fw$Z-my&?b#cj5A7NAMneD@=_@s7r%NhNyB03M} z%a&j7v2UeolgzfwgW0>+2wdn-lfRTWdA-^R%jbqV+9spR#PrrDa^--HYzBX_OpDUW7r?m#iEA3r!9nNrR(;V}J;Hg8d|# z&3&6`sFd|pTS&3S>#b~C%=XzkEb{wK)+X5HU0-^T^xOV;YCD4gi{KR`kX}eU&HXgf z8Fs@N(;eoXPIwo!*RJi+tyY(03O6^^=DBY=9Gly`WHr$F(`axk#k#oqXIoly-A5g? z=($YbzS?K?H?Qb|`6;crtHaTr97`q7fo`|yW4Eebfl!GzC^`mrUI){N!FoK1=6EP! znzYs!(P!NOnVep8*0q|w?B$&IAJp)E-JZ<{-js#?ay}eVZxfuM$XR}#DZU4QOeNV%%E>D`EY>NHY zcD~o?eW^d~S?)Wbv$5gsJ?K(Tv>n)293C*#$vo{0#BBhk+0u7l>IaYe5+>Mx3A3kE d7b8b1fkFJ}&>ok1Ct6W$&%e@*Hqqx7005+%MeG0o diff --git a/data/index.html.gz b/data/index.html.gz deleted file mode 100644 index 33b346faac224f0a3ba06c017b599424000f6681..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 674 zcmV;T0$u$diwFo}B7JEB|7mVyWq2-VbZu+^#Z|p-+b|H`^As$Xv_NGy>C!YqW&nu*J1Liu9?ls3Ph7g+iAj^8LBRX(WfKbHjFfz{@(PVVhK#nC#*v zf`++B6*w13UM65~=ro5JtsVx61)a(G&$r9VJFFPG*|9)ft;B60u%t;k!}CF{zg+r2 z>S>O$D@HhkO+mOap7hMMapDnJC>GznMR$)=Rc7Gv#^snCip7;UF_pc?N12(ayo?VP z*J+*nIT^hMUy&jla`NqRt1Cy1(7pemGtvPf2?r-JJl1U=3@TmLnUee}WBI0`)Hy^ju+tDLz0pf};C@mGhZ;jyIQIwu?EpW<;qBd#`PgRB%V0! zDHjzMPy-T`M2V;I56bVzAL|1C0NWYINe-P(b+JLa+NbxPSouDTQ-RXL>GTY8RcM&Z zrXN4ROi77apyLEe8VfuZFoNF>(C~fu_HeI7Bv3=jcwrM-fgS^Hg_3m)H**<4kYI3g zO7m`SnOhC3=dGt|G&MAuocz|dBmk;+r zkyD{cOkQD$L|o`?DPy$bda5Jk6NGCPY%6z!YR4;P6Rk>+Si+=X^PHI5(DrcOMzZK& zdef3_8_3SG88Y9gMeSAUCu z3~i#q6JXPe*%j+|<`U!~YICyZ$T5OqiTc6SJV!?9JkQgmdHvf#S(bxH>1W;hYv}Fpe0!g(mCk<=I$ev?(g(ewkgIldrUpv54j?7J3^AT5|l=rhK_O zuNkgQlU1MjC%g0cHXSEi#+5}8GF4a1xiQ#j3suCF-j+&-&$DU1%>HlG$BlZ>cA$!m zMj!|zStqbBG-M-S-I>&I!ZynQ)~ zq2nC5LX#|&%XQrTXy?^Qa1AdaFvRbsM$f4=+D4Oen@FCaM~6BshIDY`li?Pra3;+PPnrmtR diff --git a/data/js/app.js.gz b/data/js/app.js.gz deleted file mode 100644 index c841169b06e7a5b13697276e2ae59b71f966815b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 982 zcmV;{11bC;iwFo}B7JEB|6y=&E^2cCl~&u1+cpq=_gBnA3d!BA>|E9j>Ux1};{_Ip z(;`mM$C!~uD+Us&lGJ*QjsB&N*_Zx=f2>2@EXh`o8n7Ud=gg3EW`>+h;8mK&+-HVM z0jc7`=P8R3Qkf}OuK#%c4AxQzR2)a6$po&YN|-?fgfW<;8Uln+3f~$Kn4mDDFe^W4 z+=Bm14NuV3=t~jkF+7Zbe)ORY1Z<~a0V`%AFN~#BH4G;3a`s|69nV|nh!)j@H~1ea zJQ1JIdc1^-%-}GyAP{lZr4hh#4!GI0oGujS^Y#|)H(V~@r;BN`UJ9-q&LOx; zz+K+^7{`?StsdiaVRhF z-uxzop&4@C?Mv3VS0Yge?6?altYfC-grX#2IN={5}4yZUf$n z{7HXor_R9-Q%iazvg!Y_(p~lsCpSi|89uwm>otvKW19LV~O>=sm9^SgI!C$nV({G z^|D_l>PMsbKzS`Q<5F2JE?dsOv4Ca=^C5KCxzhsX=WsD?&fi*s8^H}{G1sMZECZ@w zbz20@N=xTXYGr9+%7S2dM=x(lU8L)W>%9JbRe5|XS%3k_-QLnb!=fh#@`$$gWU6)q zfx~=1LmoF0ZMg{K){A9%8S+xPDhP^)M|Yw)qhzBx5dq$Nkx638i!WbbhysfR1!dy2 zweEJv)2*((0hSp_C2cdi2$GS-1;Fm>qPmh$8*b@*L$OhJEOy(T`eM-Ae@NV3=ky5x E0GQh88UO$Q diff --git a/data/js/crypto-js.min.js.gz b/data/js/crypto-js.min.js.gz deleted file mode 100644 index 022247489e06a845cc4561cf137fff77213e4333..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16587 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 G7#IMXL5Jf2 diff --git a/data/login.html.gz b/data/login.html.gz deleted file mode 100644 index 884711bcf805fa257af459be94fbc86665b0487f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 929 zcmV;S177?eiwFo}B7JEB|7>q(X>KlPbZu+^tyax$+c*%u=P9_tqLP6~O0y`iNGu&T zae)HcHfY?#-b#~Wi(N`oNy>^5_$_+xEA|m~C|Qy1NZBB$0K+8boBtUOr@vi)yqf=W zGb5SJWqLAoe?-z^m3T|-xdWg)Jt6o_bFh@KjA{cm@jlPrhJSkdq=M#W^%s0{}K%{@r>O_Fc|=9&NM6&FJ{KX?Qw)l=S|$YGhHaYAxu(Z5>Kj?D8iDi zpfiDs^&!!DLItYdAx2PC^F2TV%3h~Ge|-P`$Ok+>0-pCiBl-N0e285tofE-#df(#i zNbRwGjv-uWRh0w4HjZ=wk{~YeDg)Z3(n~+6oN&LgLJC7IRC*dWxDgqmC@V|sro<|+ zaEEztHa>{k)knYA@U0RWIH8qQOy#8nyAK%fDMqIrAOAYvOJu2)OXGWG2?yd1;Q4Mp z@HejayVmu7KjJ#8w8oAJdox}hf<aShZd==SM=xzpICWUrBW$lkKu8>U5RiO~$(`KyU(DWb2&iESzA zKCBnX6{~U#WV8Z1li-$rZQk*~e`pwg5>d`)Yoz=w3|>>91HX-ue@dv?6pREgAxTQ^ z53Gl%)Zo~zVM!}#gURK1r(#TvC0#+2WZbY0gLbW{tl)A$PFEFA$>}gp$=N068guuA zq#eqC%0H_&N8#zUXfP!%oY%Cb!jdIemIZ#?y=&5sN6rOR6K z`+Y0r^QU)L*v+ay)j>y{Y+rS083uOL$@a@5ZQG;x5B6ajaZYgvchT;`kL{w0;5kCX zk_1jfWGcom`IFHMb>$N^-Tt91?XFsu1n2Q`#WrJX# zGxxt*6kOGjRBgs1_aJaK#&`~Qk$dHg*tb0;Z_dsJ;qUKH3JU#ydS|)+eiwXb$ D1un?A diff --git a/data/settings.html.gz b/data/settings.html.gz deleted file mode 100644 index 17967234024a87199031c013925dd90e2f6a2b92..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1790 zcmV2iD&HGz4fQ|bQ9iYdi7@m6P-nD7@Hg~9QDVDPNKlsX%lV7`_eOq; zQy(m2#$X->LK(ykxB`x#bLJ@5a$v@6=*KVHgmI=A_4?AL9$35PPDV;G<-zEb3hFPf zl>kZl@kfi2}ASvK)e>oZ6c z8jb^Jky7AvMr@BtX&Lx9rXm$3-gH=gvp*J;JI3hWzdn7cNQI!yh(7QrM>htcnV5__ zaA|7W8>4eN_JBsj0WuDRGaS$+EIbCq_*5o{88d1tHUlGX!3{K`(4h)a)^C^uvizG| z1d-T);|0?oQpV_bd1GtkFIm6~;5kza2dI4VxK%cb_7BR?czJufyG67~ zY*=@1bQJIjn?`~vl#cw$UNEG!SAv@a!Y9np9y(z!@TEkk%uB=fry_{L8okM za0$(KQZkh8?CjMsaUvp?HxkcwIEcX-NH88oil{}1!KC04ojH>*p_ay3j(k<-qAynL z_b|GJh&KS4n)Yfe^ToGAoxn9a?x?T)DYJ(ei4xk{fDMOxu%$VkneoJ^P zht5-cT5zpLGY$=#OoJ|P8ZeG4o#(c_yq5R1Q=Ua?rNYvq*=QUoh4GchD5J5@)G8B= z6({)D4+Zn7SQw)dE?~+e2DV7OBtb7lFyJuHY5z8eX1aSTp3D8zFHlZZFRpz>wQZeR zg`ZRXC|RlV%X=PwnMHQ|mGuX(!=0kOF#tb1@U`APrZ+l;75nUcROqW$kIB2V1q&6C z!p08$LJnxi%nPY;lATdKm@O8e3i=n)#2ng;uol+?MdAaP5{rDeI;`qJ<$~K}5^+1O zx?Vt?qz-p`aVXQ<~g~2m0}c z#Ve=VS+&xgo;jml1jsy*!qi))-Xb4Al1`@`_p`JRw3i^O<5dBYhQ!POn#5?NT&k6g z3`%gC&$FE(pDSWhWxJ#cLIk2$b9B-T=5G>j?RHKuNn@{x!J+RArm;-=&7&#nv@h#y zlTJ*rQ##zMt?JY2VdYo7hEI%P4DwNxYxsbh>dtJ!ubR9UVyvKdF{ zTYWATd7rM6Z$4-VuHH9dX)%wI%MrxWZ$(fX?9*PVi!o!3YAoF#!%F1CO&CS~Wp}#T zCQo;Ec5+JBn15p|=_UgmjXKWB3^!h5e*`$@qcg^N6%Rbg5nZU!4$M%1MmvTHjXYHj z`K@ZM$VI7jhbZ4n7rjJdoTf$QR?1m5s;AB-q?;$#YfOBJGU1wS9<3=r8Wf;m8}W0K zW3%vUQn!hoBd3#icxTz`%fh-E$W=8+*w)m9LL#bO1rf;F_T>$j3%8j_s)u8Z1*-PuXlKds9TS%j~aL7;$*e2aEBY{m1~iO?Uj)5imUEj z>)a+KzO0{R)8PNk*Ycy}qj?)9vvlfflhMipVx!X`S)8cakjxKWo5-cZNh>K!b4-i6 zq%(0|;3t+<=6I{4yauVK7P+3%Iksp4T1c_Jh7tuss!X>&!24o^hq?k8S&s4#g5{yq){T2L4VfbC9W20WDhg>NApYa#x)R)h%j#TV^iYNyU_wH_0*S z+`GJ%cgf2wJ9PbX#pb(RDbOJnZ<=YJZ!&Is zkpr^Z+27gi^m+{w9^$m%Q5XyPdMwO`&;jX?Z%IL)jm+;- g*Yfufhv{8PcF8duB%g(cgXDAgf7_gWiyt2V0L?UOqW}N^ diff --git a/data/update.html.gz b/data/update.html.gz deleted file mode 100644 index ec683db0eb534463b9a17d1a25e43c638f692284..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2649 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 HI4uAGamgGZ diff --git a/platformio.ini b/platformio.ini index 69f41ae..1916328 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,7 +12,27 @@ lib_deps = ESP32Async/AsyncTCP bblanchon/ArduinoJson@^7.0.0 +#build_flags = +# -DBOARD_HAS_PSRAM +upload_speed = 921600 + +[env:esp32-s3-supermini] +platform = espressif32 +board = esp32-s3-devkitc-1 +framework = arduino +board_upload.flash_size = 4MB +board_build.filesystem = littlefs +board_build.partitions = default.csv +monitor_speed = 115200 +lib_deps = + ESP32Async/ESPAsyncWebServer + ESP32Async/AsyncTCP + bblanchon/ArduinoJson@^7.0.0 build_flags = + -DARDUINO_USB_CDC_ON_BOOT=1 -DBOARD_HAS_PSRAM - -upload_speed = 921600 +; Configuration ArduinoOTA pour firmware et filesystem +;upload_protocol = espota +;upload_port = 192.168.0.xx +;upload_flags = +; --auth=password \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index b4d87c1..1bcb327 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,6 +23,26 @@ String wifiEapUsername = ""; String wifiEapPassword = ""; String apPassword = "webapp123"; +void logNetworkInfo() { + logMessage(LOG_INFO, "Adresse IP: " + WiFi.localIP().toString()); + logMessage(LOG_INFO, "Passerelle: " + WiFi.gatewayIP().toString()); + logMessage(LOG_INFO, "Masque: " + WiFi.subnetMask().toString()); + logMessage(LOG_INFO, "DNS 1: " + WiFi.dnsIP(0).toString()); + logMessage(LOG_INFO, "DNS 2: " + WiFi.dnsIP(1).toString()); + logMessage(LOG_INFO, "MAC STA: " + WiFi.macAddress()); +} + +void startFallbackAP() { + String fallbackSsid = hostname + "-setup"; + WiFi.mode(WIFI_AP_STA); + if (WiFi.softAP(fallbackSsid.c_str(), apPassword.c_str())) { + logMessage(LOG_INFO, "AP de secours actif: " + fallbackSsid); + logMessage(LOG_INFO, "IP AP de secours: " + WiFi.softAPIP().toString()); + } else { + logMessage(LOG_WARN, "Échec du démarrage de l'AP de secours"); + } +} + void createDefaultConfig() { logMessage(LOG_INFO, "Création de la configuration par défaut"); @@ -132,7 +152,7 @@ bool waitForWiFiConnection() { if (WiFi.status() == WL_CONNECTED) { logMessage(LOG_INFO, "WiFi connecté"); - logMessage(LOG_INFO, "Adresse IP: " + WiFi.localIP().toString()); + logNetworkInfo(); return true; } @@ -146,19 +166,11 @@ bool connectWPA2PEAP() { } logMessage(LOG_INFO, "Connexion WPA2-PEAP au WiFi: " + wifiSSID); + logMessage(LOG_INFO, "Profil PEAP: sans certificat, domaine vide, MSCHAPv2 interne"); -#if defined(WPA2_AUTH_PEAP) - WiFi.begin( - wifiSSID.c_str(), - WPA2_AUTH_PEAP, - wifiEapUsername.c_str(), - wifiEapUsername.c_str(), - wifiEapPassword.c_str(), - nullptr - ); - return waitForWiFiConnection(); -#elif HAS_LEGACY_WPA2_ENTERPRISE - WiFi.begin(wifiSSID.c_str()); +#if HAS_LEGACY_WPA2_ENTERPRISE + WiFi.disconnect(true, true); + delay(100); esp_wifi_sta_wpa2_ent_set_identity((uint8_t*)wifiEapUsername.c_str(), wifiEapUsername.length()); esp_wifi_sta_wpa2_ent_set_username((uint8_t*)wifiEapUsername.c_str(), wifiEapUsername.length()); @@ -169,6 +181,19 @@ bool connectWPA2PEAP() { #endif esp_wifi_sta_wpa2_ent_enable(); + WiFi.begin(wifiSSID.c_str()); + return waitForWiFiConnection(); +#elif defined(WPA2_AUTH_PEAP) + WiFi.disconnect(true, true); + delay(100); + WiFi.begin( + wifiSSID.c_str(), + WPA2_AUTH_PEAP, + wifiEapUsername.c_str(), + wifiEapUsername.c_str(), + wifiEapPassword.c_str(), + nullptr + ); return waitForWiFiConnection(); #else logMessage(LOG_ERROR, "WPA2-PEAP non supporté par cette version du framework"); @@ -185,6 +210,9 @@ bool connectWPA2PSK() { void setupWiFi() { if (wifiSSID.length() > 0) { WiFi.mode(WIFI_STA); + WiFi.setSleep(false); + WiFi.setAutoReconnect(true); + WiFi.persistent(false); WiFi.setHostname(hostname.c_str()); bool connected = false; @@ -195,6 +223,7 @@ void setupWiFi() { } if (connected) { + startFallbackAP(); return; } @@ -271,9 +300,34 @@ void setup() { } unsigned long lastCleanup = 0; +static unsigned long lastReconnectAttempt = 0; +static const unsigned long RECONNECT_INTERVAL = 30000; + +void checkWiFiConnection() { + if (wifiSSID.isEmpty()) return; + if (WiFi.status() == WL_CONNECTED) return; + + unsigned long now = millis(); + if (now - lastReconnectAttempt < RECONNECT_INTERVAL) return; + lastReconnectAttempt = now; + + logMessage(LOG_WARN, "WiFi déconnecté, tentative de reconnexion..."); + WiFi.mode(WIFI_STA); + + bool connected = (wifiSecurity == "wpa2_peap") ? connectWPA2PEAP() : connectWPA2PSK(); + + if (connected) { + logMessage(LOG_INFO, "WiFi reconnecté"); + startFallbackAP(); + } else { + logMessage(LOG_WARN, "Échec de reconnexion WiFi"); + } +} void loop() { ArduinoOTA.handle(); + + checkWiFiConnection(); // Nettoyer les sessions expirées toutes les minutes unsigned long now = millis(); diff --git a/src/server.cpp b/src/server.cpp index 1307b92..9917d18 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -8,6 +8,23 @@ WebServerManager::WebServerManager() { server = new AsyncWebServer(80); } +void WebServerManager::logRequest(AsyncWebServerRequest* request, const String& routeTag) { + String method = "OTHER"; + if (request->method() == HTTP_GET) method = "GET"; + else if (request->method() == HTTP_POST) method = "POST"; + else if (request->method() == HTTP_PUT) method = "PUT"; + else if (request->method() == HTTP_DELETE) method = "DELETE"; + else if (request->method() == HTTP_OPTIONS) method = "OPTIONS"; + else if (request->method() == HTTP_PATCH) method = "PATCH"; + + String remoteIp = "unknown"; + if (request->client()) { + remoteIp = request->client()->remoteIP().toString(); + } + + logMessage(LOG_INFO, "HTTP " + method + " " + request->url() + " depuis " + remoteIp + " (" + routeTag + ")"); +} + String WebServerManager::getSessionIdFromRequest(AsyncWebServerRequest* request) { if (request->hasHeader("Cookie")) { String cookie = request->header("Cookie"); @@ -54,16 +71,25 @@ void WebServerManager::sendJsonSuccess(AsyncWebServerRequest* request, const Str void WebServerManager::setupRoutes() { // Route de login server->on("/login", HTTP_POST, [this](AsyncWebServerRequest* request) { + logRequest(request, "login"); this->handleLogin(request); }); + + // Endpoint de diagnostic réseau (sans authentification) + server->on("/api/health", HTTP_GET, [this](AsyncWebServerRequest* request) { + logRequest(request, "health"); + this->handleHealth(request); + }); // Route de logout server->on("/logout", HTTP_GET, [this](AsyncWebServerRequest* request) { + logRequest(request, "logout"); this->handleLogout(request); }); // API - Configuration server->on("/api/config", HTTP_GET, [this](AsyncWebServerRequest* request) { + logRequest(request, "get_config"); if (!isAuthenticated(request)) { sendJsonError(request, 401, "Non authentifié"); return; @@ -72,6 +98,7 @@ void WebServerManager::setupRoutes() { }); server->on("/api/config", HTTP_POST, [this](AsyncWebServerRequest* request) { + logRequest(request, "set_config"); if (!isAuthenticated(request)) { sendJsonError(request, 401, "Non authentifié"); return; @@ -81,6 +108,7 @@ void WebServerManager::setupRoutes() { // API - Changement de mot de passe server->on("/api/password", HTTP_POST, [this](AsyncWebServerRequest* request) { + logRequest(request, "change_password"); if (!isAuthenticated(request)) { sendJsonError(request, 401, "Non authentifié"); return; @@ -90,6 +118,7 @@ void WebServerManager::setupRoutes() { // API - Statistiques server->on("/api/stats", HTTP_GET, [this](AsyncWebServerRequest* request) { + logRequest(request, "get_stats"); if (!isAuthenticated(request)) { sendJsonError(request, 401, "Non authentifié"); return; @@ -100,6 +129,7 @@ void WebServerManager::setupRoutes() { // OTA Update server->on("/api/update", HTTP_POST, [this](AsyncWebServerRequest* request) { + logRequest(request, "ota_update"); if (!isAuthenticated(request)) { sendJsonError(request, 401, "Non authentifié"); return; @@ -120,7 +150,8 @@ void WebServerManager::setupRoutes() { ); // 404 - server->onNotFound([](AsyncWebServerRequest* request) { + server->onNotFound([this](AsyncWebServerRequest* request) { + logRequest(request, "not_found"); request->send(404, "text/plain", "Page non trouvée"); }); @@ -128,6 +159,19 @@ void WebServerManager::setupRoutes() { server->serveStatic("/", LittleFS, "/").setDefaultFile("login.html"); } +void WebServerManager::handleHealth(AsyncWebServerRequest* request) { + JsonDocument doc; + doc["ok"] = true; + doc["ip_sta"] = WiFi.localIP().toString(); + doc["ip_ap"] = WiFi.softAPIP().toString(); + doc["wifi_status"] = WiFi.status(); + doc["ssid"] = WiFi.SSID(); + + String response; + serializeJson(doc, response); + request->send(200, "application/json", response); +} + void WebServerManager::handleLogin(AsyncWebServerRequest* request) { if (!request->hasParam("username", true) || !request->hasParam("password", true)) { sendJsonError(request, 400, "Paramètres manquants"); diff --git a/src/server.h b/src/server.h index f665a92..bb430c3 100644 --- a/src/server.h +++ b/src/server.h @@ -13,6 +13,7 @@ private: AsyncWebServer* server; String getSessionIdFromRequest(AsyncWebServerRequest* request); + void logRequest(AsyncWebServerRequest* request, const String& routeTag); bool isAuthenticated(AsyncWebServerRequest* request); void sendJsonError(AsyncWebServerRequest* request, int code, const String& message); void sendJsonSuccess(AsyncWebServerRequest* request, const String& message); @@ -28,6 +29,7 @@ public: void handleGetConfig(AsyncWebServerRequest* request); void handleSetConfig(AsyncWebServerRequest* request); void handleChangePassword(AsyncWebServerRequest* request); + void handleHealth(AsyncWebServerRequest* request); void handleGetStats(AsyncWebServerRequest* request); void handleOTAUpdate(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final); };