Skip to content

Commit f18307e

Browse files
committed
feat(应用退出): 添加优雅退出功能防止操作中断
在应用关闭前检查是否有进行中的异步操作(部署/销毁/编排) 添加确认对话框提示用户强制退出可能导致资源状态不一致 为所有异步操作添加activeOps原子计数器进行跟踪 更新中英文国际化文本添加退出确认相关翻译
1 parent 7d37afd commit f18307e

File tree

9 files changed

+57
-1
lines changed

9 files changed

+57
-1
lines changed

app.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"os"
88
"path/filepath"
99
"sync"
10+
"sync/atomic"
1011
"time"
1112

1213
"red-cloud/i18n"
@@ -40,6 +41,7 @@ type App struct {
4041
disableRightClick bool
4142
httpSrv *HTTPServer
4243
wailsMode bool // true when running inside Wails desktop
44+
activeOps atomic.Int32 // tracks in-flight async operations (apply/destroy/compose)
4345
}
4446

4547
// NewApp creates a new App application struct
@@ -49,6 +51,29 @@ func NewApp() *App {
4951
}
5052
}
5153

54+
// HasActiveOperations returns true if any async operations (apply/destroy/compose) are in progress
55+
func (a *App) HasActiveOperations() bool {
56+
return a.activeOps.Load() > 0
57+
}
58+
59+
// beforeClose is called when the user tries to close the window
60+
func (a *App) beforeClose(ctx context.Context) bool {
61+
if a.activeOps.Load() > 0 {
62+
result, err := runtime.MessageDialog(ctx, runtime.MessageDialogOptions{
63+
Type: runtime.QuestionDialog,
64+
Title: i18n.T("app_quit_confirm_title"),
65+
Message: i18n.T("app_quit_confirm_message"),
66+
Buttons: []string{i18n.T("app_quit_btn_cancel"), i18n.T("app_quit_btn_confirm")},
67+
DefaultButton: i18n.T("app_quit_btn_cancel"),
68+
CancelButton: i18n.T("app_quit_btn_cancel"),
69+
})
70+
if err != nil || result != i18n.T("app_quit_btn_confirm") {
71+
return true // prevent close
72+
}
73+
}
74+
return false // allow close
75+
}
76+
5277
// startup is called when the app starts. The context is saved
5378
// so we can call the runtime methods
5479
func (a *App) startup(ctx context.Context) {

app_compose.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ func (a *App) ComposeUp(filePath string, profiles []string) error {
9191
a.emitLog(i18n.Tf("app_compose_up_start", filePath))
9292
a.emitEvent("compose-status", map[string]string{"action": "up", "phase": "running"})
9393
go func() {
94+
a.activeOps.Add(1)
95+
defer a.activeOps.Add(-1)
9496
defer a.emitRefresh()
9597
if err := compose.RunComposeUp(opts); err != nil {
9698
errMsg := i18n.Tf("app_compose_up_failed", err)
@@ -133,6 +135,8 @@ func (a *App) ComposeDown(filePath string, profiles []string) error {
133135
a.emitLog(i18n.Tf("app_compose_down_start", filePath))
134136
a.emitEvent("compose-status", map[string]string{"action": "down", "phase": "running"})
135137
go func() {
138+
a.activeOps.Add(1)
139+
defer a.activeOps.Add(-1)
136140
defer a.emitRefresh()
137141
if err := compose.RunComposeDown(opts); err != nil {
138142
errMsg := i18n.Tf("app_compose_down_failed", err)

app_scene.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ func (a *App) StartCase(caseID string) error {
7474
a.emitLog(i18n.Tf("app_scene_prepare_start", caseName, casePath, caseState))
7575

7676
go func() {
77+
a.activeOps.Add(1)
78+
defer a.activeOps.Add(-1)
7779
defer func() {
7880
if r := recover(); r != nil {
7981
a.emitLog(i18n.Tf("app_scene_start_error", r))
@@ -125,6 +127,8 @@ func (a *App) StopCase(caseID string) error {
125127
}
126128

127129
go func() {
130+
a.activeOps.Add(1)
131+
defer a.activeOps.Add(-1)
128132
defer func() {
129133
if r := recover(); r != nil {
130134
a.emitLog(i18n.Tf("app_scene_stop_error", r))
@@ -165,6 +169,8 @@ func (a *App) RemoveCase(caseID string) error {
165169
}
166170

167171
go func() {
172+
a.activeOps.Add(1)
173+
defer a.activeOps.Add(-1)
168174
defer func() {
169175
if r := recover(); r != nil {
170176
a.emitLog(i18n.Tf("app_scene_delete_error", r))
@@ -228,6 +234,8 @@ func (a *App) CreateAndRunCase(templateName string, name string, vars map[string
228234
a.emitLog(i18n.Tf("app_creating_running_scene", name, templateName))
229235

230236
go func() {
237+
a.activeOps.Add(1)
238+
defer a.activeOps.Add(-1)
231239
defer func() {
232240
if r := recover(); r != nil {
233241
a.emitLog(i18n.Tf("app_scene_init_error", r))

frontend/wailsjs/wailsjs/go/main/App.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ export function GetVersion():Promise<string>;
177177

178178
export function GetWebhookConfig():Promise<main.WebhookConfig>;
179179

180+
export function HasActiveOperations():Promise<boolean>;
181+
180182
export function ImportConfigTemplate(arg1:string,arg2:string):Promise<void>;
181183

182184
export function ImportTemplates(arg1:string):Promise<Array<string>>;

frontend/wailsjs/wailsjs/go/main/App.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,10 @@ export function GetWebhookConfig() {
342342
return window['go']['main']['App']['GetWebhookConfig']();
343343
}
344344

345+
export function HasActiveOperations() {
346+
return window['go']['main']['App']['HasActiveOperations']();
347+
}
348+
345349
export function ImportConfigTemplate(arg1, arg2) {
346350
return window['go']['main']['App']['ImportConfigTemplate'](arg1, arg2);
347351
}

i18n/en.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,4 +550,10 @@ var enMessages = map[string]string{
550550
"plugin_disable_short": "Disable a plugin",
551551
"plugin_update_short": "Update a plugin (git pull)",
552552
"plugin_info_short": "Show plugin details",
553+
554+
// Graceful quit
555+
"app_quit_confirm_title": "Confirm Exit",
556+
"app_quit_confirm_message": "There are active operations (deploy/destroy/compose). Are you sure you want to exit? Force quitting may leave resources in an inconsistent state.",
557+
"app_quit_btn_confirm": "Force Quit",
558+
"app_quit_btn_cancel": "Cancel",
553559
}

i18n/zh.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,4 +550,10 @@ var zhMessages = map[string]string{
550550
"plugin_disable_short": "禁用插件",
551551
"plugin_update_short": "更新插件 (git pull)",
552552
"plugin_info_short": "查看插件详情",
553+
554+
// Graceful quit
555+
"app_quit_confirm_title": "确认退出",
556+
"app_quit_confirm_message": "有正在进行的操作(部署/销毁/编排),确定要退出吗?强制退出可能导致资源状态不一致。",
557+
"app_quit_btn_confirm": "强制退出",
558+
"app_quit_btn_cancel": "取消",
553559
}

main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func main() {
4040
},
4141
BackgroundColour: &options.RGBA{R: 250, G: 251, B: 252, A: 1}, // 匹配 App.svelte 的 bg-[#fafbfc]
4242
OnStartup: app.startup,
43+
OnBeforeClose: app.beforeClose,
4344
Bind: []interface{}{
4445
app,
4546
},

mod/flag.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ var (
88
Domain string
99
Domain2 string
1010
Base64Command string
11-
Version = "v3.1.1"
11+
Version = "v3.1.2"
1212
)

0 commit comments

Comments
 (0)