From 4c44c3531b495c119e58b2aa106450491510944f Mon Sep 17 00:00:00 2001 From: Anton Franzluebbers Date: Sun, 10 Jul 2022 15:16:44 -0400 Subject: [PATCH] control panel works more now, added install script to add services and compile --- .gitignore | 4 +- Cargo.lock | 84 +- Cargo.toml | 6 +- config.json | 5 + config.txt | 4 - control-panel/Cargo.lock | 240 ++++- control-panel/Cargo.toml | 10 +- control-panel/compile_server.sh | 4 + control-panel/config.json | 9 +- control-panel/git_pull.sh | 2 +- control-panel/onefetch_file.sh | 4 +- control-panel/src/main.rs | 113 ++- control-panel/src/main.rs.bak | 223 +++++ .../favicons/android-chrome-192x192.png | Bin 0 -> 6078 bytes .../favicons/android-chrome-256x256.png | Bin 0 -> 6563 bytes .../static/favicons/apple-touch-icon.png | Bin 0 -> 1526 bytes .../static/favicons/browserconfig.xml | 9 + .../static/favicons/favicon-16x16.png | Bin 0 -> 815 bytes .../static/favicons/favicon-32x32.png | Bin 0 -> 1294 bytes control-panel/static/favicons/favicon.ico | Bin 0 -> 15086 bytes .../static/favicons/mstile-150x150.png | Bin 0 -> 1727 bytes .../static/favicons/safari-pinned-tab.svg | 20 + .../static/favicons/site.webmanifest | 19 + control-panel/static/templates/index.hbs | 153 ++-- install.sh | 35 + run.sh | 15 - src/main.rs | 626 ++++++------- src/main_save.rs | 846 ------------------ velnet.service | 13 + 29 files changed, 1099 insertions(+), 1345 deletions(-) create mode 100644 config.json delete mode 100644 config.txt create mode 100644 control-panel/compile_server.sh create mode 100644 control-panel/src/main.rs.bak create mode 100644 control-panel/static/favicons/android-chrome-192x192.png create mode 100644 control-panel/static/favicons/android-chrome-256x256.png create mode 100644 control-panel/static/favicons/apple-touch-icon.png create mode 100644 control-panel/static/favicons/browserconfig.xml create mode 100644 control-panel/static/favicons/favicon-16x16.png create mode 100644 control-panel/static/favicons/favicon-32x32.png create mode 100644 control-panel/static/favicons/favicon.ico create mode 100644 control-panel/static/favicons/mstile-150x150.png create mode 100644 control-panel/static/favicons/safari-pinned-tab.svg create mode 100644 control-panel/static/favicons/site.webmanifest create mode 100755 install.sh delete mode 100755 run.sh delete mode 100644 src/main_save.rs create mode 100644 velnet.service diff --git a/.gitignore b/.gitignore index 6ef783c..fe026e3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ main.exe main.pdb .idea/ restarts.log -nohup.out \ No newline at end of file +nohup.out +server.log +control_panel.log \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 4933522..c86c789 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,18 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "VelNetServerRust" -version = "0.2.0" -dependencies = [ - "async-notify", - "async-std", - "chrono", - "futures", - "serde", - "serde_json", -] - [[package]] name = "async-attributes" version = "1.1.2" @@ -205,7 +193,7 @@ dependencies = [ "libc", "num-integer", "num-traits", - "time", + "time 0.1.44", "winapi", ] @@ -468,6 +456,15 @@ dependencies = [ "libc", ] +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + [[package]] name = "once_cell" version = "1.10.0" @@ -560,6 +557,17 @@ dependencies = [ "serde", ] +[[package]] +name = "simplelog" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48dfff04aade74dd495b007c831cd6f4e0cee19c344dd9dc0884c0289b70a786" +dependencies = [ + "log", + "termcolor", + "time 0.3.11", +] + [[package]] name = "slab" version = "0.4.5" @@ -587,6 +595,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + [[package]] name = "time" version = "0.1.44" @@ -598,6 +615,24 @@ dependencies = [ "winapi", ] +[[package]] +name = "time" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217" +dependencies = [ + "itoa", + "libc", + "num_threads", + "time-macros", +] + +[[package]] +name = "time-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" + [[package]] name = "unicode-xid" version = "0.2.2" @@ -614,6 +649,20 @@ dependencies = [ "version_check", ] +[[package]] +name = "velnet_server" +version = "0.2.0" +dependencies = [ + "async-notify", + "async-std", + "chrono", + "futures", + "log", + "serde", + "serde_json", + "simplelog", +] + [[package]] name = "version_check" version = "0.9.4" @@ -733,6 +782,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 8f52e53..823b4a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "VelNetServerRust" +name = "velnet_server" version = "0.2.0" edition = "2021" @@ -11,4 +11,6 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" async-std = { version = "1.6", features = ["attributes"]} futures = "*" -async-notify = "*" \ No newline at end of file +async-notify = "*" +simplelog = "^0.12.0" +log = "*" \ No newline at end of file diff --git a/config.json b/config.json new file mode 100644 index 0000000..4b32a32 --- /dev/null +++ b/config.json @@ -0,0 +1,5 @@ +{ + "port": 5000, + "tcp_timeout": 30, + "log_file": "server.log" +} diff --git a/config.txt b/config.txt deleted file mode 100644 index 81bd2ee..0000000 --- a/config.txt +++ /dev/null @@ -1,4 +0,0 @@ -{ -"port":5000, -"tcp_timeout":30 -} diff --git a/control-panel/Cargo.lock b/control-panel/Cargo.lock index 2884cfc..79200c4 100644 --- a/control-panel/Cargo.lock +++ b/control-panel/Cargo.lock @@ -19,6 +19,29 @@ dependencies = [ "tokio-util 0.7.0", ] +[[package]] +name = "actix-files" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04dcf7654254676d434b0285e2298d577ed4826f67f536e7a39bb0f64721164" +dependencies = [ + "actix-http", + "actix-service", + "actix-utils", + "actix-web", + "askama_escape", + "bitflags", + "bytes", + "derive_more", + "futures-core", + "http-range", + "log", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", +] + [[package]] name = "actix-http" version = "3.0.0" @@ -62,8 +85,8 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" dependencies = [ - "quote", - "syn", + "quote 1.0.15", + "syn 1.0.86", ] [[package]] @@ -176,9 +199,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7525bedf54704abb1d469e88d7e7e9226df73778798a69cea5022d53b2ae91bc" dependencies = [ "actix-router", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.36", + "quote 1.0.15", + "syn 1.0.86", ] [[package]] @@ -222,6 +245,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "askama_escape" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + [[package]] name = "autocfg" version = "1.1.0" @@ -333,16 +362,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "control-panel" -version = "0.1.0" -dependencies = [ - "actix-web", - "handlebars", - "serde", - "serde_json", -] - [[package]] name = "convert_case" version = "0.4.0" @@ -388,6 +407,17 @@ dependencies = [ "typenum", ] +[[package]] +name = "default-env" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f753eb82d29277e79efc625e84aecacfd4851ee50e05a8573a4740239a77bfd3" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -395,10 +425,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2", - "quote", + "proc-macro2 1.0.36", + "quote 1.0.15", "rustc_version", - "syn", + "syn 1.0.86", ] [[package]] @@ -420,6 +450,12 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "either" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" + [[package]] name = "encoding_rs" version = "0.8.30" @@ -569,6 +605,12 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -589,6 +631,12 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-range" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" + [[package]] name = "httparse" version = "1.6.0" @@ -622,6 +670,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.1" @@ -715,6 +772,16 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "miniz_oxide" version = "0.4.4" @@ -849,9 +916,9 @@ checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" dependencies = [ "pest", "pest_meta", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.36", + "quote 1.0.15", + "syn 1.0.86", ] [[package]] @@ -883,13 +950,22 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", +] + [[package]] name = "proc-macro2" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" dependencies = [ - "unicode-xid", + "unicode-xid 0.2.2", ] [[package]] @@ -898,13 +974,22 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", +] + [[package]] name = "quote" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.36", ] [[package]] @@ -972,6 +1057,12 @@ dependencies = [ "semver", ] +[[package]] +name = "rustversion" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf" + [[package]] name = "ryu" version = "1.0.9" @@ -1014,9 +1105,9 @@ version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.36", + "quote 1.0.15", + "syn 1.0.86", ] [[package]] @@ -1074,6 +1165,17 @@ dependencies = [ "libc", ] +[[package]] +name = "simplelog" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48dfff04aade74dd495b007c831cd6f4e0cee19c344dd9dc0884c0289b70a786" +dependencies = [ + "log", + "termcolor", + "time", +] + [[package]] name = "slab" version = "0.4.5" @@ -1096,15 +1198,66 @@ dependencies = [ "winapi", ] +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" + +[[package]] +name = "strum_macros" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4faebde00e8ff94316c01800f9054fd2ba77d30d9e922541913051d1d978918b" +dependencies = [ + "heck", + "proc-macro2 1.0.36", + "quote 1.0.15", + "rustversion", + "syn 1.0.86", +] + +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", +] + [[package]] name = "syn" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", + "proc-macro2 1.0.36", + "quote 1.0.15", + "unicode-xid 0.2.2", +] + +[[package]] +name = "systemctl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3134cbab79000bee5ed4d71331d2c9071db897f8369e3e3089b6be85cc781d1" +dependencies = [ + "default-env", + "itertools", + "strum", + "strum_macros", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", ] [[package]] @@ -1218,6 +1371,15 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.7" @@ -1233,6 +1395,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + [[package]] name = "unicode-xid" version = "0.2.2" @@ -1251,6 +1419,20 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "velnet_control_panel" +version = "0.2.0" +dependencies = [ + "actix-files", + "actix-web", + "handlebars", + "log", + "serde", + "serde_json", + "simplelog", + "systemctl", +] + [[package]] name = "version_check" version = "0.9.4" diff --git a/control-panel/Cargo.toml b/control-panel/Cargo.toml index 77992fe..118ad7d 100644 --- a/control-panel/Cargo.toml +++ b/control-panel/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "control-panel" -version = "0.1.0" +name = "velnet_control_panel" +version = "0.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -9,4 +9,8 @@ edition = "2021" actix-web = "4" handlebars = { version = "4.2.1", features = ["dir_source"] } serde_json = "1.0" -serde = { version = "1.0", features = ["derive"] } \ No newline at end of file +serde = { version = "1.0", features = ["derive"] } +simplelog = "^0.12.0" +log = "*" +systemctl = "0.1.7" +actix-files = "0.6.1" \ No newline at end of file diff --git a/control-panel/compile_server.sh b/control-panel/compile_server.sh new file mode 100644 index 0000000..e88d724 --- /dev/null +++ b/control-panel/compile_server.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +cd .. +sudo -u "$1" bash -c "~/.cargo/bin/cargo build --release" diff --git a/control-panel/config.json b/control-panel/config.json index 9a7f6c0..4147840 100644 --- a/control-panel/config.json +++ b/control-panel/config.json @@ -1,5 +1,8 @@ { - "port": 8080, - "log_file": "/home/ubuntu/VelNetServerRust2/nohup.out", - "server_dir": "/home/ubuntu/VelNetServerRust2/" + "port": 8080, + "user": "ntsfranz", + "server_log_file": "../server.log", + "control_panel_log_file": "control_panel.log", + "server_dir": "../", + "handlebars_dev_mode": true } diff --git a/control-panel/git_pull.sh b/control-panel/git_pull.sh index 5535a8b..db50a60 100644 --- a/control-panel/git_pull.sh +++ b/control-panel/git_pull.sh @@ -1 +1 @@ -sudo -u ubuntu bash -c "git pull" +sudo -u "$1" bash -c "git pull" diff --git a/control-panel/onefetch_file.sh b/control-panel/onefetch_file.sh index 9e241f2..5aedc64 100755 --- a/control-panel/onefetch_file.sh +++ b/control-panel/onefetch_file.sh @@ -1 +1,3 @@ -sudo -u ubuntu bash -c "onefetch > onefetch.out" +#!/bin/bash + +sudo -u "$1" bash -c "~/.cargo/bin/onefetch > onefetch.out" diff --git a/control-panel/src/main.rs b/control-panel/src/main.rs index b1e14af..e2bf4f7 100644 --- a/control-panel/src/main.rs +++ b/control-panel/src/main.rs @@ -9,18 +9,22 @@ use serde_json::json; use std::io; use std::io::{prelude::*, BufReader}; use std::fs; -use std::fs::File; +use std::fs::{File, OpenOptions}; use std::path::Path; use serde::{Serialize, Deserialize}; -use std::process::{Command, Stdio}; -use std::env; +use std::process::{Command}; use std::cmp; +use std::error::Error; +use simplelog; #[derive(Serialize, Deserialize)] -struct Config { +struct ControlPanelConfig { port: u16, - log_file: String, + user: String, + server_log_file: String, + control_panel_log_file: String, server_dir: String, + handlebars_dev_mode: bool, } @@ -38,15 +42,12 @@ fn lines_from_file(filename: impl AsRef) -> Vec { #[get("/")] async fn index(hb: web::Data>) -> HttpResponse { - - //read the config file - let file = fs::read_to_string("config.json").unwrap(); - let config: Config = serde_json::from_str(&file).unwrap(); + let config = read_config_file().unwrap(); //read the log file - let log_file = lines_from_file(config.log_file); + let log_file = lines_from_file(config.server_log_file); - let restarts_log = lines_from_file("../restarts.log"); + // let restarts_log = lines_from_file(config.restarts_log_file); let uptime = Command::new("uptime") .output() @@ -54,6 +55,7 @@ async fn index(hb: web::Data>) -> HttpResponse { let _onefetch = Command::new("sh") .arg("onefetch_file.sh") + .arg(config.user) .output() .expect("failed"); @@ -61,10 +63,10 @@ async fn index(hb: web::Data>) -> HttpResponse { let data = json!({ "log_output": &log_file[(cmp::max((log_file.len() as i64) - 1000, 0) as usize)..], - "restarts_output": &restarts_log[(cmp::max((restarts_log.len() as i64) - 1000, 0) as usize)..], + // "restarts_output": &restarts_log[(cmp::max((restarts_log.len() as i64) - 1000, 0) as usize)..], "uptime": format!("{}", String::from_utf8_lossy(&uptime.stdout)), //"onefetch": format!("{}", String::from_utf8_lossy(&onefetch.stdout)) - "onefetch": onefetch + "onefetch": onefetch.trim_end() }); let body = hb.render("index", &data).unwrap(); @@ -75,21 +77,33 @@ async fn index(hb: web::Data>) -> HttpResponse { async fn restart_server() -> HttpResponse { // Restart the process - let _output = Command::new("sh") - .arg("../run.sh") - .stdin(Stdio::null()) - //.stdout(Stdio::null()) - //.stderr(Stdio::null()) - .spawn(); + // let _output = Command::new("sh") + // .arg("../restart_server.sh") + // .stdin(Stdio::null()) + // .spawn(); + // let _output = Command::new("systemd") + // .arg("restart") + // .arg("velnet") + // .stdin(Stdio::null()) + // .spawn(); - HttpResponse::Ok().body("DONE") + systemctl::restart("velnet.service").unwrap(); + + let ret = systemctl::status("velnet.service").unwrap(); + + HttpResponse::Ok().body(ret) } #[get("/git_pull")] async fn git_pull() -> HttpResponse { + + //read the config file + let config = read_config_file().unwrap(); + let output = Command::new("sh") .arg("git_pull.sh") + .arg(config.user) .output() .expect("failed to execute process"); @@ -101,37 +115,63 @@ async fn git_pull() -> HttpResponse { async fn compile() -> HttpResponse { //read the config file - let file = fs::read_to_string("config.json").unwrap(); - let config: Config = serde_json::from_str(&file).unwrap(); + let config = read_config_file().unwrap(); - let orig_dir = std::env::current_dir().unwrap(); + log::debug!("before"); - let root = Path::new(&config.server_dir); - let _new_dir = env::set_current_dir(&root); - - print!("before"); - - let output = Command::new("cargo") - .arg("build") - .arg("--release") + let output = Command::new("sh") + .arg("compile_server.sh") + .arg(config.user) .output() .expect("failed to execute process"); - print!("after"); - - let _new_dir = env::set_current_dir(orig_dir); + log::debug!("after"); HttpResponse::Ok().body(output.stdout) } +fn read_config_file() -> Result> { + // Open the file in read-only mode with buffer. + let file = File::open("config.json")?; + let reader = BufReader::new(file); + + let config = serde_json::from_reader(reader)?; + + Ok(config) +} + #[actix_web::main] async fn main() -> io::Result<()> { + + //read the config file + let config: ControlPanelConfig = read_config_file().unwrap(); + + let f = OpenOptions::new() + .write(true) + .append(true) + .create(true) + .open(config.control_panel_log_file) + .unwrap(); + simplelog::CombinedLogger::init( + vec![ + simplelog::TermLogger::new(simplelog::LevelFilter::Info, simplelog::Config::default(), simplelog::TerminalMode::Mixed, simplelog::ColorChoice::Auto), + simplelog::WriteLogger::new(simplelog::LevelFilter::Debug, simplelog::Config::default(), f), + ] + ).unwrap(); + + + log::info!("Starting control panel server."); + + let mut handlebars = Handlebars::new(); - handlebars.set_dev_mode(true); + handlebars.set_dev_mode(config.handlebars_dev_mode); handlebars.register_templates_directory(".hbs", "./static/templates").unwrap(); let handlebars_ref = web::Data::new(handlebars); + + log::info!("http://127.0.0.1:{}", config.port); + HttpServer::new(move || { App::new() .wrap(error_handlers()) @@ -140,8 +180,9 @@ async fn main() -> io::Result<()> { .service(restart_server) .service(git_pull) .service(compile) + .service(actix_files::Files::new("/static", "./static")) }) - .bind(("127.0.0.1", 8080))? + .bind(("0.0.0.0", config.port))? .run() .await } diff --git a/control-panel/src/main.rs.bak b/control-panel/src/main.rs.bak new file mode 100644 index 0000000..5cb203b --- /dev/null +++ b/control-panel/src/main.rs.bak @@ -0,0 +1,223 @@ +use actix_web::body::BoxBody; +use actix_web::dev::ServiceResponse; +use actix_web::http::header::ContentType; +use actix_web::http::StatusCode; +use actix_web::middleware::{ErrorHandlerResponse, ErrorHandlers}; +use actix_web::{get, web, App, HttpResponse, HttpServer, Result}; +use handlebars::Handlebars; +use serde_json::json; +use std::io; +use std::io::{prelude::*, BufReader}; +use std::fs; +use std::fs::File; +use std::path::Path; +use serde::{Serialize, Deserialize}; +use std::process::{Command, Stdio}; +use std::env; +use std::cmp; + +#[derive(Serialize, Deserialize)] +struct Config { + port: u16, + log_file: String, + server_dir: String, +} + + +fn lines_from_file(filename: impl AsRef) -> Vec { + let file = File::open(filename).expect("no such file"); + let buf = BufReader::new(file); + buf.lines() + .map(|l| l.expect("Could not parse line")) + .collect() +} + +#[get("/")] +async fn index(hb: web::Data>) -> HttpResponse { + + //read the config file + let file = fs::read_to_string("config.json").unwrap(); + let config: Config = serde_json::from_str(&file).unwrap(); + + //read the log file + let log_file = lines_from_file(config.log_file); + + let restarts_log = lines_from_file("../restarts.log"); + + let uptime = Command::new("uptime") + .output() + .expect("failed to execute process"); + + let _onefetch = Command::new("sh") + .arg("onefetch_file.sh") + .output() + .expect("failed"); + + let onefetch = fs::read_to_string("onefetch.out").unwrap(); + + let data = json!({ + "log_output": log_file, + "restarts_output": restarts_log, + "uptime": format!("{}", String::from_utf8_lossy(&uptime.stdout)), + //"onefetch": format!("{}", String::from_utf8_lossy(&onefetch.stdout)) + "onefetch": onefetch + }); + let body = hb.render("index", &data).unwrap(); + + HttpResponse::Ok().body(body) +} + +#[get("/nohup.out")] +async fn nohup() -> HttpResponse { + + //read the config file + let file = fs::read_to_string("config.json").unwrap(); + let config: Config = serde_json::from_str(&file).unwrap(); + + //read the log file + let log_file = lines_from_file(config.log_file); + + // get the last 100 lines + let all_lines = log_file.as_slice()[cmp::max(log_file.len()-100, 0)..].to_vec().join("\n"); + + HttpResponse::Ok().body(all_lines) +} + +#[get("/restarts.log2")] +async fn restarts() -> HttpResponse { + + //read the log file + let log_file = lines_from_file("../restarts.log"); + + // get the last 100 lines + let all_lines = log_file.as_slice()[cmp::max(log_file.len()-100, 0)..].to_vec().join("\n"); + + HttpResponse::Ok().body(all_lines) +} + +#[get("/restart_server")] +async fn restart_server() -> HttpResponse { + + // Restart the process + let _output = Command::new("sh") + .arg("../run.sh") + .stdin(Stdio::null()) + //.stdout(Stdio::null()) + //.stderr(Stdio::null()) + .spawn(); + + HttpResponse::Ok().body("DONE") +} + + + +#[get("/git_pull")] +async fn git_pull() -> HttpResponse { + + let output = Command::new("git") + .arg("pull") + .output() + .expect("failed to execute process"); + + HttpResponse::Ok().body(output.stdout) +} + + +#[get("/compile")] +async fn compile() -> HttpResponse { + + //read the config file + let file = fs::read_to_string("config.json").unwrap(); + let config: Config = serde_json::from_str(&file).unwrap(); + + let orig_dir = std::env::current_dir().unwrap(); + + let root = Path::new(&config.server_dir); + let _new_dir = env::set_current_dir(&root); + + print!("before"); + + let output = Command::new("cargo") + .arg("build") + .arg("--release") + .output() + .expect("failed to execute process"); + + print!("after"); + + let _new_dir = env::set_current_dir(orig_dir); + + HttpResponse::Ok().body(output.stdout) +} + + +#[actix_web::main] +async fn main() -> io::Result<()> { + let mut handlebars = Handlebars::new(); + handlebars.set_dev_mode(true); + handlebars.register_templates_directory(".hbs", "./static/templates").unwrap(); + let handlebars_ref = web::Data::new(handlebars); + + HttpServer::new(move || { + App::new() + .wrap(error_handlers()) + .app_data(handlebars_ref.clone()) + .service(index) + .service(restart_server) + .service(git_pull) + .service(compile) + .service(nohup) + .service(restarts) + }) + .bind(("127.0.0.1", 8080))? + .run() + .await +} + +// Custom error handlers, to return HTML responses when an error occurs. +fn error_handlers() -> ErrorHandlers { + ErrorHandlers::new().handler(StatusCode::NOT_FOUND, not_found) +} + +// Error handler for a 404 Page not found error. +fn not_found(res: ServiceResponse) -> Result> { + let response = get_error_response(&res, "Page not found"); + Ok(ErrorHandlerResponse::Response(ServiceResponse::new( + res.into_parts().0, + response.map_into_left_body(), + ))) +} + +// Generic error handler. +fn get_error_response(res: &ServiceResponse, error: &str) -> HttpResponse { + let request = res.request(); + + // Provide a fallback to a simple plain text response in case an error occurs during the + // rendering of the error page. + let fallback = |e: &str| { + HttpResponse::build(res.status()) + .content_type(ContentType::plaintext()) + .body(e.to_string()) + }; + + let hb = request + .app_data::>() + .map(|t| t.get_ref()); + match hb { + Some(hb) => { + let data = json!({ + "error": error, + "status_code": res.status().as_str() + }); + let body = hb.render("error", &data); + + match body { + Ok(body) => HttpResponse::build(res.status()) + .content_type(ContentType::html()) + .body(body), + Err(_) => fallback(error), + } + } + None => fallback(error), + } +} diff --git a/control-panel/static/favicons/android-chrome-192x192.png b/control-panel/static/favicons/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..b3cd1715955f6ca91ea7ddb65e133f2192c8792c GIT binary patch literal 6078 zcmcIobx_n%*Z#3IEJ#R*lypmXcS%aaN=PH3bcd86sjPH|)Kb#5E3v>*0us^<5=(

SbW#%jog`t%I|h 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 L_0?+b*+%{sHBsEn literal 0 HcmV?d00001 diff --git a/control-panel/static/favicons/apple-touch-icon.png b/control-panel/static/favicons/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ef6f509afbd4953e04ea62e2b18cf67a1c1cd015 GIT binary patch literal 1526 zcmZ`%doLKBK9aWILZ@)27t7a0FZeX0JasUObGzoum%9> zWdMMe0Kn0N(oTOT08nc4^Ts0A*VhFNEz^b6>4&uGqQ`=!)-{Ply0FMEt(GbUE?A+YkoSYmYk;r1PRyc3xY8uwW;`!E>EH|GneMmP1zP7q58XWF9b%t@mMlP4* z@pwfv2n3=~C>*aK>6~D(gTZq3?zV6cz8~(fIm0l73L0BuVq%n)m8Tw*aLxyIn>)x? zrHh>mrupf`PA1D0z4W@9X=abZ;mBliR#uj|xw(~LhLmb!Y_2;ZrK;w47URDSUi^9|F~ZYsZsX=1m;>}4bUCi5P$k^UtmB^V3_i^T>7 z1vNJ}FL(E`&tWF-6|RZIlaxZX2WFD|V7aGn`3;Lor3MBDhJ=I!2M33Rg|)P_Op@+1 zjcu2E*%PD!j%NS|g`FT4tO-TSy_^;H+o8CmE(5ETK8{c*6bJ-j#f*;27MC(JGtJD* zAP@+{7{(2cSz-0}I(qaud9HBYalC{3(INdlAzffghS4b{B_$OVl|E;bkUKO(E$36q z#p4t0?d>EIX{G<&OmXRls+!&1UE}z~dc|{0x#ti=m#^d7Vs3-{pqk zTZRi)-o2uKb4f1WitpeirOs_7qRM~G)pXlyaQDB!Z}4#>OZry~&-Y8)tBA1F>lQ8y zf>V^pLX5MwD;lCkl}yoQbq_noXH>QpckXe~e%VfyX;1DtUZkfG>#F6|ww#q054g(t zY2Vab8Dsfr6LCp1v=>I7;YMjYr#quIFtrQ#P=@sbqxGQty3LK^`=b|llE#QCQwTHKM(;S!CT(`}n)v40ha+%! zZkXHny7Rzhc7E|h*?QbDd3C|)!=Y_#(%B2V?-zL%BN-D;BfPdtSbe`bYQ;`%M@6l% zEPU`Mv}$#zeqzukSFV)OMmxlRP1B+sBufn$2jhb9p=Gic^YTw_0rUP=aX37AyQGZv zGi`gD{hjXvsmd=uT`S?O)>WDQ(Uf;f9C=EnhMf&Ci1z>FaUo{z!+<_IxjIzi z$n4iDq2r09W+aI2B>TQkZ=^L$gI-9pryYL^L(JA2k7($drdw3zlv7|PI<=i?ky?L# zo=HN^jh$(?Y6X$DFJ(K*f=yNF-DPC2W_PB0gA|;OcqF(>c@~Lrhuu4AiwOAGn2kp@ z?xUTXkImKQxiT{;Drojr6i$C&o|b|A9IJcv1jni9>qR}-?Qc3(Tk`bPuO7?3NIAMX zDPt!uUFKUf@;|lHbhDLO&7yQ%k}J_S72P)?AVUaOA_&p&sKjUm0XEh)Pzcl-0=2_h z+rXg?a2q>wYiqc*_2i^v;$McV@mFGTssC?yPWFpc7=qKVAp}fBGB_bI9vAytG?N~T01D}YXmY!J=WjaS)1LqU literal 0 HcmV?d00001 diff --git a/control-panel/static/favicons/browserconfig.xml b/control-panel/static/favicons/browserconfig.xml new file mode 100644 index 0000000..e8b57e5 --- /dev/null +++ b/control-panel/static/favicons/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #b91d47 + + + diff --git a/control-panel/static/favicons/favicon-16x16.png b/control-panel/static/favicons/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..c9c768a00defd4c298704bcfa6e7f4559304cb96 GIT binary patch literal 815 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>L3?YlH{-ks~$@7%g|zHjo; z(D>_n_uqZ==+fLp2eXRi&7VJI>eP)hXCIGEzkTWQ`TnU#L*i~6ICOd8(*2o*UESTa zb#+U6`%fff9*symTi3cjt7vKOgxgoI-F@)j{KV-8jO`C*6wIAJe=^XJS#yr~M_!mR z>*Ab+Hx3`YbNkMvISY?Wo}QALI;*e$TvO-ivbxjdb=S6RzkC1Q|NsBHWtRX$gF&<; z$S;^R?%Lm5Y3%WDK4e~f@aN{^&xH@4a{8JIEdwfGO!9Vj37yQc!x+e6FY)wsWq-;g zz$7fkG2u8cco~*?x;TbNTuwdrl33hm%FE^7z5Dj>{Grqd8(KfVugSc{ zl(EI?kK1x_7Xyhv!O4<3E4oB;vK$=Tjz~;8|6ZA=U_pY0%)$(vc^=CyeR5HoeOkP0 zY0=WC)%zP2vrVGk_FZ}V_4Rz&7`DR`ugHE^4^C>xpLtgN^wp}(ci(n0Pe1%o#6EuA z_TSEIdj3Ck1ULOZ{^r#?hJ#Dq_=$I!C8<`) zMX5lF!N|bKP}k5v*U&7)z{twb(#qIC+rYrez@T!?-9i)%x%nxXX_dG&aC}TX3)B!^ z6%tVrlvu7%P?VpRnUkteQdy9ykXcZY%)n4F=kX^Vj>0evjZ^-o&v-r!VqjM0)=TCV zRu=Z2EW#|T;L>1nIE7hxbBMy}8&^&oIdeqj2>a;|^+ew_dS literal 0 HcmV?d00001 diff --git a/control-panel/static/favicons/favicon-32x32.png b/control-panel/static/favicons/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..fd5a65ed9421e4ec475a516c54371071840a6078 GIT binary patch literal 1294 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 UeZaz4v0K#8V}S7S$UMyZKZj1H2mk;8 literal 0 HcmV?d00001 diff --git a/control-panel/static/favicons/mstile-150x150.png b/control-panel/static/favicons/mstile-150x150.png new file mode 100644 index 0000000000000000000000000000000000000000..145dcc22c611c548ad15be18a3ab50444c15829c GIT binary patch literal 1727 zcmbtUc~H^`6#gMOo_S=OXh>+~Sq8$kd6g1|7n(|Dhp7SH3MT4L*)cKDER{;hOszJ$ zQ(Cf2vn>xi63ry9Xtfp8)HDOLENgdv`{(|#Gdt7Fd-LXdZ@%x%ym|8#><%EU)HT-y z0AQt$H!&ChUg-~9waS}Y&3m)3-?k%kHSsY+e}0Gc!( zq9+BE&y7eK6go0r^(LvI;1tC}enjUoHktLNg*fEs?@viS6t-*_tH~93>YN=Nw(Gq5 zyS+|(jo*m5iBP03;noKkRa+rkVh)+=56f_@0M{3|Hy=JH%BNpl$b2)z7?nOPcSif1 z+3>v~d!(D0@kh2Q4rO%>Uzln~srZ1b*oT{NZf`(XN$75E3jh3D?^Ek_u)mfMgcN5M zMG(4K)pfPD^9KG4Zu8>v%T%NK7SR$N*Ja^4MYR#IcT9~tj2_k)mLS~mexwoo$d5>N3Y?N#xZv!brssu+jA|jsNBF|>0;@%mB`6GN^FE2gEX>7MnzPCg$Vv< z+1arhWJh)p>9HJ-yx%9xb~}Va)eL1f3fP2=DA;!Ymko)L2!$zm2j8gt5pg|*?Pb9+M<5#0C>+A)&l#bC1$q9gl|dW zhuRUhMI+pIA>H>2(PKHg$HJ<>YU;TfG3L&dZMqbhnPO~Q#?sU!CC!N&liljf_>=aO zZRici7{jAi@HN!qNdb;Ml={Pi)@4~LW}KYJ)9cVzA)*_Fq}qF*rx#e31sxoC^@YKH zDO$2n>v|JawxI9l6u#4?IdE*(=whsZkOzkjA?#`{g%%$->g}EJT_hLwRoEf-g5~oI zqFuLF*Zz}zO-pnt+c*DM;J6lvy%`_^{%BRiF}uo}$HZhCgL%yS5px_3$=kE@(j z#QTP9**S|rrG~s$^SCCn0YxN3crgT*6c8*%)dc0rc-)dSMPDoW&RYIU``Kjr-BXg1 zrsmQrKSQ`=Df5_Xnf-%$L7W2{*E-S33pv5;R~S2Y3SXShLTr$T^AsuN7UhAt;oZyk z9|ZWcz$^=dyKPYL=iY8DkQ9eSjtKe>%vf?YWi9;|6`HKO^h!0T|}| zMBh-+DdvgrG(}2h-}(yK54SmeLJ1=>Q6$^s{g>$((0nRz-}&@ZZG^Nj-z%tv*^S^A ztBcBNg1VR<-U>UbZ`DP5yyXzHUu<+>j3{_UJcT z(W-nnLGYrXO}K)6Sr*`s!Hsu=-%Q}IPSNa*e&qjt>ucVV-~(A8*1EiSk+I}*(S9IA z&FoKi++)x;(zJy^D;@P;x6Bfh%^ZyUs?ke-Yx_hE7V~_AzA!y*wAZfqvi3MBGNt{* zCJ~#HqUGn5j(>Gs)O#%Wj`KU+J{VW9nxmh zJc8f8`4S{_kWVgOF1GRo11G?8FP)?x0`zI34Uv@N;NyZwy<}4($#WN ziC?G*9kn=jR9s>c=4^-Lq&N;FD;H^#ufXu?k@mOP&>mIw6mbbVFZcd_L;61oyie=Q za8V9zVsRy8!5dj#VJun{WFFKbbf^GM7$@vzEM_xyD+S}^f^~MmIXPf3E*Q)^TzA;N z2uTcDTy*At6D}sR#v#JqEJ_$FI4T3pJi>^MOQfS&nM^u5j>)0|09dJ1>uaZk+S+yn zO1pg~f&ut?Jx8jZ$$AsNlGSh%G%*L?N`tSjrJ$w5{hg9-2~wgZ&GEkE>j=!48ne`d R9|h0~z{e|qC?Z7W{tXJ}`_=#e literal 0 HcmV?d00001 diff --git a/control-panel/static/favicons/safari-pinned-tab.svg b/control-panel/static/favicons/safari-pinned-tab.svg new file mode 100644 index 0000000..1cd7cf2 --- /dev/null +++ b/control-panel/static/favicons/safari-pinned-tab.svg @@ -0,0 +1,20 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + + + + + diff --git a/control-panel/static/favicons/site.webmanifest b/control-panel/static/favicons/site.webmanifest new file mode 100644 index 0000000..de65106 --- /dev/null +++ b/control-panel/static/favicons/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-256x256.png", + "sizes": "256x256", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/control-panel/static/templates/index.hbs b/control-panel/static/templates/index.hbs index bfbb296..ea32a4c 100644 --- a/control-panel/static/templates/index.hbs +++ b/control-panel/static/templates/index.hbs @@ -1,15 +1,24 @@ - + + + + + + + + + + VelNet Control Panel