Compare commits
2 Commits
1bb6bd78e6
...
8c099d0586
Author | SHA1 | Date |
---|---|---|
Savanni D'Gerinel | 8c099d0586 | |
Savanni D'Gerinel | f440e6f7ad |
|
@ -622,6 +622,19 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
|
||||
dependencies = [
|
||||
"humantime",
|
||||
"is-terminal",
|
||||
"log 0.4.20",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
|
@ -706,12 +719,14 @@ dependencies = [
|
|||
"http",
|
||||
"image 0.23.14",
|
||||
"iron",
|
||||
"log 0.4.20",
|
||||
"logger",
|
||||
"mime 0.3.17",
|
||||
"mime_guess 2.0.4",
|
||||
"mustache",
|
||||
"orizentic",
|
||||
"params",
|
||||
"pretty_env_logger",
|
||||
"router",
|
||||
"serde 1.0.188",
|
||||
"serde_json",
|
||||
|
@ -1532,6 +1547,12 @@ version = "1.0.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.10.16"
|
||||
|
@ -1746,6 +1767,17 @@ dependencies = [
|
|||
"url 1.7.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.2",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
|
@ -2634,6 +2666,16 @@ version = "0.2.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "pretty_env_logger"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"log 0.4.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "1.3.1"
|
||||
|
@ -3421,6 +3463,15 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
|
@ -4079,6 +4130,15 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
|
|
|
@ -27,3 +27,5 @@ thiserror = "1.0.20"
|
|||
tokio = { version = "1", features = [ "full" ] }
|
||||
uuid = { version = "0.4", features = [ "serde", "v4" ] }
|
||||
warp = { version = "0.3" }
|
||||
pretty_env_logger = { version = "0.5" }
|
||||
log = { version = "0.4" }
|
||||
|
|
|
@ -2,6 +2,7 @@ use build_html::{self, Html, HtmlContainer};
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Form {
|
||||
path: String,
|
||||
method: String,
|
||||
encoding: Option<String>,
|
||||
elements: String,
|
||||
|
@ -10,12 +11,18 @@ pub struct Form {
|
|||
impl Form {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
path: "/".to_owned(),
|
||||
method: "get".to_owned(),
|
||||
encoding: None,
|
||||
elements: "".to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_path(mut self, path: &str) -> Self {
|
||||
self.path = path.to_owned();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_method(mut self, method: &str) -> Self {
|
||||
self.method = method.to_owned();
|
||||
self
|
||||
|
@ -30,11 +37,12 @@ impl Form {
|
|||
impl Html for Form {
|
||||
fn to_html_string(&self) -> String {
|
||||
let encoding = match self.encoding {
|
||||
Some(ref encoding) => format!("encoding={encoding}", encoding = encoding),
|
||||
Some(ref encoding) => format!("encoding=\"{encoding}\"", encoding = encoding),
|
||||
None => format!(""),
|
||||
};
|
||||
format!(
|
||||
"<form method={method} {encoding}>\n{elements}\n</form>\n",
|
||||
"<form action=\"{path}\" method=\"{method}\" {encoding}>\n{elements}\n</form>\n",
|
||||
path = self.path,
|
||||
method = self.method,
|
||||
encoding = encoding,
|
||||
elements = self.elements.to_html_string()
|
||||
|
@ -52,36 +60,58 @@ impl HtmlContainer for Form {
|
|||
pub struct Input {
|
||||
ty: String,
|
||||
name: String,
|
||||
id: String,
|
||||
id: Option<String>,
|
||||
value: Option<String>,
|
||||
content: Option<String>,
|
||||
}
|
||||
|
||||
impl Html for Input {
|
||||
fn to_html_string(&self) -> String {
|
||||
let id = match self.id {
|
||||
Some(ref id) => format!("id=\"{}\"", id),
|
||||
None => "".to_owned(),
|
||||
};
|
||||
let value = match self.value {
|
||||
Some(ref value) => format!("value=\"{}\"", value),
|
||||
None => "".to_owned(),
|
||||
};
|
||||
|
||||
format!(
|
||||
"<input type=\"{ty}\" name=\"{name}\" id=\"{id}\">{value}</input>\n",
|
||||
"<input type=\"{ty}\" name=\"{name}\" {id} {value}>{content}</input>\n",
|
||||
ty = self.ty,
|
||||
name = self.name,
|
||||
id = self.id,
|
||||
value = self.value.clone().unwrap_or("".to_owned()),
|
||||
id = id,
|
||||
value = value,
|
||||
content = self.content.clone().unwrap_or("".to_owned()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Input {
|
||||
pub fn new(ty: &str, name: &str, id: &str) -> Self {
|
||||
pub fn new(ty: &str, name: &str) -> Self {
|
||||
Self {
|
||||
ty: ty.to_owned(),
|
||||
name: name.to_owned(),
|
||||
id: id.to_owned(),
|
||||
id: None,
|
||||
value: None,
|
||||
content: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_id(mut self, val: &str) -> Self {
|
||||
self.id = Some(val.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_value(mut self, val: &str) -> Self {
|
||||
self.value = Some(val.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_content(mut self, val: &str) -> Self {
|
||||
self.content = Some(val.to_owned());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -111,25 +141,29 @@ impl Html for Label {
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Button {
|
||||
name: String,
|
||||
text: String,
|
||||
name: Option<String>,
|
||||
label: String,
|
||||
}
|
||||
|
||||
impl Button {
|
||||
pub fn new(name: &str, text: &str) -> Self {
|
||||
pub fn new(label: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_owned(),
|
||||
text: text.to_owned(),
|
||||
name: None,
|
||||
label: label.to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Html for Button {
|
||||
fn to_html_string(&self) -> String {
|
||||
let name = match self.name {
|
||||
Some(ref name) => format!("name={}", name),
|
||||
None => "".to_owned(),
|
||||
};
|
||||
format!(
|
||||
"<button name={name}>{text}</button>",
|
||||
name = self.name,
|
||||
text = self.text
|
||||
"<button {name}>{label}</button>",
|
||||
name = name,
|
||||
label = self.label
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ impl App {
|
|||
FileInfo::open(&id, &self.files_root)
|
||||
}
|
||||
|
||||
pub fn get_file(&self, id: String) -> Result<(FileInfo, std::fs::File), FileError> {
|
||||
pub fn get_file(&self, id: &str) -> Result<(FileInfo, std::fs::File), FileError> {
|
||||
let f = File::open(&id, &self.files_root)?;
|
||||
let info = f.info();
|
||||
let stream = f.stream()?;
|
||||
|
|
|
@ -6,11 +6,15 @@ use iron::prelude::*;
|
|||
use iron::response::BodyReader;
|
||||
use iron::status;
|
||||
*/
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
use http::status::StatusCode;
|
||||
// use mustache::{compile_path, Template};
|
||||
// use orizentic::{Permissions, ResourceName, Secret};
|
||||
use build_html::Html;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io::Read,
|
||||
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||
path::Path,
|
||||
|
@ -221,6 +225,28 @@ fn script(_: &mut Request) -> IronResult<Response> {
|
|||
}
|
||||
*/
|
||||
|
||||
fn serve_file(
|
||||
info: FileInfo,
|
||||
mut file: std::fs::File,
|
||||
old_etags: Option<String>,
|
||||
) -> http::Result<http::Response<Vec<u8>>> {
|
||||
let mut content = Vec::new();
|
||||
match old_etags {
|
||||
Some(old_etags) if old_etags != info.hash => warp::http::Response::builder()
|
||||
.header("content-type", info.file_type)
|
||||
.status(StatusCode::NOT_MODIFIED)
|
||||
.body(content),
|
||||
_ => {
|
||||
let _ = file.read_to_end(&mut content);
|
||||
warp::http::Response::builder()
|
||||
.header("content-type", info.file_type)
|
||||
.header("etag", info.hash)
|
||||
.status(StatusCode::OK)
|
||||
.body(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn main() {
|
||||
/*
|
||||
|
@ -232,21 +258,6 @@ pub async fn main() {
|
|||
|
||||
let mut router = Router::new();
|
||||
|
||||
router.get(
|
||||
"/:id",
|
||||
files::GetFileHandler {
|
||||
app: app.clone(),
|
||||
template: compile_path("templates/file.html").expect("the template to compile"),
|
||||
},
|
||||
"get-file-page",
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/:id/raw",
|
||||
files::GetHandler { app: app.clone() },
|
||||
"get-file",
|
||||
);
|
||||
|
||||
router.post("/", files::PostHandler { app: app.clone() }, "upload-file");
|
||||
|
||||
router.delete(
|
||||
|
@ -279,13 +290,18 @@ pub async fn main() {
|
|||
.await;
|
||||
*/
|
||||
|
||||
pretty_env_logger::init();
|
||||
|
||||
let app = Arc::new(RwLock::new(App::new(Path::new(
|
||||
&std::env::var("FILE_SHARE_DIR").unwrap(),
|
||||
))));
|
||||
|
||||
let root = warp::path!().map({
|
||||
let log = warp::log("file_service");
|
||||
|
||||
let root = warp::path!().and(warp::get()).map({
|
||||
let app = app.clone();
|
||||
move || {
|
||||
info!("root handler");
|
||||
warp::http::Response::builder()
|
||||
.header("content-type", "text/html")
|
||||
.status(StatusCode::OK)
|
||||
|
@ -293,37 +309,66 @@ pub async fn main() {
|
|||
}
|
||||
});
|
||||
|
||||
let post_handler = warp::path!(String)
|
||||
.and(warp::post())
|
||||
.and(warp::filters::body::form())
|
||||
.map(|id: String, form: HashMap<String, String>| {
|
||||
info!("post_delete {}", id);
|
||||
info!("form: {:?}", form);
|
||||
warp::http::Response::builder()
|
||||
.header("location", "/")
|
||||
.status(StatusCode::SEE_OTHER)
|
||||
.body(vec![])
|
||||
});
|
||||
|
||||
let thumbnail = warp::path!(String / "tn")
|
||||
.and(warp::get())
|
||||
.and(warp::header::optional::<String>("if-none-match"))
|
||||
.map({
|
||||
let app = app.clone();
|
||||
move |id: String, old_etags: Option<String>| {
|
||||
let mut content = Vec::new();
|
||||
match app.read().unwrap().get_thumbnail(&id) {
|
||||
Ok((info, mut stream)) => match old_etags {
|
||||
Some(old_etags) if old_etags != info.hash => {
|
||||
warp::http::Response::builder()
|
||||
.header("content-type", info.file_type)
|
||||
.status(StatusCode::NOT_MODIFIED)
|
||||
.body(content)
|
||||
}
|
||||
_ => {
|
||||
let _ = stream.read_to_end(&mut content);
|
||||
warp::http::Response::builder()
|
||||
.header("content-type", info.file_type)
|
||||
.header("etag", info.hash)
|
||||
.status(StatusCode::OK)
|
||||
.body(content)
|
||||
}
|
||||
},
|
||||
move |id: String, old_etags: Option<String>| match app
|
||||
.read()
|
||||
.unwrap()
|
||||
.get_thumbnail(&id)
|
||||
{
|
||||
Ok((info, file)) => serve_file(info, file, old_etags),
|
||||
Err(_err) => warp::http::Response::builder()
|
||||
.status(StatusCode::NOT_FOUND)
|
||||
.body(content),
|
||||
}
|
||||
.body(vec![]),
|
||||
}
|
||||
});
|
||||
|
||||
let server = warp::serve(root.or(thumbnail));
|
||||
let file = warp::path!(String)
|
||||
.and(warp::get())
|
||||
.and(warp::header::optional::<String>("if-none-match"))
|
||||
.map({
|
||||
let app = app.clone();
|
||||
move |id: String, old_etags: Option<String>| match app.read().unwrap().get_file(&id) {
|
||||
Ok((info, file)) => serve_file(info, file, old_etags),
|
||||
Err(_err) => warp::http::Response::builder()
|
||||
.status(StatusCode::NOT_FOUND)
|
||||
.body(vec![]),
|
||||
}
|
||||
});
|
||||
|
||||
let upload = warp::path!().and(warp::post()).map(|| {
|
||||
println!("upload");
|
||||
warp::reply()
|
||||
});
|
||||
|
||||
let delete = warp::path!(String).and(warp::delete()).map(|id: String| {
|
||||
println!("delete {}", id);
|
||||
warp::reply()
|
||||
});
|
||||
|
||||
let server = warp::serve(
|
||||
root.or(post_handler)
|
||||
.or(file)
|
||||
.or(thumbnail)
|
||||
.or(upload)
|
||||
.or(delete)
|
||||
.with(log),
|
||||
);
|
||||
server
|
||||
.run(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8002))
|
||||
.await;
|
||||
|
|
|
@ -11,17 +11,33 @@ pub fn index(files: Vec<Result<File, FileError>>) -> build_html::HtmlPage {
|
|||
.with_encoding("multipart/form-data")
|
||||
.with_container(
|
||||
Container::new(ContainerType::Div)
|
||||
.with_html(Input::new("file", "file", "file-selector-input"))
|
||||
.with_html(Input::new("file", "file"))
|
||||
.with_html(Label::new("for-selector-input", "Select a file")),
|
||||
)
|
||||
.with_html(Button::new("upload", "Upload file")),
|
||||
.with_html(Button::new("Upload file")),
|
||||
);
|
||||
|
||||
for file in files {
|
||||
let mut container =
|
||||
Container::new(ContainerType::Div).with_attributes(vec![("class", "file")]);
|
||||
match file {
|
||||
Ok(file) => {
|
||||
let container = match file {
|
||||
Ok(ref file) => thumbnail(file).with_html(
|
||||
Form::new()
|
||||
.with_path(&format!("/{}", file.info().id))
|
||||
.with_method("post")
|
||||
.with_html(Input::new("hidden", "_method").with_value("delete"))
|
||||
.with_html(Button::new("Delete")),
|
||||
),
|
||||
|
||||
Err(err) => Container::new(ContainerType::Div)
|
||||
.with_attributes(vec![("class", "file")])
|
||||
.with_paragraph(format!("{:?}", err)),
|
||||
};
|
||||
page.add_container(container)
|
||||
}
|
||||
page
|
||||
}
|
||||
|
||||
pub fn thumbnail(file: &File) -> Container {
|
||||
let mut container = Container::new(ContainerType::Div).with_attributes(vec![("class", "file")]);
|
||||
let tn = Container::new(ContainerType::Div)
|
||||
.with_attributes(vec![("class", "thumbnail")])
|
||||
.with_link(
|
||||
|
@ -29,12 +45,5 @@ pub fn index(files: Vec<Result<File, FileError>>) -> build_html::HtmlPage {
|
|||
Image::new(&format!("{}/tn", file.info().id)).to_html_string(),
|
||||
);
|
||||
container.add_html(tn);
|
||||
}
|
||||
Err(err) => {
|
||||
container.add_paragraph(format!("{:?}", err));
|
||||
}
|
||||
}
|
||||
page.add_container(container);
|
||||
}
|
||||
page
|
||||
container
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue