diff --git a/backend/src/db/connection.rs b/backend/src/db/connection.rs index 339fb03..8a21234 100644 --- a/backend/src/db/connection.rs +++ b/backend/src/db/connection.rs @@ -162,6 +162,10 @@ impl DBConnection { pub fn save_node(&mut self, span: &Span, node: &super::Inode) { DB_MANAGER.save_node(span, &mut self.db, node); } + pub fn move_node(&mut self, span: &Span, node: &mut super::Inode, target: i32) { + DB_MANAGER.move_node(span, &mut self.db, node, target); + } + pub fn delete_node(&mut self, span: &Span, node: &super::Inode) { DB_MANAGER.delete_node(span, &mut self.db, node); } diff --git a/backend/src/db/manager.rs b/backend/src/db/manager.rs index e25863a..408cba3 100644 --- a/backend/src/db/manager.rs +++ b/backend/src/db/manager.rs @@ -124,6 +124,12 @@ impl DBManager { diesel::update(node).set(node.clone()).execute(db).expect("Failed to save node"); } + pub fn move_node(&self, span: &Span, db: &mut super::RawDBConnection, node: &mut Inode, target: i32) { + self.children_cache.remove(&node.parent_id.unwrap_or(0)); + node.parent_id = Some(target); + self.save_node(span, db, node); + } + pub fn delete_node(&self, span: &Span, db: &mut super::RawDBConnection, node: &Inode) { let inner_span = metrics::span("delete_node", span); let owner = node.owner_id.to_string(); diff --git a/backend/src/dto.rs b/backend/src/dto.rs index d7858c5..0aeac87 100644 --- a/backend/src/dto.rs +++ b/backend/src/dto.rs @@ -186,6 +186,12 @@ pub mod requests { pub name: String } + #[derive(Debug, Deserialize, Serialize, Clone)] + pub struct MoveNode { + pub nodes: Vec, + pub target: i32 + } + #[derive(Debug, Deserialize, Serialize, Clone)] pub struct CreateZip { pub nodes: Vec diff --git a/backend/src/main.rs b/backend/src/main.rs index 36f90e6..231e6ff 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -77,16 +77,18 @@ fn handle_request(mut req: Request, db: db::DBPool) { AppError::NotFound => 404, AppError::InternalError(_) => 500 }; + let msg = match v { + AppError::BadRequest(v) => v.to_string(), + AppError::Unauthorized(v) => v.to_string(), + AppError::Forbidden(v) => v.to_string(), + AppError::NotFound => "Not found".to_owned(), + AppError::InternalError(v) => v.to_string() + }; + span.set_tag(|| Tag::new("http.error_msg", msg.clone())); Response::from_data( serde_json::to_vec(&dto::responses::Error { statusCode: code, - message: match v { - AppError::BadRequest(v) => v.to_string(), - AppError::Unauthorized(v) => v.to_string(), - AppError::Forbidden(v) => v.to_string(), - AppError::NotFound => "Not found".to_owned(), - AppError::InternalError(v) => v.to_string() - } + message: msg }) .unwrap() ) @@ -165,6 +167,7 @@ fn handle_api_request(span: &mut Span, req: &mut Request, pool: db::DBPool) -> R ("/api/fs/create_zip", Method::Post, None) => parse_body(span, req).and_then(|v| routes::fs::routes::create_zip(span, req, db, info, v, &pool)), ("/api/fs/download_preview", Method::Get, Some(v)) => routes::fs::routes::download_preview(span, req, db, info, v), ("/api/fs/get_type", Method::Get, Some(v)) => routes::fs::routes::get_type(span, req, db, info, v), + ("/api/fs/move", Method::Post, None) => parse_body(span, req).and_then(|v| routes::fs::routes::move_node(span, req, db, info, v)), _ => AppError::NotFound.err() } } diff --git a/backend/src/routes/fs/routes.rs b/backend/src/routes/fs/routes.rs index 727bb03..88f837d 100644 --- a/backend/src/routes/fs/routes.rs +++ b/backend/src/routes/fs/routes.rs @@ -549,3 +549,43 @@ pub fn get_type( _type: mime_guess::from_path(std::path::Path::new(&node.name)).first_or_octet_stream().to_string() }) } + +pub fn move_node( + span: &Span, + _: &mut Request, + db: &mut DBConnection, + info: UserInfo, + data: dto::requests::MoveNode +) -> Result { + let guard_lock = DBConnection::get_lock(info.0.id); + let _guard = guard_lock.write(span); + + let target = + super::get_node_and_validate(span, &info.0, data.target, db).ok_or(AppError::BadRequest("Invalid target"))?; + if target.is_file { + return AppError::BadRequest("Invalid target").err(); + } + + let mut nodes = data + .nodes + .iter() + .map(|v| super::get_node_and_validate(span, &info.0, *v, db)) + .into_iter() + .collect::>>() + .ok_or(AppError::BadRequest("Invalid node"))?; + + for parent in super::get_node_path(span, target.clone(), db) { + if nodes.contains(&parent) { + return AppError::BadRequest("Can't move node into one of it's subfolders").err(); + } + } + for child in db.get_children(span, target.id) { + if nodes.iter().any(|n| n.name == child.name) { + return AppError::BadRequest("Can't overwrite existing file").err(); + } + } + + nodes.iter_mut().for_each(|n| db.move_node(span, n, target.id)); + + get_reply(&dto::responses::Success { statusCode: 200 }) +} diff --git a/frontend/src/api/fs.ts b/frontend/src/api/fs.ts index 753b30d..bd6f0ba 100644 --- a/frontend/src/api/fs.ts +++ b/frontend/src/api/fs.ts @@ -51,6 +51,20 @@ export const create_file = ( token ); +export const move = ( + token: string, + nodes: number[], + target: number +): Promise => + post_token( + '/api/fs/move', + { + nodes: nodes, + target: target + }, + token + ); + export const create_zip = ( token: string, nodes: number[] diff --git a/frontend/src/dto/index.ts b/frontend/src/dto/index.ts index d18f201..3ca4591 100644 --- a/frontend/src/dto/index.ts +++ b/frontend/src/dto/index.ts @@ -60,6 +60,11 @@ export namespace Requests { name: string; } + export interface Move extends Base { + nodes: number[]; + target: number; + } + export interface CreateZip extends Base { nodes: number[]; } diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index e8e7a53..e324f7f 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -2,6 +2,7 @@ import type { RouteRecordRaw } from 'vue-router'; import { createRouter, createWebHistory } from 'vue-router'; import LoginView from '@/views/LoginView.vue'; import SignupView from '@/views/SignupView.vue'; +import ForgotPasswordView from '@/views/ForgotPasswordView.vue'; import HomeView from '@/views/HomeView.vue'; import FSView from '@/views/FSView.vue'; import SetTokenView from '@/views/SetTokenView.vue'; @@ -39,6 +40,11 @@ const routes: Array = [ name: 'signup', component: SignupView }, + { + path: '/forgot', + name: 'forgot', + component: ForgotPasswordView + }, { path: '/fs/:node_id', name: 'fs', diff --git a/frontend/src/views/LoginView.vue b/frontend/src/views/LoginView.vue index f1b2797..970a05b 100644 --- a/frontend/src/views/LoginView.vue +++ b/frontend/src/views/LoginView.vue @@ -68,6 +68,10 @@ function signup() { router.replace('signup'); } +function forgot() { + router.replace('forgot'); +} + function onKey(event: KeyboardEvent) { if (event.key == 'Enter') login(); } @@ -95,10 +99,10 @@ function onKey(event: KeyboardEvent) { @keyup="onKey" /> - + Login - + - + Signup + + Forgot password? +