Feature: Check for out of memory situations when sending json responses

Also shows a nice message in the frontend if an internal error occours
This commit is contained in:
Thomas Basler 2024-04-04 20:43:07 +02:00
parent 2e3125fe8d
commit 980e847ccb
26 changed files with 171 additions and 229 deletions

View File

@ -39,6 +39,7 @@ public:
static void writeConfig(JsonVariant& retMsg, const WebApiError code = WebApiError::GenericSuccess, const String& message = "Settings saved!");
static bool parseRequestData(AsyncWebServerRequest* request, AsyncJsonResponse* response, JsonDocument& json_document);
static bool sendJsonResponse(AsyncWebServerRequest* request, AsyncJsonResponse* response, const char* function, const uint16_t line);
private:
AsyncWebServer _server;

View File

@ -9,6 +9,7 @@ enum WebApiError {
GenericParseError,
GenericValueMissing,
GenericWriteFailed,
GenericInternalServerError,
DtuBase = 2000,
DtuSerialZero,

View File

@ -4,6 +4,7 @@
*/
#include "WebApi.h"
#include "Configuration.h"
#include "MessageOutput.h"
#include "defaults.h"
#include <AsyncJson.h>
@ -93,8 +94,7 @@ bool WebApiClass::parseRequestData(AsyncWebServerRequest* request, AsyncJsonResp
if (!request->hasParam("data", true)) {
retMsg["message"] = "No values found!";
retMsg["code"] = WebApiError::GenericNoValueFound;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return false;
}
@ -103,12 +103,31 @@ bool WebApiClass::parseRequestData(AsyncWebServerRequest* request, AsyncJsonResp
if (error) {
retMsg["message"] = "Failed to parse data!";
retMsg["code"] = WebApiError::GenericParseError;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return false;
}
return true;
}
bool WebApiClass::sendJsonResponse(AsyncWebServerRequest* request, AsyncJsonResponse* response, const char* function, const uint16_t line)
{
bool ret_val = true;
if (response->overflowed()) {
auto& root = response->getRoot();
root.clear();
root["message"] = String("500 Internal Server Error: ") + function + ", " + line;
root["code"] = WebApiError::GenericInternalServerError;
root["type"] = "danger";
response->setCode(500);
MessageOutput.printf("WebResponse failed: %s, %d\r\n", function, line);
ret_val = false;
}
response->setLength();
request->send(response);
return ret_val;
}
WebApiClass WebApi;

View File

@ -63,16 +63,14 @@ void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request)
if (!(root.containsKey("delete"))) {
retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (root["delete"].as<bool>() == false) {
retMsg["message"] = "Not deleted anything!";
retMsg["code"] = WebApiError::ConfigNotDeleted;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -80,8 +78,7 @@ void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request)
retMsg["message"] = "Configuration resettet. Rebooting now...";
retMsg["code"] = WebApiError::ConfigSuccess;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
Utils::removeAllFiles();
Utils::restartDtu();
@ -110,8 +107,7 @@ void WebApiConfigClass::onConfigListGet(AsyncWebServerRequest* request)
}
file.close();
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}
void WebApiConfigClass::onConfigUploadFinish(AsyncWebServerRequest* request)

View File

@ -86,8 +86,7 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
led["brightness"] = config.Led_Single[i].Brightness;
}
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}
void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
@ -108,8 +107,7 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
|| root.containsKey("display"))) {
retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -117,8 +115,7 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
retMsg["message"] = "Pin mapping must between 1 and " STR(DEV_MAX_MAPPING_NAME_STRLEN) " characters long!";
retMsg["code"] = WebApiError::HardwarePinMappingLength;
retMsg["param"]["max"] = DEV_MAX_MAPPING_NAME_STRLEN;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -149,8 +146,7 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
WebApi.writeConfig(retMsg);
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
if (performRestart) {
Utils::restartDtu();

View File

@ -43,6 +43,5 @@ void WebApiDevInfoClass::onDevInfoStatus(AsyncWebServerRequest* request)
root["fw_build_datetime"] = inv->DevInfo()->getFwBuildDateTimeStr();
}
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}

View File

