From 55caeabd94e23714e0e6b6bce0232c4ed3a7f31c Mon Sep 17 00:00:00 2001 From: Warren Date: Wed, 24 Jun 2026 03:31:43 +0800 Subject: [PATCH] Add root parameter to backup/snapshot REST API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit API Enhancement: - All snapshot endpoints now accept 'root' query parameter - Default root: /data (for production) - Test root: configurable (e.g., /tmp/backup_test) Endpoints updated: - GET /api/v2/snapshots?root= - POST /api/v2/snapshots/:name?root= - DELETE /api/v2/snapshots/:name?root= - POST /api/v2/snapshots/:name/restore?root= - GET /api/v2/storage/stats?root= Integration Testing Results ✅: - Create snapshot: test_snap1 created - List snapshots: ['test_snap1'] returned - Modify file: 'original content' → 'modified content' - Restore snapshot: 'modified content' → 'original content' ✅ - Delete snapshot: test_snap1 removed Snapshot metadata format: { 'name': 'test_snap1', 'created': {'secs_since_epoch': 1782243041, 'nanos_since_epoch': 344384000}, 'source_path': '/tmp/backup_test' } Build: 495 tests pass Server: Port 11438 running with root parameter support --- data/auth.sqlite | Bin 81920 -> 81920 bytes markbase-core/src/server.rs | 29 +++++++++++++++++++---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/data/auth.sqlite b/data/auth.sqlite index ee5f56c548b8fc1181565596a2ded6577399835c..346595fbda53f2485d80662292d0096bd70062f6 100644 GIT binary patch delta 352 zcmZo@U~On%ogmG4V4{pOl8bqnWtnpGb5rw5iYhsla59TBrKINOb1d5YMgIf8z+zTrP9}~e z3=Aw_22j)V9)fmVCv{ThGlb%~)KToSa%* zT(?>Bm6|AHn9CJ2*(f`0NFqf5?lZj&< z0|N_~0Ti{zD(e5=pRbky32<#@Ech?K=>UrWb1nCT$?O+?Z&noOu*&l#J#W^=izETrq44>@qe-qeY|NrwdUf=$OpAkaKGYSZZaq^vJ;J?hjiGL!0 j0lz=L9zQSN8@|(<6%{t}G0E3X542|#-TugqQH~J+W))hZ diff --git a/markbase-core/src/server.rs b/markbase-core/src/server.rs index 40469ce..43acfef 100644 --- a/markbase-core/src/server.rs +++ b/markbase-core/src/server.rs @@ -2821,45 +2821,54 @@ async fn run_backup_handler() -> Json { } } -async fn list_snapshots_handler() -> Json> { +async fn list_snapshots_handler(Query(params): Query>) -> Json> { + let root = params.get("root").map(|p| PathBuf::from(p)).unwrap_or_else(|| PathBuf::from("/data")); let backend = LocalFs::new(); - let root = PathBuf::from("/data"); match backend.list_snapshots(&root) { Ok(list) => Json(list), Err(_) => Json(Vec::new()), } } -async fn create_snapshot_handler(Path(name): Path) -> Json { +async fn create_snapshot_handler( + Path(name): Path, + Query(params): Query>, +) -> Json { + let root = params.get("root").map(|p| PathBuf::from(p)).unwrap_or_else(|| PathBuf::from("/data")); let backend = LocalFs::new(); - let root = PathBuf::from("/data"); match backend.create_snapshot(&root, &name) { Ok(_) => Json(serde_json::json!({"success": true, "name": name})), Err(e) => Json(serde_json::json!({"success": false, "error": e.to_string()})), } } -async fn delete_snapshot_handler(Path(name): Path) -> Json { +async fn delete_snapshot_handler( + Path(name): Path, + Query(params): Query>, +) -> Json { + let root = params.get("root").map(|p| PathBuf::from(p)).unwrap_or_else(|| PathBuf::from("/data")); let backend = LocalFs::new(); - let root = PathBuf::from("/data"); match backend.delete_snapshot(&root, &name) { Ok(_) => Json(serde_json::json!({"success": true, "name": name})), Err(e) => Json(serde_json::json!({"success": false, "error": e.to_string()})), } } -async fn restore_snapshot_handler(Path(name): Path) -> Json { +async fn restore_snapshot_handler( + Path(name): Path, + Query(params): Query>, +) -> Json { + let root = params.get("root").map(|p| PathBuf::from(p)).unwrap_or_else(|| PathBuf::from("/data")); let backend = LocalFs::new(); - let root = PathBuf::from("/data"); match backend.restore_snapshot(&root, &name) { Ok(_) => Json(serde_json::json!({"success": true, "name": name})), Err(e) => Json(serde_json::json!({"success": false, "error": e.to_string()})), } } -async fn get_storage_stats_handler() -> Json { +async fn get_storage_stats_handler(Query(params): Query>) -> Json { + let root = params.get("root").map(|p| PathBuf::from(p)).unwrap_or_else(|| PathBuf::from("/data")); let backend = LocalFs::new(); - let root = PathBuf::from("/data"); match backend.stat(&root) { Ok(stat) => Json(StorageStatsResponse { total_size: stat.size,