- 路径校验提取validateFilePath+sentinel error替代字符串匹配 - requireUpdateAPI收敛7处重复nil检查 - 端口18765统一为8073,消除分散魔法数字 - CodeMirror添加搜索功能+滚动位置LRU缓存恢复 - 文件列表新增列排序+搜索过滤 - Toolbar重排:快捷访问内嵌+搜索框集成+历史改图标 - 重命名零闪烁:updateFilePath草稿迁移 - changelog用marked渲染+sanitizeHtml防XSS - MigrateTabConfig扩展map驱动覆盖openclaw-manager→version迁移 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
209 lines
5.1 KiB
Go
209 lines
5.1 KiB
Go
package api
|
||
|
||
import (
|
||
"fmt"
|
||
"u-desk/internal/service"
|
||
)
|
||
|
||
// ConfigAPI 配置 API
|
||
type ConfigAPI struct {
|
||
configService *service.ConfigService
|
||
}
|
||
|
||
// NewConfigAPI 创建配置 API 实例
|
||
func NewConfigAPI() (*ConfigAPI, error) {
|
||
configService, err := service.NewConfigService()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return &ConfigAPI{
|
||
configService: configService,
|
||
}, nil
|
||
}
|
||
|
||
// GetAppConfigResponse 获取应用配置响应
|
||
type GetAppConfigResponse struct {
|
||
Tabs []AppTabDefinition `json:"tabs"`
|
||
VisibleTabs []string `json:"visibleTabs"`
|
||
DefaultTab string `json:"defaultTab"`
|
||
}
|
||
|
||
// AppTabDefinition 应用 Tab 定义(前端格式)
|
||
type AppTabDefinition struct {
|
||
Key string `json:"key"`
|
||
Title string `json:"title"`
|
||
Visible bool `json:"visible"`
|
||
Enabled bool `json:"enabled"`
|
||
}
|
||
|
||
// SaveAppConfigRequest 保存应用配置请求(前端格式)
|
||
type SaveAppConfigRequest struct {
|
||
Tabs []AppTabDefinition `json:"tabs"`
|
||
VisibleTabs []string `json:"visibleTabs"`
|
||
DefaultTab string `json:"defaultTab"`
|
||
}
|
||
|
||
// GetAppConfig 获取应用配置
|
||
func (api *ConfigAPI) GetAppConfig() (map[string]interface{}, error) {
|
||
tabConfig, err := api.configService.GetTabConfig()
|
||
if err != nil {
|
||
return map[string]interface{}{
|
||
"success": false,
|
||
"message": fmt.Sprintf("获取配置失败: %v", err),
|
||
}, err
|
||
}
|
||
|
||
// 转换为前端格式
|
||
tabs := make([]AppTabDefinition, len(tabConfig.AvailableTabs))
|
||
visibleTabSet := make(map[string]bool)
|
||
for _, key := range tabConfig.VisibleTabs {
|
||
visibleTabSet[key] = true
|
||
}
|
||
|
||
for i, tab := range tabConfig.AvailableTabs {
|
||
tabs[i] = AppTabDefinition{
|
||
Key: tab.Key,
|
||
Title: tab.Title,
|
||
Visible: visibleTabSet[tab.Key],
|
||
Enabled: tab.Enabled,
|
||
}
|
||
}
|
||
|
||
return map[string]interface{}{
|
||
"success": true,
|
||
"data": GetAppConfigResponse{
|
||
Tabs: tabs,
|
||
VisibleTabs: tabConfig.VisibleTabs,
|
||
DefaultTab: tabConfig.DefaultTab,
|
||
},
|
||
}, nil
|
||
}
|
||
|
||
// SaveAppConfig 保存应用配置
|
||
func (api *ConfigAPI) SaveAppConfig(req SaveAppConfigRequest) (map[string]interface{}, error) {
|
||
// 验证:至少保留一个可见 Tab
|
||
if len(req.VisibleTabs) < 1 {
|
||
return map[string]interface{}{
|
||
"success": false,
|
||
"message": "至少需要保留一个可见的 Tab",
|
||
}, fmt.Errorf("至少需要保留一个可见的 Tab")
|
||
}
|
||
|
||
// 验证:默认 Tab 必须在可见列表中
|
||
defaultTabExists := false
|
||
for _, key := range req.VisibleTabs {
|
||
if key == req.DefaultTab {
|
||
defaultTabExists = true
|
||
break
|
||
}
|
||
}
|
||
if !defaultTabExists {
|
||
return map[string]interface{}{
|
||
"success": false,
|
||
"message": "默认 Tab 必须在可见列表中",
|
||
}, fmt.Errorf("默认 Tab 必须在可见列表中")
|
||
}
|
||
|
||
// 转换为服务层格式
|
||
availableTabs := make([]service.TabDefinition, len(req.Tabs))
|
||
for i, tab := range req.Tabs {
|
||
availableTabs[i] = service.TabDefinition{
|
||
Key: tab.Key,
|
||
Title: tab.Title,
|
||
Enabled: tab.Enabled,
|
||
}
|
||
}
|
||
|
||
tabConfig := &service.TabConfig{
|
||
AvailableTabs: availableTabs,
|
||
VisibleTabs: req.VisibleTabs,
|
||
DefaultTab: req.DefaultTab,
|
||
}
|
||
|
||
// 保存配置
|
||
if err := api.configService.SaveTabConfig(tabConfig); err != nil {
|
||
return map[string]interface{}{
|
||
"success": false,
|
||
"message": fmt.Sprintf("保存配置失败: %v", err),
|
||
}, err
|
||
}
|
||
|
||
return map[string]interface{}{
|
||
"success": true,
|
||
"message": "配置保存成功",
|
||
"data": nil,
|
||
}, nil
|
||
}
|
||
|
||
// MigrateTabConfig 迁移旧配置(device 移除 + openclaw-manager 重命名)
|
||
func (api *ConfigAPI) MigrateTabConfig() error {
|
||
config, _ := api.configService.GetTabConfig()
|
||
if config == nil {
|
||
return nil
|
||
}
|
||
|
||
needMigrate := false
|
||
|
||
// 检查是否包含需要迁移的旧 key
|
||
for _, tab := range config.AvailableTabs {
|
||
if tab.Key == "device" || tab.Key == "openclaw-manager" {
|
||
needMigrate = true
|
||
break
|
||
}
|
||
}
|
||
if !needMigrate {
|
||
return nil
|
||
}
|
||
|
||
// 映射:旧 key → 新 key(不需要的移除)
|
||
keyMap := map[string]string{
|
||
"openclaw-manager": "version",
|
||
// "device": "" // 直接过滤
|
||
}
|
||
|
||
newTabs := make([]service.TabDefinition, 0, len(config.AvailableTabs))
|
||
newVisible := make([]string, 0, len(config.VisibleTabs))
|
||
seenKeys := map[string]bool{}
|
||
|
||
for _, tab := range config.AvailableTabs {
|
||
newKey, shouldRename := keyMap[tab.Key]
|
||
if shouldRename {
|
||
if newKey == "" {
|
||
continue // 移除(如 device)
|
||
}
|
||
if seenKeys[newKey] {
|
||
continue // 避免重复
|
||
}
|
||
seenKeys[newKey] = true
|
||
newTabs = append(newTabs, service.TabDefinition{Key: newKey, Title: tab.Title, Enabled: tab.Enabled})
|
||
} else {
|
||
newTabs = append(newTabs, tab)
|
||
}
|
||
}
|
||
for _, key := range config.VisibleTabs {
|
||
if newKey, ok := keyMap[key]; ok {
|
||
if newKey != "" && !seenKeys[newKey] {
|
||
newVisible = append(newVisible, newKey)
|
||
}
|
||
// newKey == "" 时跳过(如 device)
|
||
} else {
|
||
newVisible = append(newVisible, key)
|
||
}
|
||
}
|
||
|
||
defaultTab := config.DefaultTab
|
||
if newKey, ok := keyMap[defaultTab]; ok && newKey != "" {
|
||
defaultTab = newKey
|
||
}
|
||
if defaultTab == "device" {
|
||
defaultTab = "file-system"
|
||
}
|
||
|
||
return api.configService.SaveTabConfig(&service.TabConfig{
|
||
AvailableTabs: newTabs,
|
||
VisibleTabs: newVisible,
|
||
DefaultTab: defaultTab,
|
||
})
|
||
}
|