@ -73,8 +73,7 @@ void WebApiDtuClass::onDtuAdminGet(AsyncWebServerRequest* request)
obj["freq_legal_max"] = definition.definition.Freq_Legal_Max;
}
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}
void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
@ -99,8 +98,7 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
&& root.containsKey("cmt_country"))) {
retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -110,40 +108,35 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
if (serial == 0) {
retMsg["message"] = "Serial cannot be zero!";
retMsg["code"] = WebApiError::DtuSerialZero;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (root["pollinterval"].as<uint32_t>() == 0) {
retMsg["message"] = "Poll interval must be greater zero!";
retMsg["code"] = WebApiError::DtuPollZero;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (root["nrf_palevel"].as<uint8_t>() > 3) {
retMsg["message"] = "Invalid power level setting!";
retMsg["code"] = WebApiError::DtuInvalidPowerLevel;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (root["cmt_palevel"].as<int8_t>() < -10 || root["cmt_palevel"].as<int8_t>() > 20) {
retMsg["message"] = "Invalid power level setting!";
retMsg["code"] = WebApiError::DtuInvalidPowerLevel;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (root["cmt_country"].as<uint8_t>() >= CountryModeId_t::CountryModeId_Max) {
retMsg["message"] = "Invalid country setting!";
retMsg["code"] = WebApiError::DtuInvalidCmtCountry;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -156,8 +149,7 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
retMsg["code"] = WebApiError::DtuInvalidCmtFrequency;
retMsg["param"]["min"] = FrequencyDefinition.Freq_Min;
retMsg["param"]["max"] = FrequencyDefinition.Freq_Max;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -172,8 +164,7 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
WebApi.writeConfig(retMsg);
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
_applyDataTask.enable();
_applyDataTask.restart();

View File

@ -62,6 +62,5 @@ void WebApiEventlogClass::onEventlogStatus(AsyncWebServerRequest* request)
}
}
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}

View File

@ -55,8 +55,7 @@ void WebApiGridProfileClass::onGridProfileStatus(AsyncWebServerRequest* request)
}
}
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}
void WebApiGridProfileClass::onGridProfileRawdata(AsyncWebServerRequest* request)
@ -83,6 +82,5 @@ void WebApiGridProfileClass::onGridProfileRawdata(AsyncWebServerRequest* request
copyArray(&data[0], data.size(), raw);
}
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}

View File

