a2201d4c8ad5b6efa1e9cd2af0f928721ebdbd66
[valheim_web.git] / backend / src / main.rs
1 extern crate askama;
2
3 use std::{ sync::Mutex, env::args, fs::File, io::prelude::* };
4
5 use actix_files as fs;
6 use actix_web::{ get, web, Responder, middleware, App, HttpServer };
7 use askama::Template;
8 use ron::{ de::from_reader, ser::{ to_string_pretty, PrettyConfig } };
9 use serde::{ Deserialize, Serialize };
10 use cached::proc_macro::cached;
11
12 mod consts;
13 mod tests;
14 mod valheim_controller;
15
16 #[derive(Template)]
17 #[template(path = "main.html")]
18 struct MainTemplate {
19 text_status: String,
20 memory: String,
21 load_average: String,
22 uptime: String,
23 world_size: String,
24 nb_of_players: u32,
25 last_backup: String,
26 }
27
28 const VALUE_UNKNOWN: &str = "-";
29
30 #[cached(size = 1, time = 10)]
31 fn get_valheim_executable_information_cached(world_path: String) -> Option<valheim_controller::ValheimExe> {
32 valheim_controller::get_valheim_executable_information(&world_path)
33 }
34
35 #[get("/")]
36 async fn main_page(config_shared: web::Data<Mutex<Config>>) -> impl Responder {
37 let config = config_shared.lock().unwrap();
38
39 match get_valheim_executable_information_cached(config.world_path.clone()) {
40 Some(info) =>
41 MainTemplate {
42 text_status: String::from("Valheim server is up and running :)"),
43 memory: info.format_memory(),
44 load_average: info.format_load_average(),
45 uptime: info.format_uptime(),
46 world_size: info.format_world_size(),
47 nb_of_players: info.get_nb_of_player(),
48 last_backup: info.format_last_backup()
49 },
50 None => {
51 let value_unknown = String::from(VALUE_UNKNOWN);
52 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, last_backup: value_unknown.clone() }
53 }
54 }
55 }
56
57 #[derive(Debug, Deserialize, Serialize)]
58 struct Config {
59 port: u16,
60
61 #[serde(default = "empty_string")]
62 world_path: String,
63
64 #[serde(default = "empty_string")]
65 backup_path: String,
66 }
67
68 fn empty_string() -> String { "".to_owned() }
69
70 impl Config {
71 fn default() -> Self {
72 Config { port: 8082, world_path: String::from(""), backup_path: String::from("") }
73 }
74 }
75
76 fn get_exe_name() -> String {
77 let first_arg = std::env::args().next().unwrap();
78 let sep: &[_] = &['\\', '/'];
79 first_arg[first_arg.rfind(sep).unwrap()+1..].to_string()
80 }
81
82 fn load_config() -> Config {
83 // unwrap_or_else(|_| panic!("Failed to open configuration file {}", consts::FILE_CONF));
84 match File::open(consts::FILE_CONF) {
85 Ok(file) => from_reader(file).unwrap_or_else(|_| panic!("Failed to open configuration file {}", consts::FILE_CONF)),
86 Err(_) => {
87 let mut file = File::create(consts::FILE_CONF) .unwrap();
88 let default_config = Config::default();
89 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.
90 default_config
91 }
92 }
93 }
94
95 #[actix_rt::main]
96 async fn main() -> std::io::Result<()> {
97 let config = load_config();
98 let port = config.port;
99
100 if process_args(&config) { return Ok(()) }
101
102 println!("Starting Valheim Admin as web server...");
103
104 println!("Configuration: {:?}", config);
105
106 let config_shared = web::Data::new(Mutex::new(config));
107
108 let server =
109 HttpServer::new(
110 move || {
111 App::new()
112 .app_data(config_shared.clone())
113 .wrap(middleware::Compress::default())
114 .wrap(middleware::Logger::default())
115 .service(main_page)
116 .service(fs::Files::new("/static", "static").show_files_listing())
117 }
118 )
119 .bind(&format!("0.0.0.0:{}", port))
120 .unwrap();
121
122 server.run().await
123 }
124
125 fn process_args(config: &Config) -> bool {
126 fn print_usage() {
127 println!("Usage:");
128 println!(" {} [--help] [--status]", get_exe_name());
129 }
130
131 let args: Vec<String> = args().collect();
132
133 if args.iter().any(|arg| arg == "--help") {
134 print_usage();
135 return true
136 } else if args.iter().any(|arg| arg == "--status") {
137 println!("{:?}", valheim_controller::get_valheim_executable_information(&config.world_path, &config.backup_path));
138 return true
139 }
140
141 false
142 }