From 959a15c11f9fdf938b2094683c359d89a686e26f Mon Sep 17 00:00:00 2001 From: Yurii Date: Tue, 20 Oct 2026 07:05:00 +0300 Subject: [PATCH] feat: add authenticated WebSerial log viewer --- platformio.ini | 1 + src/PortalTask.h | 25 +- src_data/locales/cn.json | 26 ++ src_data/locales/en.json | 26 ++ src_data/locales/it.json | 26 ++ src_data/locales/nl.json | 26 ++ src_data/locales/ru.json | 26 ++ src_data/pages/index.html | 3 +- src_data/pages/logs.html | 666 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 822 insertions(+), 3 deletions(-) create mode 100644 src_data/pages/logs.html diff --git a/platformio.ini b/platformio.ini index cc11b3e..91cb164 100644 --- a/platformio.ini +++ b/platformio.ini @@ -28,6 +28,7 @@ build_flags = ;-mtext-section-literals -O2 -Wno-deprecated-declarations -D MQTT_CLIENT_STD_FUNCTION_CALLBACK=1 + -D WSL_CUSTOM_PAGE ;-D DEBUG_ESP_CORE -D DEBUG_ESP_WIFI -D DEBUG_ESP_HTTP_SERVER -D DEBUG_ESP_PORT=Serial -D BUILD_VERSION='"${this.version}"' -D BUILD_ENV='"$PIOENV"' diff --git a/src/PortalTask.h b/src/PortalTask.h index f50ef23..b741e08 100644 --- a/src/PortalTask.h +++ b/src/PortalTask.h @@ -66,7 +66,7 @@ protected: // auth middleware static AsyncMiddlewareFunction authMiddleware([](AsyncWebServerRequest *request, ArMiddlewareNext next) { - if (network->isApEnabled() || !settings.portal.auth || !strlen(settings.portal.password)) { + if (!isAuthenticationRequired()) { return next(); } @@ -79,7 +79,20 @@ protected: // web serial if (webSerial != nullptr) { - webSerial->begin(this->webServer); + webSerial->begin(this->webServer, nullptr, "/webserial"); + + // Refresh credentials before each handshake to use the current portal settings. + this->webServer->addMiddleware([](AsyncWebServerRequest *request, ArMiddlewareNext next) { + if (request->url().equals("/webserial")) { + if (isAuthenticationRequired()) { + webSerial->setAuthentication(settings.portal.login, settings.portal.password); + } else { + webSerial->setAuthentication("", ""); + } + } + + return next(); + }); } // index page @@ -114,6 +127,10 @@ protected: this->webServer->serveStatic("/upgrade.html", LittleFS, "/pages/upgrade.html", PORTAL_CACHE) .addMiddleware(&authMiddleware); + // logs page + this->webServer->serveStatic("/logs.html", LittleFS, "/pages/logs.html", PORTAL_CACHE) + .addMiddleware(&authMiddleware); + // OTA auto upgradeHandler = (new UpgradeHandler("/api/upgrade")) ->setBeforeUpgradeCallback([](AsyncWebServerRequest *request, UpgradeHandler::UpgradeType type) -> bool { @@ -796,6 +813,10 @@ protected: this->dnsServerEnabled = false; } + static bool isAuthenticationRequired() { + return !network->isApEnabled() && settings.portal.auth && strlen(settings.portal.password); + } + static void getFilename(char* filename, size_t maxSizeFilename, const char* type) { const time_t now = time(nullptr); diff --git a/src_data/locales/cn.json b/src_data/locales/cn.json index 8879ce7..73cea76 100644 --- a/src_data/locales/cn.json +++ b/src_data/locales/cn.json @@ -505,6 +505,32 @@ } } }, + "logs": { + "title": "日志 - OpenTherm Gateway", + "name": "日志", + "desc": "实时设备日志。", + "status": { + "connecting": "正在连接...", + "connected": "已连接", + "disconnected": "已断开连接" + }, + "autoscroll": "自动滚动", + "limitHistory": "最多保留 1000 条日志", + "historyNote": "不限制历史记录会占用更多浏览器内存。下载包含所有保留的日志,包括被筛选器隐藏的日志。", + "filter": "筛选", + "search": { + "title": "搜索", + "previous": "上一个", + "next": "下一个" + }, + "level": { + "note": "此级别适用于设备日志(Serial 和 WebSerial)。下方的筛选器仅影响显示。", + "loadError": "无法加载日志级别,请重试。", + "saveError": "无法保存日志级别,请检查连接后重试。" + }, + "clear": "清空", + "download": "下载日志" + }, "upgrade": { "title": "固件升级 - OpenTherm Gateway", "name": "固件升级", diff --git a/src_data/locales/en.json b/src_data/locales/en.json index 38d85f0..9912938 100644 --- a/src_data/locales/en.json +++ b/src_data/locales/en.json @@ -505,6 +505,32 @@ } } }, + "logs": { + "title": "Logs - OpenTherm Gateway", + "name": "Logs", + "desc": "Device logs in real time.", + "status": { + "connecting": "Connecting...", + "connected": "Connected", + "disconnected": "Disconnected" + }, + "autoscroll": "Autoscroll", + "limitHistory": "Keep at most 1000 entries", + "historyNote": "Unlimited history uses more browser memory. Downloads include all retained entries, including those hidden by the filter.", + "filter": "Filter", + "search": { + "title": "Search", + "previous": "Previous", + "next": "Next" + }, + "level": { + "note": "The level applies to device logging (Serial and WebSerial). The filter below only changes the display.", + "loadError": "Could not load the log level. Retry the request.", + "saveError": "Could not save the log level. Check the connection and try again." + }, + "clear": "Clear", + "download": "Download logs" + }, "upgrade": { "title": "Upgrade - OpenTherm Gateway", "name": "Upgrade", diff --git a/src_data/locales/it.json b/src_data/locales/it.json index cd95f33..e503a5e 100644 --- a/src_data/locales/it.json +++ b/src_data/locales/it.json @@ -505,6 +505,32 @@ } } }, + "logs": { + "title": "Registro - OpenTherm Gateway", + "name": "Registro", + "desc": "Registro del dispositivo in tempo reale.", + "status": { + "connecting": "Connessione in corso...", + "connected": "Connesso", + "disconnected": "Disconnesso" + }, + "autoscroll": "Scorrimento automatico", + "limitHistory": "Conserva al massimo 1000 voci", + "historyNote": "La cronologia illimitata usa più memoria del browser. Il download include tutte le voci conservate, anche quelle nascoste dal filtro.", + "filter": "Filtro", + "search": { + "title": "Cerca", + "previous": "Precedente", + "next": "Successivo" + }, + "level": { + "note": "Il livello si applica al registro del dispositivo (Serial e WebSerial). Il filtro sottostante cambia solo la visualizzazione.", + "loadError": "Impossibile caricare il livello del registro. Riprovare la richiesta.", + "saveError": "Impossibile salvare il livello del registro. Controllare la connessione e riprovare." + }, + "clear": "Cancella", + "download": "Scarica il registro" + }, "upgrade": { "title": "Aggiornamenti - OpenTherm Gateway", "name": "Aggiornamenti", diff --git a/src_data/locales/nl.json b/src_data/locales/nl.json index a358439..aa368bc 100644 --- a/src_data/locales/nl.json +++ b/src_data/locales/nl.json @@ -505,6 +505,32 @@ } } }, + "logs": { + "title": "Logboek - OpenTherm Gateway", + "name": "Logboek", + "desc": "Apparaatlogboek in realtime.", + "status": { + "connecting": "Verbinden...", + "connected": "Verbonden", + "disconnected": "Verbinding verbroken" + }, + "autoscroll": "Automatisch scrollen", + "limitHistory": "Bewaar maximaal 1000 regels", + "historyNote": "Onbeperkte geschiedenis gebruikt meer browsergeheugen. Downloads bevatten alle bewaarde regels, ook de regels die door het filter zijn verborgen.", + "filter": "Filter", + "search": { + "title": "Zoeken", + "previous": "Vorige", + "next": "Volgende" + }, + "level": { + "note": "Het niveau geldt voor het apparaatlogboek (Serial en WebSerial). Het onderstaande filter wijzigt alleen de weergave.", + "loadError": "Het logniveau kon niet worden geladen. Probeer opnieuw.", + "saveError": "Het logniveau kon niet worden opgeslagen. Controleer de verbinding en probeer opnieuw." + }, + "clear": "Wissen", + "download": "Logboek downloaden" + }, "upgrade": { "title": "Upgrade - OpenTherm Gateway", "name": "Upgrade", diff --git a/src_data/locales/ru.json b/src_data/locales/ru.json index b52ed79..7eed3d8 100644 --- a/src_data/locales/ru.json +++ b/src_data/locales/ru.json @@ -505,6 +505,32 @@ } } }, + "logs": { + "title": "Журнал - OpenTherm Gateway", + "name": "Журнал", + "desc": "Журнал устройства в реальном времени.", + "status": { + "connecting": "Подключение...", + "connected": "Подключено", + "disconnected": "Нет подключения" + }, + "autoscroll": "Автопрокрутка", + "limitHistory": "Хранить не более 1000 записей", + "historyNote": "Без ограничения журнал использует больше памяти браузера. Скачиваются все сохранённые записи, включая скрытые фильтром.", + "filter": "Фильтр", + "search": { + "title": "Поиск", + "previous": "Назад", + "next": "Вперёд" + }, + "level": { + "note": "Уровень применяется к журналу устройства (Serial и WebSerial). Фильтр ниже меняет только отображение.", + "loadError": "Не удалось загрузить уровень журнала. Повторите запрос.", + "saveError": "Не удалось сохранить уровень журнала. Проверьте соединение и повторите попытку." + }, + "clear": "Очистить", + "download": "Скачать журнал" + }, "upgrade": { "title": "Обновление - OpenTherm Gateway", "name": "Обновление", diff --git a/src_data/pages/index.html b/src_data/pages/index.html index aa048c4..6cdde46 100644 --- a/src_data/pages/index.html +++ b/src_data/pages/index.html @@ -155,6 +155,7 @@ dashboard.name settings.name sensors.name + logs.name upgrade.name button.restart @@ -249,4 +250,4 @@ }); - \ No newline at end of file + diff --git a/src_data/pages/logs.html b/src_data/pages/logs.html new file mode 100644 index 0000000..ac31f77 --- /dev/null +++ b/src_data/pages/logs.html @@ -0,0 +1,666 @@ + + + + + + logs.title + + + + + +
+ +
+ +
+
+
+

logs.name

+

logs.desc

+
+ +
+ +
+ + +
+ logs.level.note + +
+ +
+ +
+ + +
+ logs.historyNote + +
+ +
+ +
+ + + +
+
+
+ +
+
+ 0 / 0 + + + +
+
+
+ +
+ + +
+
+
+ + + + + + +