added control panel site using rust
parent
b37edb0222
commit
961a688068
|
|
@ -3,3 +3,6 @@
|
|||
main
|
||||
main.exe
|
||||
main.pdb
|
||||
.idea/
|
||||
restarts.log
|
||||
nohup.out
|
||||
15
README.md
15
README.md
|
|
@ -4,8 +4,19 @@
|
|||
|
||||
1. Get a linoox server
|
||||
2. Clone this repo
|
||||
3. Install rust: `curl https://sh.rustup.rs -sSf | sh`
|
||||
4. Set up env: `source $HOME/.cargo/env` or add to `.bashrc`
|
||||
3. Install rust: `sudo apt install cargo`
|
||||
5. Build: `cargo build --release`
|
||||
6. Run: `sudo ./target/release/VelNetServerRust`
|
||||
7. Or run in the background so that it doesn't quit when you leave ssh: `nohup sudo ./target/release/VelNetServerRust`. You'll have to install `nohup` with apt.
|
||||
|
||||
|
||||
## Running with control panel server
|
||||
|
||||
You don't need to do both of these steps. The control panel runs the other server.
|
||||
|
||||
1. Get a linoox server
|
||||
2. Clone this repo
|
||||
3. Install rust: `sudo apt install cargo`
|
||||
3. Switch to control panel: `cd control-panel`
|
||||
5. Build: `cargo build --release`
|
||||
6. Run the web server in the background so that it doesn't quit when you leave ssh: `nohup sudo ./target/release/control-panel`. You'll have to install `nohup` with apt.
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
/target
|
||||
.vscode
|
||||
main
|
||||
main.exe
|
||||
main.pdb
|
||||
.idea/
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "control-panel"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
actix-web = "4"
|
||||
handlebars = { version = "4.2.1", features = ["dir_source"] }
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"port": 8080,
|
||||
"log_file": "/home/ntsfranz/Documents/VelNetServerRust/nohup.out",
|
||||
"server_dir": "/home/ntsfranz/Documents/VelNetServerRust/"
|
||||
}
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
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;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Config {
|
||||
port: u16,
|
||||
log_file: String,
|
||||
server_dir: String
|
||||
}
|
||||
|
||||
|
||||
fn lines_from_file(filename: impl AsRef<Path>) -> Vec<String> {
|
||||
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()
|
||||
}
|
||||
|
||||
// Macro documentation can be found in the actix_web_codegen crate
|
||||
#[get("/")]
|
||||
async fn index(hb: web::Data<Handlebars<'_>>) -> 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 data = json!({
|
||||
"log_output": log_file,
|
||||
"restarts_output": restarts_log,
|
||||
"uptime": format!("{}", String::from_utf8_lossy(&uptime.stdout)),
|
||||
});
|
||||
let body = hb.render("index", &data).unwrap();
|
||||
|
||||
HttpResponse::Ok().body(body)
|
||||
}
|
||||
|
||||
#[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);
|
||||
|
||||
let output = Command::new("cargo")
|
||||
.arg("build")
|
||||
.arg("--release")
|
||||
.output()
|
||||
.expect("failed to execute process");
|
||||
|
||||
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)
|
||||
})
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
|
||||
// Custom error handlers, to return HTML responses when an error occurs.
|
||||
fn error_handlers() -> ErrorHandlers<BoxBody> {
|
||||
ErrorHandlers::new().handler(StatusCode::NOT_FOUND, not_found)
|
||||
}
|
||||
|
||||
// Error handler for a 404 Page not found error.
|
||||
fn not_found<B>(res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<BoxBody>> {
|
||||
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<B>(res: &ServiceResponse<B>, error: &str) -> HttpResponse<BoxBody> {
|
||||
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::<web::Data<Handlebars>>()
|
||||
.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),
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>VelNet Control Panel</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
|
||||
|
||||
<style>
|
||||
.bottom-scroller {
|
||||
height: 30em;
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.log-output .panel-block {
|
||||
padding: .1em .75em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h1 class="title">
|
||||
VelNet Control Panel
|
||||
</h1>
|
||||
<p class="subtitle">
|
||||
Log output and utilites for <strong>VelNet</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<div class="block">
|
||||
<button class="button" id="restart-button">Restart Server</button>
|
||||
<button class="button" id="pull-button">Git Pull</button>
|
||||
<button class="button" id="compile-button">Compile</button>
|
||||
</div>
|
||||
<div class="block">Uptime: {{uptime}}</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<nav class="panel">
|
||||
<p class="panel-heading">
|
||||
Server Log
|
||||
<code>nohup.out</code>
|
||||
</p>
|
||||
|
||||
<div class="content bottom-scroller">
|
||||
<code class="log-output">
|
||||
<div>
|
||||
{{#each log_output}}
|
||||
<div class="panel-block">
|
||||
{{this}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</code>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
<nav class="panel">
|
||||
<p class="panel-heading">
|
||||
Server Restarts
|
||||
<code>restarts.log</code>
|
||||
</p>
|
||||
|
||||
<div class="content bottom-scroller">
|
||||
<code class="log-output">
|
||||
<div>
|
||||
{{#each restarts_output}}
|
||||
<div class="panel-block">
|
||||
{{this}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</code>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
document.getElementById('restart-button').addEventListener('click', c=> {
|
||||
fetch('/restart_server').then(r=> {
|
||||
setTimeout(location.reload(), 1000);
|
||||
});
|
||||
|
||||
});
|
||||
document.getElementById('pull-button').addEventListener('click', c=> {
|
||||
fetch('/git_pull').then(r=> {
|
||||
location.reload();
|
||||
});
|
||||
|
||||
});
|
||||
document.getElementById('compile-button').addEventListener('click', c=> {
|
||||
fetch('/compile').then(r=> {
|
||||
location.reload();
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
BASEDIR=$(dirname "$0")
|
||||
# pushd $BASEDIR
|
||||
echo $(date +"%Y-%m-%dT%T.%3N%z") >> $BASEDIR/restarts.log
|
||||
echo "Before: "
|
||||
pgrep VelNetServer
|
||||
pkill VelNetServer
|
||||
echo "\nAfter: "
|
||||
pgrep VelNetServer
|
||||
echo "\n"
|
||||
(cd $BASEDIR && nohup "./target/release/VelNetServerRust" &)
|
||||
# nohup "$BASEDIR/target/release/VelNetServerRust" &
|
||||
echo "Starting..." >> $BASEDIR/restarts.log
|
||||
# popd $BASEDIR
|
||||
Loading…
Reference in New Issue