Files
OTGateway/src/PortalTask.h
2025-11-02 11:28:46 +03:00

780 lines
26 KiB
C++

//#define PORTAL_CACHE "max-age=86400"
#define PORTAL_CACHE nullptr
#include <ESPmDNS.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h>
#include <Update.h>
#include <UpgradeHandler.h>
#include <DNSServer.h>
using namespace NetworkUtils;
extern NetworkMgr* network;
extern FileData fsNetworkSettings, fsSettings, fsSensorsSettings;
extern MqttTask* tMqtt;
class PortalTask : public LeanTask {
public:
PortalTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {
this->webServer = new AsyncWebServer(80);
this->dnsServer = new DNSServer();
}
~PortalTask() {
if (this->webServer != nullptr) {
this->stopWebServer();
delete this->webServer;
}
if (this->dnsServer != nullptr) {
this->stopDnsServer();
delete this->dnsServer;
}
}
protected:
const unsigned int changeStateInterval = 5000;
AsyncWebServer* webServer = nullptr;
DNSServer* dnsServer = nullptr;
bool webServerEnabled = false;
bool dnsServerEnabled = false;
unsigned long webServerChangeState = 0;
bool mDnsState = false;
#if defined(ARDUINO_ARCH_ESP32)
const char* getTaskName() override {
return "Portal";
}
/*BaseType_t getTaskCore() override {
return 1;
}*/
int getTaskPriority() override {
return 1;
}
#endif
void setup() {
this->dnsServer->setTTL(0);
this->dnsServer->setErrorReplyCode(DNSReplyCode::NoError);
// auth middleware
static AsyncMiddlewareFunction authMiddleware([](AsyncWebServerRequest *request, ArMiddlewareNext next) {
if (network->isApEnabled() || !settings.portal.auth || !strlen(settings.portal.password)) {
return next();
}
if (request->authenticate(settings.portal.login, settings.portal.password, PROJECT_NAME)) {
return next();
}
return request->requestAuthentication(AsyncAuthType::AUTH_BASIC, PROJECT_NAME, "Authentication failed");
});
// index page
this->webServer->on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
request->redirect("/index.html");
});
this->webServer->serveStatic("/index.html", LittleFS, "/pages/index.html", PORTAL_CACHE);
// dashboard page
this->webServer->serveStatic("/dashboard.html", LittleFS, "/pages/dashboard.html", PORTAL_CACHE)
.addMiddleware(&authMiddleware);
// restart
this->webServer->on("/restart.html", HTTP_GET, [](AsyncWebServerRequest *request) {
vars.actions.restart = true;
request->redirect("/");
}).addMiddleware(&authMiddleware);
// network settings page
this->webServer->serveStatic("/network.html", LittleFS, "/pages/network.html", PORTAL_CACHE)
.addMiddleware(&authMiddleware);
// settings page
this->webServer->serveStatic("/settings.html", LittleFS, "/pages/settings.html", PORTAL_CACHE)
.addMiddleware(&authMiddleware);
// sensors page
this->webServer->serveStatic("/sensors.html", LittleFS, "/pages/sensors.html", PORTAL_CACHE)
.addMiddleware(&authMiddleware);
// upgrade page
this->webServer->serveStatic("/upgrade.html", LittleFS, "/pages/upgrade.html", PORTAL_CACHE)
.addMiddleware(&authMiddleware);
// OTA
auto upgradeHandler = (new UpgradeHandler("/api/upgrade"))
->setBeforeUpgradeCallback([](AsyncWebServerRequest *request, UpgradeHandler::UpgradeType type) -> bool {
if (vars.states.restarting) {
return false;
}
vars.states.upgrading = true;
return true;
})->setAfterUpgradeCallback([](AsyncWebServerRequest *request, const UpgradeHandler::UpgradeResult& fwResult, const UpgradeHandler::UpgradeResult& fsResult) {
unsigned short status = 406;
if (fwResult.status == UpgradeHandler::UpgradeStatus::SUCCESS || fsResult.status == UpgradeHandler::UpgradeStatus::SUCCESS) {
status = 202;
vars.actions.restart = true;
}
vars.states.upgrading = false;
// make response
auto *response = new AsyncJsonResponse();
auto& doc = response->getRoot();
auto fwDoc = doc["firmware"].to<JsonObject>();
fwDoc["status"] = static_cast<uint8_t>(fwResult.status);
fwDoc["error"] = fwResult.error;
auto fsDoc = doc["filesystem"].to<JsonObject>();
fsDoc["status"] = static_cast<uint8_t>(fsResult.status);
fsDoc["error"] = fsResult.error;
// send response
response->setLength();
response->setCode(status);
request->send(response);
});
this->webServer->addHandler(upgradeHandler);
// backup
this->webServer->on("/api/backup/save", HTTP_GET, [](AsyncWebServerRequest *request) {
// make response
auto *response = new AsyncJsonResponse();
auto& doc = response->getRoot();
// add network settings
auto networkDoc = doc[FPSTR(S_NETWORK)].to<JsonObject>();
networkSettingsToJson(networkSettings, networkDoc);
// add main settings
auto settingsDoc = doc[FPSTR(S_SETTINGS)].to<JsonObject>();
settingsToJson(settings, settingsDoc);
// add sensors settings
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
auto sensorsettingsDoc = doc[FPSTR(S_SENSORS)][sensorId].to<JsonObject>();
sensorSettingsToJson(sensorId, Sensors::settings[sensorId], sensorsettingsDoc);
}
// send response
response->addHeader("Content-Disposition", "attachment; filename=\"backup.json\"");
response->setLength();
request->send(response);
}).addMiddleware(&authMiddleware);
// backup restore
auto& brHandler = this->webServer->on("/api/backup/restore", HTTP_POST, [](AsyncWebServerRequest *request, JsonVariant &requestDoc) {
if (vars.states.restarting) {
return request->send(503);
}
if (requestDoc.isNull() || !requestDoc.size()) {
return request->send(400);
}
// prepare request
bool changed = false;
// restore network settings
if (!requestDoc[FPSTR(S_NETWORK)].isNull() && jsonToNetworkSettings(requestDoc[FPSTR(S_NETWORK)], networkSettings)) {
fsNetworkSettings.update();
network->setHostname(networkSettings.hostname)
->setStaCredentials(networkSettings.sta.ssid, networkSettings.sta.password, networkSettings.sta.channel)
->setApCredentials(networkSettings.ap.ssid, networkSettings.ap.password, networkSettings.ap.channel)
->setUseDhcp(networkSettings.useDhcp)
->setStaticConfig(
networkSettings.staticConfig.ip,
networkSettings.staticConfig.gateway,
networkSettings.staticConfig.subnet,
networkSettings.staticConfig.dns
);
changed = true;
}
// restore main settings
if (!requestDoc[FPSTR(S_SETTINGS)].isNull() && jsonToSettings(requestDoc[FPSTR(S_SETTINGS)], settings)) {
fsSettings.update();
changed = true;
}
// restore sensors settings
if (!requestDoc[FPSTR(S_SENSORS)].isNull()) {
for (auto sensor : requestDoc[FPSTR(S_SENSORS)].as<JsonObject>()) {
if (!isDigit(sensor.key().c_str())) {
continue;
}
int sensorId = atoi(sensor.key().c_str());
if (sensorId < 0 || sensorId > 255 || !Sensors::isValidSensorId(sensorId)) {
continue;
}
if (jsonToSensorSettings(sensorId, sensor.value(), Sensors::settings[sensorId])) {
fsSensorsSettings.update();
changed = true;
}
}
}
requestDoc.clear();
// send response
request->send(changed ? 201 : 200);
if (changed) {
vars.actions.restart = true;
}
});
brHandler.setMaxContentLength(3072);
brHandler.addMiddleware(&authMiddleware);
// network
this->webServer->on("/api/network/settings", HTTP_GET, [](AsyncWebServerRequest *request) {
// make response
auto *response = new AsyncJsonResponse();
auto& doc = response->getRoot();
networkSettingsToJson(networkSettings, doc);
// send response
response->setLength();
request->send(response);
}).addMiddleware(&authMiddleware);
auto& nsHandler = this->webServer->on("/api/network/settings", HTTP_POST, [](AsyncWebServerRequest *request, JsonVariant &requestDoc) {
if (vars.states.restarting) {
return request->send(503);
}
if (requestDoc.isNull() || !requestDoc.size()) {
return request->send(400);
}
// prepare request
bool changed = jsonToNetworkSettings(requestDoc, networkSettings);
requestDoc.clear();
// make response
auto *response = new AsyncJsonResponse();
auto& responseDoc = response->getRoot();
networkSettingsToJson(networkSettings, responseDoc);
// send response
response->setLength();
response->setCode(changed ? 201 : 200);
request->send(response);
if (changed) {
fsNetworkSettings.update();
network->setHostname(networkSettings.hostname)
->setStaCredentials(networkSettings.sta.ssid, networkSettings.sta.password, networkSettings.sta.channel)
->setApCredentials(networkSettings.ap.ssid, networkSettings.ap.password, networkSettings.ap.channel)
->setUseDhcp(networkSettings.useDhcp)
->setStaticConfig(
networkSettings.staticConfig.ip,
networkSettings.staticConfig.gateway,
networkSettings.staticConfig.subnet,
networkSettings.staticConfig.dns
)
->reconnect();
}
});
nsHandler.setMaxContentLength(512);
nsHandler.addMiddleware(&authMiddleware);
this->webServer->on("/api/network/scan", HTTP_GET, [](AsyncWebServerRequest *request) {
auto apCount = WiFi.scanComplete();
switch (apCount) {
case WIFI_SCAN_FAILED:
WiFi.scanNetworks(true, true, false);
request->send(404);
break;
case WIFI_SCAN_RUNNING:
request->send(202);
break;
default:
// make respponse
auto *response = new AsyncJsonResponse(true);
auto& doc = response->getRoot();
for (short int i = 0; i < apCount; i++) {
doc[i][FPSTR(S_SSID)] = WiFi.SSID(i);
doc[i][FPSTR(S_BSSID)] = WiFi.BSSIDstr(i);
doc[i][FPSTR(S_SIGNAL_QUALITY)] = NetworkMgr::rssiToSignalQuality(WiFi.RSSI(i));
doc[i][FPSTR(S_CHANNEL)] = WiFi.channel(i);
doc[i][FPSTR(S_HIDDEN)] = !WiFi.SSID(i).length();
doc[i][FPSTR(S_AUTH)] = WiFi.encryptionType(i);
}
// send response
response->setLength();
request->send(response);
// clear
WiFi.scanDelete();
}
}).addMiddleware(&authMiddleware);
// settings
this->webServer->on("/api/settings", HTTP_GET, [](AsyncWebServerRequest *request) {
// make response
auto *response = new AsyncJsonResponse();
auto& doc = response->getRoot();
settingsToJson(settings, doc);
// send response
response->setLength();
request->send(response);
}).addMiddleware(&authMiddleware);
auto& sHandler = this->webServer->on("/api/settings", HTTP_POST, [](AsyncWebServerRequest *request, JsonVariant &requestDoc) {
if (vars.states.restarting) {
return request->send(503);
}
if (requestDoc.isNull() || !requestDoc.size()) {
return request->send(400);
}
// prepare request
bool changed = jsonToSettings(requestDoc, settings);
requestDoc.clear();
// make response
auto *response = new AsyncJsonResponse();
auto& responseDoc = response->getRoot();
settingsToJson(settings, responseDoc);
// send response
response->setLength();
response->setCode(changed ? 201 : 200);
request->send(response);
if (changed) {
fsSettings.update();
tMqtt->resetPublishedSettingsTime();
}
});
sHandler.setMaxContentLength(3072);
sHandler.addMiddleware(&authMiddleware);
// sensors list
this->webServer->on("/api/sensors", HTTP_GET, [](AsyncWebServerRequest *request) {
// make response
bool detailed = request->hasParam("detailed");
auto *response = new AsyncJsonResponse(true);
auto& responseDoc = response->getRoot();
// add sensors
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
if (detailed) {
auto& sSensor = Sensors::settings[sensorId];
responseDoc[sensorId][FPSTR(S_ENABLED)] = sSensor.enabled;
responseDoc[sensorId][FPSTR(S_NAME)] = sSensor.name;
responseDoc[sensorId][FPSTR(S_PURPOSE)] = static_cast<uint8_t>(sSensor.purpose);
sensorResultToJson(sensorId, responseDoc[sensorId]);
} else {
responseDoc[sensorId] = Sensors::settings[sensorId].name;
}
}
// send response
response->setLength();
request->send(response);
}).addMiddleware(&authMiddleware);
// sensor settings
this->webServer->on("/api/sensor", HTTP_GET, [](AsyncWebServerRequest *request) {
if (!request->hasParam("id")) {
return request->send(400);
}
const auto& id = request->getParam("id")->value();
if (!isDigit(id.c_str())) {
return request->send(400);
}
uint8_t sensorId = id.toInt();
if (!Sensors::isValidSensorId(sensorId)) {
return request->send(404);
}
auto *response = new AsyncJsonResponse();
auto& doc = response->getRoot();
sensorSettingsToJson(sensorId, Sensors::settings[sensorId], doc);
response->setLength();
request->send(response);
}).addMiddleware(&authMiddleware);
auto& ssHandler = this->webServer->on("/api/sensor", HTTP_POST, [](AsyncWebServerRequest *request, JsonVariant &requestDoc) {
if (vars.states.restarting) {
return request->send(503);
}
if (requestDoc.isNull() || !requestDoc.size()) {
return request->send(400);
}
if (!request->hasParam("id")) {
return request->send(400);
}
auto& id = request->getParam("id")->value();
if (!isDigit(id.c_str())) {
return request->send(400);
}
uint8_t sensorId = id.toInt();
if (!Sensors::isValidSensorId(sensorId)) {
return request->send(404);
}
// prepare request
auto prevSettings = Sensors::settings[sensorId];
bool changed = jsonToSensorSettings(sensorId, requestDoc, Sensors::settings[sensorId]);
requestDoc.clear();
// make response
auto *response = new AsyncJsonResponse();
auto& responseDoc = response->getRoot();
auto& sSettings = Sensors::settings[sensorId];
sensorSettingsToJson(sensorId, sSettings, responseDoc);
// send response
response->setLength();
response->setCode(changed ? 201 : 200);
request->send(response);
if (changed) {
tMqtt->reconfigureSensor(sensorId, prevSettings);
fsSensorsSettings.update();
}
});
ssHandler.setMaxContentLength(1024);
ssHandler.addMiddleware(&authMiddleware);
// vars
this->webServer->on("/api/vars", HTTP_GET, [](AsyncWebServerRequest *request) {
// make response
auto *response = new AsyncJsonResponse();
auto& doc = response->getRoot();
varsToJson(vars, doc);
// send response
response->setLength();
request->send(response);
});
auto& vHandler = this->webServer->on("/api/vars", HTTP_POST, [](AsyncWebServerRequest *request, JsonVariant &requestDoc) {
if (vars.states.restarting) {
return request->send(503);
}
if (requestDoc.isNull() || !requestDoc.size()) {
return request->send(400);
}
// prepare request
bool changed = jsonToVars(requestDoc, vars);
requestDoc.clear();
// make response
auto *response = new AsyncJsonResponse();
auto& responseDoc = response->getRoot();
varsToJson(vars, responseDoc);
// send response
response->setLength();
response->setCode(changed ? 201 : 200);
request->send(response);
if (changed) {
tMqtt->resetPublishedVarsTime();
}
});
vHandler.setMaxContentLength(1536);
vHandler.addMiddleware(&authMiddleware);
this->webServer->on("/api/info", HTTP_GET, [](AsyncWebServerRequest *request) {
// make response
auto *response = new AsyncJsonResponse();
auto& doc = response->getRoot();
auto docSystem = doc[FPSTR(S_SYSTEM)].to<JsonObject>();
docSystem[FPSTR(S_RESET_REASON)] = getResetReason();
docSystem[FPSTR(S_UPTIME)] = millis() / 1000;
bool isConnected = network->isConnected();
auto docNetwork = doc[FPSTR(S_NETWORK)].to<JsonObject>();
docNetwork[FPSTR(S_HOSTNAME)] = networkSettings.hostname;
docNetwork[FPSTR(S_MAC)] = network->getStaMac();
docNetwork[FPSTR(S_CONNECTED)] = isConnected;
docNetwork[FPSTR(S_SSID)] = network->getStaSsid();
docNetwork[FPSTR(S_SIGNAL_QUALITY)] = isConnected ? NetworkMgr::rssiToSignalQuality(network->getRssi()) : 0;
docNetwork[FPSTR(S_CHANNEL)] = isConnected ? network->getStaChannel() : 0;
docNetwork[FPSTR(S_IP)] = isConnected ? network->getStaIp().toString() : "";
docNetwork[FPSTR(S_SUBNET)] = isConnected ? network->getStaSubnet().toString() : "";
docNetwork[FPSTR(S_GATEWAY)] = isConnected ? network->getStaGateway().toString() : "";
docNetwork[FPSTR(S_DNS)] = isConnected ? network->getStaDns().toString() : "";
auto docBuild = doc[FPSTR(S_BUILD)].to<JsonObject>();
docBuild[FPSTR(S_VERSION)] = BUILD_VERSION;
docBuild[FPSTR(S_DATE)] = __DATE__ " " __TIME__;
docBuild[FPSTR(S_ENV)] = BUILD_ENV;
docBuild[FPSTR(S_CORE)] = ESP.getCoreVersion();
docBuild[FPSTR(S_SDK)] = ESP.getSdkVersion();
auto docHeap = doc[FPSTR(S_HEAP)].to<JsonObject>();
docHeap[FPSTR(S_TOTAL)] = getTotalHeap();
docHeap[FPSTR(S_FREE)] = getFreeHeap();
docHeap[FPSTR(S_MIN_FREE)] = getFreeHeap(true);
docHeap[FPSTR(S_MAX_FREE_BLOCK)] = getMaxFreeBlockHeap();
docHeap[FPSTR(S_MIN_MAX_FREE_BLOCK)] = getMaxFreeBlockHeap(true);
auto docPsram = doc[FPSTR(S_PSRAM)].to<JsonObject>();
docPsram[FPSTR(S_TOTAL)] = ESP.getPsramSize();
docPsram[FPSTR(S_FREE)] = ESP.getFreePsram();
docPsram[FPSTR(S_MIN_FREE)] = ESP.getMinFreePsram();
docPsram[FPSTR(S_MAX_FREE_BLOCK)] = ESP.getMaxAllocPsram();
auto docChip = doc[FPSTR(S_CHIP)].to<JsonObject>();
docChip[FPSTR(S_MODEL)] = ESP.getChipModel();
docChip[FPSTR(S_REV)] = ESP.getChipRevision();
docChip[FPSTR(S_CORES)] = ESP.getChipCores();
docChip[FPSTR(S_FREQ)] = ESP.getCpuFreqMHz();
auto docFlash = doc[FPSTR(S_FLASH)].to<JsonObject>();
docFlash[FPSTR(S_SIZE)] = ESP.getFlashChipSize();
docFlash[FPSTR(S_REAL_SIZE)] = docFlash[FPSTR(S_SIZE)];
// send response
response->setLength();
request->send(response);
});
this->webServer->on("/api/debug", HTTP_GET, [](AsyncWebServerRequest *request) {
// make response
auto *response = new AsyncJsonResponse();
auto& doc = response->getRoot();
auto docBuild = doc[FPSTR(S_BUILD)].to<JsonObject>();
docBuild[FPSTR(S_VERSION)] = BUILD_VERSION;
docBuild[FPSTR(S_DATE)] = __DATE__ " " __TIME__;
docBuild[FPSTR(S_ENV)] = BUILD_ENV;
docBuild[FPSTR(S_CORE)] = ESP.getCoreVersion();
docBuild[FPSTR(S_SDK)] = ESP.getSdkVersion();
auto docHeap = doc[FPSTR(S_HEAP)].to<JsonObject>();
docHeap[FPSTR(S_TOTAL)] = getTotalHeap();
docHeap[FPSTR(S_FREE)] = getFreeHeap();
docHeap[FPSTR(S_MIN_FREE)] = getFreeHeap(true);
docHeap[FPSTR(S_MAX_FREE_BLOCK)] = getMaxFreeBlockHeap();
docHeap[FPSTR(S_MIN_MAX_FREE_BLOCK)] = getMaxFreeBlockHeap(true);
auto docChip = doc[FPSTR(S_CHIP)].to<JsonObject>();
docChip[FPSTR(S_MODEL)] = ESP.getChipModel();
docChip[FPSTR(S_REV)] = ESP.getChipRevision();
docChip[FPSTR(S_CORES)] = ESP.getChipCores();
docChip[FPSTR(S_FREQ)] = ESP.getCpuFreqMHz();
auto docFlash = doc[FPSTR(S_FLASH)].to<JsonObject>();
docFlash[FPSTR(S_SIZE)] = ESP.getFlashChipSize();
docFlash[FPSTR(S_REAL_SIZE)] = docFlash[FPSTR(S_SIZE)];
auto reason = esp_reset_reason();
if (reason != ESP_RST_UNKNOWN && reason != ESP_RST_POWERON && reason != ESP_RST_SW) {
auto docCrash = doc[FPSTR(S_CRASH)].to<JsonObject>();
docCrash[FPSTR(S_REASON)] = getResetReason();
docCrash[FPSTR(S_CORE)] = CrashRecorder::ext.core;
docCrash[FPSTR(S_HEAP)] = CrashRecorder::ext.heap;
docCrash[FPSTR(S_UPTIME)] = CrashRecorder::ext.uptime;
if (CrashRecorder::backtrace.length > 0 && CrashRecorder::backtrace.length <= CrashRecorder::backtraceMaxLength) {
String backtraceStr;
arr2str(backtraceStr, CrashRecorder::backtrace.data, CrashRecorder::backtrace.length);
docCrash[FPSTR(S_BACKTRACE)][FPSTR(S_DATA)] = backtraceStr;
docCrash[FPSTR(S_BACKTRACE)][FPSTR(S_CONTINUES)] = CrashRecorder::backtrace.continues;
}
if (CrashRecorder::epc.length > 0 && CrashRecorder::epc.length <= CrashRecorder::epcMaxLength) {
String epcStr;
arr2str(epcStr, CrashRecorder::epc.data, CrashRecorder::epc.length);
docCrash[FPSTR(S_EPC)] = epcStr;
}
}
// send response
response->addHeader("Content-Disposition", "attachment; filename=\"debug.json\"");
response->setLength();
request->send(response);
}).addMiddleware(&authMiddleware);
// not found
this->webServer->onNotFound([this](AsyncWebServerRequest *request) {
const auto& url = request->url();
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Page not found, uri: %s"), url.c_str());
if (url.equals("/")) {
request->send(200, "text/plain", "The file system is not flashed!");
} else if (network->isApEnabled()) {
this->onCaptivePortal(request);
} else {
request->send(404, "text/plain", "Page not found");
}
});
this->webServer->serveStatic("/robots.txt", LittleFS, "/static/robots.txt", PORTAL_CACHE);
this->webServer->serveStatic("/favicon.ico", LittleFS, "/static/images/favicon.ico", PORTAL_CACHE);
this->webServer->serveStatic("/static", LittleFS, "/static", PORTAL_CACHE);
}
void loop() {
// web server
if (!this->stateWebServer() && (network->isApEnabled() || network->isConnected()) && millis() - this->webServerChangeState >= this->changeStateInterval) {
this->startWebServer();
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Started: AP up or STA connected"));
// Enabling mDNS
if (!this->mDnsState && settings.portal.mdns) {
if (MDNS.begin(networkSettings.hostname)) {
MDNS.addService("http", "tcp", 80);
this->mDnsState = true;
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("mDNS enabled and service added"));
}
}
} else if (this->stateWebServer() && !network->isApEnabled() && !network->isStaEnabled()) {
this->stopWebServer();
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Stopped: AP and STA down"));
// Disabling mDNS
if (this->mDnsState) {
MDNS.end();
this->mDnsState = false;
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("mDNS disabled"));
}
}
// Disabling mDNS if disabled in settings
if (this->mDnsState && !settings.portal.mdns) {
MDNS.end();
this->mDnsState = false;
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("mDNS disabled"));
}
// dns server
if (!this->stateDnsServer() && !network->isConnected() && network->isApEnabled() && this->stateWebServer()) {
this->startDnsServer();
Log.straceln(FPSTR(L_PORTAL_DNSSERVER), F("Started: AP up"));
} else if (this->stateDnsServer() && (network->isConnected() || !network->isApEnabled() || !this->stateWebServer())) {
this->stopDnsServer();
Log.straceln(FPSTR(L_PORTAL_DNSSERVER), F("Stopped: AP down/STA connected"));
}
if (this->stateDnsServer()) {
this->dnsServer->processNextRequest();
}
if (!this->stateDnsServer()) {
this->delay(250);
}
}
void onCaptivePortal(AsyncWebServerRequest *request) {
const auto& url = request->url();
if (url.equals("/connecttest.txt")) {
request->redirect("http://logout.net", 302);
Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Redirect to http://logout.net with 302 code"));
} else if (url.equals(F("/wpad.dat"))) {
request->send(404);
Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Send empty page with 404 code"));
} else if (url.equals(F("/success.txt"))) {
request->send(200);
Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Send empty page with 200 code"));
} else {
String portalUrl = "http://";
portalUrl += network->getApIp().toString().c_str();
portalUrl += '/';
request->redirect(portalUrl, 302);
Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Redirect to portal page with 302 code"));
}
}
bool stateWebServer() {
return this->webServerEnabled;
}
void startWebServer() {
if (this->stateWebServer()) {
return;
}
this->webServer->begin();
this->webServerEnabled = true;
this->webServerChangeState = millis();
}
void stopWebServer() {
if (!this->stateWebServer()) {
return;
}
this->webServer->end();
this->webServerEnabled = false;
this->webServerChangeState = millis();
}
bool stateDnsServer() {
return this->dnsServerEnabled;
}
void startDnsServer() {
if (this->stateDnsServer()) {
return;
}
this->dnsServer->start(53, "*", network->getApIp());
this->dnsServerEnabled = true;
}
void stopDnsServer() {
if (!this->stateDnsServer()) {
return;
}
//this->dnsServer->processNextRequest();
this->dnsServer->stop();
this->dnsServerEnabled = false;
}
};