breeze/src/main.rs

123 lines
3.0 KiB
Rust

use std::{path::PathBuf, sync::Arc};
use argh::FromArgs;
use color_eyre::eyre::{self, Context, bail};
use engine::Engine;
use axum::{
Router,
routing::{get, post},
};
use tokio::{fs, net::TcpListener, signal};
use tracing::{info, warn};
mod cache;
mod config;
mod delete;
mod disk;
mod engine;
mod index;
mod new;
mod view;
#[cfg(not(target_env = "msvc"))]
use tikv_jemallocator::Jemalloc;
#[cfg(not(target_env = "msvc"))]
#[global_allocator]
static GLOBAL: Jemalloc = Jemalloc;
/// breeze file server.
#[derive(FromArgs, Debug)]
struct Args {
/// the path to *.toml configuration file
#[argh(option, short = 'c', arg_name = "file")]
config: PathBuf,
}
#[tokio::main]
async fn main() -> eyre::Result<()> {
// Install color-eyre
color_eyre::install()?;
// Read & parse args
let args: Args = argh::from_env();
// Read & parse config
let cfg: config::Config = {
let config_str = fs::read_to_string(args.config).await.wrap_err(
"failed to read config file! make sure it exists and you have read permissions",
)?;
toml::from_str(&config_str).wrap_err(
"invalid config! ensure proper fields and structure. reference config is in readme",
)?
};
// Set up tracing
tracing_subscriber::fmt()
.with_max_level(cfg.logger.level)
.init();
// Check config
{
let save_path = cfg.engine.disk.save_path.clone();
if !save_path.exists() || !save_path.is_dir() {
bail!("the save path does not exist or is not a directory! this is invalid");
}
}
if cfg.engine.upload_key.is_empty() {
warn!("engine upload_key is empty! no key will be required for uploading new files");
}
// Create engine
let engine = Engine::with_config(cfg.engine);
// Build main router
let app = Router::new()
.route("/new", post(new::new))
.route("/p/{saved_name}", get(view::view))
.route("/del", get(delete::delete))
.route("/", get(index::index))
.route("/robots.txt", get(index::robots_txt))
.with_state(Arc::new(engine));
// Start web server
info!("starting server.");
let listener = TcpListener::bind(&cfg.http.listen_on)
.await
.wrap_err("failed to bind to given `http.listen_on` address! make sure it's valid, and the port isn't already bound")?;
axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal())
.await
.wrap_err("failed to start server")?;
Ok(())
}
async fn shutdown_signal() {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to add SIGINT handler");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to add SIGTERM handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
() = ctrl_c => {},
() = terminate => {},
}
info!("shutting down!");
}