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