mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-07-12 16:01:56 +08:00
feat: 精简页面结构并增强核心功能
- 删除 MCP 配置、Agent 配置、部署 3 个页面,保留 6 个核心页面 - 重写模型配置页:Provider/模型 CRUD + 一键应用默认模型(自动生成 fallback) - 增强服务管理页:版本检测 + 配置备份管理(创建/恢复/删除) - 增强记忆文件页:单个文件下载 + 分类打包 zip 下载 - Rust 后端新增 5 个命令(4 个备份 + export_memory_zip) - 更新路由和侧边栏,同步清理
This commit is contained in:
@@ -11,6 +11,10 @@ fn openclaw_dir() -> PathBuf {
|
||||
.join(".openclaw")
|
||||
}
|
||||
|
||||
fn backups_dir() -> PathBuf {
|
||||
openclaw_dir().join("backups")
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn read_openclaw_config() -> Result<Value, String> {
|
||||
let path = openclaw_dir().join("openclaw.json");
|
||||
@@ -96,3 +100,103 @@ pub fn write_env_file(path: String, config: String) -> Result<(), String> {
|
||||
fs::write(&expanded, &config)
|
||||
.map_err(|e| format!("写入 .env 失败: {e}"))
|
||||
}
|
||||
|
||||
// ===== 备份管理 =====
|
||||
|
||||
#[tauri::command]
|
||||
pub fn list_backups() -> Result<Value, String> {
|
||||
let dir = backups_dir();
|
||||
if !dir.exists() {
|
||||
return Ok(Value::Array(vec![]));
|
||||
}
|
||||
let mut backups: Vec<Value> = vec![];
|
||||
let entries = fs::read_dir(&dir)
|
||||
.map_err(|e| format!("读取备份目录失败: {e}"))?;
|
||||
|
||||
for entry in entries.flatten() {
|
||||
let path = entry.path();
|
||||
if path.extension().and_then(|e| e.to_str()) != Some("json") {
|
||||
continue;
|
||||
}
|
||||
let name = path.file_name().unwrap_or_default().to_string_lossy().to_string();
|
||||
let meta = fs::metadata(&path).ok();
|
||||
let size = meta.as_ref().map(|m| m.len()).unwrap_or(0);
|
||||
let created = meta
|
||||
.and_then(|m| m.modified().ok())
|
||||
.and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
|
||||
.map(|d| d.as_secs())
|
||||
.unwrap_or(0);
|
||||
|
||||
let mut obj = serde_json::Map::new();
|
||||
obj.insert("name".into(), Value::String(name));
|
||||
obj.insert("size".into(), Value::Number(size.into()));
|
||||
obj.insert("created_at".into(), Value::Number(created.into()));
|
||||
backups.push(Value::Object(obj));
|
||||
}
|
||||
// 按时间倒序
|
||||
backups.sort_by(|a, b| {
|
||||
let ta = a.get("created_at").and_then(|v| v.as_u64()).unwrap_or(0);
|
||||
let tb = b.get("created_at").and_then(|v| v.as_u64()).unwrap_or(0);
|
||||
tb.cmp(&ta)
|
||||
});
|
||||
Ok(Value::Array(backups))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn create_backup() -> Result<Value, String> {
|
||||
let dir = backups_dir();
|
||||
fs::create_dir_all(&dir)
|
||||
.map_err(|e| format!("创建备份目录失败: {e}"))?;
|
||||
|
||||
let src = openclaw_dir().join("openclaw.json");
|
||||
if !src.exists() {
|
||||
return Err("openclaw.json 不存在".into());
|
||||
}
|
||||
|
||||
let now = chrono::Local::now();
|
||||
let name = format!("openclaw-{}.json", now.format("%Y%m%d-%H%M%S"));
|
||||
let dest = dir.join(&name);
|
||||
fs::copy(&src, &dest)
|
||||
.map_err(|e| format!("备份失败: {e}"))?;
|
||||
|
||||
let size = fs::metadata(&dest).map(|m| m.len()).unwrap_or(0);
|
||||
let mut obj = serde_json::Map::new();
|
||||
obj.insert("name".into(), Value::String(name));
|
||||
obj.insert("size".into(), Value::Number(size.into()));
|
||||
Ok(Value::Object(obj))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn restore_backup(name: String) -> Result<(), String> {
|
||||
// 安全检查
|
||||
if name.contains("..") || name.contains('/') {
|
||||
return Err("非法文件名".into());
|
||||
}
|
||||
let backup_path = backups_dir().join(&name);
|
||||
if !backup_path.exists() {
|
||||
return Err(format!("备份文件不存在: {name}"));
|
||||
}
|
||||
let target = openclaw_dir().join("openclaw.json");
|
||||
|
||||
// 恢复前先自动备份当前配置
|
||||
if target.exists() {
|
||||
let _ = create_backup();
|
||||
}
|
||||
|
||||
fs::copy(&backup_path, &target)
|
||||
.map_err(|e| format!("恢复失败: {e}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn delete_backup(name: String) -> Result<(), String> {
|
||||
if name.contains("..") || name.contains('/') {
|
||||
return Err("非法文件名".into());
|
||||
}
|
||||
let path = backups_dir().join(&name);
|
||||
if !path.exists() {
|
||||
return Err(format!("备份文件不存在: {name}"));
|
||||
}
|
||||
fs::remove_file(&path)
|
||||
.map_err(|e| format!("删除失败: {e}"))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/// 记忆文件管理命令
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn openclaw_dir() -> PathBuf {
|
||||
@@ -132,3 +133,44 @@ pub fn delete_memory_file(path: String) -> Result<(), String> {
|
||||
|
||||
Err(format!("文件不存在: {path}"))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn export_memory_zip(category: String) -> Result<String, String> {
|
||||
let dir = memory_dir(&category);
|
||||
if !dir.exists() {
|
||||
return Err("目录不存在".to_string());
|
||||
}
|
||||
|
||||
let mut files = Vec::new();
|
||||
collect_files(&dir, &dir, &mut files, &category)?;
|
||||
if files.is_empty() {
|
||||
return Err("没有可导出的文件".to_string());
|
||||
}
|
||||
|
||||
let tmp_dir = std::env::temp_dir();
|
||||
let zip_name = format!(
|
||||
"openclaw-{}-{}.zip",
|
||||
category,
|
||||
chrono::Local::now().format("%Y%m%d-%H%M%S")
|
||||
);
|
||||
let zip_path = tmp_dir.join(&zip_name);
|
||||
|
||||
let file = fs::File::create(&zip_path)
|
||||
.map_err(|e| format!("创建 zip 失败: {e}"))?;
|
||||
let mut zip = zip::ZipWriter::new(file);
|
||||
let options = zip::write::SimpleFileOptions::default()
|
||||
.compression_method(zip::CompressionMethod::Deflated);
|
||||
|
||||
for rel_path in &files {
|
||||
let full_path = dir.join(rel_path);
|
||||
let content = fs::read_to_string(&full_path)
|
||||
.map_err(|e| format!("读取 {rel_path} 失败: {e}"))?;
|
||||
zip.start_file(rel_path, options)
|
||||
.map_err(|e| format!("写入 zip 失败: {e}"))?;
|
||||
zip.write_all(content.as_bytes())
|
||||
.map_err(|e| format!("写入内容失败: {e}"))?;
|
||||
}
|
||||
|
||||
zip.finish().map_err(|e| format!("完成 zip 失败: {e}"))?;
|
||||
Ok(zip_path.to_string_lossy().to_string())
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ pub fn run() {
|
||||
config::get_version_info,
|
||||
config::check_installation,
|
||||
config::write_env_file,
|
||||
config::list_backups,
|
||||
config::create_backup,
|
||||
config::restore_backup,
|
||||
config::delete_backup,
|
||||
// 服务
|
||||
service::get_services_status,
|
||||
service::start_service,
|
||||
@@ -28,6 +32,7 @@ pub fn run() {
|
||||
memory::read_memory_file,
|
||||
memory::write_memory_file,
|
||||
memory::delete_memory_file,
|
||||
memory::export_memory_zip,
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("启动 ClawPanel 失败");
|
||||
|
||||
Reference in New Issue
Block a user