2022-04-27 20:15:51 -04:00
|
|
|
mod download;
|
2022-04-26 23:54:29 -04:00
|
|
|
mod file;
|
|
|
|
mod upload;
|
|
|
|
mod zip;
|
|
|
|
|
2022-04-27 00:55:36 -04:00
|
|
|
use std::{
|
|
|
|
collections::HashMap,
|
|
|
|
path::PathBuf,
|
2022-04-27 20:15:51 -04:00
|
|
|
fs::File,
|
2022-04-27 00:55:36 -04:00
|
|
|
};
|
2022-04-26 23:54:29 -04:00
|
|
|
|
|
|
|
use actix::Addr;
|
|
|
|
use actix_web::{
|
2022-04-27 20:15:51 -04:00
|
|
|
get, middleware::Logger, web, App, HttpRequest, HttpServer, Responder, HttpResponse,
|
2022-04-26 23:54:29 -04:00
|
|
|
};
|
|
|
|
use actix_web_actors::ws;
|
|
|
|
use time::OffsetDateTime;
|
2022-04-27 20:15:51 -04:00
|
|
|
use tokio::sync::RwLock;
|
2022-04-26 23:54:29 -04:00
|
|
|
|
2022-04-27 00:44:35 -04:00
|
|
|
const APP_NAME: &'static str = "transbeam";
|
|
|
|
|
2022-04-26 23:54:29 -04:00
|
|
|
pub struct UploadedFile {
|
|
|
|
name: String,
|
2022-04-27 20:15:51 -04:00
|
|
|
size: u64,
|
2022-04-26 23:54:29 -04:00
|
|
|
modtime: OffsetDateTime,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl UploadedFile {
|
2022-04-27 20:15:51 -04:00
|
|
|
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-27 20:15:51 -04:00
|
|
|
#[derive(Clone)]
|
2022-04-26 23:54:29 -04:00
|
|
|
pub struct DownloadableFile {
|
|
|
|
name: String,
|
2022-04-27 20:15:51 -04:00
|
|
|
size: u64,
|
2022-04-26 23:54:29 -04:00
|
|
|
modtime: OffsetDateTime,
|
|
|
|
uploader: Option<Addr<upload::Uploader>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
type AppData = web::Data<RwLock<HashMap<String, DownloadableFile>>>;
|
|
|
|
|
|
|
|
fn storage_dir() -> PathBuf {
|
|
|
|
PathBuf::from(std::env::var("STORAGE_DIR").unwrap_or_else(|_| String::from("storage")))
|
|
|
|
}
|
|
|
|
|
2022-04-27 20:15:51 -04:00
|
|
|
#[get("/download/{file_code}")]
|
|
|
|
async fn handle_download(req: HttpRequest, path: web::Path<String>, data: AppData) -> actix_web::Result<HttpResponse> {
|
|
|
|
let file_code = path.into_inner();
|
|
|
|
if !file_code.as_bytes().iter().all(|c| c.is_ascii_alphanumeric()) {
|
|
|
|
return Ok(HttpResponse::NotFound().finish());
|
|
|
|
}
|
|
|
|
let data = data.read().await;
|
|
|
|
let info = data.get(&file_code);
|
|
|
|
if let Some(info) = info {
|
|
|
|
Ok(download::DownloadingFile {
|
|
|
|
file: File::open(storage_dir().join(file_code))?,
|
|
|
|
info: info.clone(),
|
|
|
|
}.into_response(&req))
|
|
|
|
} else {
|
|
|
|
Ok(HttpResponse::NotFound().finish())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-26 23:54:29 -04:00
|
|
|
#[get("/upload")]
|
2022-04-27 20:15:51 -04:00
|
|
|
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();
|
|
|
|
|
|
|
|
let data: AppData = web::Data::new(RwLock::new(HashMap::new()));
|
|
|
|
|
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())
|
2022-04-27 20:15:51 -04:00
|
|
|
.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(())
|
|
|
|
}
|