From 65b29b105fddabf1b1a97d3e3ad3ca468b847cc1 Mon Sep 17 00:00:00 2001 From: TolaMironcenko Date: Tue, 4 Mar 2025 19:24:50 +0700 Subject: [PATCH 1/5] added update --- CMakeLists.txt | 2 +- Makefile | 1 + meson.build | 2 ++ src/system/update/update.cpp | 20 ++++++++++++++++++++ src/system/update/update.hpp | 13 +++++++++++++ src/web/css/update.css | 6 ++++++ src/web/index.html | 17 +++++++++++++++++ 7 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/system/update/update.cpp create mode 100644 src/system/update/update.hpp create mode 100644 src/web/css/update.css diff --git a/CMakeLists.txt b/CMakeLists.txt index 5602614..8a811f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,9 +34,9 @@ add_executable( src/settings/ftp/ftp.cpp src/system/reboot/reboot.cpp - src/system/syslog/syslog.cpp src/system/uptime/uptime.cpp + src/system/update/update.cpp src/OpenRouter/OpenRouter.cpp diff --git a/Makefile b/Makefile index 7ef417e..2e9212b 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,7 @@ SOURCES=src/openrouter.cpp \ src/system/reboot/reboot.cpp \ src/system/syslog/syslog.cpp \ src/system/uptime/uptime.cpp \ + src/system/update/update.cpp \ src/OpenRouter/OpenRouter.cpp \ src/auth/auth.cpp \ src/libs/QR-Code-generator/qrcodegen.cpp \ diff --git a/meson.build b/meson.build index f8de5af..ed9827d 100644 --- a/meson.build +++ b/meson.build @@ -31,6 +31,8 @@ executable( 'src/system/reboot/reboot.cpp', 'src/system/syslog/syslog.cpp', 'src/system/uptime/uptime.cpp', + 'src/system/update/update.cpp', + 'src/OpenRouter/OpenRouter.cpp', 'src/auth/auth.cpp', 'src/libs/QR-Code-generator/qrcodegen.cpp', diff --git a/src/system/update/update.cpp b/src/system/update/update.cpp new file mode 100644 index 0000000..4544e37 --- /dev/null +++ b/src/system/update/update.cpp @@ -0,0 +1,20 @@ +#include +#include + +namespace api +{ + namespace system + { + void upload(const httplib::Request& request, httplib::Response& response) { + std::size_t size = request.files.size(); + std::cout << "file size::" << size << std::endl; + bool ret = request.has_file("image"); + std::cout << "file ret::" << ret << std::endl; + const httplib::MultipartFormData& file = request.get_file_value("restore_settings"); + std::ofstream tmp_image_file("/tmp/image"); + tmp_image_file << file.content; + tmp_image_file.close(); + response.set_content(R"({"status":true})", JSON_TYPE); + } + } +} diff --git a/src/system/update/update.hpp b/src/system/update/update.hpp new file mode 100644 index 0000000..c759e8d --- /dev/null +++ b/src/system/update/update.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace api +{ + namespace system + { + void upload(const httplib::Request& request, httplib::Response& response); + // void update(const httplib::Request& request, httplib::Response& response); + // void progress(const httplib::Request& request, httplib::Response& response); + } +} diff --git a/src/web/css/update.css b/src/web/css/update.css new file mode 100644 index 0000000..e4919da --- /dev/null +++ b/src/web/css/update.css @@ -0,0 +1,6 @@ +.uploadprogress { + width: 100%; + height: 20px; + display: flex; + align-items: center; +} diff --git a/src/web/index.html b/src/web/index.html index f9a29b6..567cffa 100644 --- a/src/web/index.html +++ b/src/web/index.html @@ -13,6 +13,7 @@ + Openrouter Admin @@ -208,6 +209,22 @@ + +
+
+

Update Firmware

+
+
+ + +
+
+
Uploaded 50 %
+
+ +
+
+
From dcc4f5955242a2a707a794818db95bfd9c5719a5 Mon Sep 17 00:00:00 2001 From: TolaMironcenko Date: Tue, 4 Mar 2025 17:27:10 +0700 Subject: [PATCH 2/5] upload testing --- src/OpenRouter/OpenRouter.cpp | 1 + src/system/system.hpp | 1 + src/web/css/index.css | 1 + src/web/css/update.css | 113 ++++- src/web/index.html | 832 ++++++++++++++++++++++------------ src/web/js/login.js | 129 +++--- src/web/js/routes.js | 37 +- src/web/js/update.js | 88 ++++ 8 files changed, 845 insertions(+), 357 deletions(-) create mode 100644 src/web/js/update.js diff --git a/src/OpenRouter/OpenRouter.cpp b/src/OpenRouter/OpenRouter.cpp index ab33511..b73dc69 100644 --- a/src/OpenRouter/OpenRouter.cpp +++ b/src/OpenRouter/OpenRouter.cpp @@ -23,6 +23,7 @@ OpenRouter::OpenRouter() #ifdef __linux__ this->srv.Get("/api/system/uptime", api::system::get_uptime); #endif // __linux__ + this->srv.Post("/api/system/upload", api::system::upload); this->srv.Options("/(.*)", api::security::set_cors_headers); diff --git a/src/system/system.hpp b/src/system/system.hpp index 9350088..6cd36cd 100644 --- a/src/system/system.hpp +++ b/src/system/system.hpp @@ -5,3 +5,4 @@ #endif // __linux__ #include #include +#include diff --git a/src/web/css/index.css b/src/web/css/index.css index dd7f3a3..acac930 100644 --- a/src/web/css/index.css +++ b/src/web/css/index.css @@ -6,6 +6,7 @@ margin: 0; padding: 0; box-sizing: border-box; + transition: 0.3s; } body { diff --git a/src/web/css/update.css b/src/web/css/update.css index e4919da..f0edacf 100644 --- a/src/web/css/update.css +++ b/src/web/css/update.css @@ -1,6 +1,117 @@ -.uploadprogress { +.progressbar { + position: relative; width: 100%; height: 20px; display: flex; align-items: center; + background-color: black; + border-radius: 7px; +} + +.progressbar.hidden { + height: 0; + margin: 0; + padding: 0; + overflow: hidden; +} + +.bar { + position: absolute; + width: 50%; + height: 20px; + background-color: rgb(100, 100, 100); + border-radius: 7px; +} + +.hidden { + height: 0; + margin: 0; + padding: 0; + overflow: hidden; +} + +.percent { + position: absolute; + width: 100%; + text-align: center; +} + +.settingrectfooterbutton.hidden { + width: 0; + margin: 0; + padding: 0; + overflow: hidden; +} + +.hstack { + display: flex; + gap: 10px; + align-items: center; +} + +.hstack.hidden { + height: 0; + margin: 0; + padding: 0; + overflow: hidden; +} + +.vstack { + display: flex; + flex-direction: column; + padding: 10px; +} + +.vstack.hidden { + height: 0; + margin: 0; + padding: 0; + overflow: hidden; +} + +.checker { + width: 45px; + height: 15px; + background-color: #8d9fa4; + border-radius: 20px; + display: flex; + align-items: center; + margin: 5px; + position: relative; + cursor: pointer; +} + +.checker.active { + background-color: #93bc9a; +} + +.checker.hidden { + height: 0; + padding: 0; + margin: 0; + overflow: hidden; +} + +.checked { + width: 25px; + height: 25px; + background: white; + /* border: 1px solid; */ + border-radius: 25px; + box-shadow: -1px -1px 5px black; + cursor: pointer; + position: absolute; +} + +.checked.active { + background-color: #5fac5a; + box-shadow: -1px -1px 5px #5fac5a; + transform: translateX(25px); +} + +.checked.hidden { + height: 0; + padding: 0; + margin: 0; + overflow: hidden; } diff --git a/src/web/index.html b/src/web/index.html index 567cffa..4220273 100644 --- a/src/web/index.html +++ b/src/web/index.html @@ -1,304 +1,566 @@ - + + + + + + + + + + + + + + + Openrouter Admin + - - - - - - - - - - - - - - Openrouter Admin - - - -
- -
- - -

iconOpenrouter Admin

-
-

- -
-
- - -
-
- -
-
-

Telnet

-
- -
-
-
-
- - -
-
-
- - -
+ +
+ +
+ + - - -
-
-

DNS

-
- -
-
-
- - - - - - - - -
DNS servers
-

Domains Table

- - - - - - - - - - -
IPDomain
-
-
- - - -
+

+ iconOpenrouter Admin +

+
+

+
- - -
-
-

Ftp

-
- -
-
-
- - -
-
- - -
-
-

SSH

-
- -
-
-
-
- - - -
-
- - -
- - - - - - - - -
SSH keys
-
-
- - -
-
- - -
-
-

WIFI

-
- -
-
-
-
- - -
-
- - - -
-
- - - -
-
- - -
-
- +
+ + +
+
+ +
+
+

Telnet

- +
-
+
+
+
+ + +
+
+
+ +
-
- -
-
- - -
-
-

Backup settings

-
-
- - - - - - - -
-
- - -
-
-

Update Firmware

-
-
- - -
-
-
Uploaded 50 %
+ + +
+
+

DNS

+
+ +
+
+
+ + + + + + + +
DNS servers
+

Domains Table

+ + + + + + + + + +
IPDomain
+
+
+ + +
-
+ + +
+
+

Ftp

+
+ +
+
+
+ + +
+
+ + +
+
+

SSH

+
+ +
+
+
+
+ + + +
+
+ + +
+ + + + + + + +
SSH keys
+
+
+ + +
+
+ + +
+
+

WIFI

+
+ +
+
+
+
+ + +
+
+ + + +
+
+ + + +
+
+ + +
+
+ +
+ +
+
+
+
+
+ +
+
+ + +
+
+

Backup settings

+
+
+ + + + + + + +
+
+ + +
+
+

Update Firmware

+
+
+ + + + + +
+
+ +
+ +
+
+

Syslog

+ + + + + +
+
+
+ +
+
+ +

0.1.1

+
+
+ +

+
+
+
+ +
+ + +
+ + +
+
+

OpenRouter Login

+
+ +
- -
- -
-
-

Syslog

- - - - - +
+ +
-
-
- -
-
- -

0.1.1

+
+
-
- -

-
-
+ - -
- - -
- - -
-
-

OpenRouter Login

-
- - + + +
+

Reboting

+
+
-
- - -
-
- -
- -
- - -
-

Reboting

-
-
-
- - - - - + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/src/web/js/login.js b/src/web/js/login.js index ee0b795..49928b3 100644 --- a/src/web/js/login.js +++ b/src/web/js/login.js @@ -1,79 +1,96 @@ -const loginform = document.querySelector('.loginform') -const loginformbody = document.querySelector('.loginformbody') -const loginusername = document.querySelector("#loginusernameinput") -const loginpassword = document.querySelector("#loginpasswordinput") -const exitbutton = document.querySelector('.exitbutton') -const adminpanelsection = document.querySelector('.adminpanel') -const headerusername = document.querySelector('.headerusername') +const loginform = document.querySelector(".loginform"); +const loginformbody = document.querySelector(".loginformbody"); +const loginusername = document.querySelector("#loginusernameinput"); +const loginpassword = document.querySelector("#loginpasswordinput"); +const exitbutton = document.querySelector(".exitbutton"); +const adminpanelsection = document.querySelector(".adminpanel"); +const headerusername = document.querySelector(".headerusername"); const hashPassword = async (password) => { - const encoder = new TextEncoder() - const data = encoder.encode(password) - const hashBuffer = await crypto.subtle.digest('SHA-256', data) - const hashArray = Array.from(new Uint8Array(hashBuffer)) - const hashHex = hashArray.map(b=>('00' + b.toString(16)).slice(-2)).join('') - return hashHex -} + const encoder = new TextEncoder(); + const data = encoder.encode(password); + const hashBuffer = await crypto.subtle.digest("SHA-256", data); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const hashHex = hashArray + .map((b) => ("00" + b.toString(16)).slice(-2)) + .join(""); + return hashHex; +}; if (localStorage.getItem("token") === null) { - adminpanelsection.classList.remove('active') - loginform.classList.add('active') + adminpanelsection.classList.remove("active"); + loginform.classList.add("active"); } else { - adminpanelsection.classList.add('active') - infosection.classList.add('active') - headerusername.innerHTML = JSON.parse(localStorage.getItem('userdata')).username + adminpanelsection.classList.add("active"); + infosection.classList.add("active"); + headerusername.innerHTML = JSON.parse( + localStorage.getItem("userdata"), + ).username; } -loginformbody.addEventListener('submit', (e) => { - e.preventDefault() - hashPassword(loginpassword.value).then(hashedpassword => { - login(loginusername.value, hashedpassword) - }) -}) +loginformbody.addEventListener("submit", (e) => { + e.preventDefault(); + hashPassword(loginpassword.value).then((hashedpassword) => { + login(loginusername.value, hashedpassword); + }); +}); const login = async (username, password) => { const response = await fetch(routes.login(), { - method: 'POST', - body: `{"username":"${username}","password":"${password}"}` - }).then(data => data.json()).then(jsondata => jsondata).catch((error) => { - if (error.message === "Unexpected end of JSON input") { - notification(`Ошибка на сервере: Создайте файл users.json и запишите в него первого пользователя`, "error") - } else { - notification(`Ошибка на сервере: ${error}`, "error") - } + method: "POST", + body: `{"username":"${username}","password":"${password}"}`, }) + .then((data) => data.json()) + .then((jsondata) => jsondata) + .catch((error) => { + if (error.message === "Unexpected end of JSON input") { + notification( + `Ошибка на сервере: Создайте файл users.json и запишите в него первого пользователя`, + "error", + ); + } else { + notification(`Ошибка на сервере: ${error}`, "error"); + } + }); if (response.status === "403") { - notification("Неправильный логин или пароль", "error") + notification("Неправильный логин или пароль", "error"); } if (response.token) { - notification("Успешно", "success") - loginform.classList.remove("active") - localStorage.setItem("token", response.token) - await get_all_settings() - adminpanelsection.classList.add('active') - await get_user_data() - loginusername.value = "" - loginpassword.value = "" - settingssection.classList.add('active') + notification("Успешно", "success"); + loginform.classList.remove("active"); + localStorage.setItem("token", response.token); + await get_all_settings(); + adminpanelsection.classList.add("active"); + await get_user_data(); + loginusername.value = ""; + loginpassword.value = ""; + settingssection.classList.add("active"); } -} +}; const get_user_data = async () => { const response = await fetch(routes.user(), { method: "POST", - body: `{"token":"${localStorage.getItem("token")}"}` - }).then(data => data.json()).then(jsondata => jsondata).catch((error) => { - notification(`Ошибка на сервере: ${error}`, "error") + body: `{"token":"${localStorage.getItem("token")}"}`, }) + .then((data) => data.json()) + .then((jsondata) => jsondata) + .catch((error) => { + notification(`Ошибка на сервере: ${error}`, "error"); + }); if (response.id) { - headerusername.innerHTML = response.username - localStorage.setItem("userdata", JSON.stringify(response)) + headerusername.innerHTML = response.username; + localStorage.setItem("userdata", JSON.stringify(response)); } -} +}; -exitbutton.addEventListener('click', () => { - localStorage.clear() - loginform.classList.add('active') - notification('Вы вышли', "success") - adminpanelsection.classList.remove('active') -}) +const logout = () => { + localStorage.clear(); + loginform.classList.add("active"); + notification("Вы вышли", "success"); + adminpanelsection.classList.remove("active"); +}; + +exitbutton.addEventListener("click", () => { + logout(); +}); diff --git a/src/web/js/routes.js b/src/web/js/routes.js index 9cb37f4..e87088d 100644 --- a/src/web/js/routes.js +++ b/src/web/js/routes.js @@ -1,20 +1,27 @@ -const host = window.location.origin -const prefix = 'api' +const host = window.location.origin; +const prefix = "api"; const routes = { - login: () => ["http://localhost:43243", prefix, 'token'].join('/'), - user: () => ["http://localhost:43243", prefix, 'users', 'user'].join('/'), + login: () => ["http://localhost:43243", prefix, "token"].join("/"), + user: () => ["http://localhost:43243", prefix, "users", "user"].join("/"), - wifi_password_set: () => [host, prefix, 'settings', 'wifi', 'password', 'set'].join('/'), - wifi_qr_get: () => [host, prefix, 'settings', 'wifi', 'qr', 'get'].join('/'), + wifi_password_set: () => + [host, prefix, "settings", "wifi", "password", "set"].join("/"), + wifi_qr_get: () => + [host, prefix, "settings", "wifi", "qr", "get"].join("/"), - settings_get: () => [host, prefix, 'settings', 'get'].join('/'), - settings_set: () => [host, prefix, 'settings', 'set'].join('/'), - settings_backup: () => [host, prefix, 'settings', 'backup'].join('/'), - settings_restore: () => [host, prefix, 'settings', 'restore'].join('/'), - restore_defaults: () => [host, prefix, 'settings', 'restore', 'defaults'].join('/'), + settings_get: () => [host, prefix, "settings", "get"].join("/"), + settings_set: () => [host, prefix, "settings", "set"].join("/"), + settings_backup: () => [host, prefix, "settings", "backup"].join("/"), + settings_restore: () => [host, prefix, "settings", "restore"].join("/"), + restore_defaults: () => + [host, prefix, "settings", "restore", "defaults"].join("/"), - reboot: () => [host, prefix, 'system', 'reboot'].join('/'), - syslog: () => [host, prefix, 'system', 'syslog'].join('/'), - uptime: () => [host, prefix, 'system', 'uptime'].join('/'), -} + reboot: () => [host, prefix, "system", "reboot"].join("/"), + syslog: () => [host, prefix, "system", "syslog"].join("/"), + uptime: () => [host, prefix, "system", "uptime"].join("/"), + + upload_image: () => [host, prefix, "system", "upload"].join("/"), + update_image: () => [host, prefix, "system", "update"].join("/"), + update_progress: () => [host, prefix, "system", "progress"].join("/"), +}; diff --git a/src/web/js/update.js b/src/web/js/update.js new file mode 100644 index 0000000..0c17a52 --- /dev/null +++ b/src/web/js/update.js @@ -0,0 +1,88 @@ +const checker = document.querySelector(".checker"); +const checked = document.querySelector(".checked"); +const noyes = document.querySelector("#noyes"); +const auto_reboot_flag = document.querySelector("#auto_reboot_flag"); + +const upload_image_button = document.querySelector("#upload_image_button"); +const upload_image_input = document.querySelector("#upload_image_input"); +const update_block = document.querySelector("#update_block"); +const uploadprogress_block = document.querySelector("#uploadprogress"); +const upload_progress_bar = document.querySelector("#uploadbar"); +const upload_percents = document.querySelector("#uploadpercent"); + +const update_image_button = document.querySelector("#update_image_button"); +const updateprogress = document.querySelector("#updateprogress"); + +checker.addEventListener("click", () => { + auto_reboot_flag.checked = !auto_reboot_flag.checked; + if (auto_reboot_flag.checked) { + checker.classList.add("active"); + checked.classList.add("active"); + noyes.innerHTML = "(Yes)"; + } else { + checked.classList.remove("active"); + checker.classList.remove("active"); + noyes.innerHTML = "(No)"; + } +}); + +upload_image_button.addEventListener("click", () => { + upload_image_input.click(); + update_block.classList.remove("hidden"); + uploadprogress.classList.remove("hidden"); +}); + +update_image_button.addEventListener("click", () => { + updateprogress.classList.remove("hidden"); +}); + +const upload_firmware_image = async () => { + upload_percents.innerHTML = "Upload progress 0 %"; + upload_progress_bar.style.width = "0%"; + uploadprogress_block.classList.remove("hidden"); + let image = update_firmware_input.files[0]; + let formData = new FormData(); + formData.append(image.name, image); + var request = new XMLHttpRequest(); + request.upload.addEventListener("progress", (e) => { + var file1Size = image.size; + if (e.loaded <= file1Size) { + var percent = Math.round((e.loaded / file1Size) * 100); + upload_progress_bar.style.width = `${percent}%`; + upload_percents.innerHTML = `Upload progress ${percent} %`; + } + + if (e.loaded == e.total) { + upload_progress_bar.style.width = "100%"; + upload_percents.innerHTML = "Upload progress 100 %"; + } + }); + request.onreadystatechange = () => { + if (request.readyState === 4) { + let jsondata = JSON.parse(request.responseText); + if (jsondata.success) { + image_table_body.innerHTML = ` + ${jsondata.response.file.name} + ${(jsondata.response.file.size / 1024 / 1024).toFixed(2)}MB + ${jsondata.response.cpu} + ${jsondata.response.deviceName} + ${jsondata.response.version} + `; + image_table.classList.remove("hidden-table"); + start_update_button.classList.remove("hidden-button"); + } + } + }; + request.open( + "post", + window.location.origin + "/wimic/updatefirmware/upload", + ); + request.timeout = 45000; + request.send(formData); +}; + +upload_image_input.addEventListener("change", (event) => { + if (event.target.files[0]) { + upload_firmware_image(); + } +}); From 1601be10602c187a06fdbe7890a065ef833b1cc4 Mon Sep 17 00:00:00 2001 From: TolaMironcenko Date: Tue, 4 Mar 2025 23:11:03 +0700 Subject: [PATCH 3/5] safe upload and get update progress --- src/OpenRouter/OpenRouter.cpp | 5 ++-- src/settings/backup.cpp | 2 -- src/system/update/update.cpp | 52 ++++++++++++++++++++++++++++------- src/system/update/update.hpp | 9 ++++-- src/system/uptime/uptime.cpp | 4 --- src/web/js/routes.js | 6 ++-- src/web/js/update.js | 29 ++++++++----------- 7 files changed, 66 insertions(+), 41 deletions(-) diff --git a/src/OpenRouter/OpenRouter.cpp b/src/OpenRouter/OpenRouter.cpp index b73dc69..85f8d73 100644 --- a/src/OpenRouter/OpenRouter.cpp +++ b/src/OpenRouter/OpenRouter.cpp @@ -18,12 +18,13 @@ OpenRouter::OpenRouter() this->srv.Post("/api/settings/restore/defaults", api::settings::restore_defaults); this->srv.Post("/api/system/reboot", api::system::reboot); - this->srv.Get("/api/system/syslog", api::system::get_syslog); #ifdef __linux__ this->srv.Get("/api/system/uptime", api::system::get_uptime); #endif // __linux__ - this->srv.Post("/api/system/upload", api::system::upload); + this->srv.Post("/api/system/update/upload", api::system::update::upload); + this->srv.Post("/api/system/update/update", api::system::update::update); + this->srv.Get("/api/system/update/progress", api::system::update::progress); this->srv.Options("/(.*)", api::security::set_cors_headers); diff --git a/src/settings/backup.cpp b/src/settings/backup.cpp index f1088b5..5232aa5 100644 --- a/src/settings/backup.cpp +++ b/src/settings/backup.cpp @@ -21,9 +21,7 @@ namespace api void restore(const httplib::Request& request, httplib::Response& response) { std::size_t size = request.files.size(); - std::cout << "file size::" << size << std::endl; bool ret = request.has_file("restore_settings"); - std::cout << "file ret::" << ret << std::endl; 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 4544e37..586ae25 100644 --- a/src/system/update/update.cpp +++ b/src/system/update/update.cpp @@ -1,20 +1,52 @@ #include #include +#include +#include +#include namespace api { namespace system { - void upload(const httplib::Request& request, httplib::Response& response) { - std::size_t size = request.files.size(); - std::cout << "file size::" << size << std::endl; - bool ret = request.has_file("image"); - std::cout << "file ret::" << ret << std::endl; - const httplib::MultipartFormData& file = request.get_file_value("restore_settings"); - std::ofstream tmp_image_file("/tmp/image"); - tmp_image_file << file.content; - tmp_image_file.close(); - response.set_content(R"({"status":true})", JSON_TYPE); + namespace update + { + static int update_progress = 0; + + void upload(const httplib::Request& request, httplib::Response& response) + { + std::cout << GREEN << request.path << RESET << " " << request.method << std::endl; + bool ret = request.has_file("image"); + if (!ret) { + response.set_content(R"({"required":[token, file]})", JSON_TYPE); + } + bool has_token = request.has_file("token"); + if (!has_token) { + response.set_content(ACCESS_REQUIRED_STRING, JSON_TYPE); + return; + } + if (!authenticate(request.get_file_value("token").content)) + { + response.set_content(R"({"access":"reject"})", JSON_TYPE); + return; + } + const httplib::MultipartFormData& file = request.get_file_value("image"); + std::ofstream tmp_image_file("/tmp/image"); + tmp_image_file << file.content; + tmp_image_file.close(); + response.set_content(R"({"status":true})", JSON_TYPE); + } + + void update(const httplib::Request& request, httplib::Response& response) + { + std::cout << GREEN << request.path << RESET << " " << request.method << std::endl; + } + + void progress(const httplib::Request& request, httplib::Response& response) + { + std::cout << GREEN << request.path << RESET << " " << request.method << std::endl; + std::string responsedata = R"({"progress":)" + std::to_string(update_progress) + "}"; + response.set_content(responsedata, JSON_TYPE); + } } } } diff --git a/src/system/update/update.hpp b/src/system/update/update.hpp index c759e8d..2bfd24f 100644 --- a/src/system/update/update.hpp +++ b/src/system/update/update.hpp @@ -6,8 +6,11 @@ namespace api { namespace system { - void upload(const httplib::Request& request, httplib::Response& response); - // void update(const httplib::Request& request, httplib::Response& response); - // void progress(const httplib::Request& request, httplib::Response& response); + namespace update + { + void upload(const httplib::Request& request, httplib::Response& response); + void update(const httplib::Request& request, httplib::Response& response); + void progress(const httplib::Request& request, httplib::Response& response); + } } } diff --git a/src/system/uptime/uptime.cpp b/src/system/uptime/uptime.cpp index e835c83..de7f759 100644 --- a/src/system/uptime/uptime.cpp +++ b/src/system/uptime/uptime.cpp @@ -15,10 +15,6 @@ namespace api struct sysinfo x; if (sysinfo(&x) == 0) { - // std::cout << static_cast(x.uptime)/3600ULL << "h " - // << static_cast(x.uptime)/60ULL%60ULL << "m " - // << static_cast(x.uptime)%60ULL << "s " - // << std::endl; std::stringstream responsedata; responsedata << R"({"uptime":{"hour":")" << static_cast(x.uptime) / 3600ULL diff --git a/src/web/js/routes.js b/src/web/js/routes.js index e87088d..c74d19d 100644 --- a/src/web/js/routes.js +++ b/src/web/js/routes.js @@ -21,7 +21,7 @@ const routes = { syslog: () => [host, prefix, "system", "syslog"].join("/"), uptime: () => [host, prefix, "system", "uptime"].join("/"), - upload_image: () => [host, prefix, "system", "upload"].join("/"), - update_image: () => [host, prefix, "system", "update"].join("/"), - update_progress: () => [host, prefix, "system", "progress"].join("/"), + upload_image: () => [host, prefix, "system", "update", "upload"].join("/"), + update_image: () => [host, prefix, "system", "update", "update"].join("/"), + update_progress: () => [host, prefix, "system", "update", "progress"].join("/"), }; diff --git a/src/web/js/update.js b/src/web/js/update.js index 0c17a52..51c8eaa 100644 --- a/src/web/js/update.js +++ b/src/web/js/update.js @@ -28,8 +28,6 @@ checker.addEventListener("click", () => { upload_image_button.addEventListener("click", () => { upload_image_input.click(); - update_block.classList.remove("hidden"); - uploadprogress.classList.remove("hidden"); }); update_image_button.addEventListener("click", () => { @@ -40,9 +38,10 @@ const upload_firmware_image = async () => { upload_percents.innerHTML = "Upload progress 0 %"; upload_progress_bar.style.width = "0%"; uploadprogress_block.classList.remove("hidden"); - let image = update_firmware_input.files[0]; + let image = upload_image_input.files[0]; let formData = new FormData(); - formData.append(image.name, image); + formData.append("image", image); + formData.append("token", localStorage.getItem('token')) var request = new XMLHttpRequest(); request.upload.addEventListener("progress", (e) => { var file1Size = image.size; @@ -59,25 +58,21 @@ const upload_firmware_image = async () => { }); request.onreadystatechange = () => { if (request.readyState === 4) { - let jsondata = JSON.parse(request.responseText); - if (jsondata.success) { - image_table_body.innerHTML = ` - ${jsondata.response.file.name} - ${(jsondata.response.file.size / 1024 / 1024).toFixed(2)}MB - ${jsondata.response.cpu} - ${jsondata.response.deviceName} - ${jsondata.response.version} - `; - image_table.classList.remove("hidden-table"); - start_update_button.classList.remove("hidden-button"); + console.log(request.responseText) + const jsondata = JSON.parse(request.responseText) + if (jsondata.status === true) { + update_image_button.classList.remove('hidden') + update_block.classList.remove("hidden"); + } else { + notification('Upload error', 'error') } } }; request.open( "post", - window.location.origin + "/wimic/updatefirmware/upload", + routes.upload_image(), ); - request.timeout = 45000; + request.timeout = 450000; request.send(formData); }; From ddd4777be9373e3fed6c43451e880f8cdb195f50 Mon Sep 17 00:00:00 2001 From: TolaMironcenko Date: Wed, 5 Mar 2025 10:21:13 +0700 Subject: [PATCH 4/5] update firmware added --- Makefile | 2 +- meson.build | 6 ++- src/settings/backup.cpp | 2 - src/system/update/update.cpp | 92 +++++++++++++++++++++++++++++++++++- src/web/js/update.js | 64 +++++++++++++++++++++---- 5 files changed, 152 insertions(+), 14 deletions(-) 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); +}; From 9b48cd7e68869ffaea8f39bab5df2b73a232e2b4 Mon Sep 17 00:00:00 2001 From: TolaMironcenko Date: Wed, 5 Mar 2025 10:27:46 +0700 Subject: [PATCH 5/5] update firmware added --- Makefile | 2 +- meson.build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 40669f2..2e9212b 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CXX=g++ STRIP=strip -CXXFLAGS=-Wall -I src/libs -I src -std=c++20 -lpthread +CXXFLAGS=-Wall -I src/libs -I src -std=c++20 SOURCES=src/openrouter.cpp \ src/security/cors.cpp \ src/settings/settings.cpp \ diff --git a/meson.build b/meson.build index c76f194..15b0285 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'openrouter', 'cpp', - default_options: ['cpp_std=c++20', 'cpp_args=-Wall -lpthread'], + default_options: ['cpp_std=c++20', 'cpp_args=-Wall'], ) include_dirs = include_directories('src', 'src/libs') executable(