add upload route
This commit is contained in:
parent
b4f804d39b
commit
8de83749a3
|
@ -1,7 +1,7 @@
|
||||||
use warp::{reply::Reply, reject::Rejection, Filter};
|
use warp::{reply::Reply, reject::Rejection, Filter};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
use self::files::{get_all::get_all_f, delete::delete_f};
|
use self::files::{get_all::get_all_f, delete::delete_f, upload::upload_f};
|
||||||
|
|
||||||
use super::state::SharedState;
|
use super::state::SharedState;
|
||||||
|
|
||||||
|
@ -25,5 +25,6 @@ pub fn get_routes(state: SharedState) -> impl Filter<Extract = impl Reply, Error
|
||||||
.and(warp::path::end())
|
.and(warp::path::end())
|
||||||
.map(api_root)
|
.map(api_root)
|
||||||
.or(get_all_f(state.clone()))
|
.or(get_all_f(state.clone()))
|
||||||
.or(delete_f(state))
|
.or(delete_f(state.clone()))
|
||||||
|
.or(upload_f(state))
|
||||||
}
|
}
|
|
@ -16,6 +16,39 @@ fn check_api_enabled(state: &SharedState) -> Result<(), WithStatus<Json>> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_api_pass(state: &SharedState) -> bool {
|
||||||
|
if let Some(keys) = state.config.api.apikeys.clone() {
|
||||||
|
keys.len() != 0
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_api_pass(state: &SharedState, key: String) -> Result<(), WithStatus<Json>> {
|
||||||
|
let mut valid = {
|
||||||
|
if let Some(keys) = state.config.api.apikeys.clone() {
|
||||||
|
keys.iter().find(|x| (**x) == key).is_some()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if key.len() == 0 {
|
||||||
|
valid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if valid {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(
|
||||||
|
warp::reply::with_status(
|
||||||
|
json(&ErrorMessage::new(Error::APIPasswordDenied)),
|
||||||
|
StatusCode::FORBIDDEN
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn function_disabled_err() -> WithStatus<Json> {
|
fn function_disabled_err() -> WithStatus<Json> {
|
||||||
warp::reply::with_status(
|
warp::reply::with_status(
|
||||||
json(&ErrorMessage::new(Error::APIFunctionDisabled)),
|
json(&ErrorMessage::new(Error::APIFunctionDisabled)),
|
||||||
|
@ -24,4 +57,5 @@ fn function_disabled_err() -> WithStatus<Json> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod get_all;
|
pub mod get_all;
|
||||||
pub mod delete;
|
pub mod delete;
|
||||||
|
pub mod upload;
|
|
@ -0,0 +1,137 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
use warp::{reply::{Reply, with_status, json}, http::StatusCode, reject::Rejection, Filter, filters::multipart::FormData};
|
||||||
|
|
||||||
|
use crate::web::{state::SharedState, forms::FormElement, rejection::HttpReject, api::types::{ErrorMessage, Error}};
|
||||||
|
|
||||||
|
use super::{is_api_pass, check_api_pass};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct UploadAPIMetadata {
|
||||||
|
sha512: String,
|
||||||
|
name: Option<String>,
|
||||||
|
pass: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UploadAPIPayload {
|
||||||
|
file: Vec<u8>,
|
||||||
|
instance_pass: Option<String>,
|
||||||
|
metadata: UploadAPIMetadata
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for UploadAPIPayload {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
file: vec![],
|
||||||
|
instance_pass: None,
|
||||||
|
metadata: UploadAPIMetadata {
|
||||||
|
sha512: "".into(),
|
||||||
|
name: None,
|
||||||
|
pass: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UploadAPIPayload {
|
||||||
|
pub fn from_form(data: HashMap<String, FormElement>) -> Option<UploadAPIPayload> {
|
||||||
|
|
||||||
|
let mut out = Self::default();
|
||||||
|
|
||||||
|
let file = data.get("file");
|
||||||
|
let instance_pass = data.get("instance_pass");
|
||||||
|
let metadata = data.get("metadata");
|
||||||
|
|
||||||
|
let mut fields_set = false;
|
||||||
|
|
||||||
|
// required fields
|
||||||
|
if let Some(file) = file {
|
||||||
|
if let Some(metadata) = metadata {
|
||||||
|
if let Some(metadata) = metadata.as_atr_or_none() {
|
||||||
|
out.file = file.data.clone();
|
||||||
|
if let Ok(metadata) = serde_json::from_str(&metadata) {
|
||||||
|
out.metadata = metadata;
|
||||||
|
}
|
||||||
|
fields_set = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// optional ones
|
||||||
|
if let Some(pass) = instance_pass {
|
||||||
|
if let Some(pass) = pass.as_atr_or_none() {
|
||||||
|
out.instance_pass = Some(pass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! fields_set {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn upload(state: SharedState, data: FormData) -> Result<Box<dyn Reply>, Rejection> {
|
||||||
|
|
||||||
|
let data = FormElement::from_formdata(data)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if let Err(err) = data {
|
||||||
|
return Ok(
|
||||||
|
Box::new(
|
||||||
|
with_status(
|
||||||
|
json(
|
||||||
|
&ErrorMessage {
|
||||||
|
error: Error::APIError,
|
||||||
|
details: Some(
|
||||||
|
format!("Error while parsing payload: {err}")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
StatusCode::BAD_REQUEST
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = data.unwrap(); // this is guaranteed to be `Ok` at this point
|
||||||
|
|
||||||
|
let payload = UploadAPIPayload::from_form(data);
|
||||||
|
if let Some(payload) = payload {
|
||||||
|
|
||||||
|
if is_api_pass(&state) {
|
||||||
|
if let Err(res) = check_api_pass(
|
||||||
|
&state,
|
||||||
|
match payload.instance_pass {
|
||||||
|
Some(x) => x,
|
||||||
|
None => "".into()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
return Ok(Box::new(res))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(
|
||||||
|
Box::new(
|
||||||
|
with_status(
|
||||||
|
json(&ErrorMessage {
|
||||||
|
error: Error::APIError,
|
||||||
|
details: Some("Request payload invalid".into())
|
||||||
|
}),
|
||||||
|
StatusCode::BAD_REQUEST
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn upload_f(state: SharedState) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||||
|
warp::path!("api" / "files" / "upload")
|
||||||
|
.and(warp::post())
|
||||||
|
.map(move || state.clone())
|
||||||
|
.and(warp::multipart::form())
|
||||||
|
.and_then(upload)
|
||||||
|
}
|
|
@ -4,7 +4,8 @@ use serde::{Serialize, Deserialize};
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
APIDisabled,
|
APIDisabled,
|
||||||
APIFunctionDisabled,
|
APIFunctionDisabled,
|
||||||
APIError
|
APIError,
|
||||||
|
APIPasswordDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
@ -19,7 +20,8 @@ impl ErrorMessage {
|
||||||
details: match error {
|
details: match error {
|
||||||
Error::APIDisabled => Some("API is disabled by the administrator. Please contact them for further details".into()),
|
Error::APIDisabled => Some("API is disabled by the administrator. Please contact them for further details".into()),
|
||||||
Error::APIFunctionDisabled => Some("This API function is disabled by the administrator. Please contact them for further details.".into()),
|
Error::APIFunctionDisabled => Some("This API function is disabled by the administrator. Please contact them for further details.".into()),
|
||||||
Error::APIError => Some("An error has occured while executing the API request".into())
|
Error::APIError => Some("An error has occured while executing the API request".into()),
|
||||||
|
Error::APIPasswordDenied => Some("API password authorization has been denied.".into())
|
||||||
},
|
},
|
||||||
error,
|
error,
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,10 @@ use state::SharedState;
|
||||||
pub fn routes(state: SharedState) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
pub fn routes(state: SharedState) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||||
static_dir!("static")
|
static_dir!("static")
|
||||||
.or(curlapi::get_routes(state.clone()))
|
.or(curlapi::get_routes(state.clone()))
|
||||||
.or(pages::get_routes(state.clone()))
|
|
||||||
.or(forms::get_routes(state.clone()))
|
.or(forms::get_routes(state.clone()))
|
||||||
.or(api::get_routes(state.clone()))
|
.or(api::get_routes(state.clone()))
|
||||||
.or(uploaded::get_uploaded(state))
|
.or(uploaded::get_uploaded(state.clone()))
|
||||||
|
.or(pages::get_routes(state))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue