diff --git a/pentaract/src/errors.rs b/pentaract/src/errors.rs index eaba1f6..18154ff 100644 --- a/pentaract/src/errors.rs +++ b/pentaract/src/errors.rs @@ -35,6 +35,8 @@ pub enum PentaractError { InvalidFolderName, #[error("You cannot manage access of yourself")] CannotManageAccessOfYourself, + #[error("Storage does not have workers")] + StorageDoesNotHaveWorkers, #[error("unknown error")] Unknown, #[error("{0} header is required")] @@ -51,6 +53,7 @@ impl From for (StatusCode, String) { | PentaractError::StorageChatIdConflict | PentaractError::StorageWorkerNameConflict | PentaractError::StorageWorkerTokenConflict + | PentaractError::StorageDoesNotHaveWorkers | PentaractError::CannotManageAccessOfYourself => (StatusCode::CONFLICT, e.to_string()), PentaractError::NotAuthenticated => (StatusCode::UNAUTHORIZED, e.to_string()), PentaractError::DoesNotExist(_) => (StatusCode::NOT_FOUND, e.to_string()), diff --git a/pentaract/src/repositories/storage_workers.rs b/pentaract/src/repositories/storage_workers.rs index c507a6b..df4625f 100644 --- a/pentaract/src/repositories/storage_workers.rs +++ b/pentaract/src/repositories/storage_workers.rs @@ -56,6 +56,18 @@ impl<'d> StorageWorkersRepository<'d> { Ok(sw) } + pub async fn storage_has_any(&self, storage_id: Uuid) -> PentaractResult { + let has_sws: (_,) = sqlx::query_as(&format!( + "SELECT COUNT(*) > 0 FROM {STORAGE_WORKERS_TABLE} WHERE storage_id = $1" + )) + .bind(storage_id) + .fetch_one(self.db) + .await + .map_err(|e| map_not_found(e, "storage_workers"))?; + + Ok(has_sws.0) + } + pub async fn list_by_user_id(&self, user_id: Uuid) -> PentaractResult> { sqlx::query_as(&format!( "SELECT * FROM {STORAGE_WORKERS_TABLE} WHERE user_id = $1" diff --git a/pentaract/src/routers/storage_workers.rs b/pentaract/src/routers/storage_workers.rs index fa92074..99b3ed3 100644 --- a/pentaract/src/routers/storage_workers.rs +++ b/pentaract/src/routers/storage_workers.rs @@ -1,8 +1,12 @@ use std::sync::Arc; use axum::{ - extract::State, http::StatusCode, middleware, response::IntoResponse, routing::get, Extension, - Json, Router, + extract::{Query, State}, + http::StatusCode, + middleware, + response::{IntoResponse, Response}, + routing::get, + Extension, Json, Router, }; use crate::{ @@ -10,7 +14,9 @@ use crate::{ jwt_manager::AuthUser, routing::{app_state::AppState, middlewares::auth::logged_in_required}, }, - schemas::storage_workers::InStorageWorkerSchema, + schemas::storage_workers::{ + HasStorageWorkers, InStorageWorkerSchema, StorageWorkersStorageIDQuery, + }, services::storage_workers::StorageWorkersService, }; @@ -20,6 +26,7 @@ impl StorageWorkersRouter { pub fn get_router(state: Arc) -> Router { Router::new() .route("/", get(Self::list).post(Self::create)) + .route("/has_workers", get(Self::has_storages_workers)) .route_layer(middleware::from_fn_with_state( state.clone(), logged_in_required, @@ -45,4 +52,15 @@ impl StorageWorkersRouter { let sws = StorageWorkersService::new(&state.db).list(&user).await?; Ok::<_, (StatusCode, String)>((StatusCode::OK, Json(sws))) } + + pub async fn has_storages_workers( + State(state): State>, + Extension(user): Extension, + query: Query, + ) -> Result { + let has = StorageWorkersService::new(&state.db) + .has_storage_workers(query.0.storage_id, &user) + .await?; + Ok(Json(HasStorageWorkers { has }).into_response()) + } } diff --git a/pentaract/src/schemas/storage_workers.rs b/pentaract/src/schemas/storage_workers.rs index e817224..6caacc2 100644 --- a/pentaract/src/schemas/storage_workers.rs +++ b/pentaract/src/schemas/storage_workers.rs @@ -1,8 +1,19 @@ -use serde::Deserialize; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; #[derive(Deserialize)] pub struct InStorageWorkerSchema { pub name: String, pub token: String, - pub storage_id: Option, + pub storage_id: Option, +} + +#[derive(Deserialize)] +pub struct StorageWorkersStorageIDQuery { + pub storage_id: Uuid, +} + +#[derive(Serialize)] +pub struct HasStorageWorkers { + pub has: bool, } diff --git a/pentaract/src/services/files.rs b/pentaract/src/services/files.rs index fed935e..699894f 100644 --- a/pentaract/src/services/files.rs +++ b/pentaract/src/services/files.rs @@ -17,12 +17,15 @@ use crate::{ access::AccessType, files::{FSElement, File, InFile}, }, - repositories::{access::AccessRepository, files::FilesRepository}, + repositories::{ + access::AccessRepository, files::FilesRepository, storage_workers::StorageWorkersRepository, + }, schemas::files::{InFileSchema, InFolderSchema}, }; pub struct FilesService<'d> { repo: FilesRepository<'d>, + storage_workers_repo: StorageWorkersRepository<'d>, access_repo: AccessRepository<'d>, tx: ClientSender, } @@ -30,10 +33,12 @@ pub struct FilesService<'d> { impl<'d> FilesService<'d> { pub fn new(db: &'d PgPool, tx: ClientSender) -> Self { let repo = FilesRepository::new(db); + let storage_workers_repo = StorageWorkersRepository::new(db); let access_repo = AccessRepository::new(db); Self { repo, access_repo, + storage_workers_repo, tx, } } @@ -82,14 +87,17 @@ impl<'d> FilesService<'d> { ) .await?; - // 1. path validation + // 1. check whether storage got workers + Self::check_storage_workers(&self, in_schema.storage_id).await?; + + // 2. path validation if !Self::validate_filepath(&in_schema.path) { return Err(PentaractError::InvalidPath); } let in_file = InFile::new(in_schema.path, in_schema.size, in_schema.storage_id); - // 2. saving file to db + // 3. saving file to db let file = self.repo.create_file(in_file).await?; self._upload(file, in_schema.file, user).await @@ -110,7 +118,10 @@ impl<'d> FilesService<'d> { ) .await?; - // 1. saving file in db + // 1. check whether storage got workers + Self::check_storage_workers(&self, in_file.storage_id).await?; + + // 2. saving file in db let file = self.repo.create_file_anyway(in_file).await?; self._upload(file, file_data, user).await @@ -157,6 +168,18 @@ impl<'d> FilesService<'d> { Ok(()) } + async fn check_storage_workers(&self, storage_id: Uuid) -> PentaractResult<()> { + if !self + .storage_workers_repo + .storage_has_any(storage_id) + .await? + { + Err(PentaractError::StorageDoesNotHaveWorkers) + } else { + Ok(()) + } + } + pub async fn download( &self, path: &str, diff --git a/pentaract/src/services/storage_workers.rs b/pentaract/src/services/storage_workers.rs index f6613d8..f87cdd0 100644 --- a/pentaract/src/services/storage_workers.rs +++ b/pentaract/src/services/storage_workers.rs @@ -1,21 +1,27 @@ use sqlx::PgPool; +use uuid::Uuid; use crate::{ - common::jwt_manager::AuthUser, + common::{access::check_access, jwt_manager::AuthUser}, errors::{PentaractError, PentaractResult}, - models::storage_workers::{InStorageWorker, StorageWorker}, - repositories::storage_workers::StorageWorkersRepository, + models::{ + access::AccessType, + storage_workers::{InStorageWorker, StorageWorker}, + }, + repositories::{access::AccessRepository, storage_workers::StorageWorkersRepository}, schemas::storage_workers::InStorageWorkerSchema, }; pub struct StorageWorkersService<'d> { repo: StorageWorkersRepository<'d>, + access_repo: AccessRepository<'d>, } impl<'d> StorageWorkersService<'d> { pub fn new(db: &'d PgPool) -> Self { let repo = StorageWorkersRepository::new(db); - Self { repo } + let access_repo = AccessRepository::new(db); + Self { repo, access_repo } } pub async fn create( @@ -45,4 +51,15 @@ impl<'d> StorageWorkersService<'d> { pub async fn list(&self, user: &AuthUser) -> PentaractResult> { self.repo.list_by_user_id(user.id).await } + + pub async fn has_storage_workers( + &self, + storage_id: Uuid, + user: &AuthUser, + ) -> PentaractResult { + // 0. checking access + check_access(&self.access_repo, user.id, storage_id, &AccessType::R).await?; + + self.repo.storage_has_any(storage_id).await + } } diff --git a/ui/src/App.jsx b/ui/src/App.jsx index 9e31ca9..fd4fc0d 100644 --- a/ui/src/App.jsx +++ b/ui/src/App.jsx @@ -11,6 +11,7 @@ import StorageWorkerCreateForm from './pages/StorageWorkers/StorageWorkerCreateF import Files from './pages/Files' import UploadFileTo from './pages/Files/UploadFileTo' import Register from './pages/Register' +import NotFound from './pages/404' const theme = createTheme({ palette: { @@ -41,6 +42,7 @@ const App = () => { path="/storage_workers/register" component={StorageWorkerCreateForm} /> + diff --git a/ui/src/pages/404.jsx b/ui/src/pages/404.jsx new file mode 100644 index 0000000..03ba594 --- /dev/null +++ b/ui/src/pages/404.jsx @@ -0,0 +1,21 @@ +import Typography from '@suid/material/Typography' +import Box from '@suid/material/Box' + +const NotFound = () => { + return ( + + 404 + Not found + + ) +} + +export default NotFound