From 5fd7d780265f365f80456b97abd31a9671e2e349 Mon Sep 17 00:00:00 2001 From: engigu Date: Tue, 17 Mar 2026 11:58:32 +0800 Subject: [PATCH] feat: add default env settings --- internal/controllers/mise_controller.go | 20 +++++++++ internal/router/api_routes.go | 1 + internal/services/mise_service.go | 57 +++++++++++-------------- web/src/api/index.ts | 5 ++- web/src/views/languages/Languages.vue | 32 ++++++++++++-- 5 files changed, 77 insertions(+), 38 deletions(-) diff --git a/internal/controllers/mise_controller.go b/internal/controllers/mise_controller.go index 8c79919..83fed38 100644 --- a/internal/controllers/mise_controller.go +++ b/internal/controllers/mise_controller.go @@ -76,3 +76,23 @@ func (c *MiseController) VerifyCommand(ctx *gin.Context) { } utils.Success(ctx, gin.H{"command": cmd}) } +// UseGlobal 设置全局默认版本 +func (c *MiseController) UseGlobal(ctx *gin.Context) { + var req struct { + Plugin string `json:"plugin"` + Version string `json:"version"` + } + if err := ctx.ShouldBindJSON(&req); err != nil { + utils.BadRequest(ctx, "参数错误: "+err.Error()) + return + } + if req.Plugin == "" || req.Version == "" { + utils.BadRequest(ctx, "参数 plugin 和 version 不能为空") + return + } + if err := c.service.UseGlobal(req.Plugin, req.Version); err != nil { + utils.ServerError(ctx, "设置全局版本失败: "+err.Error()) + return + } + utils.Success(ctx, nil) +} diff --git a/internal/router/api_routes.go b/internal/router/api_routes.go index 1853826..39daeb3 100644 --- a/internal/router/api_routes.go +++ b/internal/router/api_routes.go @@ -210,6 +210,7 @@ func registerMiseRoutes(g *gin.RouterGroup, c *Controllers) { mise.GET("/plugins", c.Mise.Plugins) mise.GET("/versions", c.Mise.Versions) mise.GET("/verify-cmd", c.Mise.VerifyCommand) + mise.POST("/use-global", c.Mise.UseGlobal) } } diff --git a/internal/services/mise_service.go b/internal/services/mise_service.go index 84719f4..5585d04 100644 --- a/internal/services/mise_service.go +++ b/internal/services/mise_service.go @@ -28,7 +28,7 @@ type MiseLanguage struct { Plugin string `json:"plugin"` Version string `json:"version"` Source MiseSource `json:"source"` - Active bool `json:"active"` + IsGlobal bool `json:"is_global"` InstallPath string `json:"install_path,omitempty"` InstalledAt string `json:"installed_at,omitempty"` // 安装日期 } @@ -38,42 +38,17 @@ type MiseSource struct { Path string `json:"path"` } -// List 从数据库获取已保存的语言列表 +// List 实时从系统检测 mise 环境并同步到数据库 func (s *MiseService) List() ([]MiseLanguage, error) { - db := database.GetDB() - var models []models.Language - if err := db.Order("installed_at DESC, plugin ASC").Find(&models).Error; err != nil { + langs, err := s.fetchLiveLanguages() + if err != nil { return nil, err } - // 如果数据库为空,尝试进行一次自动同步(延迟加载) - if len(models) == 0 { - logger.Info("[Mise] 数据库中没有语言记录,正在进行首次同步...") - if err := s.Sync(); err == nil { - // 同步成功后重新查询 - if err := db.Order("installed_at DESC, plugin ASC").Find(&models).Error; err != nil { - return nil, err - } - } else { - logger.Warnf("[Mise] 首次自动同步失败: %v", err) - } - } + // 异步同步到数据库,确保列表响应速度 + go s.syncToDB(langs) - result := make([]MiseLanguage, 0, len(models)) - for _, m := range models { - lang := MiseLanguage{ - Plugin: m.Plugin, - Version: m.Version, - InstallPath: m.InstallPath, - Active: true, - } - lang.Source.Path = m.Source - if m.InstalledAt != nil { - lang.InstalledAt = time.Time(*m.InstalledAt).Format("2006-01-02 15:04:05") - } - result = append(result, lang) - } - return result, nil + return langs, nil } // Sync 实时检测本地 mise 环境并同步到数据库 @@ -166,7 +141,6 @@ func (s *MiseService) listFallback() ([]MiseLanguage, error) { lang := MiseLanguage{ Plugin: parts[0], Version: parts[1], - Active: true, } if len(parts) >= 3 { lang.Source = MiseSource{Path: parts[2]} @@ -220,6 +194,13 @@ func (s *MiseService) Versions(plugin string) ([]string, error) { // enrichInstallDates 为语言列表添加安装日期信息 func (s *MiseService) enrichInstallDates(languages []MiseLanguage) { for i := range languages { + // 判断是否是 global + if languages[i].Source.Type == "global" { + languages[i].IsGlobal = true + } else if strings.Contains(languages[i].Source.Path, ".config/mise/config.toml") { + languages[i].IsGlobal = true + } + if languages[i].InstallPath != "" { if installDate := s.getInstallDate(languages[i].InstallPath); installDate != "" { languages[i].InstalledAt = installDate @@ -349,3 +330,13 @@ func (s *MiseService) GetVerifyCommand(plugin, version string) (string, error) { } return m.GetVerifyCommand(version) } +// UseGlobal 设置全局默认版本 +func (s *MiseService) UseGlobal(plugin, version string) error { + cmd := exec.Command("mise", "use", "-g", fmt.Sprintf("%s@%s", plugin, version)) + cmd.Env = os.Environ() + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("mise use -g failed: %v, output: %s", err, string(output)) + } + return nil +} diff --git a/web/src/api/index.ts b/web/src/api/index.ts index e0278be..a97db9a 100644 --- a/web/src/api/index.ts +++ b/web/src/api/index.ts @@ -267,7 +267,8 @@ export const api = { sync: () => request('/mise/sync', { method: 'POST' }), plugins: () => request('/mise/plugins'), versions: (plugin: string) => request(`/mise/versions?plugin=${plugin}`), - verifyCommand: (plugin: string, version: string) => request<{ command: string }>(`/mise/verify-cmd?plugin=${plugin}&version=${version}`) + verifyCommand: (plugin: string, version: string) => request<{ command: string }>(`/mise/verify-cmd?plugin=${plugin}&version=${version}`), + useGlobal: (plugin: string, version: string) => request('/mise/use-global', { method: 'POST', body: JSON.stringify({ plugin, version }) }) }, terminal: { cmds: () => request<{ name: string, description: string }[]>('/terminal/cmds') @@ -543,7 +544,7 @@ export interface MiseLanguage { plugin: string version: string source: { type?: string; path?: string } | string - active: boolean + is_global: boolean install_path?: string installed_at?: string // 安装日期 } diff --git a/web/src/views/languages/Languages.vue b/web/src/views/languages/Languages.vue index 049b71c..9325bbb 100644 --- a/web/src/views/languages/Languages.vue +++ b/web/src/views/languages/Languages.vue @@ -25,6 +25,7 @@ const SUPPORTED_DEPS_LANGS = [ interface DisplayLanguage extends Omit { source: string + isGlobal: boolean } const languages = ref([]) @@ -83,7 +84,8 @@ async function loadLanguages() { } languages.value = data.map(item => ({ ...item, - source: typeof item.source === 'object' ? (item.source.path || item.source.type || '-') : (item.source || '-') + source: typeof item.source === 'object' ? (item.source.path || item.source.type || '-') : (item.source || '-'), + isGlobal: !!item.is_global })) } catch (e) { toast.error('获取语言列表失败') @@ -243,15 +245,31 @@ async function handleVerify(lang: MiseLanguage) { } } +async function handleSetDefault(lang: MiseLanguage) { + try { + await api.mise.useGlobal(lang.plugin, lang.version) + toast.success(`已将 ${lang.plugin} ${lang.version} 设为全局默认版本`) + await loadLanguages() + } catch (e) { + toast.error('设置默认版本失败: ' + e) + } +} + onMounted(loadLanguages)