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