diff --git a/include/Configuration.h b/include/Configuration.h index d355f46f..673b2e9b 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -5,7 +5,7 @@ #include #define CONFIG_FILENAME "/config.json" -#define CONFIG_VERSION 0x00011900 // 0.1.24 // make sure to clean all after change +#define CONFIG_VERSION 0x00011a00 // 0.1.26 // make sure to clean all after change #define WIFI_MAX_SSID_STRLEN 32 #define WIFI_MAX_PASSWORD_STRLEN 64 diff --git a/include/Utils.h b/include/Utils.h index 6de962b0..4d4bfee3 100644 --- a/include/Utils.h +++ b/include/Utils.h @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include #include class Utils { @@ -9,4 +10,5 @@ public: static uint64_t generateDtuSerial(); static int getTimezoneOffset(); static void restartDtu(); + static bool checkJsonAlloc(const DynamicJsonDocument& doc, const char* function, const uint16_t line); }; diff --git a/include/WebApi.h b/include/WebApi.h index 537af6c3..f9ed2eba 100644 --- a/include/WebApi.h +++ b/include/WebApi.h @@ -5,6 +5,7 @@ #include "WebApi_device.h" #include "WebApi_devinfo.h" #include "WebApi_dtu.h" +#include "WebApi_errors.h" #include "WebApi_eventlog.h" #include "WebApi_firmware.h" #include "WebApi_gridprofile.h" @@ -35,6 +36,8 @@ public: static void sendTooManyRequests(AsyncWebServerRequest* request); + static void writeConfig(JsonVariant& retMsg, const WebApiError code = WebApiError::GenericSuccess, const String& message = "Settings saved!"); + private: void loop(); diff --git a/include/WebApi_errors.h b/include/WebApi_errors.h index 31ddae03..e5703a83 100644 --- a/include/WebApi_errors.h +++ b/include/WebApi_errors.h @@ -8,6 +8,7 @@ enum WebApiError { GenericDataTooLarge, GenericParseError, GenericValueMissing, + GenericWriteFailed, DtuBase = 2000, DtuSerialZero, diff --git a/include/defaults.h b/include/defaults.h index e783df2d..0070e09c 100644 --- a/include/defaults.h +++ b/include/defaults.h @@ -13,7 +13,7 @@ #define AUTH_USERNAME "admin" #define SECURITY_ALLOW_READONLY true -#define WIFI_RECONNECT_TIMEOUT 15 +#define WIFI_RECONNECT_TIMEOUT 30 #define WIFI_RECONNECT_REDO_TIMEOUT 600 #define WIFI_SSID "" diff --git a/platformio.ini b/platformio.ini index 019ee67c..5c9f7a3a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -24,6 +24,7 @@ platform = espressif32@6.5.0 build_flags = -DPIOENV=\"$PIOENV\" -D_TASK_STD_FUNCTION=1 + -D_TASK_THREAD_SAFE=1 -Wall -Wextra -Wunused -Wmisleading-indentation -Wduplicated-cond -Wlogical-op -Wnull-dereference ; Have to remove -Werror because of ; https://github.com/espressif/arduino-esp32/issues/9044 and diff --git a/src/Configuration.cpp b/src/Configuration.cpp index d1ff8fec..f191ab7c 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -4,9 +4,11 @@ */ #include "Configuration.h" #include "MessageOutput.h" +#include "Utils.h" #include "defaults.h" #include #include +#include CONFIG_T config; @@ -25,6 +27,10 @@ bool ConfigurationClass::write() DynamicJsonDocument doc(JSON_BUFFER_SIZE); + if (!Utils::checkJsonAlloc(doc, __FUNCTION__, __LINE__)) { + return false; + } + JsonObject cfg = doc.createNestedObject("cfg"); cfg["version"] = config.Cfg.Version; cfg["save_count"] = config.Cfg.SaveCount; @@ -150,6 +156,11 @@ bool ConfigurationClass::read() File f = LittleFS.open(CONFIG_FILENAME, "r", false); DynamicJsonDocument doc(JSON_BUFFER_SIZE); + + if (!Utils::checkJsonAlloc(doc, __FUNCTION__, __LINE__)) { + return false; + } + // Deserialize the JSON document const DeserializationError error = deserializeJson(doc, f); if (error) { @@ -310,6 +321,11 @@ void ConfigurationClass::migrate() } DynamicJsonDocument doc(JSON_BUFFER_SIZE); + + if (!Utils::checkJsonAlloc(doc, __FUNCTION__, __LINE__)) { + return; + } + // Deserialize the JSON document const DeserializationError error = deserializeJson(doc, f); if (error) { @@ -339,6 +355,14 @@ void ConfigurationClass::migrate() config.Dtu.Nrf.PaLevel = dtu["pa_level"]; } + if (config.Cfg.Version < 0x00011a00) { + // This migration fixes this issue: https://github.com/espressif/arduino-esp32/issues/8828 + // It occours when migrating from Core 2.0.9 to 2.0.14 + // which was done by updating ESP32 PlatformIO from 6.3.2 to 6.5.0 + nvs_flash_erase(); + nvs_flash_init(); + } + f.close(); config.Cfg.Version = CONFIG_VERSION; diff --git a/src/MqttHandleHass.cpp b/src/MqttHandleHass.cpp index 88553e15..0f5c293c 100644 --- a/src/MqttHandleHass.cpp +++ b/src/MqttHandleHass.cpp @@ -135,6 +135,10 @@ void MqttHandleHassClass::publishInverterField(std::shared_ptr } DynamicJsonDocument root(1024); + if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) { + return; + } + root["name"] = name; root["stat_t"] = stateTopic; root["uniq_id"] = serial + "_ch" + chanNum + "_" + fieldName; @@ -179,6 +183,10 @@ void MqttHandleHassClass::publishInverterButton(std::shared_ptr uint32_t Utils::getChipId() @@ -65,3 +66,13 @@ void Utils::restartDtu() yield(); ESP.restart(); } + +bool Utils::checkJsonAlloc(const DynamicJsonDocument& doc, const char* function, const uint16_t line) +{ + if (doc.capacity() == 0) { + MessageOutput.printf("Alloc failed: %s, %d\r\n", function, line); + return false; + } + + return true; +} diff --git a/src/WebApi.cpp b/src/WebApi.cpp index 3e2e124d..aa589d57 100644 --- a/src/WebApi.cpp +++ b/src/WebApi.cpp @@ -103,4 +103,16 @@ void WebApiClass::sendTooManyRequests(AsyncWebServerRequest* request) request->send(response); } +void WebApiClass::writeConfig(JsonVariant& retMsg, const WebApiError code, const String& message) +{ + if (!Configuration.write()) { + retMsg["message"] = "Write failed!"; + retMsg["code"] = WebApiError::GenericWriteFailed; + } else { + retMsg["type"] = "success"; + retMsg["message"] = message; + retMsg["code"] = code; + } +} + WebApiClass WebApi; \ No newline at end of file diff --git a/src/WebApi_config.cpp b/src/WebApi_config.cpp index e466c79c..73df1e3d 100644 --- a/src/WebApi_config.cpp +++ b/src/WebApi_config.cpp @@ -59,7 +59,7 @@ void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject retMsg = response->getRoot(); + auto& retMsg = response->getRoot(); retMsg["type"] = "warning"; if (!request->hasParam("data", true)) { @@ -125,8 +125,8 @@ void WebApiConfigClass::onConfigListGet(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject root = response->getRoot(); - JsonArray data = root.createNestedArray("configs"); + auto& root = response->getRoot(); + auto data = root.createNestedArray("configs"); File rootfs = LittleFS.open("/"); File file = rootfs.openNextFile(); diff --git a/src/WebApi_device.cpp b/src/WebApi_device.cpp index f8f93d9c..c8c216e8 100644 --- a/src/WebApi_device.cpp +++ b/src/WebApi_device.cpp @@ -33,14 +33,14 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE); - JsonObject root = response->getRoot(); + auto& root = response->getRoot(); const CONFIG_T& config = Configuration.get(); const PinMapping_t& pin = PinMapping.get(); - JsonObject curPin = root.createNestedObject("curPin"); + auto curPin = root.createNestedObject("curPin"); curPin["name"] = config.Dev_PinMapping; - JsonObject nrfPinObj = curPin.createNestedObject("nrf24"); + auto nrfPinObj = curPin.createNestedObject("nrf24"); nrfPinObj["clk"] = pin.nrf24_clk; nrfPinObj["cs"] = pin.nrf24_cs; nrfPinObj["en"] = pin.nrf24_en; @@ -48,7 +48,7 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request) nrfPinObj["miso"] = pin.nrf24_miso; nrfPinObj["mosi"] = pin.nrf24_mosi; - JsonObject cmtPinObj = curPin.createNestedObject("cmt"); + auto cmtPinObj = curPin.createNestedObject("cmt"); cmtPinObj["clk"] = pin.cmt_clk; cmtPinObj["cs"] = pin.cmt_cs; cmtPinObj["fcs"] = pin.cmt_fcs; @@ -56,7 +56,7 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request) cmtPinObj["gpio2"] = pin.cmt_gpio2; cmtPinObj["gpio3"] = pin.cmt_gpio3; - JsonObject ethPinObj = curPin.createNestedObject("eth"); + auto ethPinObj = curPin.createNestedObject("eth"); ethPinObj["enabled"] = pin.eth_enabled; ethPinObj["phy_addr"] = pin.eth_phy_addr; ethPinObj["power"] = pin.eth_power; @@ -65,19 +65,19 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request) ethPinObj["type"] = pin.eth_type; ethPinObj["clk_mode"] = pin.eth_clk_mode; - JsonObject displayPinObj = curPin.createNestedObject("display"); + auto displayPinObj = curPin.createNestedObject("display"); displayPinObj["type"] = pin.display_type; displayPinObj["data"] = pin.display_data; displayPinObj["clk"] = pin.display_clk; displayPinObj["cs"] = pin.display_cs; displayPinObj["reset"] = pin.display_reset; - JsonObject ledPinObj = curPin.createNestedObject("led"); + auto ledPinObj = curPin.createNestedObject("led"); for (uint8_t i = 0; i < PINMAPPING_LED_COUNT; i++) { ledPinObj["led" + String(i)] = pin.led[i]; } - JsonObject display = root.createNestedObject("display"); + auto display = root.createNestedObject("display"); display["rotation"] = config.Display.Rotation; display["power_safe"] = config.Display.PowerSafe; display["screensaver"] = config.Display.ScreenSaver; @@ -85,9 +85,9 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request) display["language"] = config.Display.Language; display["diagramduration"] = config.Display.DiagramDuration; - JsonArray leds = root.createNestedArray("led"); + auto leds = root.createNestedArray("led"); for (uint8_t i = 0; i < PINMAPPING_LED_COUNT; i++) { - JsonObject led = leds.createNestedObject(); + auto led = leds.createNestedObject(); led["brightness"] = config.Led_Single[i].Brightness; } @@ -102,7 +102,7 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE); - JsonObject retMsg = response->getRoot(); + auto& retMsg = response->getRoot(); retMsg["type"] = "warning"; if (!request->hasParam("data", true)) { @@ -175,11 +175,7 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request) Display.setLanguage(config.Display.Language); Display.Diagram().updatePeriod(); - Configuration.write(); - - retMsg["type"] = "success"; - retMsg["message"] = "Settings saved!"; - retMsg["code"] = WebApiError::GenericSuccess; + WebApi.writeConfig(retMsg); response->setLength(); request->send(response); diff --git a/src/WebApi_devinfo.cpp b/src/WebApi_devinfo.cpp index 04b8a581..b5f3e370 100644 --- a/src/WebApi_devinfo.cpp +++ b/src/WebApi_devinfo.cpp @@ -28,7 +28,7 @@ void WebApiDevInfoClass::onDevInfoStatus(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject root = response->getRoot(); + auto& root = response->getRoot(); uint64_t serial = 0; if (request->hasParam("inv")) { diff --git a/src/WebApi_dtu.cpp b/src/WebApi_dtu.cpp index e13d9f5c..c8a87678 100644 --- a/src/WebApi_dtu.cpp +++ b/src/WebApi_dtu.cpp @@ -30,7 +30,7 @@ void WebApiDtuClass::onDtuAdminGet(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject root = response->getRoot(); + auto& root = response->getRoot(); const CONFIG_T& config = Configuration.get(); // DTU Serial is read as HEX @@ -57,7 +57,7 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject retMsg = response->getRoot(); + auto& retMsg = response->getRoot(); retMsg["type"] = "warning"; if (!request->hasParam("data", true)) { @@ -154,11 +154,8 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request) config.Dtu.Nrf.PaLevel = root["nrf_palevel"].as(); config.Dtu.Cmt.PaLevel = root["cmt_palevel"].as(); config.Dtu.Cmt.Frequency = root["cmt_frequency"].as(); - Configuration.write(); - retMsg["type"] = "success"; - retMsg["message"] = "Settings saved!"; - retMsg["code"] = WebApiError::GenericSuccess; + WebApi.writeConfig(retMsg); response->setLength(); request->send(response); diff --git a/src/WebApi_eventlog.cpp b/src/WebApi_eventlog.cpp index e0c8b316..0551978a 100644 --- a/src/WebApi_eventlog.cpp +++ b/src/WebApi_eventlog.cpp @@ -27,7 +27,7 @@ void WebApiEventlogClass::onEventlogStatus(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(false, 2048); - JsonObject root = response->getRoot(); + auto& root = response->getRoot(); uint64_t serial = 0; if (request->hasParam("inv")) { diff --git a/src/WebApi_gridprofile.cpp b/src/WebApi_gridprofile.cpp index ee945bc4..a5793e77 100644 --- a/src/WebApi_gridprofile.cpp +++ b/src/WebApi_gridprofile.cpp @@ -28,7 +28,7 @@ void WebApiGridProfileClass::onGridProfileStatus(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(false, 8192); - JsonObject root = response->getRoot(); + auto& root = response->getRoot(); uint64_t serial = 0; if (request->hasParam("inv")) { @@ -72,7 +72,7 @@ void WebApiGridProfileClass::onGridProfileRawdata(AsyncWebServerRequest* request } AsyncJsonResponse* response = new AsyncJsonResponse(false, 4096); - JsonObject root = response->getRoot(); + auto& root = response->getRoot(); uint64_t serial = 0; if (request->hasParam("inv")) { diff --git a/src/WebApi_inverter.cpp b/src/WebApi_inverter.cpp index 88ddd37e..7538b2c0 100644 --- a/src/WebApi_inverter.cpp +++ b/src/WebApi_inverter.cpp @@ -36,7 +36,7 @@ void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(false, 768 * INV_MAX_COUNT); - JsonObject root = response->getRoot(); + auto& root = response->getRoot(); JsonArray data = root.createNestedArray("inverter"); const CONFIG_T& config = Configuration.get(); @@ -94,7 +94,7 @@ void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject retMsg = response->getRoot(); + auto& retMsg = response->getRoot(); retMsg["type"] = "warning"; if (!request->hasParam("data", true)) { @@ -167,11 +167,8 @@ void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request) inverter->Serial = strtoll(root["serial"].as().c_str(), NULL, 16); strncpy(inverter->Name, root["name"].as().c_str(), INV_MAX_NAME_STRLEN); - Configuration.write(); - retMsg["type"] = "success"; - retMsg["message"] = "Inverter created!"; - retMsg["code"] = WebApiError::InverterAdded; + WebApi.writeConfig(retMsg, WebApiError::InverterAdded, "Inverter created!"); response->setLength(); request->send(response); @@ -194,7 +191,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject retMsg = response->getRoot(); + auto& retMsg = response->getRoot(); retMsg["type"] = "warning"; if (!request->hasParam("data", true)) { @@ -294,11 +291,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request) arrayCount++; } - Configuration.write(); - - retMsg["type"] = "success"; - retMsg["code"] = WebApiError::InverterChanged; - retMsg["message"] = "Inverter changed!"; + WebApi.writeConfig(retMsg, WebApiError::InverterChanged, "Inverter changed!"); response->setLength(); request->send(response); @@ -340,7 +333,7 @@ void WebApiInverterClass::onInverterDelete(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject retMsg = response->getRoot(); + auto& retMsg = response->getRoot(); retMsg["type"] = "warning"; if (!request->hasParam("data", true)) { @@ -395,11 +388,8 @@ void WebApiInverterClass::onInverterDelete(AsyncWebServerRequest* request) inverter.Serial = 0; strncpy(inverter.Name, "", sizeof(inverter.Name)); - Configuration.write(); - retMsg["type"] = "success"; - retMsg["message"] = "Inverter deleted!"; - retMsg["code"] = WebApiError::InverterDeleted; + WebApi.writeConfig(retMsg, WebApiError::InverterDeleted, "Inverter deleted!"); response->setLength(); request->send(response); @@ -414,7 +404,7 @@ void WebApiInverterClass::onInverterOrder(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject retMsg = response->getRoot(); + auto& retMsg = response->getRoot(); retMsg["type"] = "warning"; if (!request->hasParam("data", true)) { @@ -466,11 +456,7 @@ void WebApiInverterClass::onInverterOrder(AsyncWebServerRequest* request) order++; } - Configuration.write(); - - retMsg["type"] = "success"; - retMsg["message"] = "Inverter order saved!"; - retMsg["code"] = WebApiError::InverterOrdered; + WebApi.writeConfig(retMsg, WebApiError::InverterOrdered, "Inverter order saved!"); response->setLength(); request->send(response); diff --git a/src/WebApi_limit.cpp b/src/WebApi_limit.cpp index 1e5b3f21..8bf0c942 100644 --- a/src/WebApi_limit.cpp +++ b/src/WebApi_limit.cpp @@ -31,7 +31,7 @@ void WebApiLimitClass::onLimitStatus(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject root = response->getRoot(); + auto& root = response->getRoot(); for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) { auto inv = Hoymiles.getInverterByPos(i); @@ -64,7 +64,7 @@ void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject retMsg = response->getRoot(); + auto& retMsg = response->getRoot(); retMsg["type"] = "warning"; if (!request->hasParam("data", true)) { diff --git a/src/WebApi_maintenance.cpp b/src/WebApi_maintenance.cpp index 8b65c935..0c62394c 100644 --- a/src/WebApi_maintenance.cpp +++ b/src/WebApi_maintenance.cpp @@ -29,7 +29,7 @@ void WebApiMaintenanceClass::onRebootPost(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE); - JsonObject retMsg = response->getRoot(); + auto& retMsg = response->getRoot(); retMsg["type"] = "warning"; if (!request->hasParam("data", true)) { diff --git a/src/WebApi_mqtt.cpp b/src/WebApi_mqtt.cpp index 4290b4da..32c7568c 100644 --- a/src/WebApi_mqtt.cpp +++ b/src/WebApi_mqtt.cpp @@ -33,7 +33,7 @@ void WebApiMqttClass::onMqttStatus(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE); - JsonObject root = response->getRoot(); + auto& root = response->getRoot(); const CONFIG_T& config = Configuration.get(); root["mqtt_enabled"] = config.Mqtt.Enabled; @@ -67,7 +67,7 @@ void WebApiMqttClass::onMqttAdminGet(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE); - JsonObject root = response->getRoot(); + auto& root = response->getRoot(); const CONFIG_T& config = Configuration.get(); root["mqtt_enabled"] = config.Mqtt.Enabled; @@ -105,7 +105,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE); - JsonObject retMsg = response->getRoot(); + auto& retMsg = response->getRoot(); retMsg["type"] = "warning"; if (!request->hasParam("data", true)) { @@ -334,11 +334,8 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request) config.Mqtt.Hass.Retain = root["mqtt_hass_retain"].as(); config.Mqtt.Hass.IndividualPanels = root["mqtt_hass_individualpanels"].as(); strlcpy(config.Mqtt.Hass.Topic, root["mqtt_hass_topic"].as().c_str(), sizeof(config.Mqtt.Hass.Topic)); - Configuration.write(); - retMsg["type"] = "success"; - retMsg["message"] = "Settings saved!"; - retMsg["code"] = WebApiError::GenericSuccess; + WebApi.writeConfig(retMsg); response->setLength(); request->send(response); diff --git a/src/WebApi_network.cpp b/src/WebApi_network.cpp index a6e91f3d..b95beb34 100644 --- a/src/WebApi_network.cpp +++ b/src/WebApi_network.cpp @@ -32,7 +32,7 @@ void WebApiNetworkClass::onNetworkStatus(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject root = response->getRoot(); + auto& root = response->getRoot(); root["sta_status"] = ((WiFi.getMode() & WIFI_STA) != 0); root["sta_ssid"] = WiFi.SSID(); @@ -63,7 +63,7 @@ void WebApiNetworkClass::onNetworkAdminGet(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject root = response->getRoot(); + auto& root = response->getRoot(); const CONFIG_T& config = Configuration.get(); root["hostname"] = config.WiFi.Hostname; @@ -89,7 +89,7 @@ void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject retMsg = response->getRoot(); + auto& retMsg = response->getRoot(); retMsg["type"] = "warning"; if (!request->hasParam("data", true)) { @@ -238,11 +238,8 @@ void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request) } config.WiFi.ApTimeout = root["aptimeout"].as(); config.Mdns.Enabled = root["mdnsenabled"].as(); - Configuration.write(); - retMsg["type"] = "success"; - retMsg["message"] = "Settings saved!"; - retMsg["code"] = WebApiError::GenericSuccess; + WebApi.writeConfig(retMsg); response->setLength(); request->send(response); diff --git a/src/WebApi_ntp.cpp b/src/WebApi_ntp.cpp index ed841ba9..2eb4c087 100644 --- a/src/WebApi_ntp.cpp +++ b/src/WebApi_ntp.cpp @@ -35,7 +35,7 @@ void WebApiNtpClass::onNtpStatus(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject root = response->getRoot(); + auto& root = response->getRoot(); const CONFIG_T& config = Configuration.get(); root["ntp_server"] = config.Ntp.Server; @@ -80,7 +80,7 @@ void WebApiNtpClass::onNtpAdminGet(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject root = response->getRoot(); + auto& root = response->getRoot(); const CONFIG_T& config = Configuration.get(); root["ntp_server"] = config.Ntp.Server; @@ -101,7 +101,7 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject retMsg = response->getRoot(); + auto& retMsg = response->getRoot(); retMsg["type"] = "warning"; if (!request->hasParam("data", true)) { @@ -179,11 +179,8 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request) config.Ntp.Latitude = root["latitude"].as(); config.Ntp.Longitude = root["longitude"].as(); config.Ntp.SunsetType = root["sunsettype"].as(); - Configuration.write(); - retMsg["type"] = "success"; - retMsg["message"] = "Settings saved!"; - retMsg["code"] = WebApiError::GenericSuccess; + WebApi.writeConfig(retMsg); response->setLength(); request->send(response); @@ -201,7 +198,7 @@ void WebApiNtpClass::onNtpTimeGet(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject root = response->getRoot(); + auto& root = response->getRoot(); struct tm timeinfo; if (!getLocalTime(&timeinfo, 5)) { @@ -228,7 +225,7 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject retMsg = response->getRoot(); + auto& retMsg = response->getRoot(); retMsg["type"] = "warning"; if (!request->hasParam("data", true)) { diff --git a/src/WebApi_power.cpp b/src/WebApi_power.cpp index 3fa47984..697c1e87 100644 --- a/src/WebApi_power.cpp +++ b/src/WebApi_power.cpp @@ -29,7 +29,7 @@ void WebApiPowerClass::onPowerStatus(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject root = response->getRoot(); + auto& root = response->getRoot(); for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) { auto inv = Hoymiles.getInverterByPos(i); @@ -57,7 +57,7 @@ void WebApiPowerClass::onPowerPost(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject retMsg = response->getRoot(); + auto& retMsg = response->getRoot(); retMsg["type"] = "warning"; if (!request->hasParam("data", true)) { diff --git a/src/WebApi_security.cpp b/src/WebApi_security.cpp index 274d0eb2..8e6815d8 100644 --- a/src/WebApi_security.cpp +++ b/src/WebApi_security.cpp @@ -31,7 +31,7 @@ void WebApiSecurityClass::onSecurityGet(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject root = response->getRoot(); + auto& root = response->getRoot(); const CONFIG_T& config = Configuration.get(); root["password"] = config.Security.Password; @@ -48,7 +48,7 @@ void WebApiSecurityClass::onSecurityPost(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject retMsg = response->getRoot(); + auto& retMsg = response->getRoot(); retMsg["type"] = "warning"; if (!request->hasParam("data", true)) { @@ -101,11 +101,8 @@ void WebApiSecurityClass::onSecurityPost(AsyncWebServerRequest* request) CONFIG_T& config = Configuration.get(); strlcpy(config.Security.Password, root["password"].as().c_str(), sizeof(config.Security.Password)); config.Security.AllowReadonly = root["allow_readonly"].as(); - Configuration.write(); - retMsg["type"] = "success"; - retMsg["message"] = "Settings saved!"; - retMsg["code"] = WebApiError::GenericSuccess; + WebApi.writeConfig(retMsg); response->setLength(); request->send(response); @@ -118,7 +115,7 @@ void WebApiSecurityClass::onAuthenticateGet(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject retMsg = response->getRoot(); + auto& retMsg = response->getRoot(); retMsg["type"] = "success"; retMsg["message"] = "Authentication successful!"; retMsg["code"] = WebApiError::SecurityAuthSuccess; diff --git a/src/WebApi_sysstatus.cpp b/src/WebApi_sysstatus.cpp index 35904bf8..8cda1bd1 100644 --- a/src/WebApi_sysstatus.cpp +++ b/src/WebApi_sysstatus.cpp @@ -36,7 +36,7 @@ void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request) } AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonObject root = response->getRoot(); + auto& root = response->getRoot(); root["hostname"] = NetworkSettings.getHostname(); diff --git a/src/WebApi_ws_live.cpp b/src/WebApi_ws_live.cpp index 9b42b0ee..a64db3a5 100644 --- a/src/WebApi_ws_live.cpp +++ b/src/WebApi_ws_live.cpp @@ -6,6 +6,7 @@ #include "Configuration.h" #include "Datastore.h" #include "MessageOutput.h" +#include "Utils.h" #include "WebApi.h" #include "defaults.h" #include @@ -64,11 +65,11 @@ void WebApiWsLiveClass::loop() try { std::lock_guard lock(_mutex); DynamicJsonDocument root(4096 * INV_MAX_COUNT); - JsonVariant var = root; - generateJsonResponse(var); + if (Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) { + JsonVariant var = root; + generateJsonResponse(var); - String buffer; - if (buffer) { + String buffer; serializeJson(root, buffer); if (Configuration.get().Security.AllowReadonly) { @@ -221,7 +222,7 @@ void WebApiWsLiveClass::onLivedataStatus(AsyncWebServerRequest* request) try { std::lock_guard lock(_mutex); AsyncJsonResponse* response = new AsyncJsonResponse(false, 4096 * INV_MAX_COUNT); - JsonVariant root = response->getRoot(); + auto& root = response->getRoot(); generateJsonResponse(root); diff --git a/webapp/package.json b/webapp/package.json index 3629830c..c7642008 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -18,9 +18,7 @@ "mitt": "^3.0.1", "sortablejs": "^1.15.1", "spark-md5": "^3.0.2", - "tippy.js": "^6.3.7", - "vue": "^3.4.3", - "vue-google-charts": "^1.1.0", + "vue": "^3.4.5", "vue-i18n": "^9.8.0", "vue-router": "^4.2.5", "vue3-calendar-heatmap": "^2.0.5" @@ -39,7 +37,7 @@ "eslint": "^8.56.0", "eslint-plugin-vue": "^9.19.2", "npm-run-all": "^4.1.5", - "sass": "^1.69.6", + "sass": "^1.69.7", "terser": "^5.26.0", "typescript": "^5.3.3", "vite": "^5.0.10", diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index 164c8fdf..88cdf8ad 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -39,6 +39,7 @@ "1003": "Daten zu groß!", "1004": "Fehler beim interpretieren der Daten!", "1005": "Benötigte Werte fehlen!", + "1006": "Schreiben fehlgeschlagen!", "2001": "Die Seriennummer darf nicht 0 sein!", "2002": "Das Abfraginterval muss größer als 0 sein!", "2003": "Ungültige Sendeleistung angegeben!", diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index 7c6a74f6..a04d9c42 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -39,6 +39,7 @@ "1003": "Data too large!", "1004": "Failed to parse data!", "1005": "Values are missing!", + "1006": "Write failed!", "2001": "Serial cannot be zero!", "2002": "Poll interval must be greater zero!", "2003": "Invalid power level setting!", diff --git a/webapp/src/locales/fr.json b/webapp/src/locales/fr.json index 9c31ad6b..e41436e9 100644 --- a/webapp/src/locales/fr.json +++ b/webapp/src/locales/fr.json @@ -39,6 +39,7 @@ "1003": "Données trop importantes !", "1004": "Échec de l'analyse des données !", "1005": "Certaines valeurs sont manquantes !", + "1006": "Write failed!", "2001": "Le numéro de série ne peut pas être nul !", "2002": "L'intervalle de sondage doit être supérieur à zéro !", "2003": "Réglage du niveau de puissance invalide !", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 73d0712d..3f88e2d8 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -569,13 +569,13 @@ estree-walker "^2.0.2" source-map-js "^1.0.2" -"@vue/compiler-core@3.4.3": - version "3.4.3" - resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.3.tgz#8e8f88273f061cf0a49bf958255f5f0621f12d8b" - integrity sha512-u8jzgFg0EDtSrb/hG53Wwh1bAOQFtc1ZCegBpA/glyvTlgHl+tq13o1zvRfLbegYUw/E4mSTGOiCnAJ9SJ+lsg== +"@vue/compiler-core@3.4.5": + version "3.4.5" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.5.tgz#9565aebaadef8649eb7c8e150a5f4f4e2542667d" + integrity sha512-Daka7P1z2AgKjzuueWXhwzIsKu0NkLB6vGbNVEV2iJ8GJTrzraZo/Sk4GWCMRtd/qVi3zwnk+Owbd/xSZbwHtQ== dependencies: "@babel/parser" "^7.23.6" - "@vue/shared" "3.4.3" + "@vue/shared" "3.4.5" entities "^4.5.0" estree-walker "^2.0.2" source-map-js "^1.0.2" @@ -588,13 +588,13 @@ "@vue/compiler-core" "3.2.47" "@vue/shared" "3.2.47" -"@vue/compiler-dom@3.4.3": - version "3.4.3" - resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.3.tgz#bea8acde9585d5ce92a3f11c062c863fb33e44d7" - integrity sha512-oGF1E9/htI6JWj/lTJgr6UgxNCtNHbM6xKVreBWeZL9QhRGABRVoWGAzxmtBfSOd+w0Zi5BY0Es/tlJrN6WgEg== +"@vue/compiler-dom@3.4.5": + version "3.4.5" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.5.tgz#c53c9d7715b777b1d6d2adcbc491bfd4f9510edd" + integrity sha512-J8YlxknJVd90SXFJ4HwGANSAXsx5I0lK30sO/zvYV7s5gXf7gZR7r/1BmZ2ju7RGH1lnc6bpBc6nL61yW+PsAQ== dependencies: - "@vue/compiler-core" "3.4.3" - "@vue/shared" "3.4.3" + "@vue/compiler-core" "3.4.5" + "@vue/shared" "3.4.5" "@vue/compiler-dom@^3.3.0": version "3.3.2" @@ -604,16 +604,16 @@ "@vue/compiler-core" "3.3.2" "@vue/shared" "3.3.2" -"@vue/compiler-sfc@3.4.3": - version "3.4.3" - resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.3.tgz#a9d35b2deef38576dedd9938851c032fb2ca8617" - integrity sha512-NuJqb5is9I4uzv316VRUDYgIlPZCG8D+ARt5P4t5UDShIHKL25J3TGZAUryY/Aiy0DsY7srJnZL5ryB6DD63Zw== +"@vue/compiler-sfc@3.4.5": + version "3.4.5" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.5.tgz#f93f986dfc5c7f72b9a5e00b48be75d9116cc948" + integrity sha512-jauvkDuSSUbP0ebhfNqljhShA90YEfX/0wZ+w40oZF43IjGyWYjqYaJbvMJwGOd+9+vODW6eSvnk28f0SGV7OQ== dependencies: "@babel/parser" "^7.23.6" - "@vue/compiler-core" "3.4.3" - "@vue/compiler-dom" "3.4.3" - "@vue/compiler-ssr" "3.4.3" - "@vue/shared" "3.4.3" + "@vue/compiler-core" "3.4.5" + "@vue/compiler-dom" "3.4.5" + "@vue/compiler-ssr" "3.4.5" + "@vue/shared" "3.4.5" estree-walker "^2.0.2" magic-string "^0.30.5" postcss "^8.4.32" @@ -643,13 +643,13 @@ "@vue/compiler-dom" "3.2.47" "@vue/shared" "3.2.47" -"@vue/compiler-ssr@3.4.3": - version "3.4.3" - resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.3.tgz#c3f641a15a04893b5bc3278f3dac65bed44dce1d" - integrity sha512-wnYQtMBkeFSxgSSQbYGQeXPhQacQiog2c6AlvMldQH6DB+gSXK/0F6DVXAJfEiuBSgBhUc8dwrrG5JQcqwalsA== +"@vue/compiler-ssr@3.4.5": + version "3.4.5" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.5.tgz#d412a4c9b10d69172a5ce0ec78de98dad441a58d" + integrity sha512-DDdEcDzj2lWTMfUMMtEpLDhURai9LhM0zSZ219jCt7b2Vyl0/jy3keFgCPMitG0V1S1YG4Cmws3lWHWdxHQOpg== dependencies: - "@vue/compiler-dom" "3.4.3" - "@vue/shared" "3.4.3" + "@vue/compiler-dom" "3.4.5" + "@vue/shared" "3.4.5" "@vue/devtools-api@^6.5.0": version "6.5.1" @@ -691,37 +691,37 @@ estree-walker "^2.0.2" magic-string "^0.25.7" -"@vue/reactivity@3.4.3": - version "3.4.3" - resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.3.tgz#95287b5950b328df4a942a7cf14a0e13487f1eac" - integrity sha512-q5f9HLDU+5aBKizXHAx0w4whkIANs1Muiq9R5YXm0HtorSlflqv9u/ohaMxuuhHWCji4xqpQ1eL04WvmAmGnFg== +"@vue/reactivity@3.4.5": + version "3.4.5" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.5.tgz#68bc91cd356eed95dc5e9e0570e3f7becaee578b" + integrity sha512-BcWkKvjdvqJwb7BhhFkXPLDCecX4d4a6GATvCduJQDLv21PkPowAE5GKuIE5p6RC07/Lp9FMkkq4AYCTVF5KlQ== dependencies: - "@vue/shared" "3.4.3" + "@vue/shared" "3.4.5" -"@vue/runtime-core@3.4.3": - version "3.4.3" - resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.3.tgz#fe7649a93d9b20b9b351cd699f69f0e34a26e3ab" - integrity sha512-C1r6QhB1qY7D591RCSFhMULyzL9CuyrGc+3PpB0h7dU4Qqw6GNyo4BNFjHZVvsWncrUlKX3DIKg0Y7rNNr06NQ== +"@vue/runtime-core@3.4.5": + version "3.4.5" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.5.tgz#2bf253a6f6b0430af1aacf0fdfd8f5782feefce9" + integrity sha512-wh9ELIOQKeWT9SaUPdLrsxRkZv14jp+SJm9aiQGWio+/MWNM3Lib0wE6CoKEqQ9+SCYyGjDBhTOTtO47kCgbkg== dependencies: - "@vue/reactivity" "3.4.3" - "@vue/shared" "3.4.3" + "@vue/reactivity" "3.4.5" + "@vue/shared" "3.4.5" -"@vue/runtime-dom@3.4.3": - version "3.4.3" - resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.3.tgz#54a6115cfba364f20cdf5a44c2ff87337a57def8" - integrity sha512-wrsprg7An5Ec+EhPngWdPuzkp0BEUxAKaQtN9dPU/iZctPyD9aaXmVtehPJerdQxQale6gEnhpnfywNw3zOv2A== +"@vue/runtime-dom@3.4.5": + version "3.4.5" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.5.tgz#b43736d66c32f6038778024587592cb9d68495de" + integrity sha512-n5ewvOjyG3IEpqGBahdPXODFSpVlSz3H4LF76Sx0XAqpIOqyJ5bIb2PrdYuH2ogBMAQPh+o5tnoH4nJpBr8U0Q== dependencies: - "@vue/runtime-core" "3.4.3" - "@vue/shared" "3.4.3" + "@vue/runtime-core" "3.4.5" + "@vue/shared" "3.4.5" csstype "^3.1.3" -"@vue/server-renderer@3.4.3": - version "3.4.3" - resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.3.tgz#c508f58b9f83f0959085d5aa6854eac9141b4bc6" - integrity sha512-BUxt8oVGMKKsqSkM1uU3d3Houyfy4WAc2SpSQRebNd+XJGATVkW/rO129jkyL+kpB/2VRKzE63zwf5RtJ3XuZw== +"@vue/server-renderer@3.4.5": + version "3.4.5" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.5.tgz#4bfa7aa763217d8b2d4767d2c8d968a9d40352c1" + integrity sha512-jOFc/VE87yvifQpNju12VcqimH8pBLxdcT+t3xMeiED1K6DfH9SORyhFEoZlW5TG2Vwfn3Ul5KE+1aC99xnSBg== dependencies: - "@vue/compiler-ssr" "3.4.3" - "@vue/shared" "3.4.3" + "@vue/compiler-ssr" "3.4.5" + "@vue/shared" "3.4.5" "@vue/shared@3.2.47": version "3.2.47" @@ -733,10 +733,10 @@ resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.2.tgz#774cd9b4635ce801b70a3fc3713779a5ef5d77c3" integrity sha512-0rFu3h8JbclbnvvKrs7Fe5FNGV9/5X2rPD7KmOzhLSUAiQH5//Hq437Gv0fR5Mev3u/nbtvmLl8XgwCU20/ZfQ== -"@vue/shared@3.4.3": - version "3.4.3" - resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.3.tgz#01d54b32b9796c85c853c670d9395a813f23a8c2" - integrity sha512-rIwlkkP1n4uKrRzivAKPZIEkHiuwY5mmhMJ2nZKCBLz8lTUlE73rQh4n1OnnMurXt1vcUNyH4ZPfdh8QweTjpQ== +"@vue/shared@3.4.5": + version "3.4.5" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.5.tgz#c8b4eb6399a7fc986565ea736d938b3a1579256d" + integrity sha512-6XptuzlMvN4l4cDnDw36pdGEV+9njYkQ1ZE0Q6iZLwrKefKaOJyiFmcP3/KBDHbt72cJZGtllAc1GaHe6XGAyg== "@vue/tsconfig@^0.5.1": version "0.5.1" @@ -2226,10 +2226,10 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" -sass@^1.69.6: - version "1.69.6" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.69.6.tgz#88ae1f93facc46d2da9b0bdd652d65068bcfa397" - integrity sha512-qbRr3k9JGHWXCvZU77SD2OTwUlC+gNT+61JOLcmLm+XqH4h/5D+p4IIsxvpkB89S9AwJOyb5+rWNpIucaFxSFQ== +sass@^1.69.7: + version "1.69.7" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.69.7.tgz#6e7e1c8f51e8162faec3e9619babc7da780af3b7" + integrity sha512-rzj2soDeZ8wtE2egyLXgOOHQvaC2iosZrkF6v3EUG+tBwEvhqUCzm0VP3k9gHF9LXbSrRhT5SksoI56Iw8NPnQ== dependencies: chokidar ">=3.0.0 <4.0.0" immutable "^4.0.0" @@ -2666,16 +2666,16 @@ vue3-calendar-heatmap@^2.0.5: resolved "https://registry.yarnpkg.com/vue3-calendar-heatmap/-/vue3-calendar-heatmap-2.0.5.tgz#775a8d4e9d7bc62f242d7d6f28a899a3fae2bb82" integrity sha512-qvveNQlTS5Aw7AvRLs0zOyu3uP5iGJlXJAnkrkG2ElDdyQ8H1TJhQ8rL702CROjAg16ezIveUY10nCO7lqZ25w== -vue@^3.4.3: - version "3.4.3" - resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.3.tgz#e1ba36a64134dcedc12cfb2c28e7cd15ba121f04" - integrity sha512-GjN+culMAGv/mUbkIv8zMKItno8npcj5gWlXkSxf1SPTQf8eJ4A+YfHIvQFyL1IfuJcMl3soA7SmN1fRxbf/wA== +vue@^3.4.5: + version "3.4.5" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.5.tgz#c08b9d903a20faaf4df7270bf2fa7487741b2294" + integrity sha512-VH6nHFhLPjgu2oh5vEBXoNZxsGHuZNr3qf4PHClwJWw6IDqw6B3x+4J+ABdoZ0aJuT8Zi0zf3GpGlLQCrGWHrw== dependencies: - "@vue/compiler-dom" "3.4.3" - "@vue/compiler-sfc" "3.4.3" - "@vue/runtime-dom" "3.4.3" - "@vue/server-renderer" "3.4.3" - "@vue/shared" "3.4.3" + "@vue/compiler-dom" "3.4.5" + "@vue/compiler-sfc" "3.4.5" + "@vue/runtime-dom" "3.4.5" + "@vue/server-renderer" "3.4.5" + "@vue/shared" "3.4.5" webpack-sources@^3.2.3: version "3.2.3"