Merge pull request 'update' (#22) from update into main
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-Make (push) Has been cancelled
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-CMake (push) Has been cancelled
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-Meson (push) Has been cancelled

Reviewed-on: https://gitea.home.com:3000/TolaMironcenko/openrouter/pulls/22 #21
This commit was merged in pull request #22.
This commit is contained in:
wt committed 2025-03-05 10:24:25 +07:00
commit 2f0ccc55b6
15 files changed
+1059 -349

No files matched your search

+1 -1
View File
@@ -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
+1
View File
@@ -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 \
+7 -1
View File
@@ -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'],
)
include_dirs = include_directories('src', 'src/libs')
executable(
'openrouter',
@@ -31,6 +35,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',
+3 -1
View File
@@ -18,11 +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/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);
-4
View File
@@ -20,10 +20,6 @@ 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);
+1
View File
@@ -5,3 +5,4 @@
#endif // __linux__
#include <system/syslog/syslog.hpp>
#include <system/reboot/reboot.hpp>
#include <system/update/update.hpp>
+142
View File
@@ -0,0 +1,142 @@
#include <system/update/update.hpp>
#include <types.hpp>
#include <auth/auth.hpp>
#include <filesystem>
#include <colors.h>
#include <iostream>
#include <json.hpp>
#include <pthread.h>
#define DEFAULT_TMP_FILE_NAME "/tmp/image"
#define DEFAULT_DST_FILE_NAME "/images/image"
#define BLOCK_SIZE 131072
namespace api
{
namespace system
{
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)
{
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(DEFAULT_TMP_FILE_NAME);
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;
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)
{
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);
}
}
}
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include <httplib.h>
namespace api
{
namespace system
{
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);
}
}
}
-4
View File
@@ -15,10 +15,6 @@ namespace api
struct sysinfo x;
if (sysinfo(&x) == 0)
{
// std::cout << static_cast<unsigned long long>(x.uptime)/3600ULL << "h "
// << static_cast<unsigned long long>(x.uptime)/60ULL%60ULL << "m "
// << static_cast<unsigned long long>(x.uptime)%60ULL << "s "
// << std::endl;
std::stringstream responsedata;
responsedata << R"({"uptime":{"hour":")"
<< static_cast<unsigned long long>(x.uptime) / 3600ULL
+1
View File
@@ -6,6 +6,7 @@
margin: 0;
padding: 0;
box-sizing: border-box;
transition: 0.3s;
}
body {
+117
View File
@@ -0,0 +1,117 @@
.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;
}
+546 -267
View File
@@ -1,287 +1,566 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="css/index.css" />
<link rel="stylesheet" href="css/form.css" />
<link rel="stylesheet" href="css/settings.css" />
<link rel="stylesheet" href="css/notification.css" />
<link rel="stylesheet" href="css/header.css" />
<link rel="stylesheet" href="css/button.css" />
<link rel="stylesheet" href="css/loader.css" />
<link rel="stylesheet" href="css/syslog.css" />
<link rel="stylesheet" href="css/info.css" />
<link rel="stylesheet" href="css/update.css" />
<title>Openrouter Admin</title>
</head>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/index.css">
<link rel="stylesheet" href="css/form.css">
<link rel="stylesheet" href="css/settings.css">
<link rel="stylesheet" href="css/notification.css">
<link rel="stylesheet" href="css/header.css">
<link rel="stylesheet" href="css/button.css">
<link rel="stylesheet" href="css/loader.css">
<link rel="stylesheet" href="css/syslog.css">
<link rel="stylesheet" href="css/info.css">
<title>Openrouter Admin</title>
</head>
<body>
<section class="adminpanel">
<!-- header -->
<header class="header">
<button id="headermenubutton">&#9636;</button>
<div class="links">
<button class="linkbutton" id="syslogbutton">Syslog</button>
<button class="linkbutton" id="settingsbutton">Settings</button>
<button class="linkbutton" id="infobutton">Info</button>
<button class="linkbutton" id="rebootbutton">Reboot</button>
</div>
<h1><img id="openroutericon" src="favicon.ico" alt="icon">Openrouter Admin</h1>
<div class="userbuttons">
<h1 class="headerusername"></h1>
<button class="exitbutton">Exit</button>
</div>
</header>
<!-- header -->
<!-- settings block -->
<div class="section">
<section class="settingssection">
<!-- telnet settings -->
<div class="settingrect ">
<div class="settingrectheader">
<h2>Telnet</h2>
<div class="checkbox-wrapper-2">
<input type="checkbox" class="sc-gJwTLC ikxBAC" id="telnetenable">
</div>
</div>
<div class="settingrectbody telnetsettingbody">
<div style="display:flex;align-items:center;justify-content:space-between;">
<label for="telnetportinput" class="forminputlabel">Port</label>
<input type="text" id="telnetportinput" class="forminput">
</div>
</div>
<div class="settingrectfooter">
<!-- <button class="settingrectfooterbutton hiddenbutton" id="add_telnet_user_button">Add telnet user</button> -->
<button class="settingrectfooterbutton" id="set_telnet_settings">Confirm</button>
</div>
<body>
<section class="adminpanel">
<!-- header -->
<header class="header">
<button id="headermenubutton">&#9636;</button>
<div class="links">
<button class="linkbutton" id="syslogbutton">Syslog</button>
<button class="linkbutton" id="settingsbutton">
Settings
</button>
<button class="linkbutton" id="infobutton">Info</button>
<button class="linkbutton" id="rebootbutton">Reboot</button>
</div>
<!-- telnet settings -->
<!-- DNS settings -->
<div class="settingrect ">
<div class="settingrectheader">
<h2>DNS</h2>
<div class="checkbox-wrapper-2">
<input type="checkbox" class="sc-gJwTLC ikxBAC" id="dnsenable">
</div>
</div>
<div class="settingrectbody" id="dnssettingsbody">
<table class="dnsservers">
<thead>
<tr>
<th>DNS servers</th>
</tr>
</thead>
<tbody id="dnsserverstablebody">
</tbody>
</table>
<h3>Domains Table</h3>
<table class="domains">
<thead>
<tr>
<th class="three">IP</th>
<th class="three">Domain</th>
<!-- <th class="three">Del</th> -->
</tr>
</thead>
<tbody id="domainsstablebody">
</tbody>
</table>
</div>
<div class="settingrectfooter">
<button id="add_local_domain_button" class="hiddenbutton settingrectfooterbutton">Add Local
Domain</button>
<button id="add_dns_server_button" class="hiddenbutton settingrectfooterbutton">Add DNS
server</button>
<button class="settingrectfooterbutton" id="set_dns_settings">Confirm</button>
</div>
<h1>
<img
id="openroutericon"
src="favicon.ico"
alt="icon"
/>Openrouter Admin
</h1>
<div class="userbuttons">
<h1 class="headerusername"></h1>
<button class="exitbutton">Exit</button>
</div>
<!-- DNS settings -->
<!-- ftp settings -->
<div class="settingrect ">
<div class="settingrectheader">
<h2>Ftp</h2>
<div class="checkbox-wrapper-2">
<input type="checkbox" class="sc-gJwTLC ikxBAC" id="ftpenable">
</div>
</div>
<div class="settingrectfooter">
<!-- <button>Add Ftp server</button> -->
<button class="settingrectfooterbutton" id="set_ftp_settings">Confirm</button>
</div>
</div>
<!-- ftp settings -->
<!-- ssh settings -->
<div class="settingrect ">
<div class="settingrectheader">
<h2>SSH</h2>
<div class="checkbox-wrapper-2">
<input type="checkbox" class="sc-gJwTLC ikxBAC" id="sshenable">
</div>
</div>
<div class="settingrectbody" id="sshsettingsbody">
<div style="display:flex;align-items:center;justify-content:space-between;">
<label for="sshserverinput" class="forminputlabel">Server</label>
<select name="selectserver" id="sshserverinput">
<option value="dropbear">dropbear</option>
<option value="openssh">openssh</option>
</select>
<!-- <input type="text" id="sshserverinput" class="forminput"> -->
</div>
<div style="display:flex;align-items:center;justify-content:space-between;">
<label for="sshportinput" class="forminputlabel">Port</label>
<input type="text" id="sshportinput" class="forminput">
</div>
<table class="sshkeys">
<thead>
<tr>
<th>SSH keys</th>
</tr>
</thead>
<tbody id="sshkeystablebody">
</tbody>
</table>
</div>
<div class="settingrectfooter onebutton">
<button id="add_ssh_key" class="hiddenbutton settingrectfooterbutton">Add SSH key</button>
<button class="settingrectfooterbutton" id="set_ssh_settings">Confirm</button>
</div>
</div>
<!-- ssh settings -->
<!-- Wi-Fi settings -->
<div class="settingrect ">
<div class="settingrectheader">
<h2>WIFI</h2>
<div class="checkbox-wrapper-2">
<input type="checkbox" class="sc-gJwTLC ikxBAC" id="wifistatus">
</div>
</div>
<div class="settingrectbody" id="wifisettingsbody">
<div style="display:flex;align-items:center;justify-content:space-between;">
<label for="SSID" class="forminputlabel">SSID</label>
<input type="text" id="SSID" class="forminput">
</div>
<div style="display:flex;align-items:center;justify-content:space-between;">
<label for="wifipassword" class="forminputlabel">Old password</label>
<button id="showpasswordbutton">Show</button>
<input type="password" id="wifipassword" class="forminput">
</div>
<div style="display:flex;align-items:center;justify-content:space-between;">
<label for="newwifipassword" class="forminputlabel">New password</label>
<button id="shownewpasswordbutton">Show</button>
<input type="password" id="newwifipassword" class="forminput">
</div>
<div style="display:flex;align-items:center;justify-content:space-between;">
<label for="wifichannel" class="forminputlabel">WIFI channel</label>
<select id="wifichannel" class="forminput">
<option class="channeloption" value="1" style="color: black">1</option>
<option class="channeloption" value="6">6</option>
<option class="channeloption" value="11">11</option>
</select>
</div>
<div style="display:flex;align-items:center;justify-content:space-between;">
<label for="wifiqr" class="forminputlabel">Show qr code</label>
</header>
<!-- header -->
<!-- settings block -->
<div class="section">
<section class="settingssection">
<!-- telnet settings -->
<div class="settingrect">
<div class="settingrectheader">
<h2>Telnet</h2>
<div class="checkbox-wrapper-2">
<input type="checkbox" class="sc-gJwTLC ikxBAC" id="wifiqr">
<input
type="checkbox"
class="sc-gJwTLC ikxBAC"
id="telnetenable"
/>
</div>
<div id="wifiqrimg"></div>
</div>
<div class="settingrectbody telnetsettingbody">
<div
style="
display: flex;
align-items: center;
justify-content: space-between;
"
>
<label
for="telnetportinput"
class="forminputlabel"
>Port</label
>
<input
type="text"
id="telnetportinput"
class="forminput"
/>
</div>
</div>
<div class="settingrectfooter">
<!-- <button class="settingrectfooterbutton hiddenbutton" id="add_telnet_user_button">Add telnet user</button> -->
<button
class="settingrectfooterbutton"
id="set_telnet_settings"
>
Confirm
</button>
</div>
</div>
<div class="settingrectfooter onebutton">
<button class="settingrectfooterbutton" id="setwifisettings">Confirm</button>
<!-- telnet settings -->
<!-- DNS settings -->
<div class="settingrect">
<div class="settingrectheader">
<h2>DNS</h2>
<div class="checkbox-wrapper-2">
<input
type="checkbox"
class="sc-gJwTLC ikxBAC"
id="dnsenable"
/>
</div>
</div>
<div class="settingrectbody" id="dnssettingsbody">
<table class="dnsservers">
<thead>
<tr>
<th>DNS servers</th>
</tr>
</thead>
<tbody id="dnsserverstablebody"></tbody>
</table>
<h3>Domains Table</h3>
<table class="domains">
<thead>
<tr>
<th class="three">IP</th>
<th class="three">Domain</th>
<!-- <th class="three">Del</th> -->
</tr>
</thead>
<tbody id="domainsstablebody"></tbody>
</table>
</div>
<div class="settingrectfooter">
<button
id="add_local_domain_button"
class="hiddenbutton settingrectfooterbutton"
>
Add Local Domain
</button>
<button
id="add_dns_server_button"
class="hiddenbutton settingrectfooterbutton"
>
Add DNS server
</button>
<button
class="settingrectfooterbutton"
id="set_dns_settings"
>
Confirm
</button>
</div>
</div>
</div>
<!-- Wi-Fi settings -->
<!-- backup settings -->
<div class="settingrect ">
<div class="settingrectheader">
<h2>Backup settings</h2>
<!-- DNS settings -->
<!-- ftp settings -->
<div class="settingrect">
<div class="settingrectheader">
<h2>Ftp</h2>
<div class="checkbox-wrapper-2">
<input
type="checkbox"
class="sc-gJwTLC ikxBAC"
id="ftpenable"
/>
</div>
</div>
<div class="settingrectfooter">
<!-- <button>Add Ftp server</button> -->
<button
class="settingrectfooterbutton"
id="set_ftp_settings"
>
Confirm
</button>
</div>
</div>
<div class="settingrectfooter" style="flex-direction: column;">
<a id="backup_settings" class="settingrectfooterbutton" href="" download="backup_settings.json">
<button class="settingrectfooterbutton">Backup settings</button>
</a>
<input type="file" name="" id="restore_settings_input" hidden>
<button class="settingrectfooterbutton" id="restore_settings">Restore settings</button>
<button class="settingrectfooterbutton" id="start_restore_settings">Start restore settings</button>
<button class="settingrectfooterbutton" id="restore_default_settings">Rrestore default settings</button>
<!-- ftp settings -->
<!-- ssh settings -->
<div class="settingrect">
<div class="settingrectheader">
<h2>SSH</h2>
<div class="checkbox-wrapper-2">
<input
type="checkbox"
class="sc-gJwTLC ikxBAC"
id="sshenable"
/>
</div>
</div>
<div class="settingrectbody" id="sshsettingsbody">
<div
style="
display: flex;
align-items: center;
justify-content: space-between;
"
>
<label
for="sshserverinput"
class="forminputlabel"
>Server</label
>
<select name="selectserver" id="sshserverinput">
<option value="dropbear">dropbear</option>
<option value="openssh">openssh</option>
</select>
<!-- <input type="text" id="sshserverinput" class="forminput"> -->
</div>
<div
style="
display: flex;
align-items: center;
justify-content: space-between;
"
>
<label for="sshportinput" class="forminputlabel"
>Port</label
>
<input
type="text"
id="sshportinput"
class="forminput"
/>
</div>
<table class="sshkeys">
<thead>
<tr>
<th>SSH keys</th>
</tr>
</thead>
<tbody id="sshkeystablebody"></tbody>
</table>
</div>
<div class="settingrectfooter onebutton">
<button
id="add_ssh_key"
class="hiddenbutton settingrectfooterbutton"
>
Add SSH key
</button>
<button
class="settingrectfooterbutton"
id="set_ssh_settings"
>
Confirm
</button>
</div>
</div>
<!-- ssh settings -->
<!-- Wi-Fi settings -->
<div class="settingrect">
<div class="settingrectheader">
<h2>WIFI</h2>
<div class="checkbox-wrapper-2">
<input
type="checkbox"
class="sc-gJwTLC ikxBAC"
id="wifistatus"
/>
</div>
</div>
<div class="settingrectbody" id="wifisettingsbody">
<div
style="
display: flex;
align-items: center;
justify-content: space-between;
"
>
<label for="SSID" class="forminputlabel"
>SSID</label
>
<input
type="text"
id="SSID"
class="forminput"
/>
</div>
<div
style="
display: flex;
align-items: center;
justify-content: space-between;
"
>
<label for="wifipassword" class="forminputlabel"
>Old password</label
>
<button id="showpasswordbutton">Show</button>
<input
type="password"
id="wifipassword"
class="forminput"
/>
</div>
<div
style="
display: flex;
align-items: center;
justify-content: space-between;
"
>
<label
for="newwifipassword"
class="forminputlabel"
>New password</label
>
<button id="shownewpasswordbutton">Show</button>
<input
type="password"
id="newwifipassword"
class="forminput"
/>
</div>
<div
style="
display: flex;
align-items: center;
justify-content: space-between;
"
>
<label for="wifichannel" class="forminputlabel"
>WIFI channel</label
>
<select id="wifichannel" class="forminput">
<option
class="channeloption"
value="1"
style="color: black"
>
1
</option>
<option class="channeloption" value="6">
6
</option>
<option class="channeloption" value="11">
11
</option>
</select>
</div>
<div
style="
display: flex;
align-items: center;
justify-content: space-between;
"
>
<label for="wifiqr" class="forminputlabel"
>Show qr code</label
>
<div class="checkbox-wrapper-2">
<input
type="checkbox"
class="sc-gJwTLC ikxBAC"
id="wifiqr"
/>
</div>
<div id="wifiqrimg"></div>
</div>
</div>
<div class="settingrectfooter onebutton">
<button
class="settingrectfooterbutton"
id="setwifisettings"
>
Confirm
</button>
</div>
</div>
<!-- Wi-Fi settings -->
<!-- backup settings -->
<div class="settingrect">
<div class="settingrectheader">
<h2>Backup settings</h2>
</div>
<div
class="settingrectfooter"
style="flex-direction: column"
>
<a
id="backup_settings"
class="settingrectfooterbutton"
href=""
download="backup_settings.json"
>
<button class="settingrectfooterbutton">
Backup settings
</button>
</a>
<input
type="file"
name=""
id="restore_settings_input"
hidden
/>
<button
class="settingrectfooterbutton"
id="restore_settings"
>
Restore settings
</button>
<button
class="settingrectfooterbutton"
id="start_restore_settings"
>
Start restore settings
</button>
<button
class="settingrectfooterbutton"
id="restore_default_settings"
>
Rrestore default settings
</button>
</div>
</div>
<!-- backup settings -->
<!-- update firmware -->
<div class="settingrect">
<div class="settingrectheader">
<h2>Update Firmware</h2>
</div>
<div
class="settingrectfooter"
style="flex-direction: column"
>
<input
type="file"
name=""
id="upload_image_input"
hidden
/>
<button
class="settingrectfooterbutton"
id="upload_image_button"
>
Update firmware
</button>
<div class="progressbar hidden" id="uploadprogress">
<div class="bar" id="uploadbar"></div>
<div class="percent" id="uploadpercent">
Uploaded 50 %
</div>
</div>
<div
class="vstack hidden"
style="width: 100%; gap: 10px; padding: 0"
id="update_block"
>
<div
class="hstack"
style="
justify-content: space-around;
width: 100%;
"
>
<label for="auto_reboot_flag">
Reboot after update?
</label>
<div class="checker">
<div class="checked"></div>
</div>
<div id="noyes">(No)</div>
</div>
<input
id="auto_reboot_flag"
type="checkbox"
hidden
/>
<button
class="settingrectfooterbutton"
id="update_image_button"
>
Update
</button>
</div>
<div class="progressbar hidden" id="updateprogress">
<div class="bar" id="updatebar"></div>
<div class="percent" id="updatepercent">
Updated 50 %
</div>
</div>
</div>
</div>
<!-- update firmware -->
</section>
<!-- syslog block -->
<section class="syslogsection">
<div class="syslogheader">
<h2 class="syslogheader">Syslog</h2>
<button id="openrouterfilter">openrouter</button>
<button id="authservicefilter">auth_service</button>
<button id="refreshbutton">&#8634;</button>
<label for="realtimesyslog">Realtime</label>
<input type="checkbox" id="realtimesyslog" />
</div>
<div class="syslogdata"></div>
</section>
<!-- syslog block -->
<section class="infosection">
<div class="inforect">
<label for="infoversion">Version: </label>
<p id="version">0.1.1</p>
</div>
<div class="inforect">
<label for="infouptime">Uptime: </label>
<p id="uptime"></p>
</div>
</section>
</div>
<!-- settings block -->
</section>
<!-- notification block -->
<div class="notificationsection"></div>
<!-- notification block -->
<!-- login form -->
<div class="form loginform">
<form class="formbody loginformbody">
<h1 class="formheader">OpenRouter Login</h1>
<div class="forminputbody">
<label for="loginusernameinput" class="forminputlabel"
>Username</label
>
<input
type="text"
required
id="loginusernameinput"
class="forminput"
/>
</div>
<!-- backup settings -->
</section>
<!-- syslog block -->
<section class="syslogsection">
<div class="syslogheader">
<h2 class="syslogheader">Syslog</h2>
<button id="openrouterfilter">openrouter</button>
<button id="authservicefilter">auth_service</button>
<button id="refreshbutton">&#8634;</button>
<label for="realtimesyslog">Realtime</label>
<input type="checkbox" id="realtimesyslog">
<div class="forminputbody">
<label for="loginpasswordinput" class="forminputlabel"
>Username</label
>
<input
type="password"
required
id="loginpasswordinput"
class="forminput"
/>
</div>
<div class="syslogdata"></div>
</section>
<!-- syslog block -->
<section class="infosection">
<div class="inforect">
<label for="infoversion">Version: </label>
<p id="version">0.1.1</p>
<div class="forminputbody">
<button class="formbutton">Login</button>
</div>
<div class="inforect">
<label for="infouptime">Uptime: </label>
<p id="uptime"></p>
</div>
</section>
</form>
</div>
<!-- settings block -->
</section>
<!-- notification block -->
<div class="notificationsection"></div>
<!-- notification block -->
<!-- login form -->
<div class="form loginform">
<form class="formbody loginformbody">
<h1 class="formheader">OpenRouter Login</h1>
<div class="forminputbody">
<label for="loginusernameinput" class="forminputlabel">Username</label>
<input type="text" required id="loginusernameinput" class="forminput">
<!-- login form -->
<!-- loader -->
<div class="loader">
<h1 id="loaderheader">Reboting</h1>
<div class="loadertip">
<div class="progress"></div>
</div>
<div class="forminputbody">
<label for="loginpasswordinput" class="forminputlabel">Username</label>
<input type="password" required id="loginpasswordinput" class="forminput">
</div>
<div class="forminputbody">
<button class="formbutton">Login</button>
</div>
</form>
</div>
<!-- login form -->
<!-- loader -->
<div class="loader">
<h1 id="loaderheader">Reboting</h1>
<div class="loadertip">
<div class="progress"></div>
</div>
</div>
<!-- loader -->
<script src="js/header.js"></script>
<script src="js/routes.js"></script>
<script src="js/notification.js"></script>
<script src="js/loader.js"></script>
<!-- loader -->
<script src="js/header.js"></script>
<script src="js/routes.js"></script>
<script src="js/notification.js"></script>
<script src="js/loader.js"></script>
<script src="js/settings/settings.js"></script>
<script src="js/settings/telnet.js"></script>
<script src="js/settings/dns.js"></script>
<script src="js/settings/ftp.js"></script>
<script src="js/settings/ssh.js"></script>
<script src="js/settings/wifi.js"></script>
<script src="js/settings/backup.js"></script>
<script src="js/reboot.js"></script>
<script src="js/syslog.js"></script>
<script src="js/info/uptime.js"></script>
<script src="js/login.js"></script>
</body>
<script src="js/settings/settings.js"></script>
<script src="js/settings/telnet.js"></script>
<script src="js/settings/dns.js"></script>
<script src="js/settings/ftp.js"></script>
<script src="js/settings/ssh.js"></script>
<script src="js/settings/wifi.js"></script>
<script src="js/settings/backup.js"></script>
<script src="js/reboot.js"></script>
<script src="js/syslog.js"></script>
<script src="js/info/uptime.js"></script>
<script src="js/update.js"></script>
<script src="js/login.js"></script>
</body>
</html>
+73 -56
View File
@@ -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();
});
+22 -15
View File
@@ -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", "update", "upload"].join("/"),
update_image: () => [host, prefix, "system", "update", "update"].join("/"),
update_progress: () => [host, prefix, "system", "update", "progress"].join("/"),
};
+129
View File
@@ -0,0 +1,129 @@
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");
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;
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_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 () => {
upload_percents.innerHTML = "Upload progress 0 %";
upload_progress_bar.style.width = "0%";
uploadprogress_block.classList.remove("hidden");
let image = upload_image_input.files[0];
let formData = new FormData();
formData.append("image", image);
formData.append("token", localStorage.getItem("token"));
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) {
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", routes.upload_image());
request.timeout = 450000;
request.send(formData);
};
upload_image_input.addEventListener("change", (event) => {
if (event.target.files[0]) {
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);
};