1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
use std::path::Path;
use actix_web::{web, HttpResponse};
use tokio::fs;
use crate::{app_state::AppState, error::AppError};
use super::common::{
config_file_path, connect_backup_file_path, connect_file_path, model_limits_file_path,
};
/// Resets (deletes) the Bamboo configuration file.
pub async fn reset_bamboo_config(app_state: web::Data<AppState>) -> Result<HttpResponse, AppError> {
remove_config_file_if_exists(&config_file_path(&app_state.app_data_dir)).await?;
remove_config_file_if_exists(&model_limits_file_path(&app_state.app_data_dir)).await?;
// #455: connect.json is a sibling of config.json now, not an inline key —
// a full config reset must clear it too, or bamboo-connect bot
// tokens/allowlists would silently survive (and get re-merged back onto
// the freshly-defaulted config on the very next load). Mirrors
// config.json/model_limits.json above: only the primary file is removed
// here — same as config.json.bak, which is intentionally left alone
// (it's a low-sensitivity, multi-generation config snapshot meant to
// survive a reset for recovery).
remove_config_file_if_exists(&connect_file_path(&app_state.app_data_dir)).await?;
// #457: UNLIKE config.json.bak, connect.json.bak is NOT left alone — it
// holds an encrypted IM bot token, an immediately-usable remote-control
// credential, and a full reset must scrub it too or that token stays
// recoverable straight off disk after the user asked to wipe everything.
remove_config_file_if_exists(&connect_backup_file_path(&app_state.app_data_dir)).await?;
// Reset in-memory config and best-effort reload provider.
let new_config = app_state.reload_config().await;
if let Err(error) = app_state.reload_provider().await {
tracing::warn!(
"Config reset updated config to provider={}, but provider reload failed: {}",
new_config.provider,
error
);
}
// Config reset may remove/disable MCP servers; reconcile to stop any running servers.
app_state
.mcp_manager
.reconcile_from_config(&new_config.mcp)
.await;
Ok(HttpResponse::Ok().json(serde_json::json!({ "success": true })))
}
pub(super) async fn remove_config_file_if_exists(path: &Path) -> Result<(), AppError> {
match fs::try_exists(path).await {
Ok(true) => {
fs::remove_file(path)
.await
.map_err(AppError::StorageError)?;
Ok(())
}
Ok(false) => Ok(()),
Err(error) => Err(AppError::StorageError(error)),
}
}