255 lines
5.5 KiB
Rust
255 lines
5.5 KiB
Rust
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::<Message> ();
|
|
|
|
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::<ConfigFile, _> ("./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 <String>,
|
|
pub api_key: String,
|
|
pub relay_url: Option <String>,
|
|
pub file_server_root: Option <PathBuf>,
|
|
pub file_server_roots: Option <BTreeMap <String, PathBuf>>,
|
|
}
|
|
|
|
impl Gui {
|
|
fn new (
|
|
fltk_tx: app::Sender <Message>,
|
|
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: WidgetExt> (w: &mut W, b: bool) {
|
|
if b {
|
|
w.activate ();
|
|
}
|
|
else {
|
|
w.deactivate ();
|
|
}
|
|
}
|