Use a cached function
[valheim_web.git] / backend / src / main.rs
index 99bebc3..03963c1 100644 (file)
@@ -1,43 +1,71 @@
-
-extern crate listenfd;
 extern crate askama;
 
-use listenfd::ListenFd;
+use std::{ sync::Mutex, env::args, fs::File, io::prelude::* };
+
 use actix_files as fs;
-use actix_web::{ get, web, Responder, middleware, App, HttpServer, web::Query };
+use actix_web::{ get, web, Responder, middleware, App, HttpServer };
 use askama::Template;
-
-use std::{ sync::Mutex, fs::File, env::args, io::prelude::* };
 use ron::{ de::from_reader, ser::{ to_string_pretty, PrettyConfig } };
 use serde::{ Deserialize, Serialize };
+use cached::proc_macro::cached;
 
 mod consts;
+mod tests;
+mod valheim_controller;
 
 #[derive(Template)]
 #[template(path = "main.html")]
 struct MainTemplate {
-    sentence: String,
+    text_status: String,
+    memory: String,
+    load_average: String,
+    uptime: String,
+    world_size: String,
+    nb_of_players: u32,
 }
 
-#[derive(Deserialize)]
-pub struct Request {
-   m: Option<String>
+const VALUE_UNKNOWN: &str = "-";
+
+#[cached(size = 1, time = 10)]
+fn get_valheim_executable_information_cached(world_path: String) -> Option<valheim_controller::ValheimExe> {
+    valheim_controller::get_valheim_executable_information(&world_path)
 }
 
 #[get("/")]
-async fn main_page(/*key_shared: web::Data<Mutex<String>>,*/ query: Query<Request>) -> impl Responder {
-    //let key = key_shared.lock().unwrap();
-
-    let m = String::from("hello");
-    MainTemplate { sentence: m }
+async fn main_page(config_shared: web::Data<Mutex<Config>>) -> impl Responder {
+    let config = config_shared.lock().unwrap();
+
+    match get_valheim_executable_information_cached(config.world_path.clone()) {
+        Some(info) =>
+            MainTemplate {
+                text_status: String::from("Valheim server is up and running :)"),
+                memory: info.format_memory(),
+                load_average: info.format_load_average(),
+                uptime: info.format_uptime(),
+                world_size: info.format_world_size(),
+                nb_of_players: info.get_nb_of_player()
+            },
+        None => {
+            let value_unknown = String::from(VALUE_UNKNOWN);
+            MainTemplate { text_status: String::from("Valheim server is down :("), memory: value_unknown.clone(), load_average: value_unknown.clone(), uptime: value_unknown.clone(), world_size: value_unknown.clone(), nb_of_players: 0 }
+        }
+    }
 }
 
 #[derive(Debug, Deserialize, Serialize)]
 struct Config {
-    port: u16
+    port: u16,
+    #[serde(default = "empty_string")]
+    world_path: String,
 }
 
-const DEFAULT_CONFIG: Config = Config { port: 8082 };
+fn empty_string() -> String { "".to_owned() }
+
+impl Config {
+    fn default() -> Self {
+        Config { port: 8082, world_path: String::from("") }
+    }
+}
 
 fn get_exe_name() -> String {
     let first_arg = std::env::args().next().unwrap();
@@ -51,50 +79,47 @@ fn load_config() -> Config {
         Ok(file) => from_reader(file).unwrap_or_else(|_| panic!("Failed to open configuration file {}", consts::FILE_CONF)),
         Err(_) => {
             let mut file = File::create(consts::FILE_CONF) .unwrap();
-            file.write_all(to_string_pretty(&DEFAULT_CONFIG, PrettyConfig::new()).unwrap().as_bytes()).unwrap(); // We do not use 'to_writer' because it can't pretty format the output.
-            DEFAULT_CONFIG
+            let default_config = Config::default();
+            file.write_all(to_string_pretty(&default_config, PrettyConfig::new()).unwrap().as_bytes()).unwrap(); // We do not use 'to_writer' because it can't pretty format the output.
+            default_config
         }
     }
 }
 
 #[actix_rt::main]
 async fn main() -> std::io::Result<()> {
+    let config = load_config();
+    let port = config.port;
 
-    if process_args() { return Ok(()) }
-
-    println!("Starting RUP as web server...");
+    if process_args(&config) { return Ok(()) }
 
-    let config = load_config();
+    println!("Starting Valheim Admin as web server...");
 
     println!("Configuration: {:?}", config);
 
-    let mut listenfd = ListenFd::from_env();
-    let mut server =
+    let config_shared = web::Data::new(Mutex::new(config));
+
+    let server =
         HttpServer::new(
             move || {
                 App::new()
-                    // .app_data(key_shared.clone())
+                    .app_data(config_shared.clone())
                     .wrap(middleware::Compress::default())
                     .wrap(middleware::Logger::default())
                     .service(main_page)
                     .service(fs::Files::new("/static", "static").show_files_listing())
             }
-        );
-
-    server =
-        if let Some(l) = listenfd.take_tcp_listener(0).unwrap() {
-            server.listen(l).unwrap()
-        } else {
-            server.bind(&format!("0.0.0.0:{}", config.port)).unwrap()
-        };
+        )
+        .bind(&format!("0.0.0.0:{}", port))
+        .unwrap();
 
     server.run().await
 }
 
-fn process_args() -> bool {
+fn process_args(config: &Config) -> bool {
     fn print_usage() {
         println!("Usage:");
-        println!(" {} [--help]", get_exe_name());
+        println!(" {} [--help] [--status]", get_exe_name());
     }
 
     let args: Vec<String> = args().collect();
@@ -102,6 +127,9 @@ fn process_args() -> bool {
     if args.iter().any(|arg| arg == "--help") {
         print_usage();
         return true
+    } else if args.iter().any(|arg| arg == "--status") {
+        println!("{:?}", valheim_controller::get_valheim_executable_information(&config.world_path));
+        return true
     }
 
     false