Add folder support
This commit is contained in:
parent
df46ba93d1
commit
42976295df
7 changed files with 172 additions and 10 deletions
|
|
@ -147,6 +147,8 @@ pub async fn update_cipher(
|
|||
cipher.favorite,
|
||||
cipher.folder_id,
|
||||
cipher.updated_at,
|
||||
id,
|
||||
claims.sub,
|
||||
).map_err(|_|AppError::Database)?
|
||||
.run()
|
||||
.await?;
|
||||
|
|
|
|||
124
src/handlers/folders.rs
Normal file
124
src/handlers/folders.rs
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
use axum::{extract::State, Json};
|
||||
use chrono::Utc;
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
use worker::{query, Env};
|
||||
|
||||
use crate::auth::Claims;
|
||||
use crate::db;
|
||||
use crate::error::AppError;
|
||||
use crate::models::folder::{CreateFolderRequest, Folder, FolderResponse};
|
||||
use axum::extract::Path;
|
||||
|
||||
#[worker::send]
|
||||
pub async fn create_folder(
|
||||
claims: Claims,
|
||||
State(env): State<Arc<Env>>,
|
||||
Json(payload): Json<CreateFolderRequest>,
|
||||
) -> Result<Json<FolderResponse>, AppError> {
|
||||
let db = db::get_db(&env)?;
|
||||
let now = Utc::now();
|
||||
let now = now.format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string();
|
||||
|
||||
let folder = Folder {
|
||||
id: Uuid::new_v4().to_string(),
|
||||
user_id: claims.sub.clone(),
|
||||
name: payload.name,
|
||||
created_at: now.clone(),
|
||||
updated_at: now.clone(),
|
||||
};
|
||||
|
||||
query!(
|
||||
&db,
|
||||
"INSERT INTO folders (id, user_id, name, created_at, updated_at) VALUES (?1, ?2, ?3, ?4, ?5)",
|
||||
folder.id,
|
||||
folder.user_id,
|
||||
folder.name,
|
||||
folder.created_at,
|
||||
folder.updated_at
|
||||
)
|
||||
.map_err(|_| AppError::Database)?
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
let response = FolderResponse {
|
||||
id: folder.id,
|
||||
name: folder.name,
|
||||
revision_date: folder.updated_at,
|
||||
object: "folder".to_string(),
|
||||
};
|
||||
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
#[worker::send]
|
||||
pub async fn delete_folder(
|
||||
claims: Claims,
|
||||
State(env): State<Arc<Env>>,
|
||||
Path(id): Path<String>,
|
||||
) -> Result<Json<()>, AppError> {
|
||||
let db = db::get_db(&env)?;
|
||||
|
||||
query!(
|
||||
&db,
|
||||
"DELETE FROM folders WHERE id = ?1 AND user_id = ?2",
|
||||
id,
|
||||
claims.sub
|
||||
)
|
||||
.map_err(|_| AppError::Database)?
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
Ok(Json(()))
|
||||
}
|
||||
#[worker::send]
|
||||
pub async fn update_folder(
|
||||
claims: Claims,
|
||||
State(env): State<Arc<Env>>,
|
||||
Path(id): Path<String>,
|
||||
Json(payload): Json<CreateFolderRequest>,
|
||||
) -> Result<Json<FolderResponse>, AppError> {
|
||||
let db = db::get_db(&env)?;
|
||||
let now = Utc::now();
|
||||
let now = now.format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string();
|
||||
|
||||
let existing_folder: Folder = query!(
|
||||
&db,
|
||||
"SELECT * FROM folders WHERE id = ?1 AND user_id = ?2",
|
||||
id,
|
||||
claims.sub
|
||||
)
|
||||
.map_err(|_| AppError::Database)?
|
||||
.first(None)
|
||||
.await?
|
||||
.ok_or(AppError::NotFound("Folder not found".to_string()))?;
|
||||
|
||||
let folder = Folder {
|
||||
id: id.clone(),
|
||||
user_id: existing_folder.user_id,
|
||||
name: payload.name,
|
||||
created_at: existing_folder.created_at,
|
||||
updated_at: now.clone(),
|
||||
};
|
||||
|
||||
query!(
|
||||
&db,
|
||||
"UPDATE folders SET name = ?1, updated_at = ?2 WHERE id = ?3 AND user_id = ?4",
|
||||
folder.name,
|
||||
folder.updated_at,
|
||||
folder.id,
|
||||
folder.user_id
|
||||
)
|
||||
.map_err(|_| AppError::Database)?
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
let response = FolderResponse {
|
||||
id: folder.id,
|
||||
name: folder.name,
|
||||
revision_date: folder.updated_at,
|
||||
object: "folder".to_string(),
|
||||
};
|
||||
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
|
@ -3,3 +3,4 @@ pub mod ciphers;
|
|||
pub mod config;
|
||||
pub mod identity;
|
||||
pub mod sync;
|
||||
pub mod folders;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
error::AppError,
|
||||
models::{
|
||||
cipher::{Cipher, CipherDBModel},
|
||||
folder::Folder,
|
||||
folder::{Folder, FolderResponse},
|
||||
sync::{Profile, SyncResponse},
|
||||
user::User,
|
||||
},
|
||||
|
|
@ -32,13 +32,15 @@ pub async fn get_sync_data(
|
|||
.ok_or_else(|| AppError::NotFound("User not found".to_string()))?;
|
||||
|
||||
// Fetch folders
|
||||
let folders: Vec<Folder> = db
|
||||
let folders_db: Vec<Folder> = db
|
||||
.prepare("SELECT * FROM folders WHERE user_id = ?1")
|
||||
.bind(&[user_id.clone().into()])?
|
||||
.all()
|
||||
.await?
|
||||
.results()?;
|
||||
|
||||
let folders: Vec<FolderResponse> = folders_db.into_iter().map(|f| f.into()).collect();
|
||||
|
||||
// Fetch ciphers
|
||||
let ciphers: Vec<Value> = db
|
||||
.prepare("SELECT * FROM ciphers WHERE user_id = ?1")
|
||||
|
|
@ -91,4 +93,4 @@ pub async fn get_sync_data(
|
|||
};
|
||||
|
||||
Ok(Json(response))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Folder {
|
||||
pub id: String,
|
||||
pub user_id: String,
|
||||
|
|
@ -10,3 +9,33 @@ pub struct Folder {
|
|||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct FolderResponse {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub revision_date: String,
|
||||
#[serde(default = "default_object")]
|
||||
pub object: String,
|
||||
}
|
||||
|
||||
fn default_object() -> String {
|
||||
"folder".to_string()
|
||||
}
|
||||
|
||||
impl From<Folder> for FolderResponse {
|
||||
fn from(folder: Folder) -> Self {
|
||||
FolderResponse {
|
||||
id: folder.id,
|
||||
name: folder.name,
|
||||
revision_date: folder.updated_at,
|
||||
object: "folder".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CreateFolderRequest {
|
||||
pub name: String,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use super::{cipher::Cipher, folder::Folder};
|
||||
use super::{cipher::Cipher, folder::FolderResponse};
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
|
||||
|
|
@ -33,10 +33,10 @@ pub struct Profile {
|
|||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct SyncResponse {
|
||||
#[serde(rename = "Profile")]
|
||||
#[serde(rename = "profile")]
|
||||
pub profile: Profile,
|
||||
#[serde(rename = "Folders")]
|
||||
pub folders: Vec<Folder>,
|
||||
#[serde(rename = "folders")]
|
||||
pub folders: Vec<FolderResponse>,
|
||||
#[serde(rename = "ciphers")]
|
||||
pub ciphers: Vec<Cipher>,
|
||||
#[serde(rename = "Domains")]
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use axum::{
|
||||
routing::{get, post, put},
|
||||
routing::{get, post, put, delete},
|
||||
Router,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use worker::Env;
|
||||
|
||||
use crate::handlers::{accounts, ciphers, config, identity, sync};
|
||||
use crate::handlers::{accounts, ciphers, config, identity, sync, folders};
|
||||
|
||||
pub fn api_router(env: Env) -> Router {
|
||||
let app_state = Arc::new(env);
|
||||
|
|
@ -28,6 +28,10 @@ pub fn api_router(env: Env) -> Router {
|
|||
.route("/api/ciphers/create", post(ciphers::create_cipher))
|
||||
.route("/api/ciphers/{id}", put(ciphers::update_cipher))
|
||||
.route("/api/ciphers/{id}/delete", put(ciphers::delete_cipher))
|
||||
// Folders CRUD
|
||||
.route("/api/folders", post(folders::create_folder))
|
||||
.route("/api/folders/{id}", put(folders::update_folder))
|
||||
.route("/api/folders/{id}", delete(folders::delete_folder))
|
||||
.route("/api/config", get(config::config))
|
||||
.with_state(app_state)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue