use std::{ collections::*, path::PathBuf, }; use fltk::{ app, button::Button, enums::CallbackTrigger, frame::Frame, input::*, prelude::*, window::Window }; use tokio::{ sync::{ mpsc, oneshot, }, }; struct ServerInstance { task: tokio::task::JoinHandle <()>, shutdown_tx: oneshot::Sender <()>, } #[derive (Clone, Copy)] enum Message { Hit, RunServer, StopServer, } fn main () { let rt = tokio::runtime::Runtime::new ().unwrap (); tracing_subscriber::fmt::init (); let mut server_instance = None; // let shutdown_rx = ptth_core::graceful_shutdown::init (); let (fltk_tx, fltk_rx) = app::channel:: (); let app = app::App::default(); let mut wind = Window::new (100, 100, 500, 180, "PTTH server"); let config_file_opt = match ptth_server::load_toml::load:: ("./config/ptth_server.toml") { Ok (x) => Some (x), Err (e) => { eprintln! ("Error in `./config/ptth_server.toml`: {:?}", e); None }, }; let (hit_tx, mut hit_rx) = mpsc::channel (1); { let fltk_tx = fltk_tx; rt.spawn (async move { while hit_rx.recv ().await.is_some () { fltk_tx.send (Message::Hit); } }); } let mut gui = Gui::new (fltk_tx, config_file_opt.as_ref ()); gui.set_server_running (false); wind.end (); wind.show (); while app.wait () { match fltk_rx.recv () { Some (Message::Hit) => { gui.hits += 1; gui.refresh_label (); }, Some (Message::RunServer) => { let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel (); let roots = match &config_file_opt { None => Default::default (), Some (cf) => match &cf.file_server_roots { None => Default::default (), Some (x) => x.clone (), }, }; // `git grep JRY5NXZU` # duplicated code? let config_file = ptth_server::ConfigFile { name: gui.input_name.value ().to_string (), api_key: gui.input_api_key.value ().to_string (), relay_url: gui.input_relay_url.value ().to_string (), file_server_root: PathBuf::from (gui.input_file_server_root.value ()), file_server_roots: roots, throttle_upload: false, client_keys: Default::default (), allow_any_client: true, index_directories: true, }; let hit_tx = hit_tx.clone (); let task = rt.spawn (async { if let Err (e) = ptth_server::run_server ( config_file, shutdown_rx, None, None, Some (hit_tx), ).await { tracing::error! ("ptth_server crashed: {}", e); } }); server_instance.replace (Some (ServerInstance { task, shutdown_tx, })); gui.set_server_running (true); }, Some (Message::StopServer) => { server_instance.replace (None); gui.set_server_running (false); }, None => (), } } } struct Gui { frame: Frame, but_run: Button, but_stop: Button, input_name: Input, input_relay_url: Input, input_file_server_root: Input, input_api_key: SecretInput, server_is_running: bool, hits: u64, } #[derive (Default, serde::Deserialize)] pub struct ConfigFile { pub name: Option , pub api_key: String, pub relay_url: Option , pub file_server_root: Option , pub file_server_roots: Option >, } impl Gui { fn new ( fltk_tx: app::Sender , config_file_opt: Option <&ConfigFile>, ) -> Self { let mut input_name = Input::new (200, 10, 290, 20, "name"); input_name.set_value ("my_ptth_server"); let mut input_relay_url = Input::new (200, 40, 290, 20, "relay_url"); input_relay_url.set_value ("http://127.0.0.1:4000/7ZSFUKGV"); let mut input_file_server_root = Input::new (200, 70, 290, 20, "file_server_root"); input_file_server_root.set_value ("/home/user"); let mut input_api_key = SecretInput::new (200, 100, 290, 20, "api_key"); input_api_key.set_value (""); let mut but_run = Button::new (10, 130, 90, 40, "Run"); but_run.set_trigger (CallbackTrigger::Changed); but_run.emit (fltk_tx, Message::RunServer); let frame = Frame::new (110, 130, 280, 40, ""); let mut but_stop = Button::new (400, 130, 90, 40, "Stop"); but_stop.set_trigger (CallbackTrigger::Changed); but_stop.emit (fltk_tx, Message::StopServer); if let Some (config_file) = config_file_opt { if let Some (v) = config_file.name.as_ref () { input_name.set_value (v); } input_api_key.set_value (&config_file.api_key); if let Some (v) = config_file.relay_url.as_ref () { input_relay_url.set_value (v); } if let Some (v) = config_file.file_server_root.as_ref () { if let Some (v) = v.to_str () { input_file_server_root.set_value (v); } } } Self { frame, but_run, but_stop, input_name, input_relay_url, input_file_server_root, input_api_key, server_is_running: false, hits: 0, } } fn set_server_running (&mut self, b: bool) { self.server_is_running = b; self.refresh_label (); set_active (&mut self.but_run, ! b); set_active (&mut self.but_stop, b); self.but_run.set (b); self.but_stop.set (! b); set_active (&mut self.input_name, ! b); set_active (&mut self.input_relay_url, ! b); set_active (&mut self.input_file_server_root, ! b); set_active (&mut self.input_api_key, ! b); } fn refresh_label (&mut self) { let s = if self.server_is_running { format! ("Running. Requests: {}", self.hits) } else { "Stopped".to_string () }; self.frame.set_label (&s); } } fn set_active (w: &mut W, b: bool) { if b { w.activate (); } else { w.deactivate (); } }