mirror of
https://codeberg.org/icewind/shelve.git
synced 2026-06-03 12:04:09 +02:00
fixes
This commit is contained in:
parent
65bdd1ec88
commit
fc84fdec52
5 changed files with 180 additions and 5 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1141,6 +1141,7 @@ dependencies = [
|
||||||
"err-derive 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"err-derive 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"multipart 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"priority-queue 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"priority-queue 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rocket 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rocket 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
||||||
|
|
@ -18,3 +18,4 @@ rust-embed = "5.2.0"
|
||||||
rocket_upload = "0.1.0"
|
rocket_upload = "0.1.0"
|
||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
serde_json = "1.0.45"
|
serde_json = "1.0.45"
|
||||||
|
multipart = "0.16.1"
|
||||||
|
|
@ -7,7 +7,6 @@ use rocket::http::{ContentType, RawStr};
|
||||||
use rocket::request::FromParam;
|
use rocket::request::FromParam;
|
||||||
use rocket::response::{NamedFile, Redirect, Responder};
|
use rocket::response::{NamedFile, Redirect, Responder};
|
||||||
use rocket::*;
|
use rocket::*;
|
||||||
use rocket_upload::MultipartDatas;
|
|
||||||
use rust_embed::RustEmbed;
|
use rust_embed::RustEmbed;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
@ -20,9 +19,11 @@ use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::thread::{sleep, spawn, JoinHandle};
|
use std::thread::{sleep, spawn, JoinHandle};
|
||||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||||
|
use upload::MultipartDatas;
|
||||||
|
|
||||||
mod expire_queue;
|
mod expire_queue;
|
||||||
mod token;
|
mod token;
|
||||||
|
mod upload;
|
||||||
|
|
||||||
impl<'r> FromParam<'r> for UploadId {
|
impl<'r> FromParam<'r> for UploadId {
|
||||||
type Error = InvalidUploadIdError;
|
type Error = InvalidUploadIdError;
|
||||||
|
|
|
||||||
161
src/upload.rs
Normal file
161
src/upload.rs
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
use std::fs::{self, File};
|
||||||
|
use std::io::{Cursor, Read, Write};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use rocket::data::{self, FromDataSimple};
|
||||||
|
use rocket::http::Status;
|
||||||
|
use rocket::{Data, Outcome, Outcome::*, Request};
|
||||||
|
|
||||||
|
use multipart::server::Multipart;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TextPart {
|
||||||
|
pub key: String,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FilePart {
|
||||||
|
pub name: String,
|
||||||
|
pub path: String,
|
||||||
|
pub filename: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MultipartDatas {
|
||||||
|
pub texts: Vec<TextPart>,
|
||||||
|
pub files: Vec<FilePart>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilePart {
|
||||||
|
pub fn persist(&self, p: &Path) {
|
||||||
|
let s = Path::join(p, &self.filename);
|
||||||
|
fs::copy(Path::new(&self.path), &s).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for FilePart {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
fs::remove_file(Path::new(&self.path)).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const TMP_PATH: &str = "/tmp/rust_upload/";
|
||||||
|
|
||||||
|
impl<'t> FromDataSimple for MultipartDatas {
|
||||||
|
type Error = String;
|
||||||
|
|
||||||
|
fn from_data(request: &Request, data: Data) -> data::Outcome<Self, String> {
|
||||||
|
let ct = request
|
||||||
|
.headers()
|
||||||
|
.get_one("Content-Type")
|
||||||
|
.expect("no content-type");
|
||||||
|
let idx = ct.find("boundary=").expect("no boundary");
|
||||||
|
let boundary = &ct[(idx + "boundary=".len())..];
|
||||||
|
|
||||||
|
let mut d = Vec::new();
|
||||||
|
data.stream_to(&mut d).expect("Unable to read");
|
||||||
|
|
||||||
|
let mut mp = Multipart::with_body(Cursor::new(d), boundary);
|
||||||
|
let mut texts = Vec::new();
|
||||||
|
let mut files = Vec::new();
|
||||||
|
|
||||||
|
let mut buffer = [0u8; 4096];
|
||||||
|
|
||||||
|
let mut err_out: Option<Outcome<_, (Status, _), _>> = None;
|
||||||
|
|
||||||
|
mp.foreach_entry(|entry| {
|
||||||
|
let mut data = entry.data;
|
||||||
|
if entry.headers.filename == None {
|
||||||
|
let mut text_buffer = Vec::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let c = match data.read(&mut buffer) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(err) => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if c == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
text_buffer.extend_from_slice(&buffer[..c]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let text = match String::from_utf8(text_buffer) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_err) => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
texts.push(TextPart {
|
||||||
|
key: entry.headers.name.to_string(),
|
||||||
|
value: text,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let filename = entry.headers.filename.clone().unwrap();
|
||||||
|
if !Path::new(TMP_PATH).exists() {
|
||||||
|
fs::create_dir_all(TMP_PATH).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let target_path = Path::join(Path::new(TMP_PATH), &filename);
|
||||||
|
|
||||||
|
let mut file = match File::create(&target_path) {
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(err) => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut sum_c = 0u64;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let c = match data.read(&mut buffer) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(err) => {
|
||||||
|
try_delete(&target_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if c == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum_c = sum_c + c as u64;
|
||||||
|
|
||||||
|
match file.write(&buffer[..c]) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(err) => {
|
||||||
|
try_delete(&target_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
files.push(FilePart {
|
||||||
|
name: entry.headers.name.to_string(),
|
||||||
|
path: String::from(TMP_PATH) + &filename,
|
||||||
|
filename: entry.headers.filename.clone().unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
if let Some(failed) = err_out {
|
||||||
|
return failed;
|
||||||
|
} else {
|
||||||
|
let v = MultipartDatas {
|
||||||
|
texts: texts,
|
||||||
|
files: files,
|
||||||
|
};
|
||||||
|
return Outcome::Success(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn try_delete<P: AsRef<Path>>(path: P) {
|
||||||
|
if fs::remove_file(path.as_ref()).is_err() {}
|
||||||
|
}
|
||||||
|
|
@ -308,6 +308,18 @@
|
||||||
expireInput.value = expire;
|
expireInput.value = expire;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetForm() {
|
||||||
|
form.reset();
|
||||||
|
let token = localStorage.getItem('token');
|
||||||
|
if (token) {
|
||||||
|
tokenInput.value = token;
|
||||||
|
}
|
||||||
|
let expire = localStorage.getItem('expire');
|
||||||
|
if (expire) {
|
||||||
|
expireInput.value = expire;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// automatically submit the form on file select
|
// automatically submit the form on file select
|
||||||
input.addEventListener('change', function (e) {
|
input.addEventListener('change', function (e) {
|
||||||
showFiles(e.target.files);
|
showFiles(e.target.files);
|
||||||
|
|
@ -339,9 +351,7 @@
|
||||||
droppedFiles = e.dataTransfer.files; // the files that were dropped
|
droppedFiles = e.dataTransfer.files; // the files that were dropped
|
||||||
showFiles(droppedFiles);
|
showFiles(droppedFiles);
|
||||||
|
|
||||||
|
|
||||||
triggerFormSubmit();
|
triggerFormSubmit();
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -369,9 +379,10 @@
|
||||||
ajax.open(form.getAttribute('method'), form.getAttribute('action'), true);
|
ajax.open(form.getAttribute('method'), form.getAttribute('action'), true);
|
||||||
|
|
||||||
ajax.onload = function () {
|
ajax.onload = function () {
|
||||||
|
resetForm();
|
||||||
|
|
||||||
box.classList.remove('is-uploading');
|
box.classList.remove('is-uploading');
|
||||||
if (ajax.status >= 200 && ajax.status < 400) {
|
if (ajax.status >= 200 && ajax.status < 400) {
|
||||||
console.log(ajax.responseText);
|
|
||||||
var data = JSON.parse(ajax.responseText);
|
var data = JSON.parse(ajax.responseText);
|
||||||
box.classList.add(data.success == true ? 'is-success' : 'is-error');
|
box.classList.add(data.success == true ? 'is-success' : 'is-error');
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue