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