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