#![feature(proc_macro_hygiene, decl_macro)] #[macro_use] extern crate rocket; #[macro_use] extern crate lazy_static; use std::path::{PathBuf, Path}; use std::env; use std::fs; use std::ffi::OsStr; use std::collections::HashMap; use rocket::response::{NamedFile, Redirect}; use rocket::http::{Cookie, Cookies, ContentType, Status}; use rocket::request::{self, Request, Form, FromRequest}; use rocket_contrib::templates::Template; use rocket_upload::MultipartDatas; use serde::{Serialize, Deserialize}; lazy_static! { static ref DIR: PathBuf = Path::new(env::var("FASTCLOUD_DIR").unwrap_or_else(|_| String::from("")).as_str()).to_path_buf(); static ref ADMIN_KEY: String = env::var("FASTCLOUD_ADMIN_KEY").unwrap_or_else(|_| String::from("")); static ref USER_KEY: String = env::var("FASTCLOUD_USER_KEY").unwrap_or_else(|_| String::from("")); static ref PUBLIC_READ: bool = env::var("FASTCLOUD_PUBLIC").unwrap_or_else(|_| String::from("")) != *""; } #[derive(Serialize)] struct DirPath { is_file: bool, path: String, name: String, } #[derive(FromForm)] struct DirName { name: String } #[derive(Debug, FromForm, Deserialize)] struct GenericUser { key: String, } struct AdminUser; impl<'a, 'r> FromRequest<'a, 'r> for GenericUser { type Error = (); fn from_request(request: &'a Request<'r>) -> request::Outcome { match request.cookies().get("key") { Some(key) => { if key.value() == ADMIN_KEY.as_str() || key.value() == USER_KEY.as_str() { request::Outcome::Success(GenericUser { key: String::from(key.value()) }) } else if *PUBLIC_READ { request::Outcome::Success(GenericUser { key: String::from("") }) } else { request::Outcome::Failure((Status::BadRequest, ())) } }, None => { if *PUBLIC_READ { request::Outcome::Success(GenericUser { key: String::from("") }) } else { request::Outcome::Failure((Status::BadRequest, ())) } } } } } impl<'a, 'r> FromRequest<'a, 'r> for AdminUser { type Error = (); fn from_request(request: &'a Request<'r>) -> request::Outcome { match request.cookies().get("key") { Some(key) => { if key.value() == ADMIN_KEY.as_str() { request::Outcome::Success(AdminUser) } else { request::Outcome::Forward(()) } }, None => request::Outcome::Forward(()) } } } #[get("/")] fn root() -> Template { let map: HashMap = HashMap::new(); Template::render("home", map) } #[post("/login", data = "
")] fn login(form: Form, mut cookies: Cookies) -> Redirect { let key = form.into_inner().key; let mut cook = Cookie::new("key", key); cook.set_http_only(true); cook.set_secure(true); cookies.add(cook); Redirect::to("/serve") } #[get("/logout")] fn logout(mut cookies: Cookies) -> Redirect { match cookies.get("key") { Some(_) => { cookies.remove(Cookie::named("key")); Redirect::to("/") } None => { Redirect::to("/") } } } #[get("/serve")] fn get_root(_guard: AdminUser) -> Template { let path = DIR.as_path().to_str().unwrap_or(""); Template::render("dir", directory_structure(path.to_string())) } #[get("/serve", rank = 2)] fn get_root_generic(_guard: GenericUser) -> Template { let path = DIR.as_path().to_str().unwrap_or(""); Template::render("generic", directory_structure(path.to_string())) } #[get("/serve/")] fn get_dir_entry(path: PathBuf, _guard: AdminUser) -> Template { let dir = DIR.as_path().join(path); let path = dir.to_str().unwrap_or(""); Template::render("dir", directory_structure(path.to_string())) } #[get("/serve/", rank = 2)] fn get_dir_entry_generic(path: PathBuf, _guard: GenericUser) -> Template { let dir = DIR.as_path().join(path); let path = dir.to_str().unwrap_or(""); Template::render("generic", directory_structure(path.to_string())) } #[post("/dir/", data = "")] fn create_dir(path: PathBuf, form: Form, _guard: AdminUser) -> Redirect { let dir = DIR.as_path().join(path.clone()).join(form.into_inner().name); fs::create_dir_all(dir).unwrap(); Redirect::to(uri!(get_dir_entry: path)) } #[post("/dir", data = "")] fn create_dir_root(form: Form, _guard: AdminUser) -> Redirect { let dir = DIR.as_path().join(form.into_inner().name); fs::create_dir_all(dir).unwrap(); Redirect::to(uri!(get_root)) } #[get("/del/")] fn delete(path: PathBuf, _guard: AdminUser) -> Redirect { let dir = DIR.as_path().join(path.clone()); if dir.is_file() { fs::remove_file(dir).unwrap(); } else { fs::remove_dir_all(dir).unwrap(); } let npath = path.parent().unwrap().to_path_buf(); Redirect::to(uri!(get_dir_entry: npath)) } fn directory_structure(path: String) -> HashMap<&'static str, HashMap> { let dir = Path::new(path.as_str()); let paths = fs::read_dir(dir).unwrap() .map(|x| x.unwrap().path().into_os_string().into_string().unwrap()) .enumerate() .map(|x| (x.0 as i32, DirPath { is_file: Path::new(&x.1).is_file(), path: Path::new(&x.1).strip_prefix(DIR.clone().into_os_string().into_string().unwrap()).unwrap().to_str().unwrap().to_string(), name: Path::new(&x.1).file_name().unwrap().to_str().unwrap().to_string(), })) .collect::>(); let mut context: HashMap<&str, HashMap> = HashMap::new(); let parent = DirPath { is_file: false, path: dir.strip_prefix(DIR.clone().into_os_string().into_string().unwrap()).unwrap().parent().unwrap_or_else(|| Path::new("")).to_str().unwrap().to_string(), name: dir.parent().unwrap().file_name().unwrap_or_else(|| OsStr::new("")).to_str().unwrap().to_string(), }; let current = DirPath { is_file: false, path: dir.strip_prefix(DIR.clone().into_os_string().into_string().unwrap()).unwrap_or_else(|_| Path::new("")).to_str().unwrap().to_string(), name: dir.file_name().unwrap_or_else(|| OsStr::new("")).to_str().unwrap().to_string(), }; let mut pt = HashMap::new(); pt.insert(0, parent); context.insert("parent", pt); let mut pt = HashMap::new(); pt.insert(0, current); context.insert("current", pt); context.insert("ctx", paths); context } #[get("/file/")] fn file_get(path: PathBuf, _guard: GenericUser) -> Option { let dir = DIR.as_path().join(path); rocket::response::NamedFile::open(dir.as_path()).ok() } #[post("/upload/", data = "")] fn upload_file(path: PathBuf, _content_type: &ContentType, data:MultipartDatas, _guard: AdminUser) -> Redirect { let dir = DIR.as_path().join(path.clone()); for f in data.files { if !Path::new(&format!("{}/{}", dir.to_str().unwrap(), f.filename)).exists() { f.persist(&dir); } for i in 0.. { if !Path::new(&format!("{}/{}.{}", dir.to_str().unwrap(), f.filename, i)).exists() { f.persist(&dir); break } } } Redirect::to(uri!(get_dir_entry: path)) } #[post("/upload", data = "")] fn upload_root(_content_type: &ContentType, data:MultipartDatas, _guard: AdminUser) -> Redirect { let dir = DIR.as_path(); for f in data.files { if !Path::new(&format!("{}/{}", dir.to_str().unwrap(), f.filename)).exists() { f.persist(dir); } for i in 0.. { if !Path::new(&format!("{}/{}.{}", dir.to_str().unwrap(), f.filename, i)).exists() { f.persist(dir); break } } } Redirect::to(uri!(get_root)) } // TODO: move files // TODO: rename files // TODO: rewrite frontend fn main() { rocket::ignite() .attach(Template::fairing()) .mount("/", routes![ root, login, logout, get_root, get_root_generic, get_dir_entry, get_dir_entry_generic, create_dir, create_dir_root, file_get, upload_root, upload_file, delete]).launch(); }