Show player names (WIP)
[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 active_players: String,
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, backup_path: String) -> Option<valheim_controller::ValheimExe> {
32 valheim_controller::get_valheim_executable_information(&world_path, &backup_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(), config.backup_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 active_players: info.format_active_players(),
48 last_backup: info.format_last_backup()
49 },
50 None => {
51 let value_unknown = String::from(VALUE_UNKNOWN);
52 MainTemplate {
53 text_status: String::from("Valheim server is down :("),
54 memory: value_unknown.clone(),
55 load_average: value_unknown.clone(),
56 uptime: value_unknown.clone(),
57 world_size: value_unknown.clone(),
58 active_players: value_unknown.clone(),
59 last_backup: value_unknown.clone() }
60 }
61 }
62 }
63
64 #[derive(Debug, Deserialize, Serialize)]
65 struct Config {
66 port: u16,
67
68 #[serde(default = "empty_string")]
69 world_path: String,
70
71 #[serde(default = "empty_string")]
72 backup_path: String,
73 }
74
75 fn empty_string() -> String { "".to_owned() }
76
77 impl Config {
78 fn default() -> Self {
79 Config { port: 8082, world_path: String::from(""), backup_path: String::from("") }
80 }
81 }
82
83 fn get_exe_name() -> String {
84 let first_arg = std::env::args().next().unwrap();
85 let sep: &[_] = &['\\', '/'];
86 first_arg[first_arg.rfind(sep).unwrap()+1..].to_string()
87 }
88
89 fn load_config() -> Config {
90 // unwrap_or_else(|_| panic!("Failed to open configuration file {}", consts::FILE_CONF));
91 match File::open(consts::FILE_CONF) {
92 Ok(file) => from_reader(file).unwrap_or_else(|_| panic!("Failed to open configuration file {}", consts::FILE_CONF)),
93 Err(_) => {
94 let mut file = File::create(consts::FILE_CONF) .unwrap();
95 let default_config = Config::default();
96 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.
97 default_config
98 }
99 }
100 }
101
102 #[actix_rt::main]
103 async fn main() -> std::io::Result<()> {
104 let config = load_config();
105 let port = config.port;
106
107 if process_args(&config) { return Ok(()) }
108
109 println!("Starting Valheim Admin as web server...");
110
111 println!("Configuration: {:?}", config);
112
113 let config_shared = web::Data::new(Mutex::new(config));
114
115 let server =
116 HttpServer::new(
117 move || {
118 App::new()
119 .app_data(config_shared.clone())
120 .wrap(middleware::Compress::default())
121 .wrap(middleware::Logger::default())
122 .service(main_page)
123 .service(fs::Files::new("/static", "static").show_files_listing())
124 }
125 )
126 .bind(&format!("0.0.0.0:{}", port))
127 .unwrap();
128
129 server.run().await
130 }
131
132 fn process_args(config: &Config) -> bool {
133 fn print_usage() {
134 println!("Usage:");
135 println!(" {} [--help] [--status]", get_exe_name());
136 }
137
138 let args: Vec<String> = args().collect();
139
140 if args.iter().any(|arg| arg == "--help") {
141 print_usage();
142 return true
143 } else if args.iter().any(|arg| arg == "--status") {
144 println!("{:?}", valheim_controller::get_valheim_executable_information(&config.world_path, &config.backup_path));
145 return true
146 }
147
148 false
149 }