Data upload from web
This commit is contained in:
@@ -11,10 +11,11 @@ use axum_range::Ranged;
|
||||
use axum_extra::headers::Range;
|
||||
use axum_extra::TypedHeader;
|
||||
|
||||
use crate::models::users;
|
||||
use crate::services;
|
||||
|
||||
pub async fn get_data(
|
||||
auth: auth::JWT,
|
||||
auth: Option<auth::JWT>,
|
||||
range: Option<TypedHeader<Range>>,
|
||||
Path(file_name): Path<String>,
|
||||
State(ctx): State<AppContext>,
|
||||
@@ -27,13 +28,18 @@ pub async fn upload_data(
|
||||
State(ctx): State<AppContext>,
|
||||
payload: Multipart,
|
||||
) -> Result<Response> {
|
||||
match users::Model::find_by_pid(&ctx.db, &auth.claims.pid).await {
|
||||
Ok(_) => {}
|
||||
Err(_) => return unauthorized("Unauthorized"),
|
||||
}
|
||||
|
||||
services::data::add(&auth, &ctx, payload).await?;
|
||||
format::html("<h1>File uploaded successfully</h1>")
|
||||
}
|
||||
|
||||
pub fn routes() -> Routes {
|
||||
Routes::new()
|
||||
.prefix("api/data/")
|
||||
.prefix("api/data")
|
||||
.add("/upload", post(upload_data))
|
||||
.add("/:file_name", get(get_data))
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@
|
||||
#![allow(clippy::unused_async)]
|
||||
use loco_rs::prelude::*;
|
||||
|
||||
use crate::models::users;
|
||||
use crate::views;
|
||||
|
||||
pub async fn render_index(
|
||||
@@ -13,11 +14,22 @@ pub async fn render_index(
|
||||
}
|
||||
|
||||
pub async fn render_login(ViewEngine(v): ViewEngine<TeraView>) -> impl IntoResponse {
|
||||
views::website::login(v).await
|
||||
views::auth::login(v).await
|
||||
}
|
||||
|
||||
pub async fn render_upload(
|
||||
auth: auth::JWT,
|
||||
ViewEngine(v): ViewEngine<TeraView>,
|
||||
State(ctx): State<AppContext>,
|
||||
) -> Result<impl IntoResponse> {
|
||||
let _current_user = users::Model::find_by_pid(&ctx.db, &auth.claims.pid).await?;
|
||||
|
||||
views::data::upload(v).await
|
||||
}
|
||||
|
||||
pub fn routes() -> Routes {
|
||||
Routes::new()
|
||||
.add("/", get(render_index))
|
||||
.add("/upload", get(render_upload))
|
||||
.add("/login", get(render_login))
|
||||
}
|
||||
|
@@ -21,29 +21,43 @@ pub async fn get_data_by_file_name(file_name: &str, ctx: &AppContext) -> ModelRe
|
||||
}
|
||||
|
||||
pub async fn serve_data_file(
|
||||
auth: &auth::JWT,
|
||||
auth: &Option<auth::JWT>,
|
||||
range: Option<TypedHeader<Range>>,
|
||||
file_name: &str,
|
||||
ctx: &AppContext,
|
||||
) -> Result<Ranged<KnownSize<File>>> {
|
||||
let data = get_data_by_file_name(&file_name, &ctx).await?;
|
||||
let data = match get_data_by_file_name(&file_name, &ctx).await {
|
||||
Ok(data) => data,
|
||||
Err(_) => return not_found(),
|
||||
};
|
||||
|
||||
if data.protected {
|
||||
match users::Model::find_by_pid(&ctx.db, &auth.claims.pid).await {
|
||||
Ok(_) => {}
|
||||
Err(_) => return unauthorized("Unauthorized"),
|
||||
match auth {
|
||||
None => return unauthorized("Unauthorized"),
|
||||
Some(auth) => match users::Model::find_by_pid(&ctx.db, &auth.claims.pid).await {
|
||||
Ok(_) => {}
|
||||
Err(_) => return unauthorized("Unauthorized"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let file = File::open(&data.file_url).await?;
|
||||
let body = KnownSize::file(file).await?;
|
||||
let range = range.map(|TypedHeader(range)| range);
|
||||
Ok(Ranged::new(range, body))
|
||||
match File::open(&data.file_url).await {
|
||||
Ok(file) => {
|
||||
let body = KnownSize::file(file).await?;
|
||||
let range = range.map(|TypedHeader(range)| range);
|
||||
Ok(Ranged::new(range, body))
|
||||
}
|
||||
Err(_) => return not_found(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn add(auth: &auth::JWT, ctx: &AppContext, mut payload: Multipart) -> ModelResult<Model> {
|
||||
let _current_user = users::Model::find_by_pid(&ctx.db, &auth.claims.pid).await?;
|
||||
|
||||
let mut protected = None;
|
||||
let mut file_name = None;
|
||||
let mut content = None;
|
||||
let mut file_path = None;
|
||||
|
||||
while let Some(field) = payload
|
||||
.next_field()
|
||||
@@ -64,26 +78,25 @@ pub async fn add(auth: &auth::JWT, ctx: &AppContext, mut payload: Multipart) ->
|
||||
protected = Some(value);
|
||||
}
|
||||
"file" => {
|
||||
file_name = match field.file_name() {
|
||||
Some(file_name) => Some(String::from(file_name)),
|
||||
None => return Err(ModelError::Any("Failed to get file name".into())),
|
||||
};
|
||||
let og_file_name = field
|
||||
.file_name()
|
||||
.ok_or_else(|| ModelError::Any("Failed to get file name".into()))?;
|
||||
let ext = String::from(og_file_name.split('.').last().unwrap_or("txt"));
|
||||
|
||||
if file_name.is_none() {
|
||||
return Err(ModelError::Any("Failed to get file name".into()));
|
||||
}
|
||||
let temp_file_name = uuid::Uuid::new_v4().to_string();
|
||||
let temp_file_name = format!("{}.{}", temp_file_name, ext);
|
||||
|
||||
let path = PathBuf::from("uploads").join(file_name.as_ref().unwrap());
|
||||
file_name = Some(temp_file_name.clone());
|
||||
|
||||
let content = field
|
||||
let path = PathBuf::from(temp_file_name);
|
||||
file_path = Some(path.clone());
|
||||
|
||||
let data_content = field
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(|_| ModelError::Any("Failed to get bytes".into()))?;
|
||||
|
||||
match ctx.storage.as_ref().upload(path.as_path(), &content).await {
|
||||
Ok(_) => {}
|
||||
Err(_) => return Err(ModelError::Any("Failed to save file to storage".into())),
|
||||
}
|
||||
|
||||
content = Some(data_content.clone());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -91,6 +104,7 @@ pub async fn add(auth: &auth::JWT, ctx: &AppContext, mut payload: Multipart) ->
|
||||
|
||||
let protected =
|
||||
protected.ok_or_else(|| ModelError::Any("Protected field is required".into()))?;
|
||||
|
||||
let file_name = file_name.ok_or_else(|| ModelError::Any("File field is required".into()))?;
|
||||
|
||||
let mut item = ActiveModel {
|
||||
@@ -98,8 +112,22 @@ pub async fn add(auth: &auth::JWT, ctx: &AppContext, mut payload: Multipart) ->
|
||||
};
|
||||
|
||||
item.protected = Set(protected);
|
||||
item.file_name = Set(file_name);
|
||||
item.file_name = Set(file_name.clone());
|
||||
item.file_url = Set(format!("uploads/{}", file_name));
|
||||
|
||||
let item = item.insert(&ctx.db).await?;
|
||||
|
||||
let file_path = file_path.ok_or_else(|| ModelError::Any("File path is required".into()))?;
|
||||
let content = content.ok_or_else(|| ModelError::Any("Content is required".into()))?;
|
||||
|
||||
match ctx
|
||||
.storage
|
||||
.as_ref()
|
||||
.upload(file_path.as_path(), &content)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {}
|
||||
Err(_) => return Err(ModelError::Any("Failed to save file to storage".into())),
|
||||
}
|
||||
Ok(item)
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
use loco_rs::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::models::_entities::users;
|
||||
@@ -39,3 +40,7 @@ impl CurrentResponse {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn login(v: impl ViewRenderer) -> Result<impl IntoResponse> {
|
||||
format::render().view(&v, "website/login.html", data!({}))
|
||||
}
|
||||
|
5
src/views/data.rs
Normal file
5
src/views/data.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
use loco_rs::prelude::*;
|
||||
|
||||
pub async fn upload(v: impl ViewRenderer) -> Result<impl IntoResponse> {
|
||||
format::render().view(&v, "website/data-upload.html", data!({}))
|
||||
}
|
@@ -1,2 +1,3 @@
|
||||
pub mod auth;
|
||||
pub mod data;
|
||||
pub mod website;
|
||||
|
@@ -12,7 +12,3 @@ pub async fn index(v: impl ViewRenderer, ctx: &AppContext) -> Result<impl IntoRe
|
||||
data!({ "skills": skills, "jobs": jobs }),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn login(v: impl ViewRenderer) -> Result<impl IntoResponse> {
|
||||
format::render().view(&v, "website/login.html", data!({}))
|
||||
}
|
||||
|
Reference in New Issue
Block a user