@ -77,8 +77,7 @@ void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
}
}
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}
void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request)
@ -99,8 +98,7 @@ void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request)
&& root.containsKey("name"))) {
retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -110,8 +108,7 @@ void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request)
if (serial == 0) {
retMsg["message"] = "Serial must be a number > 0!";
retMsg["code"] = WebApiError::InverterSerialZero;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -119,8 +116,7 @@ void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request)
retMsg["message"] = "Name must between 1 and " STR(INV_MAX_NAME_STRLEN) " characters long!";
retMsg["code"] = WebApiError::InverterNameLength;
retMsg["param"]["max"] = INV_MAX_NAME_STRLEN;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -130,8 +126,7 @@ void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request)
retMsg["message"] = "Only " STR(INV_MAX_COUNT) " inverters are supported!";
retMsg["code"] = WebApiError::InverterCount;
retMsg["param"]["max"] = INV_MAX_COUNT;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -142,8 +137,7 @@ void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request)
WebApi.writeConfig(retMsg, WebApiError::InverterAdded, "Inverter created!");
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
auto inv = Hoymiles.addInverter(inverter->Name, inverter->Serial);
@ -173,16 +167,14 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
if (!(root.containsKey("id") && root.containsKey("serial") && root.containsKey("name") && root.containsKey("channel"))) {
retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (root["id"].as<uint8_t>() > INV_MAX_COUNT - 1) {
retMsg["message"] = "Invalid ID specified!";
retMsg["code"] = WebApiError::InverterInvalidId;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -192,8 +184,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
if (serial == 0) {
retMsg["message"] = "Serial must be a number > 0!";
retMsg["code"] = WebApiError::InverterSerialZero;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -201,8 +192,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
retMsg["message"] = "Name must between 1 and " STR(INV_MAX_NAME_STRLEN) " characters long!";
retMsg["code"] = WebApiError::InverterNameLength;
retMsg["param"]["max"] = INV_MAX_NAME_STRLEN;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -210,8 +200,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
if (channelArray.size() == 0 || channelArray.size() > INV_MAX_CHAN_COUNT) {
retMsg["message"] = "Invalid amount of max channel setting given!";
retMsg["code"] = WebApiError::InverterInvalidMaxChannel;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -243,8 +232,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
WebApi.writeConfig(retMsg, WebApiError::InverterChanged, "Inverter changed!");
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterBySerial(old_serial);
@ -293,16 +281,14 @@ void WebApiInverterClass::onInverterDelete(AsyncWebServerRequest* request)
if (!(root.containsKey("id"))) {
retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (root["id"].as<uint8_t>() > INV_MAX_COUNT - 1) {
retMsg["message"] = "Invalid ID specified!";
retMsg["code"] = WebApiError::InverterInvalidId;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -315,8 +301,7 @@ void WebApiInverterClass::onInverterDelete(AsyncWebServerRequest* request)
WebApi.writeConfig(retMsg, WebApiError::InverterDeleted, "Inverter deleted!");
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
MqttHandleHass.forceUpdate();
}
@ -338,8 +323,7 @@ void WebApiInverterClass::onInverterOrder(AsyncWebServerRequest* request)
if (!(root.containsKey("order"))) {
retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -357,6 +341,5 @@ void WebApiInverterClass::onInverterOrder(AsyncWebServerRequest* request)
WebApi.writeConfig(retMsg, WebApiError::InverterOrdered, "Inverter order saved!");
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}

View File

@ -47,8 +47,7 @@ void WebApiLimitClass::onLimitStatus(AsyncWebServerRequest* request)
root[serial]["limit_set_status"] = limitStatus;
}
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}
void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request)
@ -70,8 +69,7 @@ void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request)
&& root.containsKey("limit_type"))) {
retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -81,8 +79,7 @@ void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request)
if (serial == 0) {
retMsg["message"] = "Serial must be a number > 0!";
retMsg["code"] = WebApiError::LimitSerialZero;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -90,8 +87,7 @@ void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request)
retMsg["message"] = "Limit must between 0 and " STR(MAX_INVERTER_LIMIT) "!";
retMsg["code"] = WebApiError::LimitInvalidLimit;
retMsg["param"]["max"] = MAX_INVERTER_LIMIT;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -102,8 +98,7 @@ void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request)
retMsg["message"] = "Invalid type specified!";
retMsg["code"] = WebApiError::LimitInvalidType;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -114,8 +109,7 @@ void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request)
if (inv == nullptr) {
retMsg["message"] = "Invalid inverter specified!";
retMsg["code"] = WebApiError::LimitInvalidInverter;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -125,6 +119,5 @@ void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request)
retMsg["message"] = "Settings saved!";
retMsg["code"] = WebApiError::GenericSuccess;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}

View File

@ -33,8 +33,7 @@ void WebApiMaintenanceClass::onRebootPost(AsyncWebServerRequest* request)
if (!(root.containsKey("reboot"))) {
retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -43,14 +42,12 @@ void WebApiMaintenanceClass::onRebootPost(AsyncWebServerRequest* request)
retMsg["message"] = "Reboot triggered!";
retMsg["code"] = WebApiError::MaintenanceRebootTriggered;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
Utils::restartDtu();
} else {
retMsg["message"] = "Reboot cancled!";
retMsg["code"] = WebApiError::MaintenanceRebootCancled;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}
}

View File

@ -50,8 +50,7 @@ void WebApiMqttClass::onMqttStatus(AsyncWebServerRequest* request)
root["mqtt_hass_topic"] = config.Mqtt.Hass.Topic;
root["mqtt_hass_individualpanels"] = config.Mqtt.Hass.IndividualPanels;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}
void WebApiMqttClass::onMqttAdminGet(AsyncWebServerRequest* request)
@ -88,8 +87,7 @@ void WebApiMqttClass::onMqttAdminGet(AsyncWebServerRequest* request)
root["mqtt_hass_topic"] = config.Mqtt.Hass.Topic;
root["mqtt_hass_individualpanels"] = config.Mqtt.Hass.IndividualPanels;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}
void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
@ -130,8 +128,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
&& root.containsKey("mqtt_hass_individualpanels"))) {
retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -140,8 +137,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
retMsg["message"] = "MqTT Server must between 1 and " STR(MQTT_MAX_HOSTNAME_STRLEN) " characters long!";
retMsg["code"] = WebApiError::MqttHostnameLength;
retMsg["param"]["max"] = MQTT_MAX_HOSTNAME_STRLEN;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -149,48 +145,42 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
retMsg["message"] = "Username must not be longer than " STR(MQTT_MAX_USERNAME_STRLEN) " characters!";
retMsg["code"] = WebApiError::MqttUsernameLength;
retMsg["param"]["max"] = MQTT_MAX_USERNAME_STRLEN;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (root["mqtt_password"].as<String>().length() > MQTT_MAX_PASSWORD_STRLEN) {
retMsg["message"] = "Password must not be longer than " STR(MQTT_MAX_PASSWORD_STRLEN) " characters!";
retMsg["code"] = WebApiError::MqttPasswordLength;
retMsg["param"]["max"] = MQTT_MAX_PASSWORD_STRLEN;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (root["mqtt_topic"].as<String>().length() > MQTT_MAX_TOPIC_STRLEN) {
retMsg["message"] = "Topic must not be longer than " STR(MQTT_MAX_TOPIC_STRLEN) " characters!";
retMsg["code"] = WebApiError::MqttTopicLength;
retMsg["param"]["max"] = MQTT_MAX_TOPIC_STRLEN;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (root["mqtt_topic"].as<String>().indexOf(' ') != -1) {
retMsg["message"] = "Topic must not contain space characters!";
retMsg["code"] = WebApiError::MqttTopicCharacter;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (!root["mqtt_topic"].as<String>().endsWith("/")) {
retMsg["message"] = "Topic must end with a slash (/)!";
retMsg["code"] = WebApiError::MqttTopicTrailingSlash;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (root["mqtt_port"].as<uint>() == 0 || root["mqtt_port"].as<uint>() > 65535) {
retMsg["message"] = "Port must be a number between 1 and 65535!";
retMsg["code"] = WebApiError::MqttPort;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -200,8 +190,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
retMsg["message"] = "Certificates must not be longer than " STR(MQTT_MAX_CERT_STRLEN) " characters!";
retMsg["code"] = WebApiError::MqttCertificateLength;
retMsg["param"]["max"] = MQTT_MAX_CERT_STRLEN;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -209,16 +198,14 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
retMsg["message"] = "LWT topic must not be longer than " STR(MQTT_MAX_TOPIC_STRLEN) " characters!";
retMsg["code"] = WebApiError::MqttLwtTopicLength;
retMsg["param"]["max"] = MQTT_MAX_TOPIC_STRLEN;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (root["mqtt_lwt_topic"].as<String>().indexOf(' ') != -1) {
retMsg["message"] = "LWT topic must not contain space characters!";
retMsg["code"] = WebApiError::MqttLwtTopicCharacter;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -226,8 +213,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
retMsg["message"] = "LWT online value must not be longer than " STR(MQTT_MAX_LWTVALUE_STRLEN) " characters!";
retMsg["code"] = WebApiError::MqttLwtOnlineLength;
retMsg["param"]["max"] = MQTT_MAX_LWTVALUE_STRLEN;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -235,8 +221,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
retMsg["message"] = "LWT offline value must not be longer than " STR(MQTT_MAX_LWTVALUE_STRLEN) " characters!";
retMsg["code"] = WebApiError::MqttLwtOfflineLength;
retMsg["param"]["max"] = MQTT_MAX_LWTVALUE_STRLEN;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -244,8 +229,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
retMsg["message"] = "LWT QoS must not be greater than " STR(2) "!";
retMsg["code"] = WebApiError::MqttLwtQos;
retMsg["param"]["max"] = 2;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -254,8 +238,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
retMsg["code"] = WebApiError::MqttPublishInterval;
retMsg["param"]["min"] = 5;
retMsg["param"]["max"] = 65535;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -264,16 +247,14 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
retMsg["message"] = "Hass topic must not be longer than " STR(MQTT_MAX_TOPIC_STRLEN) " characters!";
retMsg["code"] = WebApiError::MqttHassTopicLength;
retMsg["param"]["max"] = MQTT_MAX_TOPIC_STRLEN;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (root["mqtt_hass_topic"].as<String>().indexOf(' ') != -1) {
retMsg["message"] = "Hass topic must not contain space characters!";
retMsg["code"] = WebApiError::MqttHassTopicCharacter;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
}
@ -306,8 +287,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
WebApi.writeConfig(retMsg);
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
MqttSettings.performReconnect();
MqttHandleHass.forceUpdate();

View File

@ -46,8 +46,7 @@ void WebApiNetworkClass::onNetworkStatus(AsyncWebServerRequest* request)
root["ap_mac"] = WiFi.softAPmacAddress();
root["ap_stationnum"] = WiFi.softAPgetStationNum();
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}
void WebApiNetworkClass::onNetworkAdminGet(AsyncWebServerRequest* request)
@ -72,8 +71,7 @@ void WebApiNetworkClass::onNetworkAdminGet(AsyncWebServerRequest* request)
root["aptimeout"] = config.WiFi.ApTimeout;
root["mdnsenabled"] = config.Mdns.Enabled;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}
void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request)
@ -102,8 +100,7 @@ void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request)
&& root.containsKey("aptimeout"))) {
retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -111,68 +108,59 @@ void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request)
if (!ipaddress.fromString(root["ipaddress"].as<String>())) {
retMsg["message"] = "IP address is invalid!";
retMsg["code"] = WebApiError::NetworkIpInvalid;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
IPAddress netmask;
if (!netmask.fromString(root["netmask"].as<String>())) {
retMsg["message"] = "Netmask is invalid!";
retMsg["code"] = WebApiError::NetworkNetmaskInvalid;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
IPAddress gateway;
if (!gateway.fromString(root["gateway"].as<String>())) {
retMsg["message"] = "Gateway is invalid!";
retMsg["code"] = WebApiError::NetworkGatewayInvalid;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
IPAddress dns1;
if (!dns1.fromString(root["dns1"].as<String>())) {
retMsg["message"] = "DNS Server IP 1 is invalid!";
retMsg["code"] = WebApiError::NetworkDns1Invalid;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
IPAddress dns2;
if (!dns2.fromString(root["dns2"].as<String>())) {
retMsg["message"] = "DNS Server IP 2 is invalid!";
retMsg["code"] = WebApiError::NetworkDns2Invalid;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (root["hostname"].as<String>().length() == 0 || root["hostname"].as<String>().length() > WIFI_MAX_HOSTNAME_STRLEN) {
retMsg["message"] = "Hostname must between 1 and " STR(WIFI_MAX_HOSTNAME_STRLEN) " characters long!";
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (NetworkSettings.NetworkMode() == network_mode::WiFi) {
if (root["ssid"].as<String>().length() == 0 || root["ssid"].as<String>().length() > WIFI_MAX_SSID_STRLEN) {
retMsg["message"] = "SSID must between 1 and " STR(WIFI_MAX_SSID_STRLEN) " characters long!";
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
}
if (root["password"].as<String>().length() > WIFI_MAX_PASSWORD_STRLEN - 1) {
retMsg["message"] = "Password must not be longer than " STR(WIFI_MAX_PASSWORD_STRLEN) " characters long!";
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (root["aptimeout"].as<uint>() > 99999) {
retMsg["message"] = "ApTimeout must be a number between 0 and 99999!";
retMsg["code"] = WebApiError::NetworkApTimeoutInvalid;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -210,8 +198,7 @@ void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request)
WebApi.writeConfig(retMsg);
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
NetworkSettings.enableAdminMode();
NetworkSettings.applyConfig();

View File

@ -63,8 +63,7 @@ void WebApiNtpClass::onNtpStatus(AsyncWebServerRequest* request)
root["sun_isSunsetAvailable"] = SunPosition.isSunsetAvailable();
root["sun_isDayPeriod"] = SunPosition.isDayPeriod();
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}
void WebApiNtpClass::onNtpAdminGet(AsyncWebServerRequest* request)
@ -84,8 +83,7 @@ void WebApiNtpClass::onNtpAdminGet(AsyncWebServerRequest* request)
root["latitude"] = config.Ntp.Latitude;
root["sunsettype"] = config.Ntp.SunsetType;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}
void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
@ -109,8 +107,7 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
&& root.containsKey("sunsettype"))) {
retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -118,8 +115,7 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
retMsg["message"] = "NTP Server must between 1 and " STR(NTP_MAX_SERVER_STRLEN) " characters long!";
retMsg["code"] = WebApiError::NtpServerLength;
retMsg["param"]["max"] = NTP_MAX_SERVER_STRLEN;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -127,8 +123,7 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
retMsg["message"] = "Timezone must between 1 and " STR(NTP_MAX_TIMEZONE_STRLEN) " characters long!";
retMsg["code"] = WebApiError::NtpTimezoneLength;
retMsg["param"]["max"] = NTP_MAX_TIMEZONE_STRLEN;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -136,8 +131,7 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
retMsg["message"] = "Timezone description must between 1 and " STR(NTP_MAX_TIMEZONEDESCR_STRLEN) " characters long!";
retMsg["code"] = WebApiError::NtpTimezoneDescriptionLength;
retMsg["param"]["max"] = NTP_MAX_TIMEZONEDESCR_STRLEN;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -151,8 +145,7 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
WebApi.writeConfig(retMsg);
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
NtpSettings.setServer();
NtpSettings.setTimezone();
@ -183,8 +176,7 @@ void WebApiNtpClass::onNtpTimeGet(AsyncWebServerRequest* request)
root["minute"] = timeinfo.tm_min;
root["second"] = timeinfo.tm_sec;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}
void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
@ -209,8 +201,7 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
&& root.containsKey("second"))) {
retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -219,8 +210,7 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
retMsg["code"] = WebApiError::NtpYearInvalid;
retMsg["param"]["min"] = 2022;
retMsg["param"]["max"] = 2100;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -229,8 +219,7 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
retMsg["code"] = WebApiError::NtpMonthInvalid;
retMsg["param"]["min"] = 1;
retMsg["param"]["max"] = 12;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -239,8 +228,7 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
retMsg["code"] = WebApiError::NtpDayInvalid;
retMsg["param"]["min"] = 1;
retMsg["param"]["max"] = 31;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -249,8 +237,7 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
retMsg["code"] = WebApiError::NtpHourInvalid;
retMsg["param"]["min"] = 0;
retMsg["param"]["max"] = 23;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -259,8 +246,7 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
retMsg["code"] = WebApiError::NtpMinuteInvalid;
retMsg["param"]["min"] = 0;
retMsg["param"]["max"] = 59;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -269,8 +255,7 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
retMsg["code"] = WebApiError::NtpSecondInvalid;
retMsg["param"]["min"] = 0;
retMsg["param"]["max"] = 59;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -291,6 +276,5 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
retMsg["message"] = "Time updated!";
retMsg["code"] = WebApiError::NtpTimeUpdated;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}

View File

@ -40,8 +40,7 @@ void WebApiPowerClass::onPowerStatus(AsyncWebServerRequest* request)
root[inv->serialString()]["power_set_status"] = limitStatus;
}
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}
void WebApiPowerClass::onPowerPost(AsyncWebServerRequest* request)
@ -63,8 +62,7 @@ void WebApiPowerClass::onPowerPost(AsyncWebServerRequest* request)
|| root.containsKey("restart")))) {
retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -74,8 +72,7 @@ void WebApiPowerClass::onPowerPost(AsyncWebServerRequest* request)
if (serial == 0) {
retMsg["message"] = "Serial must be a number > 0!";
retMsg["code"] = WebApiError::PowerSerialZero;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -83,8 +80,7 @@ void WebApiPowerClass::onPowerPost(AsyncWebServerRequest* request)
if (inv == nullptr) {
retMsg["message"] = "Invalid inverter specified!";
retMsg["code"] = WebApiError::PowerInvalidInverter;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -101,6 +97,5 @@ void WebApiPowerClass::onPowerPost(AsyncWebServerRequest* request)
retMsg["message"] = "Settings saved!";
retMsg["code"] = WebApiError::GenericSuccess;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}

View File

@ -31,8 +31,7 @@ void WebApiSecurityClass::onSecurityGet(AsyncWebServerRequest* request)
root["password"] = config.Security.Password;
root["allow_readonly"] = config.Security.AllowReadonly;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}
void WebApiSecurityClass::onSecurityPost(AsyncWebServerRequest* request)
@ -53,8 +52,7 @@ void WebApiSecurityClass::onSecurityPost(AsyncWebServerRequest* request)
&& root.containsKey("allow_readonly")) {
retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -62,8 +60,7 @@ void WebApiSecurityClass::onSecurityPost(AsyncWebServerRequest* request)
retMsg["message"] = "Password must between 8 and " STR(WIFI_MAX_PASSWORD_STRLEN) " characters long!";
retMsg["code"] = WebApiError::SecurityPasswordLength;
retMsg["param"]["max"] = WIFI_MAX_PASSWORD_STRLEN;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
@ -73,8 +70,7 @@ void WebApiSecurityClass::onSecurityPost(AsyncWebServerRequest* request)
WebApi.writeConfig(retMsg);
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}
void WebApiSecurityClass::onAuthenticateGet(AsyncWebServerRequest* request)
@ -89,6 +85,5 @@ void WebApiSecurityClass::onAuthenticateGet(AsyncWebServerRequest* request)
retMsg["message"] = "Authentication successful!";
retMsg["code"] = WebApiError::SecurityAuthSuccess;
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}

View File

@ -76,6 +76,5 @@ void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request)
root["cmt_configured"] = PinMapping.isValidCmt2300Config();
root["cmt_connected"] = Hoymiles.getRadioCmt()->isConnected();
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}

View File

@ -255,12 +255,7 @@ void WebApiWsLiveClass::onLivedataStatus(AsyncWebServerRequest* request)
generateCommonJsonResponse(root);
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
return;
}
response->setLength();
request->send(response);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
} catch (const std::bad_alloc& bad_alloc) {
MessageOutput.printf("Call to /api/livedata/status temporarely out of resources. Reason: \"%s\".\r\n", bad_alloc.what());

View File

@ -32,6 +32,9 @@
"Release": "Loslassen zum Aktualisieren",
"Close": "Schließen"
},
"Error": {
"Oops": "Oops!"
},
"localeswitcher": {
"Dark": "Dunkel",
"Light": "Hell",

View File

@ -32,6 +32,9 @@
"Release": "Release to refresh",
"Close": "Close"
},
"Error": {
"Oops": "Oops!"
},
"localeswitcher": {
"Dark": "Dark",
"Light": "Light",

View File

@ -32,6 +32,9 @@
"Release": "Release to refresh",
"Close": "Fermer"
},
"Error": {
"Oops": "Oops!"
},
"localeswitcher": {
"Dark": "Sombre",
"Light": "Clair",

View File

@ -3,6 +3,7 @@ import ConfigAdminView from '@/views/ConfigAdminView.vue';
import ConsoleInfoView from '@/views/ConsoleInfoView.vue';
import DeviceAdminView from '@/views/DeviceAdminView.vue'
import DtuAdminView from '@/views/DtuAdminView.vue';
import ErrorView from '@/views/ErrorView.vue';
import FirmwareUpgradeView from '@/views/FirmwareUpgradeView.vue';
import HomeView from '@/views/HomeView.vue';
import InverterAdminView from '@/views/InverterAdminView.vue';
@ -32,6 +33,11 @@ const router = createRouter({
name: 'Login',
component: LoginView
},
{
path: '/error?status=:status&message=:message',
name: 'Error',
component: ErrorView
},
{
path: '/about',
name: 'About',
@ -115,4 +121,4 @@ const router = createRouter({
]
});
export default router;
export default router;

View File

@ -77,6 +77,7 @@ export function handleResponse(response: Response, emitter: Emitter<Record<Event
}
const error = { message: (data && data.message) || response.statusText, status: response.status || 0 };
router.push({ name: "Error", params: error });
return Promise.reject(error);
}
@ -99,4 +100,4 @@ function handleAuthResponse(response: Response) {
return data;
});
}
}

View File

@ -0,0 +1,18 @@
<template>
<BasePage :title="$t('Error.Oops')">
<div class="alert alert-danger" role="alert">
<h2>{{ $route.params.message }}</h2>
</div>
</BasePage>
</template>
<script lang="ts">
import BasePage from '@/components/BasePage.vue';
import { defineComponent } from 'vue';
export default defineComponent({
components: {
BasePage,
},
});
</script>

View File

@ -191,7 +191,7 @@ export default defineComponent({
const remoteHostUrl = "/api/system/status";
// Use a simple fetch request to check if the remote host is reachable
fetch(remoteHostUrl, { method: 'HEAD' })
fetch(remoteHostUrl, { method: 'GET' })
.then(response => {
// Check if the response status is OK (200-299 range)
if (response.ok) {