feat: add SD-backed stats archive and dedicated /stats web console

Tento commit je obsažen v:
Jared Dohrman
2026-04-15 17:12:32 +10:00
rodič a1aa318352
revize f67aaebe7e
27 změnil soubory, kde provedl 3862 přidání a 588 odebrání
Rozdílový obsah nebyl zobrazen, protože je příliš veliký Načíst rozdílové porovnání
+15
Zobrazit soubor
@@ -17,6 +17,20 @@ public:
virtual ~WebPanelCommandRunner() = default;
virtual void runWebCommand(const char* command, char* reply, size_t reply_size) = 0;
virtual const char* getWebAdminPassword() const = 0;
virtual bool isWebStatsEnabled() const { return false; }
virtual bool formatWebStatsSummaryJson(char* reply, size_t reply_size) {
if (reply != nullptr && reply_size > 0) {
reply[0] = 0;
}
return false;
}
virtual bool formatWebStatsSeriesJson(const char* series, char* reply, size_t reply_size) {
(void)series;
if (reply != nullptr && reply_size > 0) {
reply[0] = 0;
}
return false;
}
};
class WebPanelServer {
@@ -45,6 +59,7 @@ private:
static esp_err_t handleIndex(httpd_req_t* req);
static esp_err_t handleApp(httpd_req_t* req);
static esp_err_t handleStatsPage(httpd_req_t* req);
static esp_err_t handleLogin(httpd_req_t* req);
static esp_err_t handleCommand(httpd_req_t* req);
static esp_err_t handleStats(httpd_req_t* req);
+68
Zobrazit soubor
@@ -0,0 +1,68 @@
#include "WebPrefs.h"
#include <string.h>
void WebPrefsStore::setDefaults(WebPrefs& prefs) {
memset(&prefs, 0, sizeof(prefs));
prefs.magic = kMagic;
prefs.web_enabled = 1;
prefs.web_stats_enabled = 1;
}
bool WebPrefsStore::load(FILESYSTEM* fs, WebPrefs& prefs) {
setDefaults(prefs);
if (fs == nullptr) {
return false;
}
if (!fs->exists(kFilename)) {
save(fs, prefs);
return false;
}
#if defined(RP2040_PLATFORM)
File file = fs->open(kFilename, "r");
#else
File file = fs->open(kFilename);
#endif
if (!file) {
return false;
}
WebPrefs persisted{};
size_t bytes_to_read = min(static_cast<size_t>(file.size()), sizeof(persisted));
bool ok = bytes_to_read >= sizeof(persisted.magic) &&
file.read(reinterpret_cast<uint8_t*>(&persisted), bytes_to_read) == bytes_to_read;
file.close();
if (!ok || persisted.magic != kMagic) {
fs->remove(kFilename);
save(fs, prefs);
return false;
}
prefs = persisted;
prefs.web_enabled = prefs.web_enabled ? 1 : 0;
prefs.web_stats_enabled = prefs.web_stats_enabled ? 1 : 0;
return true;
}
bool WebPrefsStore::save(FILESYSTEM* fs, const WebPrefs& prefs) {
if (fs == nullptr) {
return false;
}
if (fs->exists(kFilename) && !fs->remove(kFilename)) {
return false;
}
#if defined(RP2040_PLATFORM)
File file = fs->open(kFilename, "w");
#else
File file = fs->open(kFilename, "w", true);
#endif
if (!file) {
return false;
}
bool ok = file.write(reinterpret_cast<const uint8_t*>(&prefs), sizeof(prefs)) == sizeof(prefs);
file.close();
return ok;
}
+22
Zobrazit soubor
@@ -0,0 +1,22 @@
#pragma once
#include <helpers/IdentityStore.h>
#include <stdint.h>
struct WebPrefs {
uint32_t magic;
uint8_t web_enabled;
uint8_t web_stats_enabled;
uint8_t reserved[2];
};
class WebPrefsStore {
public:
static void setDefaults(WebPrefs& prefs);
static bool load(FILESYSTEM* fs, WebPrefs& prefs);
static bool save(FILESYSTEM* fs, const WebPrefs& prefs);
private:
static constexpr uint32_t kMagic = 0x57454250;
static constexpr const char* kFilename = "/web_prefs";
};
+85
Zobrazit soubor
@@ -0,0 +1,85 @@
#include "WebService.h"
#if defined(ESP_PLATFORM) && WITH_WEB_PANEL
#include <WiFi.h>
#endif
WebService::WebService() : _fs(nullptr), _prefs{}, _runner(nullptr), _network(nullptr) {
WebPrefsStore::setDefaults(_prefs);
}
void WebService::begin(FILESYSTEM* fs) {
_fs = fs;
WebPrefsStore::load(_fs, _prefs);
}
void WebService::end() {
#if defined(ESP_PLATFORM) && WITH_WEB_PANEL
_panel.stop();
#endif
}
void WebService::loop() {
#if defined(ESP_PLATFORM) && WITH_WEB_PANEL
ensureWebServer();
if (_panel.isRunning() && _panel.shouldAutoLock(millis())) {
_panel.lockSession();
}
#endif
}
void WebService::setCommandRunner(WebPanelCommandRunner* runner) {
_runner = runner;
_panel.setCommandRunner(runner);
}
bool WebService::savePrefs() {
return WebPrefsStore::save(_fs, _prefs);
}
bool WebService::setWebEnabled(bool enabled) {
_prefs.web_enabled = enabled ? 1 : 0;
bool ok = savePrefs();
#if defined(ESP_PLATFORM) && WITH_WEB_PANEL
if (_prefs.web_enabled != 0) {
ensureWebServer();
} else {
_panel.stop();
}
#endif
return ok;
}
bool WebService::setWebStatsEnabled(bool enabled) {
_prefs.web_stats_enabled = enabled ? 1 : 0;
return savePrefs();
}
void WebService::formatWebStatusReply(char* reply, size_t reply_size) const {
#if defined(ESP_PLATFORM) && WITH_WEB_PANEL
if (_runner == nullptr || _prefs.web_enabled == 0) {
snprintf(reply, reply_size, "> web:off");
return;
}
if (!_panel.isRunning() || _network == nullptr || !_network->isWifiConnected()) {
snprintf(reply, reply_size, "> web:down");
return;
}
snprintf(reply, reply_size, "> web:up url:https://%s/ auth:%s", WiFi.localIP().toString().c_str(),
_panel.hasSessionToken() ? "unlocked" : "locked");
#else
snprintf(reply, reply_size, "> web:unsupported");
#endif
}
#if defined(ESP_PLATFORM) && WITH_WEB_PANEL
void WebService::ensureWebServer() {
if (_runner == nullptr || _prefs.web_enabled == 0 || _network == nullptr || !_network->isWifiConnected()) {
_panel.stop();
return;
}
_panel.start();
}
#endif
+41
Zobrazit soubor
@@ -0,0 +1,41 @@
#pragma once
#include <Arduino.h>
#include <helpers/IdentityStore.h>
#include <helpers/NetworkStateProvider.h>
#include "WebPanelServer.h"
#include "WebPrefs.h"
class WebService {
public:
WebService();
void begin(FILESYSTEM* fs);
void end();
void loop();
void setCommandRunner(WebPanelCommandRunner* runner);
void setNetworkStateProvider(NetworkStateProvider* network) { _network = network; }
bool setWebEnabled(bool enabled);
bool isWebEnabled() const { return _prefs.web_enabled != 0; }
bool setWebStatsEnabled(bool enabled);
bool isWebStatsEnabled() const { return _prefs.web_stats_enabled != 0; }
void formatWebStatusReply(char* reply, size_t reply_size) const;
bool isPanelRunning() const { return _panel.isRunning(); }
bool isPanelUnlocked() const { return _panel.hasSessionToken(); }
private:
#if defined(ESP_PLATFORM) && WITH_WEB_PANEL
void ensureWebServer();
#endif
bool savePrefs();
FILESYSTEM* _fs;
WebPrefs _prefs;
WebPanelCommandRunner* _runner;
NetworkStateProvider* _network;
WebPanelServer _panel;
};