secretumvault/src/api/handlers.rs

180 lines
6.7 KiB
Rust
Raw Normal View History

use std::sync::Arc;
2025-12-22 21:34:01 +00:00
#[cfg(feature = "server")]
use axum::{
extract::{Path, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use serde_json::{json, Value};
use super::ApiResponse;
use crate::core::VaultCore;
/// GET /v1/* - Read a secret from any mounted engine
#[cfg(feature = "server")]
pub async fn read_secret(
State(vault): State<Arc<VaultCore>>,
Path(path): Path<String>,
) -> impl IntoResponse {
let full_path = path;
match vault.split_path(&full_path) {
Some((_mount_path, relative_path)) => match vault.route_to_engine(&full_path) {
Some(engine) => match engine.read(&relative_path).await {
Ok(Some(data)) => {
let response = ApiResponse::success(data);
(StatusCode::OK, Json(response)).into_response()
}
Ok(None) => {
let response = ApiResponse::<Value>::error("Secret not found");
(StatusCode::NOT_FOUND, Json(response)).into_response()
}
Err(e) => {
let response = ApiResponse::<Value>::error(format!("Failed to read: {}", e));
(StatusCode::INTERNAL_SERVER_ERROR, Json(response)).into_response()
}
},
None => {
let response = ApiResponse::<Value>::error("No engine mounted at this path");
(StatusCode::NOT_FOUND, Json(response)).into_response()
}
},
None => {
let response = ApiResponse::<Value>::error("Path not found");
(StatusCode::NOT_FOUND, Json(response)).into_response()
}
}
}
/// POST /v1/* - Write a secret to any mounted engine
#[cfg(feature = "server")]
pub async fn write_secret(
State(vault): State<Arc<VaultCore>>,
Path(path): Path<String>,
Json(payload): Json<Value>,
) -> impl IntoResponse {
let full_path = path;
match vault.split_path(&full_path) {
Some((_mount_path, relative_path)) => match vault.route_to_engine(&full_path) {
Some(engine) => match engine.write(&relative_path, &payload).await {
Ok(()) => {
let response = ApiResponse::success(json!({"path": full_path}));
(StatusCode::OK, Json(response)).into_response()
}
Err(e) => {
let response = ApiResponse::<Value>::error(format!("Failed to write: {}", e));
(StatusCode::BAD_REQUEST, Json(response)).into_response()
}
},
None => {
let response = ApiResponse::<Value>::error("No engine mounted at this path");
(StatusCode::NOT_FOUND, Json(response)).into_response()
}
},
None => {
let response = ApiResponse::<Value>::error("Path not found");
(StatusCode::NOT_FOUND, Json(response)).into_response()
}
}
}
/// PUT /v1/* - Update a secret in any mounted engine
#[cfg(feature = "server")]
pub async fn update_secret(
State(vault): State<Arc<VaultCore>>,
Path(path): Path<String>,
Json(payload): Json<Value>,
) -> impl IntoResponse {
let full_path = path;
match vault.split_path(&full_path) {
Some((_mount_path, relative_path)) => match vault.route_to_engine(&full_path) {
Some(engine) => match engine.write(&relative_path, &payload).await {
Ok(()) => {
let response = ApiResponse::success(json!({"path": full_path}));
(StatusCode::OK, Json(response)).into_response()
}
Err(e) => {
let response = ApiResponse::<Value>::error(format!("Failed to update: {}", e));
(StatusCode::BAD_REQUEST, Json(response)).into_response()
}
},
None => {
let response = ApiResponse::<Value>::error("No engine mounted at this path");
(StatusCode::NOT_FOUND, Json(response)).into_response()
}
},
None => {
let response = ApiResponse::<Value>::error("Path not found");
(StatusCode::NOT_FOUND, Json(response)).into_response()
}
}
}
/// DELETE /v1/* - Delete a secret from any mounted engine
#[cfg(feature = "server")]
pub async fn delete_secret(
State(vault): State<Arc<VaultCore>>,
Path(path): Path<String>,
) -> impl IntoResponse {
let full_path = path;
match vault.split_path(&full_path) {
Some((_mount_path, relative_path)) => match vault.route_to_engine(&full_path) {
Some(engine) => match engine.delete(&relative_path).await {
Ok(()) => {
let response: ApiResponse<Value> = ApiResponse::success(json!({}));
(StatusCode::NO_CONTENT, Json(response)).into_response()
}
Err(e) => {
let response = ApiResponse::<Value>::error(format!("Failed to delete: {}", e));
(StatusCode::BAD_REQUEST, Json(response)).into_response()
}
},
None => {
let response = ApiResponse::<Value>::error("No engine mounted at this path");
(StatusCode::NOT_FOUND, Json(response)).into_response()
}
},
None => {
let response = ApiResponse::<Value>::error("Path not found");
(StatusCode::NOT_FOUND, Json(response)).into_response()
}
}
}
/// LIST /v1/* - List secrets at a path prefix
#[cfg(feature = "server")]
pub async fn list_secrets(
State(vault): State<Arc<VaultCore>>,
Path(path): Path<String>,
) -> impl IntoResponse {
let full_path = path;
match vault.split_path(&full_path) {
Some((_mount_path, relative_path)) => match vault.route_to_engine(&full_path) {
Some(engine) => match engine.list(&relative_path).await {
Ok(items) => {
let response = ApiResponse::success(json!({"keys": items}));
(StatusCode::OK, Json(response)).into_response()
}
Err(e) => {
let response = ApiResponse::<Value>::error(format!("Failed to list: {}", e));
(StatusCode::BAD_REQUEST, Json(response)).into_response()
}
},
None => {
let response = ApiResponse::<Value>::error("No engine mounted at this path");
(StatusCode::NOT_FOUND, Json(response)).into_response()
}
},
None => {
let response = ApiResponse::<Value>::error("Path not found");
(StatusCode::NOT_FOUND, Json(response)).into_response()
}
}
}