diff --git a/backend/app/api/v1/app.go b/backend/app/api/v1/app.go index 1bec69337..ada4bb9a8 100644 --- a/backend/app/api/v1/app.go +++ b/backend/app/api/v1/app.go @@ -159,3 +159,24 @@ func (b *BaseApi) GetServices(c *gin.Context) { helper.SuccessWithData(c, services) } + +func (b *BaseApi) GetUpdateVersions(c *gin.Context) { + + appInstallId := c.Param("appInstallId") + if appInstallId == "" { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, nil) + return + } + id, err := strconv.Atoi(appInstallId) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + versions, err := appService.GetUpdateVersions(uint(id)) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + + helper.SuccessWithData(c, versions) +} diff --git a/backend/app/dto/app.go b/backend/app/dto/app.go index cbfdf735d..d4e24fb32 100644 --- a/backend/app/dto/app.go +++ b/backend/app/dto/app.go @@ -111,11 +111,13 @@ var ( Sync AppOperate = "sync" Backup AppOperate = "backup" Restore AppOperate = "restore" + Update AppOperate = "update" ) type AppInstallOperate struct { InstallId uint `json:"installId" validate:"required"` BackupId uint `json:"backupId"` + DetailId uint `json:"detailId"` Operate AppOperate `json:"operate" validate:"required"` } @@ -140,3 +142,8 @@ type ContainerExec struct { DbParam AppDatabase `json:"dbParam"` Auth AuthParam `json:"auth"` } + +type AppVersion struct { + Version string `json:"version"` + DetailId uint `json:"detailId"` +} diff --git a/backend/app/model/app_install_backup.go b/backend/app/model/app_install_backup.go index 1b254729e..dad6826ec 100644 --- a/backend/app/model/app_install_backup.go +++ b/backend/app/model/app_install_backup.go @@ -4,7 +4,7 @@ type AppInstallBackup struct { BaseModel Name string `gorm:"type:varchar(64);not null" json:"name"` Path string `gorm:"type:varchar(64);not null" json:"path"` - Param string `json:"param" gorm:"type:longtext;"` - AppDetailId uint `gorm:"type:varchar(64);not null" json:"app_detail_id"` + Param string `gorm:"type:longtext;" json:"param"` + AppDetailId uint `gorm:"type:integer;not null" json:"app_detail_id"` AppInstallId uint `gorm:"type:integer;not null" json:"app_install_id"` } diff --git a/backend/app/service/app.go b/backend/app/service/app.go index 76f080b53..96e64be82 100644 --- a/backend/app/service/app.go +++ b/backend/app/service/app.go @@ -218,6 +218,8 @@ func (a AppService) OperateInstall(req dto.AppInstallOperate) error { return err } return restoreInstall(install, installBackup) + case dto.Update: + return updateInstall(install.ID, req.DetailId) default: return errors.New("operate not support") } @@ -352,25 +354,6 @@ func (a AppService) DeleteBackup(req dto.AppBackupDeleteRequest) error { return nil } -func (a AppService) GetServices(key string) ([]dto.AppService, error) { - app, err := appRepo.GetFirst(appRepo.WithKey(key)) - if err != nil { - return nil, err - } - installs, err := appInstallRepo.GetBy(appInstallRepo.WithAppId(app.ID), appInstallRepo.WithStatus(constant.Running)) - if err != nil { - return nil, err - } - var res []dto.AppService - for _, install := range installs { - res = append(res, dto.AppService{ - Label: install.Name, - Value: install.ServiceName, - }) - } - return res, nil -} - func (a AppService) SyncInstalled(installId uint) error { appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId)) if err != nil { @@ -686,3 +669,44 @@ func syncCanUpdate() { } } + +func (a AppService) GetServices(key string) ([]dto.AppService, error) { + app, err := appRepo.GetFirst(appRepo.WithKey(key)) + if err != nil { + return nil, err + } + installs, err := appInstallRepo.GetBy(appInstallRepo.WithAppId(app.ID), appInstallRepo.WithStatus(constant.Running)) + if err != nil { + return nil, err + } + var res []dto.AppService + for _, install := range installs { + res = append(res, dto.AppService{ + Label: install.Name, + Value: install.ServiceName, + }) + } + return res, nil +} + +func (a AppService) GetUpdateVersions(installId uint) ([]dto.AppVersion, error) { + install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId)) + var versions []dto.AppVersion + if err != nil { + return versions, err + } + app, err := appRepo.GetFirst(commonRepo.WithByID(install.AppId)) + if err != nil { + return versions, err + } + details, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(app.ID)) + for _, detail := range details { + if common.CompareVersion(detail.Version, install.Version) { + versions = append(versions, dto.AppVersion{ + Version: detail.Version, + DetailId: detail.ID, + }) + } + } + return versions, nil +} diff --git a/backend/app/service/app_utils.go b/backend/app/service/app_utils.go index 6a03d4464..5de087afa 100644 --- a/backend/app/service/app_utils.go +++ b/backend/app/service/app_utils.go @@ -19,6 +19,7 @@ import ( "path" "reflect" "strconv" + "strings" "time" ) @@ -162,6 +163,38 @@ func deleteLink(ctx context.Context, install *model.AppInstall) error { return appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID)) } +func updateInstall(installId uint, detailId uint) error { + install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId)) + if err != nil { + return err + } + detail, err := appDetailRepo.GetFirst(commonRepo.WithByID(detailId)) + if err != nil { + return err + } + if install.Version == detail.Version { + return errors.New("two version is save") + } + tx, ctx := getTxAndContext() + if err := backupInstall(ctx, install); err != nil { + return err + } + tx.Commit() + if _, err = compose.Down(install.GetComposePath()); err != nil { + return err + } + install.DockerCompose = detail.DockerCompose + install.Version = detail.Version + fileOp := files.NewFileOp() + if err := fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); err != nil { + return err + } + if _, err = compose.Up(install.GetComposePath()); err != nil { + return err + } + return appInstallRepo.Save(&install) +} + func backupInstall(ctx context.Context, install model.AppInstall) error { var backup model.AppInstallBackup appPath := install.GetPath() @@ -251,6 +284,7 @@ func restoreInstall(install model.AppInstall, backup model.AppInstallBackup) err if out, err := compose.Up(install.GetComposePath()); err != nil { return handleErr(install, err, out) } + install.AppDetailId = backup.AppDetailId install.Status = constant.Running return appInstallRepo.Save(&install) } diff --git a/backend/router/ro_app.go b/backend/router/ro_app.go index cb36fc340..75649f898 100644 --- a/backend/router/ro_app.go +++ b/backend/router/ro_app.go @@ -20,6 +20,7 @@ func (a *AppRouter) InitAppRouter(Router *gin.RouterGroup) { appRouter.GET("/:id", baseApi.GetApp) appRouter.GET("/detail/:appid/:version", baseApi.GetAppDetail) appRouter.POST("/install", baseApi.InstallApp) + appRouter.GET("/installed/:appInstallId/versions", baseApi.GetUpdateVersions) appRouter.POST("/installed", baseApi.SearchInstalled) appRouter.POST("/installed/op", baseApi.OperateInstalled) appRouter.POST("/installed/sync", baseApi.SyncInstalled) diff --git a/backend/utils/common/common.go b/backend/utils/common/common.go index 4d0685ee6..edcfe1f5d 100644 --- a/backend/utils/common/common.go +++ b/backend/utils/common/common.go @@ -13,6 +13,9 @@ import ( ) func CompareVersion(version1 string, version2 string) bool { + if version1 == version2 { + return false + } version1s := strings.Split(version1, ".") version2s := strings.Split(version2, ".") diff --git a/frontend/src/api/interface/app.ts b/frontend/src/api/interface/app.ts index 4ab63bb60..f07bc6790 100644 --- a/frontend/src/api/interface/app.ts +++ b/frontend/src/api/interface/app.ts @@ -79,6 +79,7 @@ export namespace App { installId: number; operate: string; backupId?: number; + detailId?: number; } export interface AppService { @@ -100,4 +101,9 @@ export namespace App { appInstallId: string; appDetail: AppDetail; } + + export interface VersionDetail { + version: string; + detailId: number; + } } diff --git a/frontend/src/api/modules/app.ts b/frontend/src/api/modules/app.ts index 340aaa041..047c5d4e8 100644 --- a/frontend/src/api/modules/app.ts +++ b/frontend/src/api/modules/app.ts @@ -45,3 +45,7 @@ export const GetAppBackups = (info: App.AppBackupReq) => { export const DelAppBackups = (req: App.AppBackupDelReq) => { return http.post('apps/installed/backups/del', req); }; + +export const GetAppUpdateVersions = (id: number) => { + return http.get(`apps/installed/${id}/versions`); +}; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 94197a87c..1682c10d1 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -425,5 +425,7 @@ export default { restore: 'Restore', restoreWarn: 'The rollback operation will restart the application and replace the data. This operation cannot be rolled back. Do you want to continue?', + update: 'Update', + versioneSelect: 'Please Select version', }, }; diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 4208cac58..0c4470046 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -416,5 +416,7 @@ export default { backupdate: '备份时间', restore: '回滚', restoreWarn: '回滚操作会重启应用,并替换数据,此操作不可回滚,是否继续?', + update: '更新', + versioneSelect: '请选择版本', }, }; diff --git a/frontend/src/views/app-store/installed/index.vue b/frontend/src/views/app-store/installed/index.vue index 4f8f9bf35..4abc46da8 100644 --- a/frontend/src/views/app-store/installed/index.vue +++ b/frontend/src/views/app-store/installed/index.vue @@ -51,11 +51,30 @@ /> - + +
+

{{ $t('app.versioneSelect') }}

+ + + +
@@ -63,13 +82,14 @@