feat: add folder management routes and update API version
This commit is contained in:
parent
89137fb150
commit
5e04837b5d
5 changed files with 78 additions and 5 deletions
|
|
@ -838,5 +838,7 @@ pub(crate) async fn fetch_cipher_json_array_raw(
|
|||
.await
|
||||
.map_err(db::map_d1_json_error)?;
|
||||
|
||||
Ok(row.map(|r| r.ciphers_json).unwrap_or_else(|| "[]".to_string()))
|
||||
Ok(row
|
||||
.map(|r| r.ciphers_json)
|
||||
.unwrap_or_else(|| "[]".to_string()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ pub async fn config(
|
|||
// We should make sure that we keep this updated when we support the new server features
|
||||
// Version history:
|
||||
// - Individual cipher key encryption: 2024.2.0
|
||||
"version": "2024.7.0",
|
||||
"gitHash": "25cf6119-dirty",
|
||||
"version": "2025.6.0",
|
||||
"gitHash": "5d84f176",
|
||||
"server": {
|
||||
"name": "Vaultwarden",
|
||||
"url": "https://github.com/dani-garcia/vaultwarden"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use axum::{extract::State, Json};
|
||||
use axum::extract::{Path, State};
|
||||
use axum::Json;
|
||||
use chrono::Utc;
|
||||
use serde_json::{json, Value};
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
use worker::{query, Env};
|
||||
|
|
@ -8,7 +10,56 @@ use crate::auth::Claims;
|
|||
use crate::db::{self, touch_user_updated_at};
|
||||
use crate::error::AppError;
|
||||
use crate::models::folder::{CreateFolderRequest, Folder, FolderResponse};
|
||||
use axum::extract::Path;
|
||||
|
||||
#[worker::send]
|
||||
pub async fn list_folders(
|
||||
claims: Claims,
|
||||
State(env): State<Arc<Env>>,
|
||||
) -> Result<Json<Value>, AppError> {
|
||||
let db = db::get_db(&env)?;
|
||||
|
||||
let folders_db: Vec<Folder> = db
|
||||
.prepare("SELECT * FROM folders WHERE user_id = ?1")
|
||||
.bind(&[claims.sub.clone().into()])?
|
||||
.all()
|
||||
.await?
|
||||
.results()
|
||||
.map_err(|_| AppError::Database)?;
|
||||
|
||||
let folders: Vec<FolderResponse> = folders_db.into_iter().map(|f| f.into()).collect();
|
||||
|
||||
Ok(Json(json!({
|
||||
"data": folders,
|
||||
"object": "list",
|
||||
"continuationToken": null,
|
||||
})))
|
||||
}
|
||||
|
||||
#[worker::send]
|
||||
pub async fn get_folder(
|
||||
claims: Claims,
|
||||
State(env): State<Arc<Env>>,
|
||||
Path(id): Path<String>,
|
||||
) -> Result<Json<FolderResponse>, AppError> {
|
||||
let db = db::get_db(&env)?;
|
||||
|
||||
let 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_else(|| {
|
||||
AppError::BadRequest(
|
||||
"Invalid folder: Folder does not exist or belongs to another user".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(Json(folder.into()))
|
||||
}
|
||||
|
||||
#[worker::send]
|
||||
pub async fn create_folder(
|
||||
|
|
|
|||
|
|
@ -89,6 +89,21 @@ where
|
|||
deserializer.deserialize_any(BoolOrIntVisitor)
|
||||
}
|
||||
|
||||
// Custom deserialization function for cipher types
|
||||
fn deserialize_cipher_type<'de, D>(deserializer: D) -> Result<i32, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let value = i32::deserialize(deserializer)?;
|
||||
match value {
|
||||
1..=5 => Ok(value), // Valid cipher types: Login, SecureNote, Card, Identity, SshKey
|
||||
_ => Err(de::Error::invalid_value(
|
||||
de::Unexpected::Signed(value as i64),
|
||||
&"a valid cipher type (1=Login, 2=SecureNote, 3=Card, 4=Identity, 5=SshKey)",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
// The struct that is stored in the database and used in handlers.
|
||||
// For serialization to JSON for the client, we implement a custom `Serialize`.
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
|
|
@ -296,6 +311,7 @@ pub struct CipherRequestData {
|
|||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub organization_id: Option<String>,
|
||||
#[serde(rename = "type")]
|
||||
#[serde(deserialize_with = "deserialize_cipher_type")]
|
||||
pub r#type: i32,
|
||||
pub name: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ pub fn api_router(env: Env) -> Router {
|
|||
Router::new()
|
||||
// Identity/Auth routes
|
||||
.route("/identity/accounts/prelogin", post(accounts::prelogin))
|
||||
.route("/identity/accounts/register", post(accounts::register))
|
||||
.route(
|
||||
"/identity/accounts/register/finish",
|
||||
post(accounts::register),
|
||||
|
|
@ -123,9 +124,12 @@ pub fn api_router(env: Env) -> Router {
|
|||
// Purge vault - delete all ciphers and folders (requires password verification)
|
||||
.route("/api/ciphers/purge", post(ciphers::purge_vault))
|
||||
// Folders CRUD
|
||||
.route("/api/folders", get(folders::list_folders))
|
||||
.route("/api/folders", post(folders::create_folder))
|
||||
.route("/api/folders/{id}", get(folders::get_folder))
|
||||
.route("/api/folders/{id}", put(folders::update_folder))
|
||||
.route("/api/folders/{id}", delete(folders::delete_folder))
|
||||
.route("/api/folders/{id}/delete", post(folders::delete_folder))
|
||||
.route("/api/config", get(config::config))
|
||||
// Emergency access (stub - returns empty lists, feature not supported)
|
||||
.route(
|
||||
|
|
|
|||
Loading…
Reference in a new issue