From 034645f50c89075a978ca1295b5433632f78d7d7 Mon Sep 17 00:00:00 2001 From: zhengkunwang <31820853+zhengkunwang223@users.noreply.github.com> Date: Fri, 23 Aug 2024 17:22:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E5=BA=94=E7=94=A8=E6=8E=A5=E5=8F=A3=20(#6217?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agent/app/api/v2/app.go | 6 +- agent/app/service/app.go | 433 ++++++++++---------- agent/i18n/lang/en.yaml | 1 + agent/i18n/lang/zh-Hant.yaml | 1 + agent/i18n/lang/zh.yaml | 1 + agent/router/ro_app.go | 3 +- frontend/src/api/modules/app.ts | 6 +- frontend/src/lang/modules/en.ts | 1 + frontend/src/lang/modules/tw.ts | 1 + frontend/src/lang/modules/zh.ts | 3 +- frontend/src/views/app-store/apps/index.vue | 32 +- 11 files changed, 266 insertions(+), 222 deletions(-) diff --git a/agent/app/api/v2/app.go b/agent/app/api/v2/app.go index 34f746690..bba3e87c3 100644 --- a/agent/app/api/v2/app.go +++ b/agent/app/api/v2/app.go @@ -72,7 +72,11 @@ func (b *BaseApi) SyncApp(c *gin.Context) { // @Router /apps/sync/local [post] // @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"应用商店同步","formatEN":"App store synchronization"} func (b *BaseApi) SyncLocalApp(c *gin.Context) { - go appService.SyncAppListFromLocal() + var req dto.OperateWithTask + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + go appService.SyncAppListFromLocal(req.TaskID) helper.SuccessWithOutData(c) } diff --git a/agent/app/service/app.go b/agent/app/service/app.go index 144d38c1d..9249507fe 100644 --- a/agent/app/service/app.go +++ b/agent/app/service/app.go @@ -43,7 +43,7 @@ type IAppService interface { SyncAppListFromRemote(taskID string) error GetAppUpdate() (*response.AppUpdateRes, error) GetAppDetailByID(id uint) (*response.AppDetailDTO, error) - SyncAppListFromLocal() + SyncAppListFromLocal(taskID string) GetIgnoredApp() ([]response.IgnoredApp, error) } @@ -489,231 +489,236 @@ func (a AppService) Install(req request.AppInstallCreate) (appInstall *model.App return } -func (a AppService) SyncAppListFromLocal() { - fileOp := files.NewFileOp() - localAppDir := constant.LocalAppResourceDir - if !fileOp.Stat(localAppDir) { - return - } +func (a AppService) SyncAppListFromLocal(TaskID string) { var ( err error dirEntries []os.DirEntry localApps []model.App ) - defer func() { - if err != nil { - global.LOG.Errorf("Sync local app failed %v", err) - } - }() - - global.LOG.Infof("Starting local application synchronization ...") - dirEntries, err = os.ReadDir(localAppDir) + syncTask, err := task.NewTaskWithOps(i18n.GetMsgByKey("LocalApp"), task.TaskSync, task.TaskScopeAppStore, TaskID, 0) if err != nil { + global.LOG.Errorf("Create sync task failed %v", err) return } - for _, dirEntry := range dirEntries { - if dirEntry.IsDir() { - appDir := filepath.Join(localAppDir, dirEntry.Name()) - appDirEntries, err := os.ReadDir(appDir) - if err != nil { - global.LOG.Errorf(i18n.GetMsgWithMap("ErrAppDirNull", map[string]interface{}{"name": dirEntry.Name(), "err": err.Error()})) - continue - } - app, err := handleLocalApp(appDir) - if err != nil { - global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppErr", map[string]interface{}{"name": dirEntry.Name(), "err": err.Error()})) - continue - } - var appDetails []model.AppDetail - for _, appDirEntry := range appDirEntries { - if appDirEntry.IsDir() { - appDetail := model.AppDetail{ - Version: appDirEntry.Name(), - Status: constant.AppNormal, - } - versionDir := filepath.Join(appDir, appDirEntry.Name()) - if err = handleLocalAppDetail(versionDir, &appDetail); err != nil { - global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppVersionErr", map[string]interface{}{"name": app.Name, "version": appDetail.Version, "err": err.Error()})) - continue - } - appDetails = append(appDetails, appDetail) - } - } - if len(appDetails) > 0 { - app.Details = appDetails - localApps = append(localApps, *app) - } else { - global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppVersionNull", map[string]interface{}{"name": app.Name})) - } + + syncTask.AddSubTask(task.GetTaskName(i18n.GetMsgByKey("LocalApp"), task.TaskSync, task.TaskScopeAppStore), func(t *task.Task) (err error) { + fileOp := files.NewFileOp() + localAppDir := constant.LocalAppResourceDir + if !fileOp.Stat(localAppDir) { + return nil } - } - - var ( - newApps []model.App - deleteApps []model.App - updateApps []model.App - oldAppIds []uint - - deleteAppIds []uint - deleteAppDetails []model.AppDetail - newAppDetails []model.AppDetail - updateDetails []model.AppDetail - - appTags []*model.AppTag - ) - - oldApps, _ := appRepo.GetBy(appRepo.WithResource(constant.AppResourceLocal)) - apps := make(map[string]model.App, len(oldApps)) - for _, old := range oldApps { - old.Status = constant.AppTakeDown - apps[old.Key] = old - } - for _, app := range localApps { - if oldApp, ok := apps[app.Key]; ok { - app.ID = oldApp.ID - appDetails := make(map[string]model.AppDetail, len(oldApp.Details)) - for _, old := range oldApp.Details { - old.Status = constant.AppTakeDown - appDetails[old.Version] = old - } - for i, newDetail := range app.Details { - version := newDetail.Version - newDetail.Status = constant.AppNormal - newDetail.AppId = app.ID - oldDetail, exist := appDetails[version] - if exist { - newDetail.ID = oldDetail.ID - delete(appDetails, version) - } - app.Details[i] = newDetail - } - for _, v := range appDetails { - app.Details = append(app.Details, v) - } + dirEntries, err = os.ReadDir(localAppDir) + if err != nil { + return } - app.TagsKey = append(app.TagsKey, constant.AppResourceLocal) - apps[app.Key] = app - } - - for _, app := range apps { - if app.ID == 0 { - newApps = append(newApps, app) - } else { - oldAppIds = append(oldAppIds, app.ID) - if app.Status == constant.AppTakeDown { - installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(app.ID)) - if len(installs) > 0 { - updateApps = append(updateApps, app) + for _, dirEntry := range dirEntries { + if dirEntry.IsDir() { + appDir := filepath.Join(localAppDir, dirEntry.Name()) + appDirEntries, err := os.ReadDir(appDir) + if err != nil { + t.Log(i18n.GetWithNameAndErr("ErrAppDirNull", dirEntry.Name(), err)) continue } - deleteAppIds = append(deleteAppIds, app.ID) - deleteApps = append(deleteApps, app) - deleteAppDetails = append(deleteAppDetails, app.Details...) - } else { - updateApps = append(updateApps, app) - } - } - - } - - tags, _ := tagRepo.All() - tagMap := make(map[string]uint, len(tags)) - for _, tag := range tags { - tagMap[tag.Key] = tag.ID - } - - tx, ctx := getTxAndContext() - defer tx.Rollback() - if len(newApps) > 0 { - if err = appRepo.BatchCreate(ctx, newApps); err != nil { - return - } - } - for _, update := range updateApps { - if err = appRepo.Save(ctx, &update); err != nil { - return - } - } - if len(deleteApps) > 0 { - if err = appRepo.BatchDelete(ctx, deleteApps); err != nil { - return - } - if err = appDetailRepo.DeleteByAppIds(ctx, deleteAppIds); err != nil { - return - } - } - - if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil { - return - } - for _, newApp := range newApps { - if newApp.ID > 0 { - for _, detail := range newApp.Details { - detail.AppId = newApp.ID - newAppDetails = append(newAppDetails, detail) - } - } - } - for _, update := range updateApps { - for _, detail := range update.Details { - if detail.ID == 0 { - detail.AppId = update.ID - newAppDetails = append(newAppDetails, detail) - } else { - if detail.Status == constant.AppNormal { - updateDetails = append(updateDetails, detail) + app, err := handleLocalApp(appDir) + if err != nil { + t.Log(i18n.GetWithNameAndErr("LocalAppErr", dirEntry.Name(), err)) + continue + } + var appDetails []model.AppDetail + for _, appDirEntry := range appDirEntries { + if appDirEntry.IsDir() { + appDetail := model.AppDetail{ + Version: appDirEntry.Name(), + Status: constant.AppNormal, + } + versionDir := filepath.Join(appDir, appDirEntry.Name()) + if err = handleLocalAppDetail(versionDir, &appDetail); err != nil { + t.Log(i18n.GetMsgWithMap("LocalAppVersionErr", map[string]interface{}{"name": app.Name, "version": appDetail.Version, "err": err.Error()})) + continue + } + appDetails = append(appDetails, appDetail) + } + } + if len(appDetails) > 0 { + app.Details = appDetails + localApps = append(localApps, *app) } else { - deleteAppDetails = append(deleteAppDetails, detail) + t.Log(i18n.GetWithName("LocalAppVersionNull", app.Name)) } } } - } - allApps := append(newApps, updateApps...) - for _, app := range allApps { - for _, t := range app.TagsKey { - tagId, ok := tagMap[t] - if ok { - appTags = append(appTags, &model.AppTag{ - AppId: app.ID, - TagId: tagId, - }) + var ( + newApps []model.App + deleteApps []model.App + updateApps []model.App + oldAppIds []uint + + deleteAppIds []uint + deleteAppDetails []model.AppDetail + newAppDetails []model.AppDetail + updateDetails []model.AppDetail + + appTags []*model.AppTag + ) + + oldApps, _ := appRepo.GetBy(appRepo.WithResource(constant.AppResourceLocal)) + apps := make(map[string]model.App, len(oldApps)) + for _, old := range oldApps { + old.Status = constant.AppTakeDown + apps[old.Key] = old + } + for _, app := range localApps { + if oldApp, ok := apps[app.Key]; ok { + app.ID = oldApp.ID + appDetails := make(map[string]model.AppDetail, len(oldApp.Details)) + for _, old := range oldApp.Details { + old.Status = constant.AppTakeDown + appDetails[old.Version] = old + } + for i, newDetail := range app.Details { + version := newDetail.Version + newDetail.Status = constant.AppNormal + newDetail.AppId = app.ID + oldDetail, exist := appDetails[version] + if exist { + newDetail.ID = oldDetail.ID + delete(appDetails, version) + } + app.Details[i] = newDetail + } + for _, v := range appDetails { + app.Details = append(app.Details, v) + } + } + app.TagsKey = append(app.TagsKey, constant.AppResourceLocal) + apps[app.Key] = app + } + + for _, app := range apps { + if app.ID == 0 { + newApps = append(newApps, app) + } else { + oldAppIds = append(oldAppIds, app.ID) + if app.Status == constant.AppTakeDown { + installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(app.ID)) + if len(installs) > 0 { + updateApps = append(updateApps, app) + continue + } + deleteAppIds = append(deleteAppIds, app.ID) + deleteApps = append(deleteApps, app) + deleteAppDetails = append(deleteAppDetails, app.Details...) + } else { + updateApps = append(updateApps, app) + } + } + + } + + tags, _ := tagRepo.All() + tagMap := make(map[string]uint, len(tags)) + for _, tag := range tags { + tagMap[tag.Key] = tag.ID + } + + tx, ctx := getTxAndContext() + defer tx.Rollback() + if len(newApps) > 0 { + if err = appRepo.BatchCreate(ctx, newApps); err != nil { + return } } - } - - if len(newAppDetails) > 0 { - if err = appDetailRepo.BatchCreate(ctx, newAppDetails); err != nil { - return + for _, update := range updateApps { + if err = appRepo.Save(ctx, &update); err != nil { + return + } } - } - - for _, updateAppDetail := range updateDetails { - if err = appDetailRepo.Update(ctx, updateAppDetail); err != nil { - return + if len(deleteApps) > 0 { + if err = appRepo.BatchDelete(ctx, deleteApps); err != nil { + return + } + if err = appDetailRepo.DeleteByAppIds(ctx, deleteAppIds); err != nil { + return + } } - } - if len(deleteAppDetails) > 0 { - if err = appDetailRepo.BatchDelete(ctx, deleteAppDetails); err != nil { - return - } - } - - if len(oldAppIds) > 0 { if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil { return } - } - - if len(appTags) > 0 { - if err = appTagRepo.BatchCreate(ctx, appTags); err != nil { - return + for _, newApp := range newApps { + if newApp.ID > 0 { + for _, detail := range newApp.Details { + detail.AppId = newApp.ID + newAppDetails = append(newAppDetails, detail) + } + } } - } - tx.Commit() - global.LOG.Infof("Synchronization of local applications completed") + for _, update := range updateApps { + for _, detail := range update.Details { + if detail.ID == 0 { + detail.AppId = update.ID + newAppDetails = append(newAppDetails, detail) + } else { + if detail.Status == constant.AppNormal { + updateDetails = append(updateDetails, detail) + } else { + deleteAppDetails = append(deleteAppDetails, detail) + } + } + } + } + + allApps := append(newApps, updateApps...) + for _, app := range allApps { + for _, t := range app.TagsKey { + tagId, ok := tagMap[t] + if ok { + appTags = append(appTags, &model.AppTag{ + AppId: app.ID, + TagId: tagId, + }) + } + } + } + + if len(newAppDetails) > 0 { + if err = appDetailRepo.BatchCreate(ctx, newAppDetails); err != nil { + return + } + } + + for _, updateAppDetail := range updateDetails { + if err = appDetailRepo.Update(ctx, updateAppDetail); err != nil { + return + } + } + + if len(deleteAppDetails) > 0 { + if err = appDetailRepo.BatchDelete(ctx, deleteAppDetails); err != nil { + return + } + } + + if len(oldAppIds) > 0 { + if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil { + return + } + } + + if len(appTags) > 0 { + if err = appTagRepo.BatchCreate(ctx, appTags); err != nil { + return + } + } + tx.Commit() + global.LOG.Infof("Synchronization of local applications completed") + return nil + }, nil) + go func() { + _ = syncTask.Execute() + }() } func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) { @@ -941,24 +946,31 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) { } } + tx, ctx := getTxAndContext() + defer func() { + if err != nil { + tx.Rollback() + return + } + }() if len(addAppArray) > 0 { - if err = appRepo.BatchCreate(context.Background(), addAppArray); err != nil { + if err = appRepo.BatchCreate(ctx, addAppArray); err != nil { return } } if len(deleteAppArray) > 0 { - if err = appRepo.BatchDelete(context.Background(), deleteAppArray); err != nil { + if err = appRepo.BatchDelete(ctx, deleteAppArray); err != nil { return } - if err = appDetailRepo.DeleteByAppIds(context.Background(), deleteIds); err != nil { + if err = appDetailRepo.DeleteByAppIds(ctx, deleteIds); err != nil { return } } - if err = tagRepo.DeleteAll(context.Background()); err != nil { + if err = tagRepo.DeleteAll(ctx); err != nil { return } if len(tags) > 0 { - if err = tagRepo.BatchCreate(context.Background(), tags); err != nil { + if err = tagRepo.BatchCreate(ctx, tags); err != nil { return } for _, tag := range tags { @@ -966,7 +978,7 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) { } } for _, update := range updateAppArray { - if err = appRepo.Save(context.Background(), &update); err != nil { + if err = appRepo.Save(ctx, &update); err != nil { return } } @@ -1011,32 +1023,33 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) { } } if len(addDetails) > 0 { - if err = appDetailRepo.BatchCreate(context.Background(), addDetails); err != nil { + if err = appDetailRepo.BatchCreate(ctx, addDetails); err != nil { return } } if len(deleteDetails) > 0 { - if err = appDetailRepo.BatchDelete(context.Background(), deleteDetails); err != nil { + if err = appDetailRepo.BatchDelete(ctx, deleteDetails); err != nil { return } } for _, u := range updateDetails { - if err = appDetailRepo.Update(context.Background(), u); err != nil { + if err = appDetailRepo.Update(ctx, u); err != nil { return } } if len(oldAppIds) > 0 { - if err = appTagRepo.DeleteByAppIds(context.Background(), oldAppIds); err != nil { + if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil { return } } if len(appTags) > 0 { - if err = appTagRepo.BatchCreate(context.Background(), appTags); err != nil { + if err = appTagRepo.BatchCreate(ctx, appTags); err != nil { return } } + tx.Commit() _ = settingService.Update("AppStoreSyncStatus", constant.SyncSuccess) _ = settingService.Update("AppStoreLastModified", strconv.Itoa(list.LastModified)) diff --git a/agent/i18n/lang/en.yaml b/agent/i18n/lang/en.yaml index 6e72f9494..20a363c1c 100644 --- a/agent/i18n/lang/en.yaml +++ b/agent/i18n/lang/en.yaml @@ -235,4 +235,5 @@ AppLink: 'Associated Application' EnableSSL: "Enable HTTPS" AppStore: "App Store" TaskSync: "Sync" +LocalApp: "Local App" diff --git a/agent/i18n/lang/zh-Hant.yaml b/agent/i18n/lang/zh-Hant.yaml index 8453928f5..09a966996 100644 --- a/agent/i18n/lang/zh-Hant.yaml +++ b/agent/i18n/lang/zh-Hant.yaml @@ -237,4 +237,5 @@ AppLink: '關聯應用' EnableSSL: "開啟 HTTPS" AppStore: "應用商店" TaskSync: "同步" +LocalApp: "本地應用" diff --git a/agent/i18n/lang/zh.yaml b/agent/i18n/lang/zh.yaml index c260dd2e1..6bb68baba 100644 --- a/agent/i18n/lang/zh.yaml +++ b/agent/i18n/lang/zh.yaml @@ -238,3 +238,4 @@ AppLink: "关联应用" EnableSSL: "开启 HTTPS" AppStore: "应用商店" TaskSync: "同步" +LocalApp: "本地应用" \ No newline at end of file diff --git a/agent/router/ro_app.go b/agent/router/ro_app.go index feb389b15..1d8bf9685 100644 --- a/agent/router/ro_app.go +++ b/agent/router/ro_app.go @@ -13,7 +13,8 @@ func (a *AppRouter) InitRouter(Router *gin.RouterGroup) { baseApi := v2.ApiGroupApp.BaseApi { - appRouter.POST("/sync", baseApi.SyncApp) + appRouter.POST("/sync/remote", baseApi.SyncApp) + appRouter.POST("/sync/local", baseApi.SyncLocalApp) appRouter.GET("/checkupdate", baseApi.GetAppListUpdate) appRouter.POST("/search", baseApi.SearchApp) appRouter.GET("/:key", baseApi.GetApp) diff --git a/frontend/src/api/modules/app.ts b/frontend/src/api/modules/app.ts index 0b5c01b5e..e5a3f6378 100644 --- a/frontend/src/api/modules/app.ts +++ b/frontend/src/api/modules/app.ts @@ -4,7 +4,11 @@ import { App } from '../interface/app'; import { TimeoutEnum } from '@/enums/http-enum'; export const SyncApp = (req: App.AppStoreSync) => { - return http.post('apps/sync', req); + return http.post('apps/sync/remote', req); +}; + +export const SyncLocalApp = (req: App.AppStoreSync) => { + return http.post('apps/sync/local', req); }; export const GetAppListUpdate = () => { diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index ca2472f74..7d5d5c869 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -1878,6 +1878,7 @@ const message = { supportedArchitectures: 'Architectures', link: 'Link', showCurrentArch: 'Architecture', + syncLocalApp: 'Sync Local App', }, website: { website: 'Website', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index 9fa83b7da..b8ae1fde4 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -1744,6 +1744,7 @@ const message = { supportedArchitectures: '支持架構', link: '鏈接', showCurrentArch: '僅顯示當前架構', + syncLocalApp: '同步本地應用', }, website: { website: '網站', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index a00128813..15aa82db4 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -1686,7 +1686,7 @@ const message = { noService: '无{0}', toInstall: '去安装', param: '参数配置', - syncAppList: '更新应用列表', + syncAppList: '更新远程应用', alreadyRun: '已安装', less1Minute: '小于1分钟', appOfficeWebsite: '官方网站', @@ -1745,6 +1745,7 @@ const message = { supportedArchitectures: '支持架构', link: '链接', showCurrentArch: '仅显示当前服务器架构应用', + syncLocalApp: '同步本地应用', }, website: { website: '网站', diff --git a/frontend/src/views/app-store/apps/index.vue b/frontend/src/views/app-store/apps/index.vue index aa9e32c3f..5cd022049 100644 --- a/frontend/src/views/app-store/apps/index.vue +++ b/frontend/src/views/app-store/apps/index.vue @@ -53,11 +53,12 @@