transbeam/src/main.rs

108 lines
2.7 KiB
Rust
Raw Normal View History

mod download;
2022-04-26 23:54:29 -04:00
mod file;
2022-04-28 05:13:14 -04:00
mod state;
2022-04-26 23:54:29 -04:00
mod upload;
2022-04-28 05:13:14 -04:00
mod util;
2022-04-26 23:54:29 -04:00
mod zip;
2022-04-28 05:18:35 -04:00
use std::{fs::File, path::PathBuf};
2022-04-26 23:54:29 -04:00
use actix::Addr;
use actix_web::{
2022-04-28 05:18:35 -04:00
get, middleware::Logger, web, App, HttpRequest, HttpResponse, HttpServer, Responder,
2022-04-26 23:54:29 -04:00
};
use actix_web_actors::ws;
2022-04-28 05:13:14 -04:00
use serde::{Deserialize, Serialize};
use state::PersistentState;
2022-04-26 23:54:29 -04:00
use time::OffsetDateTime;
use tokio::sync::RwLock;
2022-04-26 23:54:29 -04:00
2022-04-28 05:18:35 -04:00
const APP_NAME: &str = "transbeam";
2022-04-26 23:54:29 -04:00
pub struct UploadedFile {
name: String,
size: u64,
2022-04-26 23:54:29 -04:00
modtime: OffsetDateTime,
}
impl UploadedFile {
fn new(name: &str, size: u64, modtime: OffsetDateTime) -> Self {
2022-04-26 23:54:29 -04:00
Self {
name: sanitise_file_name::sanitise(name),
size,
modtime,
}
}
}
2022-04-28 05:13:14 -04:00
#[derive(Clone, Deserialize, Serialize)]
2022-04-26 23:54:29 -04:00
pub struct DownloadableFile {
name: String,
size: u64,
2022-04-28 05:13:14 -04:00
#[serde(with = "state::timestamp")]
2022-04-26 23:54:29 -04:00
modtime: OffsetDateTime,
2022-04-28 05:13:14 -04:00
#[serde(skip)]
2022-04-26 23:54:29 -04:00
uploader: Option<Addr<upload::Uploader>>,
}
2022-04-28 05:13:14 -04:00
type AppData = web::Data<RwLock<PersistentState>>;
2022-04-26 23:54:29 -04:00
fn storage_dir() -> PathBuf {
PathBuf::from(std::env::var("STORAGE_DIR").unwrap_or_else(|_| String::from("storage")))
}
#[get("/download/{file_code}")]
2022-04-28 05:18:35 -04:00
async fn handle_download(
req: HttpRequest,
path: web::Path<String>,
data: AppData,
) -> actix_web::Result<HttpResponse> {
let file_code = path.into_inner();
2022-04-28 05:13:14 -04:00
if !util::is_ascii_alphanumeric(&file_code) {
return Ok(HttpResponse::NotFound().finish());
}
let data = data.read().await;
2022-04-28 05:13:14 -04:00
let info = data.lookup_file(&file_code);
if let Some(info) = info {
Ok(download::DownloadingFile {
file: File::open(storage_dir().join(file_code))?,
2022-04-28 05:18:35 -04:00
info,
}
.into_response(&req))
} else {
Ok(HttpResponse::NotFound().finish())
}
}
2022-04-26 23:54:29 -04:00
#[get("/upload")]
async fn handle_upload(req: HttpRequest, stream: web::Payload, data: AppData) -> impl Responder {
2022-04-27 00:55:36 -04:00
ws::start(upload::Uploader::new(data), &req, stream)
2022-04-26 23:54:29 -04:00
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
env_logger::init();
2022-04-28 05:13:14 -04:00
let data: AppData = web::Data::new(RwLock::new(PersistentState::load().await?));
2022-04-26 23:54:29 -04:00
2022-04-27 00:55:36 -04:00
let static_dir =
PathBuf::from(std::env::var("STATIC_DIR").unwrap_or_else(|_| String::from("static")));
let port = std::env::var("PORT")
.ok()
.and_then(|p| p.parse::<u16>().ok())
.unwrap_or(8080);
2022-04-27 00:53:32 -04:00
2022-04-26 23:54:29 -04:00
HttpServer::new(move || {
App::new()
.app_data(data.clone())
.wrap(Logger::default())
.service(handle_upload)
.service(handle_download)
2022-04-27 00:53:32 -04:00
.service(actix_files::Files::new("/", static_dir.clone()).index_file("index.html"))
2022-04-26 23:54:29 -04:00
})
2022-04-27 00:53:32 -04:00
.bind(("127.0.0.1", port))?
2022-04-26 23:54:29 -04:00
.run()
.await?;
Ok(())
}