feat(mongo): add GridFS manager for MongoDB

* feat(mongo): browse GridFS buckets and files

Refs #2342

* feat(mongo): add GridFS manager for MongoDB

* chore(i18n): autofill new translations

---------

Co-authored-by: dbx-i18n-bot <github-actions[bot]@users.noreply.github.com>
Co-authored-by: t8y2 <1156263951@qq.com>
This commit is contained in:
authored and GitHub committed 2026-07-03 18:34:00 +08:00
1 parent a8abb1c478
commit f826cb5d38
35 files changed
+1959 -39

No files matched your search

+7
View File
@@ -449,6 +449,13 @@ async fn main() {
.route("/document-store/list-databases", post(routes::document_store::list_databases))
.route("/document-store/list-collections", post(routes::document_store::list_collections))
.route("/document-store/find-documents", post(routes::document_store::find_documents))
.route("/document-store/list-gridfs-buckets", post(routes::document_store::list_gridfs_buckets))
.route("/document-store/create-gridfs-bucket", post(routes::document_store::create_gridfs_bucket))
.route("/document-store/delete-gridfs-bucket", post(routes::document_store::delete_gridfs_bucket))
.route("/document-store/list-gridfs-files", post(routes::document_store::list_gridfs_files))
.route("/document-store/download-gridfs-file", post(routes::document_store::download_gridfs_file))
.route("/document-store/upload-gridfs-file", post(routes::document_store::upload_gridfs_file))
.route("/document-store/delete-gridfs-file", post(routes::document_store::delete_gridfs_file))
.route("/document-store/insert-document", post(routes::document_store::insert_document))
.route("/document-store/update-document", post(routes::document_store::update_document))
.route("/document-store/delete-document", post(routes::document_store::delete_document))
+158 -1
View File
@@ -1,7 +1,7 @@
use std::future::Future;
use std::sync::Arc;
use axum::extract::State;
use axum::extract::{Multipart, State};
use axum::Json;
use serde::Deserialize;
@@ -99,6 +99,32 @@ pub struct DocumentDeleteRequest {
pub routing: Option<String>,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GridFsBucketRequest {
pub connection_id: String,
pub database: String,
pub bucket: String,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GridFsDownloadRequest {
pub connection_id: String,
pub database: String,
pub bucket: String,
pub file_id: String,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GridFsFileDeleteRequest {
pub connection_id: String,
pub database: String,
pub bucket: String,
pub file_id: String,
}
pub async fn list_databases(
State(state): State<Arc<WebState>>,
Json(req): Json<DocumentListDatabasesRequest>,
@@ -193,3 +219,134 @@ pub async fn delete_document(
.map_err(AppError)?;
Ok(Json(result))
}
pub async fn list_gridfs_files(
State(state): State<Arc<WebState>>,
Json(req): Json<GridFsBucketRequest>,
) -> Result<Json<Vec<dbx_core::document_ops::MongoGridFsFileInfo>>, AppError> {
let result =
dbx_core::document_ops::list_gridfs_files_core(&state.app, &req.connection_id, &req.database, &req.bucket)
.await
.map_err(AppError)?;
Ok(Json(result))
}
pub async fn list_gridfs_buckets(
State(state): State<Arc<WebState>>,
Json(req): Json<DocumentListCollectionsRequest>,
) -> Result<Json<Vec<dbx_core::document_ops::MongoGridFsBucketInfo>>, AppError> {
let result = dbx_core::document_ops::list_gridfs_buckets_core(&state.app, &req.connection_id, &req.database)
.await
.map_err(AppError)?;
Ok(Json(result))
}
pub async fn create_gridfs_bucket(
State(state): State<Arc<WebState>>,
Json(req): Json<GridFsBucketRequest>,
) -> Result<Json<()>, AppError> {
ensure_writable(&state.app, &req.connection_id, "Create GridFS bucket").await?;
dbx_core::document_ops::create_gridfs_bucket_core(&state.app, &req.connection_id, &req.database, &req.bucket)
.await
.map_err(AppError)?;
Ok(Json(()))
}
pub async fn delete_gridfs_bucket(
State(state): State<Arc<WebState>>,
Json(req): Json<GridFsBucketRequest>,
) -> Result<Json<()>, AppError> {
ensure_writable(&state.app, &req.connection_id, "Delete GridFS bucket").await?;
dbx_core::document_ops::delete_gridfs_bucket_core(&state.app, &req.connection_id, &req.database, &req.bucket)
.await
.map_err(AppError)?;
Ok(Json(()))
}
pub async fn download_gridfs_file(
State(state): State<Arc<WebState>>,
Json(req): Json<GridFsDownloadRequest>,
) -> Result<Json<Vec<u8>>, AppError> {
let result = dbx_core::document_ops::download_gridfs_file_core(
&state.app,
&req.connection_id,
&req.database,
&req.bucket,
&req.file_id,
)
.await
.map_err(AppError)?;
Ok(Json(result))
}
pub async fn upload_gridfs_file(
State(state): State<Arc<WebState>>,
mut multipart: Multipart,
) -> Result<Json<String>, AppError> {
let mut connection_id: Option<String> = None;
let mut database: Option<String> = None;
let mut bucket: Option<String> = None;
let mut file_name: Option<String> = None;
let mut content_type: Option<String> = None;
let mut file_bytes: Option<Vec<u8>> = None;
while let Some(field) = multipart.next_field().await.map_err(|e| AppError(e.to_string()))? {
let name = field.name().unwrap_or_default().to_string();
match name.as_str() {
"connectionId" => connection_id = Some(field.text().await.map_err(|e| AppError(e.to_string()))?),
"database" => database = Some(field.text().await.map_err(|e| AppError(e.to_string()))?),
"bucket" => bucket = Some(field.text().await.map_err(|e| AppError(e.to_string()))?),
"fileName" => file_name = Some(field.text().await.map_err(|e| AppError(e.to_string()))?),
"contentType" => content_type = Some(field.text().await.map_err(|e| AppError(e.to_string()))?),
"file" => {
if file_name.is_none() {
file_name = field.file_name().map(str::to_string);
}
if content_type.is_none() {
content_type = field.content_type().map(str::to_string);
}
file_bytes = Some(field.bytes().await.map_err(|e| AppError(e.to_string()))?.to_vec());
}
_ => {
let _ = field.bytes().await;
}
}
}
let connection_id = connection_id.ok_or_else(|| AppError("Missing connectionId".to_string()))?;
let database = database.ok_or_else(|| AppError("Missing database".to_string()))?;
let bucket = bucket.ok_or_else(|| AppError("Missing bucket".to_string()))?;
let file_name = file_name.ok_or_else(|| AppError("Missing fileName".to_string()))?;
let file_bytes = file_bytes.ok_or_else(|| AppError("No file uploaded".to_string()))?;
ensure_writable(&state.app, &connection_id, "Upload GridFS file").await?;
let result = dbx_core::document_ops::upload_gridfs_file_core(
&state.app,
&connection_id,
&database,
&bucket,
&file_name,
&file_bytes,
content_type.as_deref(),
)
.await
.map_err(AppError)?;
Ok(Json(result))
}
pub async fn delete_gridfs_file(
State(state): State<Arc<WebState>>,
Json(req): Json<GridFsFileDeleteRequest>,
) -> Result<Json<()>, AppError> {
ensure_writable(&state.app, &req.connection_id, "Delete GridFS file").await?;
dbx_core::document_ops::delete_gridfs_file_core(
&state.app,
&req.connection_id,
&req.database,
&req.bucket,
&req.file_id,
)
.await
.map_err(AppError)?;
Ok(Json(()))
}