Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions src/database/category.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ impl CategoryOperator {
}

/// Delete a category
pub async fn delete(&self, id: i32, user: Option<User>) -> Result<String, CategoryError> {
pub async fn delete(&self, id: i32) -> Result<String, CategoryError> {
let db = &self.state.database;
let category: Option<Model> = Entity::find_by_id(id).one(db).await.context(DBSnafu)?;

Expand All @@ -138,7 +138,7 @@ impl CategoryOperator {
category.delete(db).await.context(DBSnafu)?;

let operation_log = OperationLog {
user,
user: self.user.clone(),
date: Utc::now(),
table: Table::Category,
operation: OperationType::Delete,
Expand Down Expand Up @@ -167,11 +167,7 @@ impl CategoryOperator {
///
/// - name or path is already taken (they should be unique)
/// - path parent directory does not exist (to avoid completely wrong paths)
pub async fn create(
&self,
f: &CategoryForm,
user: Option<User>,
) -> Result<Model, CategoryError> {
pub async fn create(&self, f: &CategoryForm) -> Result<Model, CategoryError> {
let dir = Utf8PathBuf::from(&f.path);
let parent = dir.parent().unwrap();

Expand Down Expand Up @@ -208,7 +204,7 @@ impl CategoryOperator {
let model = model.try_into_model().unwrap();

let operation_log = OperationLog {
user,
user: self.user.clone(),
date: Utc::now(),
table: Table::Category,
operation: OperationType::Create,
Expand Down
8 changes: 2 additions & 6 deletions src/database/content_folder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,7 @@ impl ContentFolderOperator {
///
/// - name or path is already taken (they should be unique in one folder)
/// - path parent directory does not exist (to avoid completely wrong paths)
pub async fn create(
&self,
f: &ContentFolderForm,
user: Option<User>,
) -> Result<Model, ContentFolderError> {
pub async fn create(&self, f: &ContentFolderForm) -> Result<Model, ContentFolderError> {
// Check duplicates in same folder
let list = if let Some(parent_id) = f.parent_id {
self.list_child_folders(parent_id).await?
Expand Down Expand Up @@ -158,7 +154,7 @@ impl ContentFolderOperator {
let model = model.try_into_model().unwrap();

let operation_log = OperationLog {
user,
user: self.user.clone(),
date: Utc::now(),
table: Table::ContentFolder,
operation: OperationType::Create,
Expand Down
1 change: 1 addition & 0 deletions src/database/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
pub mod category;
pub mod content_folder;
pub mod operation;
pub mod operator;
29 changes: 29 additions & 0 deletions src/database/operator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use crate::database::{category::CategoryOperator, content_folder::ContentFolderOperator};
use crate::extractors::user::User;
use crate::state::AppState;

#[derive(Clone, Debug)]
pub struct DatabaseOperator {
pub state: AppState,
pub user: Option<User>,
}

impl DatabaseOperator {
pub fn new(state: AppState, user: Option<User>) -> Self {
Self { state, user }
}

pub fn category(&self) -> CategoryOperator {
CategoryOperator {
state: self.state.clone(),
user: self.user.clone(),
}
}

pub fn content_folder(&self) -> ContentFolderOperator {
ContentFolderOperator {
state: self.state.clone(),
user: self.user.clone(),
}
}
}
16 changes: 7 additions & 9 deletions src/extractors/user.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use axum::{
extract::OptionalFromRequestParts,
http::{StatusCode, request::Parts},
};
use axum::{extract::OptionalFromRequestParts, http::request::Parts};
use derive_more::Display;
use serde::{Deserialize, Serialize};

use crate::state::error::AppStateError;

/// A logged-in user, as expressed by the Remote-User header.
///
/// Cannot be produced outside of header extraction.
Expand All @@ -16,7 +15,7 @@ impl<S> OptionalFromRequestParts<S> for User
where
S: Send + Sync,
{
type Rejection = (StatusCode, &'static str);
type Rejection = AppStateError;

async fn from_request_parts(
parts: &mut Parts,
Expand All @@ -25,10 +24,9 @@ where
if let Some(username) = parts.headers.get("remote-user") {
match username.to_str() {
Ok(username) => Ok(Some(User(String::from(username)))),
Err(_e) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
"The remote-user header returned by the reverse proxy is invalid.",
)),
Err(_e) => Err(AppStateError::Static {
reason: "The remote-user header returned by the reverse proxy is invalid.",
}),
}
} else {
Ok(None)
Expand Down
55 changes: 16 additions & 39 deletions src/routes/category.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
use askama::Template;
use askama_web::WebTemplate;
use axum::Form;
use axum::extract::{Path, State};
use axum::extract::Path;
use axum::response::{IntoResponse, Redirect};
use axum_extra::extract::CookieJar;
use serde::{Deserialize, Serialize};
use snafu::prelude::*;

use crate::database::category;
use crate::database::category::CategoryError;
use crate::database::content_folder;
use crate::database::{category, category::CategoryOperator};
use crate::extractors::normalized_path::*;
use crate::extractors::user::User;
use crate::state::flash_message::{OperationStatus, get_cookie};
use crate::state::{AppState, AppStateContext, error::*};
use crate::state::{AppStateContext, error::*};

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct CategoryForm {
Expand All @@ -26,40 +25,28 @@ pub struct CategoryForm {
pub struct NewCategoryTemplate {
/// Global application state
pub state: AppStateContext,
/// Logged-in user.
pub user: Option<User>,
/// Error
pub error: Option<CategoryError>,
/// Default form with value
pub category_form: Option<CategoryForm>,
}

pub async fn new(
State(app_state): State<AppState>,
user: Option<User>,
app_state_context: AppStateContext,
) -> Result<impl axum::response::IntoResponse, AppStateError> {
let app_state_context = app_state.context().await?;

Ok(NewCategoryTemplate {
state: app_state_context,
user,
category_form: None,
error: None,
})
}

pub async fn delete(
State(app_state): State<AppState>,
user: Option<User>,
context: AppStateContext,
Path(id): Path<i32>,
jar: CookieJar,
) -> Result<impl axum::response::IntoResponse, AppStateError> {
// let app_state_context = app_state.context().await?;
let categories = CategoryOperator::new(app_state.clone(), user.clone());

let deleted = categories.delete(id, user.clone()).await;

let operation_status = match deleted {
let operation_status = match context.db.category().delete(id).await {
Ok(name) => OperationStatus {
success: true,
message: format!("The category {} has been successfully deleted", name),
Expand All @@ -76,16 +63,11 @@ pub async fn delete(
}

pub async fn create(
State(app_state): State<AppState>,
user: Option<User>,
context: AppStateContext,
jar: CookieJar,
Form(form): Form<CategoryForm>,
) -> Result<impl axum::response::IntoResponse, AppStateError> {
let categories = CategoryOperator::new(app_state.clone(), user.clone());

let created = categories.create(&form, user.clone()).await;

match created {
match context.db.category().create(&form).await {
Ok(created) => {
let operation_status = OperationStatus {
success: true,
Expand Down Expand Up @@ -119,33 +101,29 @@ pub struct CategoryShowTemplate {
pub state: AppStateContext,
/// Categories found in database
pub content_folders: Vec<content_folder::Model>,
/// Logged-in user.
pub user: Option<User>,
/// Category
category: category::Model,
/// Operation status for UI confirmation (Cookie)
pub flash: Option<OperationStatus>,
}

pub async fn show(
State(app_state): State<AppState>,
user: Option<User>,
context: AppStateContext,
Path(category_name): Path<String>,
jar: CookieJar,
) -> Result<impl IntoResponse, AppStateError> {
let app_state_context = app_state.context().await?;
let categories = context.db.category();

let category: category::Model = CategoryOperator::new(app_state.clone(), user.clone())
let category = categories
.find_by_name(category_name.to_string())
.await
.context(CategorySnafu)?;

// get all content folders in this category
let content_folders: Vec<content_folder::Model> =
CategoryOperator::new(app_state.clone(), user.clone())
.list_folders(category.id)
.await
.context(CategorySnafu)?;
let content_folders = categories
.list_folders(category.id)
.await
.context(CategorySnafu)?;

let (jar, operation_status) = get_cookie(jar);

Expand All @@ -154,8 +132,7 @@ pub async fn show(
CategoryShowTemplate {
content_folders,
category,
state: app_state_context,
user,
state: context,
flash: operation_status,
},
))
Expand Down
30 changes: 10 additions & 20 deletions src/routes/content_folder.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
use askama::Template;
use askama_web::WebTemplate;
use axum::Form;
use axum::extract::State;
use axum::response::{IntoResponse, Redirect};
use axum_extra::extract::CookieJar;
use camino::Utf8PathBuf;
use serde::{Deserialize, Serialize};
use snafu::prelude::*;

use crate::database::category::CategoryOperator;
use crate::database::content_folder::{ContentFolderOperator, PathBreadcrumb};
use crate::database::content_folder::PathBreadcrumb;
use crate::database::{category, content_folder};
use crate::extractors::folder_request::FolderRequest;
use crate::extractors::user::User;
use crate::state::flash_message::{OperationStatus, get_cookie};
use crate::state::{AppState, AppStateContext, error::*};
use crate::state::{AppStateContext, error::*};

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ContentFolderForm {
Expand All @@ -33,8 +30,6 @@ pub struct ContentFolderShowTemplate {
pub current_content_folder: content_folder::Model,
/// Folders with parent_id set to current folder
pub sub_content_folders: Vec<content_folder::Model>,
/// Logged-in user.
pub user: Option<User>,
/// Category
pub category: category::Model,
/// BreadCrumb extract from path
Expand All @@ -46,13 +41,10 @@ pub struct ContentFolderShowTemplate {
}

pub async fn show(
State(app_state): State<AppState>,
context: AppStateContext,
folder: FolderRequest,
user: Option<User>,
jar: CookieJar,
) -> Result<(CookieJar, ContentFolderShowTemplate), AppStateError> {
let app_state_context = app_state.context().await?;

let (jar, operation_status) = get_cookie(jar);

Ok((
Expand All @@ -63,25 +55,23 @@ pub async fn show(
sub_content_folders: folder.sub_folders,
current_content_folder: folder.folder,
category: folder.category,
state: app_state_context,
user,
state: context,
flash: operation_status,
},
))
}

pub async fn create(
State(app_state): State<AppState>,
user: Option<User>,
context: AppStateContext,
jar: CookieJar,
Form(mut form): Form<ContentFolderForm>,
) -> Result<impl axum::response::IntoResponse, AppStateError> {
// let app_state_context = app_state.context().await?;
let content_folder = ContentFolderOperator::new(app_state.clone(), user.clone());
let categories = context.db.category();
let content_folders = context.db.content_folder();

// build path with Parent folder path (or category path if parent is None) + folder.name
let parent_path = if let Some(parent_id) = form.parent_id {
let parent_folder = content_folder
let parent_folder = content_folders
.find_by_id(parent_id)
.await
.context(ContentFolderSnafu)?;
Expand All @@ -91,7 +81,7 @@ pub async fn create(
};

// Get folder category
let category: category::Model = CategoryOperator::new(app_state.clone(), user.clone())
let category: category::Model = categories
.find_by_id(form.category_id)
.await
.context(CategorySnafu)?;
Expand All @@ -115,7 +105,7 @@ pub async fn create(
// build final path with parent_path and path of form
form.path = format!("{}/{}", parent_path, form.name);

let created = content_folder.create(&form, user.clone()).await;
let created = content_folders.create(&form).await;

match created {
Ok(created) => {
Expand Down
Loading