diff --git a/Makefile b/Makefile index 2e9212b..40669f2 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CXX=g++ STRIP=strip -CXXFLAGS=-Wall -I src/libs -I src -std=c++20 +CXXFLAGS=-Wall -I src/libs -I src -std=c++20 -lpthread SOURCES=src/openrouter.cpp \ src/security/cors.cpp \ src/settings/settings.cpp \ diff --git a/meson.build b/meson.build index ed9827d..c76f194 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,8 @@ -project('openrouter', 'cpp', default_options: ['cpp_std=c++20', 'cpp_args=-Wall']) +project( + 'openrouter', + 'cpp', + default_options: ['cpp_std=c++20', 'cpp_args=-Wall -lpthread'], +) include_dirs = include_directories('src', 'src/libs') executable( 'openrouter', diff --git a/src/settings/backup.cpp b/src/settings/backup.cpp index 5232aa5..4e28561 100644 --- a/src/settings/backup.cpp +++ b/src/settings/backup.cpp @@ -20,8 +20,6 @@ namespace api void restore(const httplib::Request& request, httplib::Response& response) { - std::size_t size = request.files.size(); - bool ret = request.has_file("restore_settings"); const httplib::MultipartFormData& file = request.get_file_value("restore_settings"); if (file.content_type == JSON_TYPE) { nlohmann::json new_config = nlohmann::json::parse(file.content); diff --git a/src/system/update/update.cpp b/src/system/update/update.cpp index 586ae25..ee6856e 100644 --- a/src/system/update/update.cpp +++ b/src/system/update/update.cpp @@ -3,6 +3,13 @@ #include #include #include +#include +#include +#include + +#define DEFAULT_TMP_FILE_NAME "/tmp/image" +#define DEFAULT_DST_FILE_NAME "/images/image" +#define BLOCK_SIZE 131072 namespace api { @@ -11,6 +18,27 @@ namespace api namespace update { static int update_progress = 0; + static bool compare = false; + + bool compareFile(std::string tmpfilename, std::string dstfilename) { + FILE *tmp = fopen(tmpfilename.c_str(), "r"); + FILE *dst = fopen(dstfilename.c_str(), "r"); + char tmpbuffer[BLOCK_SIZE]; + char dstbuffer[BLOCK_SIZE]; + + do { + size_t tmpread = fread(tmpbuffer, 1, BLOCK_SIZE, tmp); + size_t dstread = fread(dstbuffer, 1, BLOCK_SIZE, dst); + if (tmpread != dstread || memcmp(tmpbuffer, dstbuffer, tmpread)) { + fclose(tmp); + fclose(dst); + return false; + } + } while (!feof(tmp) || !feof(dst)); + fclose(tmp); + fclose(dst); + return true; + } void upload(const httplib::Request& request, httplib::Response& response) { @@ -30,7 +58,7 @@ namespace api return; } const httplib::MultipartFormData& file = request.get_file_value("image"); - std::ofstream tmp_image_file("/tmp/image"); + std::ofstream tmp_image_file(DEFAULT_TMP_FILE_NAME); tmp_image_file << file.content; tmp_image_file.close(); response.set_content(R"({"status":true})", JSON_TYPE); @@ -39,6 +67,68 @@ namespace api void update(const httplib::Request& request, httplib::Response& response) { std::cout << GREEN << request.path << RESET << " " << request.method << std::endl; + response.set_header("Access-Control-Allow-Origin", "*"); + if (request.body.empty()) + { + response.set_content(ACCESS_REQUIRED_STRING, JSON_TYPE); + return; + } + nlohmann::json json_body = nlohmann::json::parse(request.body); + if (json_body["token"] == nullptr) + { + response.set_content(ACCESS_REQUIRED_STRING, JSON_TYPE); + return; + } + if (!authenticate(json_body["token"])) + { + response.set_content(R"({"access":"reject"})", JSON_TYPE); + return; + } + + bool needAutoReboot = json_body["needAutoReboot"]; + update_progress = 0; + compare = false; + int file_image_size = std::filesystem::file_size(DEFAULT_TMP_FILE_NAME); + int block_count = file_image_size / BLOCK_SIZE; + int remainder = file_image_size % BLOCK_SIZE; + + int skip = 0; + + char buffer[BLOCK_SIZE]; + std::ifstream tmp(DEFAULT_TMP_FILE_NAME); + std::ofstream dst(DEFAULT_DST_FILE_NAME); + + while (skip < block_count) { + tmp.read(buffer, BLOCK_SIZE); + dst.write(buffer, BLOCK_SIZE); + update_progress = 100 * skip / block_count; + skip++; + std::cout << update_progress << "% was copied" << std::endl; + memset(buffer, 0, BLOCK_SIZE); + } + if (remainder > 0) { + tmp.read(buffer, remainder); + dst.write(buffer, remainder); + update_progress = 100; + std::cout << update_progress << "% was copied" << std::endl; + } + update_progress = 100; + std::cout << update_progress << "% was copied" << std::endl; + tmp.close(); + dst.close(); + compare = compareFile(DEFAULT_TMP_FILE_NAME, DEFAULT_DST_FILE_NAME); + if (!compare) { + std::cout << "files not equvalents: writing file error." << std::endl; + response.set_content(R"({"success":false})", JSON_TYPE); + return; + } + std::remove(DEFAULT_TMP_FILE_NAME); + if (needAutoReboot) { + std::cout << "reboot" << std::endl; + std::system("sleep 2; reboot;"); + return; + } + response.set_content(R"({"success":true})", JSON_TYPE); } void progress(const httplib::Request& request, httplib::Response& response) diff --git a/src/web/js/update.js b/src/web/js/update.js index 51c8eaa..2c0b7a5 100644 --- a/src/web/js/update.js +++ b/src/web/js/update.js @@ -12,6 +12,11 @@ const upload_percents = document.querySelector("#uploadpercent"); const update_image_button = document.querySelector("#update_image_button"); const updateprogress = document.querySelector("#updateprogress"); +const updatebar = document.querySelector("#updatebar"); +const updatepercent = document.querySelector("#updatepercent"); + +let progress = 0; +let progress_interval; checker.addEventListener("click", () => { auto_reboot_flag.checked = !auto_reboot_flag.checked; @@ -31,7 +36,28 @@ upload_image_button.addEventListener("click", () => { }); update_image_button.addEventListener("click", () => { + updatebar.style.width = "0%"; + updatepercent.innerHTML = "Updated: 0 %"; updateprogress.classList.remove("hidden"); + start_progress(); + fetch(routes.update_image(), { + method: "Post", + body: `{"token":"${localStorage.getItem("token")}", "needAutoReboot": ${auto_reboot_flag.checked}}`, + }) + .then((data) => data.json()) + .then((jsondata) => { + if (jsondata.success === true) { + stop_progress(); + notification("Updated successful", "success"); + updatebar.style.width = "100%"; + updatepercent.innerHTML = "Updated: 100 %"; + } else { + stop_progress(); + updatebar.style.width = "100%"; + updatepercent.innerHTML = "Updated: 100 %"; + notification("Unexpected error", "error"); + } + }); }); const upload_firmware_image = async () => { @@ -41,7 +67,7 @@ const upload_firmware_image = async () => { let image = upload_image_input.files[0]; let formData = new FormData(); formData.append("image", image); - formData.append("token", localStorage.getItem('token')) + formData.append("token", localStorage.getItem("token")); var request = new XMLHttpRequest(); request.upload.addEventListener("progress", (e) => { var file1Size = image.size; @@ -58,20 +84,17 @@ const upload_firmware_image = async () => { }); request.onreadystatechange = () => { if (request.readyState === 4) { - console.log(request.responseText) - const jsondata = JSON.parse(request.responseText) + console.log(request.responseText); + const jsondata = JSON.parse(request.responseText); if (jsondata.status === true) { - update_image_button.classList.remove('hidden') + update_image_button.classList.remove("hidden"); update_block.classList.remove("hidden"); } else { - notification('Upload error', 'error') + notification("Upload error", "error"); } } }; - request.open( - "post", - routes.upload_image(), - ); + request.open("post", routes.upload_image()); request.timeout = 450000; request.send(formData); }; @@ -81,3 +104,26 @@ upload_image_input.addEventListener("change", (event) => { upload_firmware_image(); } }); + +const start_progress = () => { + updatebar.style.width = "0%"; + progress = 0; + updatepercent.innerHTML = "Updated: 0 %"; + updateprogress.classList.remove("hidden"); + progress_interval = setInterval(() => { + fetch(routes.update_progress()) + .then((data) => data.json()) + .then((jsondata) => { + progress = jsondata.progress; + updatebar.style.width = `${progress}%`; + updatepercent.innerHTML = `Updated: ${progress} %`; + if (progress === 100) { + stop_progress(); + } + }); + }, 500); +}; + +const stop_progress = () => { + clearInterval(progress_interval); +};