147 lines
4.7 KiB
Rust
147 lines
4.7 KiB
Rust
mod api;
|
|
mod data_mgt;
|
|
mod logs;
|
|
use actix_files::NamedFile;
|
|
|
|
use actix_web::{
|
|
App, HttpRequest, HttpServer, get, route,
|
|
web::{self},
|
|
};
|
|
|
|
use serde_json::Value;
|
|
use std::{env, fs, path::PathBuf, sync::LazyLock};
|
|
|
|
pub static HTML_DIR: &str = "data/html/";
|
|
pub static LOG_DIR: &str = "data/logs/";
|
|
pub static LOG_FILE_NAME: &str = "log.txt";
|
|
pub static MIN_ASSET_DURATION: u32 = 1; // in minutes
|
|
pub static MAX_ASSET_DURATION: u32 = 60; // in minutes
|
|
pub static MAX_ASSETS: usize = 1000;
|
|
pub static MAX_ASSET_SIZE_BYTES: usize = 3 * 1024 * 1024; // 3 MB
|
|
pub static MAX_UPLOADS_PER_USER: usize = 10;
|
|
|
|
pub static BIND_ADDR: LazyLock<String> = LazyLock::new(|| match env::var("BIND_ADDR") {
|
|
Ok(addr) => {
|
|
println!("Binding to address: {}", addr);
|
|
addr.parse().unwrap_or("127.0.0.1".to_string())
|
|
}
|
|
Err(_) => {
|
|
println!("Binding to default address: 0.0.0.0");
|
|
"0.0.0.0".to_string()
|
|
}
|
|
});
|
|
pub static BIND_PORT: LazyLock<u16> = LazyLock::new(|| match env::var("BIND_PORT") {
|
|
Ok(port_str) => {
|
|
println!("Binding to port: {}", port_str);
|
|
port_str.parse().unwrap_or(8080)
|
|
}
|
|
Err(_) => {
|
|
println!("Binding to default port: 8080");
|
|
8080
|
|
}
|
|
});
|
|
|
|
pub static STATIC_PAGES: LazyLock<Vec<String>> = LazyLock::new(|| {
|
|
fs::read_dir(HTML_DIR)
|
|
.unwrap()
|
|
.filter_map(|entry| entry.ok().and_then(|e| e.file_name().to_str().map(|s| s.to_string())))
|
|
.collect()
|
|
});
|
|
|
|
use crate::{
|
|
api::{api_get_asset, api_stats, api_upload},
|
|
logs::{LogEventType, log_event},
|
|
};
|
|
|
|
#[get("/")]
|
|
async fn index(req: HttpRequest) -> actix_web::Result<NamedFile> {
|
|
let path: PathBuf = PathBuf::from(HTML_DIR.to_string() + "index.html");
|
|
log_event(LogEventType::HttpRequest(req.into()));
|
|
Ok(NamedFile::open(path)?)
|
|
}
|
|
|
|
#[get("/stats")]
|
|
async fn stats(req: HttpRequest) -> actix_web::Result<NamedFile> {
|
|
let path: PathBuf = PathBuf::from(HTML_DIR.to_string() + "stats.html");
|
|
log_event(LogEventType::HttpRequest(req.into()));
|
|
Ok(NamedFile::open(path)?)
|
|
}
|
|
|
|
#[get("/bhs/{id}")]
|
|
async fn view_asset(req: HttpRequest) -> actix_web::Result<NamedFile> {
|
|
let path: PathBuf = PathBuf::from(HTML_DIR.to_string() + "view.html");
|
|
log_event(LogEventType::HttpRequest(req.into()));
|
|
Ok(NamedFile::open(path)?)
|
|
}
|
|
|
|
#[route("/{tail:.*}", method = "GET", method = "POST")]
|
|
async fn catch_all(req: HttpRequest, _payload: Option<web::Json<Value>>) -> actix_web::Result<NamedFile> {
|
|
println!("Catch-all route triggered for path: {}", req.uri().path());
|
|
let response = match req.uri().path() {
|
|
path if STATIC_PAGES.contains(&path[1..].into()) => {
|
|
let file_path = HTML_DIR.to_string() + path;
|
|
Ok(NamedFile::open(file_path)?)
|
|
}
|
|
_ => {
|
|
let file_path = PathBuf::from(HTML_DIR.to_string() + "error.html");
|
|
Ok(NamedFile::open(file_path)?)
|
|
}
|
|
};
|
|
|
|
log_event(LogEventType::HttpRequest(req.into()));
|
|
response
|
|
}
|
|
|
|
#[actix_web::main]
|
|
async fn main() -> std::io::Result<()> {
|
|
let _ = fs::create_dir_all(LOG_DIR);
|
|
let log_filename = format!("{}{}", LOG_DIR, LOG_FILE_NAME);
|
|
let log_filename_path = std::path::Path::new(&log_filename);
|
|
|
|
let time_tag = chrono::Local::now().format("%Y_%m_%d_%H_%M_%S");
|
|
if log_filename_path.exists() {
|
|
println!("File: {}, exists, rotating.", &log_filename_path.display());
|
|
fs::rename(
|
|
&log_filename_path,
|
|
format!("{}{}_{}", LOG_DIR, time_tag, &LOG_FILE_NAME),
|
|
)
|
|
.unwrap_or_else(|e| {
|
|
println!(
|
|
"No existing log file {} to rotate. Error: {}",
|
|
log_filename_path.to_string_lossy(),
|
|
e
|
|
)
|
|
});
|
|
println!("Rotated log file to: {}_{}", time_tag, &LOG_FILE_NAME);
|
|
}
|
|
let app_state = data_mgt::AppState::default();
|
|
|
|
println!("Starting server at http://{}:{}/", *BIND_ADDR, *BIND_PORT);
|
|
|
|
let inner_appt_state = app_state.clone();
|
|
tokio::spawn(async move {
|
|
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(1));
|
|
loop {
|
|
interval.tick().await;
|
|
if let Err(e) = data_mgt::clear_app_data(&inner_appt_state).await {
|
|
eprintln!("Error clearing assets: {}", e);
|
|
}
|
|
}
|
|
});
|
|
HttpServer::new(move || {
|
|
App::new()
|
|
.app_data(web::JsonConfig::default().limit(1024 * 1024 * 3)) // 3MB limit
|
|
.app_data(web::Data::new(app_state.clone()))
|
|
.service(index)
|
|
.service(stats)
|
|
.service(view_asset)
|
|
.service(api_get_asset)
|
|
.service(api_upload)
|
|
.service(api_stats)
|
|
.service(catch_all)
|
|
})
|
|
.bind((BIND_ADDR.clone(), *BIND_PORT))?
|
|
.run()
|
|
.await
|
|
}
|