Pour tout problème contactez-nous par mail : support@froggit.fr | La FAQ :grey_question: | Rejoignez-nous sur le Chat :speech_balloon:

Skip to content
Snippets Groups Projects
Commit 2e96d44b authored by Pierre Jarriges's avatar Pierre Jarriges
Browse files

wip Files REST API

parent 92a76613
No related branches found
No related tags found
No related merge requests found
......@@ -90,6 +90,24 @@ dependencies = [
"syn",
]
[[package]]
name = "actix-multipart"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9edfb0e7663d7fe18c8d5b668c9c1bcf79176b1dcc9d4da9592503209a6bfb0"
dependencies = [
"actix-utils",
"actix-web",
"bytes",
"derive_more",
"futures-core",
"httparse",
"local-waker",
"log",
"mime",
"twoway",
]
[[package]]
name = "actix-router"
version = "0.5.0"
......@@ -700,32 +718,90 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
[[package]]
name = "futures"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.23"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2acedae88d38235936c3922476b10fced7b2b68136f5e3c03c2d5be348a1115"
checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf"
[[package]]
name = "futures-executor"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68"
[[package]]
name = "futures-macro"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.23"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca0bae1fe9752cf7fd9b0064c674ae63f97b37bc714d745cbde0afb7ec4e6765"
checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56"
[[package]]
name = "futures-task"
version = "0.3.23"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "842fc63b931f4056a24d59de13fb1272134ce261816e063e634ad0c15cdc5306"
checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1"
[[package]]
name = "futures-util"
version = "0.3.23"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0828a5471e340229c11c77ca80017937ce3c58cb788a17e5f1c2d5c485a9577"
checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
......@@ -914,11 +990,13 @@ name = "krustacea"
version = "0.1.0"
dependencies = [
"actix-files",
"actix-multipart",
"actix-web",
"actix-web-lab",
"dirs",
"env_logger",
"fs_extra",
"futures",
"regex",
"rustls",
"rustls-pemfile",
......@@ -1641,12 +1719,28 @@ dependencies = [
"once_cell",
]
[[package]]
name = "twoway"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c57ffb460d7c24cd6eda43694110189030a3d1dfe418416d9468fd1c1d290b47"
dependencies = [
"memchr",
"unchecked-index",
]
[[package]]
name = "typenum"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "unchecked-index"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c"
[[package]]
name = "unicase"
version = "2.6.0"
......
......@@ -20,3 +20,5 @@ structopt = "0.3"
env_logger = "0.9"
actix-web-lab = "0.17.0"
actix-files = "0.6.2"
actix-multipart = "0.4"
futures = "0.3.24"
use crate::static_files::StaticFilesManager;
use crate::static_files::upload::*;
use crate::website::WebSite;
use actix_files::NamedFile;
use actix_web::{get, web, Responder};
use actix_multipart::Multipart;
use actix_web::{
delete, get, post, web,
web::{Data, Path},
HttpResponse, Responder,
};
use futures::StreamExt;
use std::{
fs::{remove_file, File},
io::Write,
};
#[get("/favicon.ico")]
pub async fn favicon(
static_files_manager: web::Data<std::sync::Mutex<StaticFilesManager>>,
) -> impl Responder {
let static_files_manager = static_files_manager.lock().unwrap();
pub async fn favicon(website: web::Data<std::sync::Mutex<WebSite>>) -> impl Responder {
let static_files_manager = &website.lock().unwrap().static_files_manager;
NamedFile::open(static_files_manager.dir.join("default").join("favicon.ico"))
}
fn upload_data_from_multipart_field(
field: &actix_multipart::Field,
) -> Result<UploadFileData, String> {
match field.content_disposition().get_filename() {
Some(fname) => match file_ext(&fname.to_string()) {
Ok(ext) => Ok(UploadFileData {
up_type: upload_type_from_file_ext(&ext),
filename: fname.to_owned(),
}),
Err(msg) => return Err(msg),
},
None => Err("Couldn't retrieve file extension".to_string()),
}
}
async fn write_uploaded_file(
website: &WebSite,
field: &mut actix_multipart::Field,
filename: &String,
upload_type: UploadFileType,
) -> Result<String, String> {
let root = &website.static_files_manager.dir;
let sub_dir = dirname_from_type(&upload_type);
let filepath = root.join(sub_dir).join(&filename);
match File::create(&filepath) {
Err(e) => Err(format!("Error creating file {:?} : {:?}", filepath, e)),
Ok(mut f) => {
// Field in turn is stream of *Bytes* object
while let Some(chunk) = field.next().await {
match chunk {
Ok(chunk) => {
if f.write_all(&chunk).is_err() {
remove_file(&filepath).unwrap();
return Err("Error writing chunk".to_string());
}
}
Err(e) => {
return Err(format!("Error writing file {} : {:?}", filename, e));
}
}
}
Ok(filepath.into_os_string().into_string().unwrap())
}
}
}
#[post("/post-files")]
pub async fn post_files(
website: Data<std::sync::Mutex<WebSite>>,
mut payload: Multipart,
) -> impl Responder {
let mut uploaded_filepathes = Vec::new();
let mut website = website.lock().unwrap();
while let Some(item) = payload.next().await {
match item {
Ok(mut field) => {
let up_data = upload_data_from_multipart_field(&field);
if let Err(msg) = up_data {
return HttpResponse::InternalServerError().body(msg);
}
let up_data = up_data.unwrap();
match write_uploaded_file(&website, &mut field, &up_data.filename, up_data.up_type)
.await
{
Err(msg) => return HttpResponse::InternalServerError().body(msg),
Ok(filepath) => uploaded_filepathes.extend(
website
.static_files_manager
.push_path(std::path::Path::new(&filepath)),
),
}
}
Err(e) => {
return HttpResponse::InternalServerError().body(format!("FIELD ERR {:?}", e))
}
}
}
HttpResponse::Ok().json(uploaded_filepathes)
}
#[get("/static-files-index")]
async fn get_static_files_index(website: Data<std::sync::Mutex<WebSite>>) -> impl Responder {
HttpResponse::Ok().json(
website
.lock()
.expect("Couldn't lock website")
.static_files_manager
.get_index(),
)
}
#[delete("/delete-file/{category}/{filename}")]
async fn delete_static_file(
website: Data<std::sync::Mutex<WebSite>>,
fileinfo: Path<(String, String)>,
) -> impl Responder {
let mut website = website.lock().unwrap();
let (cat, fname) = fileinfo.into_inner();
let fpath = std::path::PathBuf::from(cat).join(fname);
match remove_file(website.static_files_manager.dir.join(&fpath)) {
Ok(_) => {
website
.static_files_manager
.remove_path(fpath.to_string_lossy().into());
HttpResponse::Accepted().body("File was deleted")
}
Err(e) => HttpResponse::InternalServerError().body(format!("Error deleting file {:?}", e)),
}
}
mod static_files;
pub mod upload;
pub use static_files::*;
......@@ -101,10 +101,10 @@ impl StaticFilesManager {
}
}
fn push_path(&mut self, path: &Path) -> Vec<PathBuf> {
pub fn push_path(&mut self, path: &Path) -> Vec<PathBuf> {
if self.validate_path(path) {
self.index
.push(format!("/{}", path.to_str().unwrap().to_owned()));
.push(self.clean_relative_path(path).to_str().unwrap().to_owned());
vec![path.to_path_buf()]
} else {
println!(
......@@ -129,6 +129,22 @@ impl StaticFilesManager {
self.rec_read_dir(&self.dir.clone(), &self.dir.clone());
self.clone()
}
pub fn remove_path(&mut self, strpath: String) {
println!("REMOVE {}", strpath);
println!("current Index {:#?}", self.index);
self.index = self
.index
.iter()
.filter(|url| !strpath.eq(*url))
.map(|s| s.to_owned())
.collect();
println!("Updated Index {:#?}", self.index);
}
pub fn get_index(&self) -> Vec<String> {
self.index.clone()
}
}
#[cfg(test)]
......@@ -173,7 +189,7 @@ mod test_static_files_manager {
manager = manager.build();
assert!(
manager.index.contains(&"/docs/testing.txt".to_string()),
manager.index.contains(&"docs/testing.txt".to_string()),
"Index doesn't contain path /docs/testing.txt\n{:?}",
remove_test_dir(&test_dir)
);
......@@ -182,7 +198,7 @@ mod test_static_files_manager {
}
#[test]
fn test_push_path() {
fn test_pushd_andremove_path() {
let test_dir = create_test_dir();
let mut manager = StaticFilesManager::testing_new(&test_dir).unwrap().build();
let file_pth = test_dir.join("static").join("docs").join("testing.txt");
......@@ -198,11 +214,19 @@ mod test_static_files_manager {
);
assert!(
manager.index.contains(&"/docs/testing.txt".to_string()),
manager.index.contains(&"docs/testing.txt".to_string()),
"Index doesn't contain path /docs/testing.txt\n{:?}",
remove_test_dir(&test_dir)
);
manager.remove_path("docs/testing.txt".to_string());
assert!(
!manager.index.contains(&"docs/testing.txt".to_string()),
"Path docs/testing.txt should have been removed\n{:?}",
remove_test_dir(&test_dir)
);
remove_test_dir(&test_dir);
}
......@@ -221,9 +245,7 @@ mod test_static_files_manager {
);
assert!(
!manager
.index
.contains(&"/images/unexisting.png".to_string()),
!manager.index.contains(&"images/unexisting.png".to_string()),
"Index shouldn't container unexisting path\n{:?}",
remove_test_dir(&test_dir)
);
......
#[derive(Debug, PartialEq)]
pub enum UploadFileType {
Image,
Sound,
Video,
Doc,
Code,
}
pub struct UploadFileData {
pub up_type: UploadFileType,
pub filename: String,
}
pub fn dirname_from_type(upload_type: &UploadFileType) -> String {
match upload_type {
UploadFileType::Image => String::from("images"),
UploadFileType::Sound => String::from("sounds"),
UploadFileType::Video => String::from("videos"),
UploadFileType::Doc => String::from("docs"),
UploadFileType::Code => String::from("source_code"),
}
}
pub fn upload_type_from_file_ext(ext: &String) -> UploadFileType {
match &ext[..] {
"webp" | "jpg" | "png" | "jpeg" | "bmp" | "gif" => UploadFileType::Image,
"mp3" | "ogg" | "wav" | "opus" => UploadFileType::Sound,
"mp4" | "webm" | "ogv" => UploadFileType::Video,
"js" | "css" | "html" => UploadFileType::Code,
_ => UploadFileType::Doc,
}
}
pub fn file_ext(file_name: &String) -> Result<String, String> {
let parts = file_name.split(".").collect::<Vec<&str>>();
let err_msg = format!("Couldn't get extension from filename : {}", file_name);
if parts.len() < 2 {
return Err(err_msg);
}
match parts.last() {
Some(ext) => Ok(ext.to_string()),
None => Err(err_msg),
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment