aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHimbeer <himbeer@disroot.org>2024-08-07 13:18:49 +0200
committerHimbeer <himbeer@disroot.org>2024-08-07 13:18:49 +0200
commitf9c1a759c7ada21cdf41644edfd2cd27c9448688 (patch)
tree255db3c9aa96919bc45281785701fd41da90498c
parent1752873ef2a673b792acb96f91c271add00baa19 (diff)
Add system maintenance (admin password, reboot, shutdown)
-rw-r--r--src-tauri/src/main.rs141
-rw-r--r--src/sys.html111
-rw-r--r--src/sys.js115
-rw-r--r--src/wan.js1
4 files changed, 366 insertions, 2 deletions
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index c10f666..ed6ba2a 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -1089,6 +1089,142 @@ fn handle_delete_response(response: Response) -> String {
}
}
+#[tauri::command]
+async fn change_sys_password(
+ old: String,
+ to: String,
+ repeat: String,
+ state: State<'_, Mutex<Session>>,
+) -> Result<String, ()> {
+ let (client, instance) = {
+ let state = state.lock().unwrap();
+ (state.client.clone(), state.instance.clone())
+ };
+ let instance = match instance {
+ Some(instance) => instance,
+ None => {
+ return Ok(String::from(
+ "Keine Instanz ausgewählt, bitte melden Sie sich neu an!",
+ ))
+ }
+ };
+
+ if to != repeat {
+ return Ok(String::from(
+ "Das neue Passwort und seine Wiederholung stimmen nicht überein",
+ ));
+ }
+
+ let response = client
+ .post(instance.url.join("/data/write").unwrap())
+ .query(&[("path", "/data/admind.passwd")])
+ .basic_auth("rustkrazy", Some(&old))
+ .body(to)
+ .send();
+
+ Ok(match response.await {
+ Ok(response) => handle_change_sys_password_response(response),
+ Err(e) => format!("Änderung fehlgeschlagen: {}", e),
+ })
+}
+
+fn handle_change_sys_password_response(response: Response) -> String {
+ let status = response.status();
+ if status.is_success() {
+ String::from("Änderung erfolgreich")
+ } else if status == StatusCode::UNAUTHORIZED {
+ String::from("Das alte Passwort ist ungültig")
+ } else if status.is_client_error() {
+ format!("Clientseitiger Fehler: {}", status)
+ } else if status.is_server_error() {
+ format!("Serverseitiger Fehler: {}", status)
+ } else {
+ format!("Unerwarteter Statuscode: {}", status)
+ }
+}
+
+#[tauri::command]
+async fn reboot(state: State<'_, Mutex<Session>>) -> Result<String, ()> {
+ let (client, instance) = {
+ let state = state.lock().unwrap();
+ (state.client.clone(), state.instance.clone())
+ };
+ let instance = match instance {
+ Some(instance) => instance,
+ None => {
+ return Ok(String::from(
+ "Keine Instanz ausgewählt, bitte melden Sie sich neu an!",
+ ))
+ }
+ };
+
+ let response = client
+ .post(instance.url.join("/reboot").unwrap())
+ .basic_auth("rustkrazy", Some(&instance.password))
+ .send();
+
+ Ok(match response.await {
+ Ok(response) => handle_reboot_response(response),
+ Err(e) => format!("Befehl fehlgeschlagen: {}", e),
+ })
+}
+
+fn handle_reboot_response(response: Response) -> String {
+ let status = response.status();
+ if status.is_success() {
+ String::new()
+ } else if status == StatusCode::UNAUTHORIZED {
+ String::from("Ungültiges Verwaltungspasswort, bitte melden Sie sich neu an!")
+ } else if status.is_client_error() {
+ format!("Clientseitiger Fehler: {}", status)
+ } else if status.is_server_error() {
+ format!("Serverseitiger Fehler: {}", status)
+ } else {
+ format!("Unerwarteter Statuscode: {}", status)
+ }
+}
+
+#[tauri::command]
+async fn shutdown(state: State<'_, Mutex<Session>>) -> Result<String, ()> {
+ let (client, instance) = {
+ let state = state.lock().unwrap();
+ (state.client.clone(), state.instance.clone())
+ };
+ let instance = match instance {
+ Some(instance) => instance,
+ None => {
+ return Ok(String::from(
+ "Keine Instanz ausgewählt, bitte melden Sie sich neu an!",
+ ))
+ }
+ };
+
+ let response = client
+ .post(instance.url.join("/shutdown").unwrap())
+ .basic_auth("rustkrazy", Some(&instance.password))
+ .send();
+
+ Ok(match response.await {
+ Ok(response) => handle_shutdown_response(response),
+ Err(e) => format!("Befehl fehlgeschlagen: {}", e),
+ })
+}
+
+fn handle_shutdown_response(response: Response) -> String {
+ let status = response.status();
+ if status.is_success() {
+ String::new()
+ } else if status == StatusCode::UNAUTHORIZED {
+ String::from("Ungültiges Verwaltungspasswort, bitte melden Sie sich neu an!")
+ } else if status.is_client_error() {
+ format!("Clientseitiger Fehler: {}", status)
+ } else if status.is_server_error() {
+ format!("Serverseitiger Fehler: {}", status)
+ } else {
+ format!("Unerwarteter Statuscode: {}", status)
+ }
+}
+
fn main() {
tauri::Builder::default()
.manage(Mutex::new(Session {
@@ -1111,7 +1247,10 @@ fn main() {
leases,
load_domain,
change_domain,
- delete
+ delete,
+ change_sys_password,
+ reboot,
+ shutdown
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
diff --git a/src/sys.html b/src/sys.html
new file mode 100644
index 0000000..c79d9e8
--- /dev/null
+++ b/src/sys.html
@@ -0,0 +1,111 @@
+<!doctype html>
+<html lang="de">
+ <head>
+ <meta charset="UTF-8" />
+ <link rel="stylesheet" href="styles.css" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>System - RSDSL Verwaltungswerkzeug</title>
+ <script type="module" src="/dashboard.js" defer></script>
+ <script type="module" src="/sys.js" defer></script>
+ </head>
+
+ <body>
+ <div class="container">
+ <h1>System</h1>
+
+ <div class="row">
+ <form id="dashboard-form">
+ <button id="dashboard-submit" type="submit">↩ Zurück zur Übersicht</button>
+ </form>
+
+ <form id="wan-open-form">
+ <button id="wan-open-submit" type="submit">Einwahl und Zugansdaten</button>
+ </form>
+
+ <form id="lan-open-form">
+ <button id="lan-open-submit" type="submit">LAN</button>
+ </form>
+
+ <form id="ddns-open-form">
+ <button id="ddns-open-submit" type="submit">Dynamisches DNS (INWX)</button>
+ </form>
+
+ <form id="log-open-form">
+ <button id="log-open-submit" type="submit">Diagnoseprotokolle</button>
+ </form>
+
+ <form id="sys-open-form">
+ <button id="sys-open-submit" type="submit">System</button>
+ </form>
+
+ <form id="disconnect-form">
+ <button id="disconnect-submit" type="submit">🚪 Abmelden</button>
+ </form>
+ </div>
+
+ <br />
+
+ <form id="power-form">
+ <fieldset>
+ <legend>Neustart und Herunterfahren</legend>
+
+ <div class="row">
+ <button id="power-reboot">🔄 Neustart</button>
+ <button id="power-shutdown">🔌 Herunterfahren</button>
+ </div>
+
+ <p>Information: Ein Neustart dauert ca. 30 Sekunden bis 1 Minute.
+ In der Regel wird die Internetverbindung innerhalb von 1 Minute
+ wieder aufgebaut, sofern keine anderen Geräte neu gestartet wurden.
+ Bei anschließenden Verbindungsproblemen kann es ratsam sein, die
+ betroffenen Geräte neu mit dem Netzwerk zu verbinden oder für
+ mehrere Stunden verbunden zu lassen. Achtung: Insbesondere
+ Smartphones trennen die WLAN-Verbindung oft erst nach mindestens 5
+ Sekunden Abschaltzeit. Daher die WLAN-Funktion für mindestens 5
+ Sekunden deaktivieren und erst dann wieder einschalten.</p>
+
+ <p>Nach erfolgreichem Herunterfahren muss das Stromkabel abgezogen
+ werden, um die Hardware tatsächlich auszuschalten. Erst beim
+ Wiederanschließen fährt das System wieder hoch.</p>
+ </fieldset>
+ </form>
+
+ <br />
+
+ <form id="password-form">
+ <fieldset>
+ <legend>Verwaltungspasswort</legend>
+
+ <label for="password-old" form="password-form">Altes Passwort:</label>
+ <div class="row">
+ <input id="password-old" type="password" />
+ </div>
+
+ <label for="password-new" form="password-form">Neues Passwort:</label>
+ <div class="row">
+ <input id="password-new" type="password" />
+ </div>
+
+ <label for="password-repeat" form="password-form">Neues Passwort wiederholen:</label>
+ <div class="row">
+ <input id="password-repeat" type="password" />
+ </div>
+
+ <br />
+
+ <div class="row">
+ <button id="password-show">🔒 Neues Passwort ein-/ausblenden</button>
+ </div>
+
+ <br />
+
+ <div class="row">
+ <button id="password-submit" type="submit">Passwort ändern</button>
+ </div>
+
+ <p id="password-status"></p>
+ </fieldset>
+ </form>
+ </div>
+ </body>
+</html>
diff --git a/src/sys.js b/src/sys.js
new file mode 100644
index 0000000..8b19f22
--- /dev/null
+++ b/src/sys.js
@@ -0,0 +1,115 @@
+const { invoke } = window.__TAURI__.tauri;
+const { message } = window.__TAURI__.dialog;
+
+let passwordOldEl;
+let passwordNewEl;
+let passwordRepeatEl;
+let passwordSubmitEl;
+let passwordStatusEl;
+
+async function reboot() {
+ const error = await invoke("reboot", {});
+
+ if (error !== "") {
+ await message("Befehl konnte nicht erteilt werden: " + error, {
+ kind: "error",
+ title: "Neustart nicht erfolgt"
+ });
+ }
+
+ const successTime = Date.now();
+
+ while ((Date.now() - successTime) < 60) {
+ await message("Neustart läuft. Bitte ca. 1 Minute warten.", {
+ kind: "info",
+ title: "Neustart im Gange"
+ });
+ }
+}
+
+async function shutdown() {
+ const error = await invoke("shutdown", {});
+
+ if (error !== "") {
+ await message("Befehl konnte nicht erteilt werden: " + error, {
+ kind: "error",
+ title: "Herunterfahren nicht erfolgt"
+ });
+ }
+
+ await message("Router erfolgreich heruntergefahren. Bitte Stromkabel abziehen.", {
+ kind: "info",
+ title: "Herunterfahren erfolgreich"
+ });
+
+ window.location = "index.html";
+}
+
+function showPassword() {
+ switch (passwordNewEl.type) {
+ case "password":
+ passwordNewEl.type = "text";
+ passwordRepeatEl.type = "text";
+ break;
+ case "text":
+ passwordNewEl.type = "password";
+ passwordRepeatEl.type = "password";
+ break;
+ }
+}
+
+async function changePassword() {
+ passwordOldEl.disabled = true;
+ passwordNewEl.disabled = true;
+ passwordRepeatEl.disabled = true;
+ passwordSubmitEl.disabled = true;
+ passwordStatusEl.innerText = "Änderungsanfrage...";
+ document.body.style.cursor = "progress";
+
+ passwordStatusEl.innerText = await invoke("change_sys_password", {
+ old: passwordOldEl.value,
+ to: passwordNewEl.value,
+ repeat: passwordRepeatEl.value,
+ });
+
+ passwordOldEl.disabled = false;
+ passwordNewEl.disabled = false;
+ passwordRepeatEl.disabled = false;
+ passwordSubmitEl.disabled = false;
+ document.body.style.cursor = "default";
+
+ if (passwordStatusEl.innerText === "Änderung erfolgreich") {
+ await message("Passwort erfolgreich geändert. Melden Sie sich neu an, um das Verwaltungswerkzeug weiter benutzen zu können.", {
+ kind: "info",
+ title: "Neuanmeldung erforderlich",
+ });
+
+ window.location = "index.html";
+ }
+}
+
+window.addEventListener("DOMContentLoaded", () => {
+ document.querySelector("#power-reboot").addEventListener("click", (e) => {
+ e.preventDefault();
+ reboot();
+ });
+ document.querySelector("#power-shutdown").addEventListener("click", (e) => {
+ e.preventDefault();
+ shutdown();
+ });
+
+ passwordOldEl = document.querySelector("#password-old");
+ passwordNewEl = document.querySelector("#password-new");
+ passwordRepeatEl = document.querySelector("#password-repeat");
+ passwordSubmitEl = document.querySelector("#password-submit");
+ passwordStatusEl = document.querySelector("#password-status");
+
+ document.querySelector("#password-show").addEventListener("click", (e) => {
+ e.preventDefault();
+ showPassword();
+ });
+ document.querySelector("#password-form").addEventListener("submit", (e) => {
+ e.preventDefault();
+ changePassword();
+ });
+});
diff --git a/src/wan.js b/src/wan.js
index 675ef18..40e9a6c 100644
--- a/src/wan.js
+++ b/src/wan.js
@@ -214,7 +214,6 @@ window.addEventListener("DOMContentLoaded", () => {
e.preventDefault();
showCredentials();
});
-
document.querySelector("#credentials-form").addEventListener("submit", (e) => {
e.preventDefault();
changeCredentials();