♻️ refactor: move ptth_server's main into its library so I can make a busybox-style multi-use binary
parent
b8c370a0a6
commit
0b10737403
|
@ -1158,6 +1158,7 @@ dependencies = [
|
||||||
name = "ptth_core"
|
name = "ptth_core"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
"futures",
|
"futures",
|
||||||
|
|
|
@ -10,6 +10,7 @@ description = "Common code for the PTTH relay and server"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
|
anyhow = "1.0.38"
|
||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
ctrlc = { version = "3.1.8", features = [ "termination" ] }
|
ctrlc = { version = "3.1.8", features = [ "termination" ] }
|
||||||
futures = "0.3.7"
|
futures = "0.3.7"
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
pub use std::{
|
pub use std::{
|
||||||
|
io::Write,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub use anyhow::{
|
||||||
|
Context,
|
||||||
|
bail,
|
||||||
|
};
|
||||||
pub use tracing::{
|
pub use tracing::{
|
||||||
debug, error, info, trace, warn,
|
debug, error, info, trace, warn,
|
||||||
instrument,
|
instrument,
|
||||||
|
|
|
@ -1,126 +1,8 @@
|
||||||
#![warn (clippy::pedantic)]
|
#![warn (clippy::pedantic)]
|
||||||
|
|
||||||
use std::{
|
|
||||||
fs::File,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
use structopt::StructOpt;
|
|
||||||
|
|
||||||
use ptth_server::{
|
|
||||||
load_toml,
|
|
||||||
prelude::*,
|
|
||||||
run_server,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive (Debug, StructOpt)]
|
|
||||||
struct Opt {
|
|
||||||
#[structopt (long)]
|
|
||||||
auto_gen_key: bool,
|
|
||||||
|
|
||||||
#[structopt (long)]
|
|
||||||
throttle_upload: bool,
|
|
||||||
|
|
||||||
#[structopt (long)]
|
|
||||||
file_server_root: Option <PathBuf>,
|
|
||||||
|
|
||||||
#[structopt (long)]
|
|
||||||
asset_root: Option <PathBuf>,
|
|
||||||
|
|
||||||
#[structopt (long)]
|
|
||||||
config_path: Option <PathBuf>,
|
|
||||||
|
|
||||||
#[structopt (long)]
|
|
||||||
name: Option <String>,
|
|
||||||
|
|
||||||
#[structopt (long)]
|
|
||||||
print_tripcode: bool,
|
|
||||||
|
|
||||||
#[structopt (long)]
|
|
||||||
relay_url: Option <String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gen_and_save_key (path: &Path) -> anyhow::Result <()> {
|
|
||||||
let api_key = ptth_core::gen_key ();
|
|
||||||
|
|
||||||
let mut f = File::create (path).with_context (|| format! ("Can't create config file `{:?}`", path))?;
|
|
||||||
|
|
||||||
#[cfg (unix)]
|
|
||||||
{
|
|
||||||
use std::os::unix::fs::PermissionsExt;
|
|
||||||
|
|
||||||
let metadata = f.metadata ()?;
|
|
||||||
let mut permissions = metadata.permissions ();
|
|
||||||
permissions.set_mode (0o600);
|
|
||||||
f.set_permissions (permissions)?;
|
|
||||||
}
|
|
||||||
#[cfg (not (unix))]
|
|
||||||
{
|
|
||||||
tracing::warn! ("Error VR6VW5QT: API keys aren't protected from clients on non-Unix OSes yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
f.write_all (format! ("api_key = \"{}\"\n", api_key).as_bytes ())?;
|
|
||||||
|
|
||||||
Ok (())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main () -> Result <(), anyhow::Error> {
|
async fn main () -> anyhow::Result <()> {
|
||||||
tracing_subscriber::fmt::init ();
|
tracing_subscriber::fmt::init ();
|
||||||
|
|
||||||
let opt = Opt::from_args ();
|
ptth_server::executable::main ().await
|
||||||
let asset_root = opt.asset_root;
|
|
||||||
|
|
||||||
let path = opt.config_path.clone ().unwrap_or_else (|| PathBuf::from ("./config/ptth_server.toml"));
|
|
||||||
|
|
||||||
let config_file: ConfigFile = if opt.auto_gen_key {
|
|
||||||
// If we're in autonomous mode, try harder to fix things
|
|
||||||
|
|
||||||
match load_toml::load (&path) {
|
|
||||||
Err (_) => {
|
|
||||||
gen_and_save_key (&path)?;
|
|
||||||
|
|
||||||
load_toml::load (&path)?
|
|
||||||
},
|
|
||||||
Ok (x) => x,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
match load_toml::load (&path) {
|
|
||||||
Err (ptth_server::errors::LoadTomlError::Io (_)) => bail! ("API key not provided in config file and auto-gen-key not provided"),
|
|
||||||
Ok (x) => x,
|
|
||||||
Err (e) => return Err (e.into ()),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let config_file = ptth_server::ConfigFile {
|
|
||||||
name: opt.name.or (config_file.name).ok_or (anyhow::anyhow! ("`name` must be provided in command line or config file"))?,
|
|
||||||
api_key: config_file.api_key,
|
|
||||||
relay_url: opt.relay_url.or (config_file.relay_url).ok_or (anyhow::anyhow! ("`--relay-url` must be provided in command line or `relay_url` in config file"))?,
|
|
||||||
file_server_root: opt.file_server_root.or (config_file.file_server_root),
|
|
||||||
throttle_upload: opt.throttle_upload,
|
|
||||||
};
|
|
||||||
|
|
||||||
if opt.print_tripcode {
|
|
||||||
println! (r#"name = "{}""#, config_file.name);
|
|
||||||
println! (r#"tripcode = "{}""#, config_file.tripcode ());
|
|
||||||
return Ok (());
|
|
||||||
}
|
|
||||||
|
|
||||||
run_server (
|
|
||||||
config_file,
|
|
||||||
ptth_core::graceful_shutdown::init (),
|
|
||||||
Some (path),
|
|
||||||
asset_root
|
|
||||||
).await?;
|
|
||||||
|
|
||||||
Ok (())
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,17 @@
|
||||||
// False positive on futures::select! macro
|
// False positive on futures::select! macro
|
||||||
#![allow (clippy::mut_mut)]
|
#![allow (clippy::mut_mut)]
|
||||||
|
|
||||||
|
pub mod errors;
|
||||||
|
|
||||||
|
/// In-process file server module with byte range and ETag support
|
||||||
|
pub mod file_server;
|
||||||
|
|
||||||
|
/// Load and de-serialize structures from TOML, with a size limit
|
||||||
|
/// and checking permissions (On Unix)
|
||||||
|
pub mod load_toml;
|
||||||
|
|
||||||
|
pub mod prelude;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
future::Future,
|
future::Future,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
|
@ -58,21 +69,10 @@ use tokio_stream::wrappers::ReceiverStream;
|
||||||
|
|
||||||
use ptth_core::{
|
use ptth_core::{
|
||||||
http_serde,
|
http_serde,
|
||||||
prelude::*,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod errors;
|
|
||||||
|
|
||||||
/// In-process file server module with byte range and ETag support
|
|
||||||
pub mod file_server;
|
|
||||||
|
|
||||||
/// Load and de-serialize structures from TOML, with a size limit
|
|
||||||
/// and checking permissions (On Unix)
|
|
||||||
pub mod load_toml;
|
|
||||||
|
|
||||||
pub mod prelude;
|
|
||||||
|
|
||||||
use errors::ServerError;
|
use errors::ServerError;
|
||||||
|
use prelude::*;
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
// file_server: file_server::FileServer,
|
// file_server: file_server::FileServer,
|
||||||
|
@ -480,6 +480,128 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod executable {
|
||||||
|
use std::{
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
use structopt::StructOpt;
|
||||||
|
use super::{
|
||||||
|
load_toml,
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn main () -> anyhow::Result <()> {
|
||||||
|
let opt = Opt::from_args ();
|
||||||
|
let asset_root = opt.asset_root;
|
||||||
|
|
||||||
|
let path = opt.config_path.clone ().unwrap_or_else (|| PathBuf::from ("./config/ptth_server.toml"));
|
||||||
|
|
||||||
|
let config_file: ConfigFile = if opt.auto_gen_key {
|
||||||
|
// If we're in autonomous mode, try harder to fix things
|
||||||
|
|
||||||
|
match load_toml::load (&path) {
|
||||||
|
Err (_) => {
|
||||||
|
gen_and_save_key (&path)?;
|
||||||
|
|
||||||
|
load_toml::load (&path)?
|
||||||
|
},
|
||||||
|
Ok (x) => x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
match load_toml::load (&path) {
|
||||||
|
Err (super::errors::LoadTomlError::Io (_)) => bail! ("API key not provided in config file and auto-gen-key not provided"),
|
||||||
|
Ok (x) => x,
|
||||||
|
Err (e) => return Err (e.into ()),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let config_file = super::ConfigFile {
|
||||||
|
name: opt.name.or (config_file.name).ok_or (anyhow::anyhow! ("`name` must be provided in command line or config file"))?,
|
||||||
|
api_key: config_file.api_key,
|
||||||
|
relay_url: opt.relay_url.or (config_file.relay_url).ok_or (anyhow::anyhow! ("`--relay-url` must be provided in command line or `relay_url` in config file"))?,
|
||||||
|
file_server_root: opt.file_server_root.or (config_file.file_server_root),
|
||||||
|
throttle_upload: opt.throttle_upload,
|
||||||
|
};
|
||||||
|
|
||||||
|
if opt.print_tripcode {
|
||||||
|
println! (r#"name = "{}""#, config_file.name);
|
||||||
|
println! (r#"tripcode = "{}""#, config_file.tripcode ());
|
||||||
|
return Ok (());
|
||||||
|
}
|
||||||
|
|
||||||
|
super::run_server (
|
||||||
|
config_file,
|
||||||
|
ptth_core::graceful_shutdown::init (),
|
||||||
|
Some (path),
|
||||||
|
asset_root
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
Ok (())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive (Debug, StructOpt)]
|
||||||
|
struct Opt {
|
||||||
|
#[structopt (long)]
|
||||||
|
auto_gen_key: bool,
|
||||||
|
|
||||||
|
#[structopt (long)]
|
||||||
|
throttle_upload: bool,
|
||||||
|
|
||||||
|
#[structopt (long)]
|
||||||
|
file_server_root: Option <PathBuf>,
|
||||||
|
|
||||||
|
#[structopt (long)]
|
||||||
|
asset_root: Option <PathBuf>,
|
||||||
|
|
||||||
|
#[structopt (long)]
|
||||||
|
config_path: Option <PathBuf>,
|
||||||
|
|
||||||
|
#[structopt (long)]
|
||||||
|
name: Option <String>,
|
||||||
|
|
||||||
|
#[structopt (long)]
|
||||||
|
print_tripcode: bool,
|
||||||
|
|
||||||
|
#[structopt (long)]
|
||||||
|
relay_url: Option <String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive (Default, serde::Deserialize)]
|
||||||
|
struct ConfigFile {
|
||||||
|
pub name: Option <String>,
|
||||||
|
pub api_key: String,
|
||||||
|
pub relay_url: Option <String>,
|
||||||
|
pub file_server_root: Option <PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_and_save_key (path: &Path) -> anyhow::Result <()> {
|
||||||
|
use std::fs::File;
|
||||||
|
|
||||||
|
let api_key = ptth_core::gen_key ();
|
||||||
|
|
||||||
|
let mut f = File::create (path).with_context (|| format! ("Can't create config file `{:?}`", path))?;
|
||||||
|
|
||||||
|
#[cfg (unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
|
let metadata = f.metadata ()?;
|
||||||
|
let mut permissions = metadata.permissions ();
|
||||||
|
permissions.set_mode (0o600);
|
||||||
|
f.set_permissions (permissions)?;
|
||||||
|
}
|
||||||
|
#[cfg (not (unix))]
|
||||||
|
{
|
||||||
|
tracing::warn! ("Error VR6VW5QT: API keys aren't protected from clients on non-Unix OSes yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
f.write_all (format! ("api_key = \"{}\"\n", api_key).as_bytes ())?;
|
||||||
|
|
||||||
|
Ok (())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg (test)]
|
#[cfg (test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1,8 +1 @@
|
||||||
pub use std::{
|
pub use ptth_core::prelude::*;
|
||||||
io::Write,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use anyhow::{
|
|
||||||
Context,
|
|
||||||
bail,
|
|
||||||
};
|
|
||||||
|
|
Loading…
Reference in New Issue