diff --git a/agent/app/dto/app.go b/agent/app/dto/app.go index d1655e439..60abecd51 100644 --- a/agent/app/dto/app.go +++ b/agent/app/dto/app.go @@ -1,7 +1,9 @@ package dto import ( + "context" "github.com/1Panel-dev/1Panel/agent/app/model" + "github.com/1Panel-dev/1Panel/agent/app/task" ) type AppDatabase struct { @@ -147,3 +149,10 @@ type AppInstallInfo struct { Key string `json:"key"` Name string `json:"name"` } + +type DelAppLink struct { + Ctx context.Context + Task *task.Task + Install *model.AppInstall + ForceDelete bool +} diff --git a/agent/app/dto/request/app.go b/agent/app/dto/request/app.go index 2da685e7e..726f78b78 100644 --- a/agent/app/dto/request/app.go +++ b/agent/app/dto/request/app.go @@ -88,10 +88,11 @@ type AppInstallUpgrade struct { type AppInstallDelete struct { Install model.AppInstall - DeleteBackup bool `json:"deleteBackup"` - ForceDelete bool `json:"forceDelete"` - DeleteDB bool `json:"deleteDB"` - DeleteImage bool `json:"deleteImage"` + DeleteBackup bool `json:"deleteBackup"` + ForceDelete bool `json:"forceDelete"` + DeleteDB bool `json:"deleteDB"` + DeleteImage bool `json:"deleteImage"` + TaskID string `json:"taskID"` } type AppInstalledUpdate struct { diff --git a/agent/app/service/app_install.go b/agent/app/service/app_install.go index 8847fab61..b45f06697 100644 --- a/agent/app/service/app_install.go +++ b/agent/app/service/app_install.go @@ -265,6 +265,7 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error { ForceDelete: req.ForceDelete, DeleteDB: req.DeleteDB, DeleteImage: req.DeleteImage, + TaskID: req.TaskID, } if err = deleteAppInstall(deleteReq); err != nil && !req.ForceDelete { return err diff --git a/agent/app/service/app_utils.go b/agent/app/service/app_utils.go index 029ead10d..0592bd894 100644 --- a/agent/app/service/app_utils.go +++ b/agent/app/service/app_utils.go @@ -134,7 +134,12 @@ var ToolKeys = map[string]uint{ func createLink(ctx context.Context, installTask *task.Task, app model.App, appInstall *model.AppInstall, params map[string]interface{}) error { deleteAppLink := func(t *task.Task) { - _ = deleteLink(ctx, appInstall, true, true, true) + del := dto.DelAppLink{ + Ctx: ctx, + Install: appInstall, + ForceDelete: true, + } + _ = deleteLink(del) } var dbConfig dto.AppDatabase if DatabaseKeys[app.Key] > 0 { @@ -315,138 +320,171 @@ func createLink(ctx context.Context, installTask *task.Task, app model.App, appI } func deleteAppInstall(deleteReq request.AppInstallDelete) error { - op := files.NewFileOp() install := deleteReq.Install + op := files.NewFileOp() appDir := install.GetPath() - dir, _ := os.Stat(appDir) - if dir != nil { - out, err := compose.Down(install.GetComposePath()) - if err != nil && !deleteReq.ForceDelete { - return handleErr(install, err, out) - } - //TODO use task - if err = runScript(nil, &install, "uninstall"); err != nil { - _, _ = compose.Up(install.GetComposePath()) - return err - } - if deleteReq.DeleteImage { - images, _ := getImages(install) - client, err := docker.NewClient() - if err != nil { + + uninstallTask, err := task.NewTaskWithOps(install.Name, task.TaskUninstall, task.TaskScopeApp, deleteReq.TaskID, install.ID) + if err != nil { + return err + } + + uninstall := func(t *task.Task) error { + dir, _ := os.Stat(appDir) + if dir != nil { + logStr := i18n.GetMsgByKey("Stop") + i18n.GetMsgByKey("App") + t.Log(logStr) + + out, err := compose.Down(install.GetComposePath()) + if err != nil && !deleteReq.ForceDelete { + return handleErr(install, err, out) + } + t.LogSuccess(logStr) + if err = runScript(t, &install, "uninstall"); err != nil { + _, _ = compose.Up(install.GetComposePath()) return err } - defer client.Close() - for _, image := range images { - imageID, err := client.GetImageIDByName(image) - if err == nil { - if err = client.DeleteImage(imageID); err != nil { - global.LOG.Errorf("delete image %s error %s", image, err.Error()) + if deleteReq.DeleteImage { + delImageStr := i18n.GetMsgByKey("TaskDelete") + i18n.GetMsgByKey("Image") + images, _ := getImages(install) + client, err := docker.NewClient() + if err != nil { + return err + } + defer client.Close() + for _, image := range images { + imageID, err := client.GetImageIDByName(image) + if err == nil { + imgStr := delImageStr + image + t.Log(imgStr) + + if err = client.DeleteImage(imageID); err != nil { + t.LogFailedWithErr(imgStr, err) + continue + } + t.LogSuccess(delImageStr + image) } } } } - } - tx, ctx := helper.GetTxAndContext() - defer tx.Rollback() - if err := appInstallRepo.Delete(ctx, install); err != nil { - return err - } - if err := deleteLink(ctx, &install, deleteReq.DeleteDB, deleteReq.ForceDelete, deleteReq.DeleteBackup); err != nil && !deleteReq.ForceDelete { - return err - } + tx, ctx := helper.GetTxAndContext() + defer tx.Rollback() + if err = appInstallRepo.Delete(ctx, install); err != nil { + return err + } - if DatabaseKeys[install.App.Key] > 0 { - _ = databaseRepo.Delete(ctx, databaseRepo.WithAppInstallID(install.ID)) - } + if deleteReq.DeleteDB { + del := dto.DelAppLink{ + Ctx: ctx, + Install: &install, + ForceDelete: deleteReq.ForceDelete, + Task: uninstallTask, + } + t.LogWithOps(task.TaskDelete, i18n.GetMsgByKey("Database")) + if err = deleteLink(del); err != nil { + t.LogFailedWithOps(task.TaskDelete, i18n.GetMsgByKey("Database"), err) + if !deleteReq.ForceDelete { + return err + } + } + t.LogSuccessWithOps(task.TaskDelete, i18n.GetMsgByKey("Database")) + } - switch install.App.Key { - case constant.AppOpenresty: - websites, _ := websiteRepo.List() - for _, website := range websites { - if website.AppInstallID > 0 { - websiteAppInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID)) - if websiteAppInstall.AppId > 0 { - websiteApp, _ := appRepo.GetFirst(commonRepo.WithByID(websiteAppInstall.AppId)) - if websiteApp.Type == constant.RuntimePHP { - go func() { - _, _ = compose.Down(websiteAppInstall.GetComposePath()) - _ = op.DeleteDir(websiteAppInstall.GetPath()) - }() - _ = appInstallRepo.Delete(ctx, websiteAppInstall) + if DatabaseKeys[install.App.Key] > 0 { + _ = databaseRepo.Delete(ctx, databaseRepo.WithAppInstallID(install.ID)) + } + + switch install.App.Key { + case constant.AppOpenresty: + websites, _ := websiteRepo.List() + for _, website := range websites { + if website.AppInstallID > 0 { + websiteAppInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID)) + if websiteAppInstall.AppId > 0 { + websiteApp, _ := appRepo.GetFirst(commonRepo.WithByID(websiteAppInstall.AppId)) + if websiteApp.Type == constant.RuntimePHP { + go func() { + _, _ = compose.Down(websiteAppInstall.GetComposePath()) + _ = op.DeleteDir(websiteAppInstall.GetPath()) + }() + _ = appInstallRepo.Delete(ctx, websiteAppInstall) + } } } } + _ = websiteRepo.DeleteAll(ctx) + _ = websiteDomainRepo.DeleteAll(ctx) + xpack.RemoveTamper("") + case constant.AppMysql, constant.AppMariaDB: + _ = mysqlRepo.Delete(ctx, mysqlRepo.WithByMysqlName(install.Name)) + case constant.AppPostgresql: + _ = postgresqlRepo.Delete(ctx, postgresqlRepo.WithByPostgresqlName(install.Name)) } - _ = websiteRepo.DeleteAll(ctx) - _ = websiteDomainRepo.DeleteAll(ctx) - xpack.RemoveTamper("") - case constant.AppMysql, constant.AppMariaDB: - _ = mysqlRepo.Delete(ctx, mysqlRepo.WithByMysqlName(install.Name)) - case constant.AppPostgresql: - _ = postgresqlRepo.Delete(ctx, postgresqlRepo.WithByPostgresqlName(install.Name)) - } - _ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType("app"), commonRepo.WithByName(install.App.Key), backupRepo.WithByDetailName(install.Name)) - uploadDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/uploads/app/%s/%s", install.App.Key, install.Name)) - if _, err := os.Stat(uploadDir); err == nil { - _ = os.RemoveAll(uploadDir) - } - if deleteReq.DeleteBackup { - localDir, _ := loadLocalDir() - backupDir := path.Join(localDir, fmt.Sprintf("app/%s/%s", install.App.Key, install.Name)) - if _, err := os.Stat(backupDir); err == nil { - _ = os.RemoveAll(backupDir) + _ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType("app"), commonRepo.WithByName(install.App.Key), backupRepo.WithByDetailName(install.Name)) + uploadDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/uploads/app/%s/%s", install.App.Key, install.Name)) + if _, err := os.Stat(uploadDir); err == nil { + _ = os.RemoveAll(uploadDir) } - global.LOG.Infof("delete app %s-%s backups successful", install.App.Key, install.Name) + if deleteReq.DeleteBackup { + localDir, _ := loadLocalDir() + backupDir := path.Join(localDir, fmt.Sprintf("app/%s/%s", install.App.Key, install.Name)) + if _, err = os.Stat(backupDir); err == nil { + t.LogWithOps(task.TaskDelete, i18n.GetMsgByKey("TaskBackup")) + _ = os.RemoveAll(backupDir) + t.LogSuccessWithOps(task.TaskDelete, i18n.GetMsgByKey("TaskBackup")) + } + } + _ = op.DeleteDir(appDir) + tx.Commit() + return nil } - _ = op.DeleteDir(appDir) - tx.Commit() + uninstallTask.AddSubTask(task.GetTaskName(install.Name, task.TaskUninstall, task.TaskScopeApp), uninstall, nil) + go uninstallTask.Execute() return nil } -func deleteLink(ctx context.Context, install *model.AppInstall, deleteDB bool, forceDelete bool, deleteBackup bool) error { +func deleteLink(del dto.DelAppLink) error { + install := del.Install resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID)) if len(resources) == 0 { return nil } for _, re := range resources { - if deleteDB { - switch re.Key { - case constant.AppMysql, constant.AppMariaDB: - mysqlService := NewIMysqlService() - database, _ := mysqlRepo.Get(commonRepo.WithByID(re.ResourceId)) - if reflect.DeepEqual(database, model.DatabaseMysql{}) { - continue - } - if err := mysqlService.Delete(ctx, dto.MysqlDBDelete{ - ID: database.ID, - ForceDelete: forceDelete, - DeleteBackup: deleteBackup, - Type: re.Key, - Database: database.MysqlName, - }); err != nil && !forceDelete { - return err - } - case constant.AppPostgresql: - pgsqlService := NewIPostgresqlService() - database, _ := postgresqlRepo.Get(commonRepo.WithByID(re.ResourceId)) - if reflect.DeepEqual(database, model.DatabasePostgresql{}) { - continue - } - if err := pgsqlService.Delete(ctx, dto.PostgresqlDBDelete{ - ID: database.ID, - ForceDelete: forceDelete, - DeleteBackup: deleteBackup, - Type: re.Key, - Database: database.PostgresqlName, - }); err != nil { - return err - } + switch re.Key { + case constant.AppMysql, constant.AppMariaDB: + mysqlService := NewIMysqlService() + database, _ := mysqlRepo.Get(commonRepo.WithByID(re.ResourceId)) + if reflect.DeepEqual(database, model.DatabaseMysql{}) { + continue + } + if err := mysqlService.Delete(del.Ctx, dto.MysqlDBDelete{ + ID: database.ID, + ForceDelete: del.ForceDelete, + DeleteBackup: true, + Type: re.Key, + Database: database.MysqlName, + }); err != nil && !del.ForceDelete { + return err + } + case constant.AppPostgresql: + pgsqlService := NewIPostgresqlService() + database, _ := postgresqlRepo.Get(commonRepo.WithByID(re.ResourceId)) + if reflect.DeepEqual(database, model.DatabasePostgresql{}) { + continue + } + if err := pgsqlService.Delete(del.Ctx, dto.PostgresqlDBDelete{ + ID: database.ID, + ForceDelete: del.ForceDelete, + DeleteBackup: true, + Type: re.Key, + Database: database.PostgresqlName, + }); err != nil { + return err } } - } - return appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID)) + return appInstallResourceRepo.DeleteBy(del.Ctx, appInstallResourceRepo.WithAppInstallId(install.ID)) } func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string, error) { diff --git a/agent/app/task/task.go b/agent/app/task/task.go index 3267b6509..5b69fe147 100644 --- a/agent/app/task/task.go +++ b/agent/app/task/task.go @@ -219,3 +219,15 @@ func (t *Task) LogSuccess(msg string) { func (t *Task) LogStart(msg string) { t.Logger.Printf(fmt.Sprintf("%s%s", i18n.GetMsgByKey("Start"), msg)) } + +func (t *Task) LogWithOps(operate, msg string) { + t.Logger.Printf("%s%s", i18n.GetMsgByKey(operate), msg) +} + +func (t *Task) LogSuccessWithOps(operate, msg string) { + t.Logger.Printf("%s%s%s", i18n.GetMsgByKey(operate), msg, i18n.GetMsgByKey("Success")) +} + +func (t *Task) LogFailedWithOps(operate, msg string, err error) { + t.Logger.Printf("%s%s%s : %s ", i18n.GetMsgByKey(operate), msg, i18n.GetMsgByKey("Failed"), err.Error()) +} diff --git a/agent/i18n/lang/en.yaml b/agent/i18n/lang/en.yaml index 81a63bf5c..9356f7217 100644 --- a/agent/i18n/lang/en.yaml +++ b/agent/i18n/lang/en.yaml @@ -230,4 +230,7 @@ ExecShell: "Execute {{ .name }} Script" PullImage: "Pull Image" Start: "Start" Run: "Run" +Stop: 'Stop', +Image: 'Image', +AppLink: 'Associated Application' diff --git a/agent/i18n/lang/zh-Hant.yaml b/agent/i18n/lang/zh-Hant.yaml index 8dd9ba51f..2032e1fc1 100644 --- a/agent/i18n/lang/zh-Hant.yaml +++ b/agent/i18n/lang/zh-Hant.yaml @@ -232,3 +232,7 @@ ExecShell: "執行 {{ .name }} 腳本" PullImage: "拉取鏡像" Start: "開始" Run: "啟動" +Stop: '停止', +Image: '鏡像', +AppLink: '關聯應用' + diff --git a/agent/i18n/lang/zh.yaml b/agent/i18n/lang/zh.yaml index 61da03b83..c55769d49 100644 --- a/agent/i18n/lang/zh.yaml +++ b/agent/i18n/lang/zh.yaml @@ -234,3 +234,6 @@ ExecShell: "执行 {{ .name }} 脚本" PullImage: "拉取镜像" Start: "开始" Run: "启动" +Stop: "停止" +Image: "镜像" +AppLink: "关联应用" diff --git a/frontend/src/api/interface/app.ts b/frontend/src/api/interface/app.ts index 0f84e6d35..2c353ce9d 100644 --- a/frontend/src/api/interface/app.ts +++ b/frontend/src/api/interface/app.ts @@ -189,6 +189,7 @@ export namespace App { forceDelete?: boolean; deleteBackup?: boolean; deleteImage?: boolean; + taskID?: string; } export interface AppInstalledSearch extends ReqPage { diff --git a/frontend/src/components/task-log/index.vue b/frontend/src/components/task-log/index.vue index 6e17b8de6..f7ed9cfb8 100644 --- a/frontend/src/components/task-log/index.vue +++ b/frontend/src/components/task-log/index.vue @@ -45,6 +45,7 @@ const scrollerElement = ref(null); const minPage = ref(1); const maxPage = ref(1); const open = ref(false); +const em = defineEmits(['close']); const readReq = reactive({ taskID: '', @@ -148,6 +149,7 @@ const changeTail = (fromOutSide: boolean) => { const handleClose = () => { onCloseLog(); open.value = false; + em('close', open.value); }; const onCloseLog = async () => { diff --git a/frontend/src/views/app-store/installed/delete/index.vue b/frontend/src/views/app-store/installed/delete/index.vue index 343c9cb79..513e60916 100644 --- a/frontend/src/views/app-store/installed/delete/index.vue +++ b/frontend/src/views/app-store/installed/delete/index.vue @@ -47,6 +47,7 @@ +