2021-07-19 18:35:52 -04:00
|
|
|
use actix_web::middleware::Logger;
|
2022-03-17 08:41:34 -04:00
|
|
|
use actix_web::web::Data;
|
2023-05-14 12:03:53 -04:00
|
|
|
#[cfg(not(feature = "shuttle"))]
|
2021-07-23 15:52:00 -04:00
|
|
|
use actix_web::{App, HttpServer};
|
2022-03-17 08:41:34 -04:00
|
|
|
use awc::ClientBuilder;
|
2021-11-07 08:06:57 -05:00
|
|
|
use hotwatch::{Event, Hotwatch};
|
2023-05-14 12:03:53 -04:00
|
|
|
use rustypaste::config::{Config, ServerConfig};
|
2021-08-26 14:09:33 -04:00
|
|
|
use rustypaste::paste::PasteType;
|
2021-07-24 13:46:22 -04:00
|
|
|
use rustypaste::server;
|
2022-03-23 04:38:32 -04:00
|
|
|
use rustypaste::util;
|
2022-03-17 08:54:08 -04:00
|
|
|
use rustypaste::CONFIG_ENV;
|
2021-07-24 13:51:08 -04:00
|
|
|
use std::env;
|
2021-07-23 15:52:00 -04:00
|
|
|
use std::fs;
|
|
|
|
use std::io::Result as IoResult;
|
2023-05-14 12:03:53 -04:00
|
|
|
use std::path::{Path, PathBuf};
|
2022-03-23 04:38:32 -04:00
|
|
|
use std::sync::{mpsc, RwLock};
|
|
|
|
use std::thread;
|
2021-11-06 16:55:55 -04:00
|
|
|
use std::time::Duration;
|
2023-05-14 12:03:53 -04:00
|
|
|
#[cfg(feature = "shuttle")]
|
|
|
|
use {
|
|
|
|
actix_web::web::{self, ServiceConfig},
|
|
|
|
shuttle_actix_web::ShuttleActixWeb,
|
|
|
|
};
|
2021-07-19 18:35:52 -04:00
|
|
|
|
2023-05-14 12:03:53 -04:00
|
|
|
/// Sets up the application.
|
|
|
|
///
|
|
|
|
/// * loads the configuration
|
|
|
|
/// * initializes the logger
|
|
|
|
/// * creates the necessary directories
|
|
|
|
/// * spawns the threads
|
|
|
|
fn setup(config_folder: &Path) -> IoResult<(Data<RwLock<Config>>, ServerConfig, Hotwatch)> {
|
2022-05-21 02:07:37 -04:00
|
|
|
// Load the .env file.
|
2022-10-04 06:02:51 -04:00
|
|
|
dotenvy::dotenv().ok();
|
2022-05-21 02:07:37 -04:00
|
|
|
|
2021-11-07 08:06:57 -05:00
|
|
|
// Initialize logger.
|
2023-05-14 12:03:53 -04:00
|
|
|
#[cfg(not(feature = "shuttle"))]
|
2021-07-19 18:35:52 -04:00
|
|
|
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
2021-11-07 08:06:57 -05:00
|
|
|
|
|
|
|
// Parse configuration.
|
2022-03-13 14:12:10 -04:00
|
|
|
let config_path = match env::var(CONFIG_ENV).ok() {
|
|
|
|
Some(path) => {
|
|
|
|
env::remove_var(CONFIG_ENV);
|
|
|
|
PathBuf::from(path)
|
|
|
|
}
|
2023-05-14 12:03:53 -04:00
|
|
|
None => config_folder.join("config.toml"),
|
2022-03-13 14:12:10 -04:00
|
|
|
};
|
2022-03-11 16:02:25 -05:00
|
|
|
let config = Config::parse(&config_path).expect("failed to parse config");
|
2022-05-20 23:26:17 -04:00
|
|
|
log::trace!("{:#?}", config);
|
2022-03-11 16:02:25 -05:00
|
|
|
let server_config = config.server.clone();
|
2022-03-23 04:38:32 -04:00
|
|
|
let paste_config = RwLock::new(config.paste.clone());
|
|
|
|
let (config_sender, config_receiver) = mpsc::channel::<Config>();
|
2021-11-07 08:06:57 -05:00
|
|
|
|
|
|
|
// Create necessary directories.
|
2021-08-04 10:35:54 -04:00
|
|
|
fs::create_dir_all(&server_config.upload_path)?;
|
2021-08-27 09:05:40 -04:00
|
|
|
for paste_type in &[PasteType::Url, PasteType::Oneshot] {
|
2021-08-26 15:57:46 -04:00
|
|
|
fs::create_dir_all(paste_type.get_path(&server_config.upload_path))?;
|
|
|
|
}
|
2021-11-07 08:06:57 -05:00
|
|
|
|
|
|
|
// Set up a watcher for the configuration file changes.
|
2022-03-11 16:02:25 -05:00
|
|
|
let mut hotwatch = Hotwatch::new_with_custom_delay(
|
|
|
|
config
|
2022-03-13 14:12:10 -04:00
|
|
|
.settings
|
2022-03-11 16:02:25 -05:00
|
|
|
.as_ref()
|
|
|
|
.map(|v| v.refresh_rate)
|
|
|
|
.unwrap_or_else(|| Duration::from_secs(1)),
|
|
|
|
)
|
|
|
|
.expect("failed to initialize configuration file watcher");
|
2021-11-07 08:06:57 -05:00
|
|
|
|
|
|
|
// Hot-reload the configuration file.
|
2022-03-17 08:41:34 -04:00
|
|
|
let config = Data::new(RwLock::new(config));
|
|
|
|
let cloned_config = Data::clone(&config);
|
2021-11-16 11:27:18 -05:00
|
|
|
let config_watcher = move |event: Event| {
|
|
|
|
if let Event::Write(path) = event {
|
|
|
|
match Config::parse(&path) {
|
2021-12-05 07:03:51 -05:00
|
|
|
Ok(config) => match cloned_config.write() {
|
2021-11-16 11:27:18 -05:00
|
|
|
Ok(mut cloned_config) => {
|
2022-03-23 04:38:32 -04:00
|
|
|
*cloned_config = config.clone();
|
2021-11-07 08:06:57 -05:00
|
|
|
log::info!("Configuration has been updated.");
|
2022-03-23 04:38:32 -04:00
|
|
|
if let Err(e) = config_sender.send(config) {
|
|
|
|
log::error!("Failed to send config for the cleanup routine: {}", e)
|
|
|
|
}
|
2021-11-07 08:06:57 -05:00
|
|
|
}
|
|
|
|
Err(e) => {
|
2022-03-23 04:38:32 -04:00
|
|
|
log::error!("Failed to acquire config: {}", e);
|
2021-11-07 08:06:57 -05:00
|
|
|
}
|
2021-11-16 11:27:18 -05:00
|
|
|
},
|
|
|
|
Err(e) => {
|
2022-03-23 04:38:32 -04:00
|
|
|
log::error!("Failed to update config: {}", e);
|
2021-11-07 08:06:57 -05:00
|
|
|
}
|
|
|
|
}
|
2021-11-16 11:27:18 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
hotwatch
|
|
|
|
.watch(&config_path, config_watcher)
|
2022-12-18 15:12:28 -05:00
|
|
|
.unwrap_or_else(|_| panic!("failed to watch {config_path:?}"));
|
2021-11-07 08:06:57 -05:00
|
|
|
|
2022-03-23 04:38:32 -04:00
|
|
|
// Create a thread for cleaning up expired files.
|
2023-05-14 12:03:53 -04:00
|
|
|
let upload_path = server_config.upload_path.clone();
|
2022-03-23 04:38:32 -04:00
|
|
|
thread::spawn(move || loop {
|
|
|
|
let mut enabled = false;
|
|
|
|
if let Some(ref cleanup_config) = paste_config
|
|
|
|
.read()
|
|
|
|
.ok()
|
|
|
|
.and_then(|v| v.delete_expired_files.clone())
|
|
|
|
{
|
|
|
|
if cleanup_config.enabled {
|
|
|
|
log::debug!("Running cleanup...");
|
2023-05-14 12:03:53 -04:00
|
|
|
for file in util::get_expired_files(&upload_path) {
|
2022-03-23 04:38:32 -04:00
|
|
|
match fs::remove_file(&file) {
|
|
|
|
Ok(()) => log::info!("Removed expired file: {:?}", file),
|
|
|
|
Err(e) => log::error!("Cannot remove expired file: {}", e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
thread::sleep(cleanup_config.interval);
|
|
|
|
}
|
|
|
|
enabled = cleanup_config.enabled;
|
|
|
|
}
|
|
|
|
if let Some(new_config) = if enabled {
|
|
|
|
config_receiver.try_recv().ok()
|
|
|
|
} else {
|
|
|
|
config_receiver.recv().ok()
|
|
|
|
} {
|
|
|
|
match paste_config.write() {
|
|
|
|
Ok(mut paste_config) => {
|
|
|
|
*paste_config = new_config.paste;
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
log::error!("Failed to update config for the cleanup routine: {}", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-05-14 12:03:53 -04:00
|
|
|
Ok((config, server_config, hotwatch))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "shuttle"))]
|
|
|
|
#[actix_web::main]
|
|
|
|
async fn main() -> IoResult<()> {
|
|
|
|
// Set up the application.
|
|
|
|
let (config, server_config, _) = setup(&PathBuf::new())?;
|
|
|
|
|
2022-03-23 04:38:32 -04:00
|
|
|
// Create an HTTP server.
|
2021-07-23 15:52:00 -04:00
|
|
|
let mut http_server = HttpServer::new(move || {
|
2022-03-17 08:41:34 -04:00
|
|
|
let http_client = ClientBuilder::new()
|
2022-03-11 16:35:54 -05:00
|
|
|
.timeout(
|
|
|
|
server_config
|
|
|
|
.timeout
|
|
|
|
.unwrap_or_else(|| Duration::from_secs(30)),
|
|
|
|
)
|
2021-11-06 16:55:55 -04:00
|
|
|
.disable_redirects()
|
|
|
|
.finish();
|
2021-07-23 15:52:00 -04:00
|
|
|
App::new()
|
2022-03-17 08:41:34 -04:00
|
|
|
.app_data(Data::clone(&config))
|
|
|
|
.app_data(Data::new(http_client))
|
2021-07-23 15:52:00 -04:00
|
|
|
.wrap(Logger::default())
|
|
|
|
.configure(server::configure_routes)
|
|
|
|
})
|
|
|
|
.bind(server_config.address)?;
|
2021-11-07 08:06:57 -05:00
|
|
|
|
|
|
|
// Set worker count for the server.
|
2021-07-23 15:52:00 -04:00
|
|
|
if let Some(workers) = server_config.workers {
|
|
|
|
http_server = http_server.workers(workers);
|
|
|
|
}
|
2021-11-07 08:06:57 -05:00
|
|
|
|
|
|
|
// Run the server.
|
2021-07-23 15:52:00 -04:00
|
|
|
http_server.run().await
|
2021-07-19 15:16:13 -04:00
|
|
|
}
|
2023-05-14 12:03:53 -04:00
|
|
|
|
|
|
|
#[cfg(feature = "shuttle")]
|
|
|
|
#[shuttle_runtime::main]
|
|
|
|
async fn actix_web(
|
|
|
|
#[shuttle_static_folder::StaticFolder(folder = "shuttle")] static_folder: PathBuf,
|
|
|
|
) -> ShuttleActixWeb<impl FnOnce(&mut ServiceConfig) + Send + Clone + 'static> {
|
|
|
|
// Set up the application.
|
|
|
|
let (config, server_config, _) = setup(&static_folder)?;
|
|
|
|
|
|
|
|
// Create the service.
|
|
|
|
let service_config = move |cfg: &mut ServiceConfig| {
|
|
|
|
let http_client = ClientBuilder::new()
|
|
|
|
.timeout(
|
|
|
|
server_config
|
|
|
|
.timeout
|
|
|
|
.unwrap_or_else(|| Duration::from_secs(30)),
|
|
|
|
)
|
|
|
|
.disable_redirects()
|
|
|
|
.finish();
|
|
|
|
cfg.service(
|
|
|
|
web::scope("")
|
|
|
|
.app_data(Data::clone(&config))
|
|
|
|
.app_data(Data::new(http_client))
|
|
|
|
.wrap(Logger::default())
|
|
|
|
.configure(server::configure_routes),
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(service_config.into())
|
|
|
|
}
|