diff --git a/apps/icons/halo.png b/apps/icons/halo.png new file mode 100644 index 000000000..411f5cd85 Binary files /dev/null and b/apps/icons/halo.png differ diff --git a/apps/icons/mysql.png b/apps/icons/mysql.png index 73b55bd0d..3fbbeed73 100644 Binary files a/apps/icons/mysql.png and b/apps/icons/mysql.png differ diff --git a/apps/icons/wordpress.png b/apps/icons/wordpress.png new file mode 100644 index 000000000..2cae20c7d Binary files /dev/null and b/apps/icons/wordpress.png differ diff --git a/apps/list.json b/apps/list.json index 3a9606162..77feca291 100644 --- a/apps/list.json +++ b/apps/list.json @@ -1,5 +1,5 @@ { - "version": "0.1", + "version": "ddd", "tags": [ { "key": "WebSite", @@ -24,6 +24,8 @@ "icon": "mysql.png", "author": "Oracle", "type": "internal", + "required": [""], + "crossVersionUpdate": false, "source": "https://www.mysql.com" }, { @@ -35,6 +37,8 @@ "icon": "nginx.png", "author": "Nginx", "type": "internal", + "required": [""], + "crossVersionUpdate": true, "source": "http://nginx.org/" } ] diff --git a/apps/nginx/1.23.1/READEME.md b/apps/nginx/1.23.1/READEME.md new file mode 100644 index 000000000..c8815d397 --- /dev/null +++ b/apps/nginx/1.23.1/READEME.md @@ -0,0 +1,5 @@ +# What is nginx? +- - - +Nginx (pronounced "engine-x") is an open source reverse proxy server for HTTP, HTTPS, SMTP, POP3, and IMAP protocols, as well as a load balancer, HTTP cache, and a web server (origin server). The nginx project started with a strong focus on high concurrency, high performance and low memory usage. It is licensed under the 2-clause BSD-like license and it runs on Linux, BSD variants, Mac OS X, Solaris, AIX, HP-UX, as well as on other *nix flavors. It also has a proof of concept port for Microsoft Windows. + +[wikipedia.org/wiki/Nginx](http://wikipedia.org/wiki/Nginx) \ No newline at end of file diff --git a/apps/wordpress/6.0.1/docker-compose.yml b/apps/wordpress/6.0.1/docker-compose.yml new file mode 100644 index 000000000..6d4ff9ba1 --- /dev/null +++ b/apps/wordpress/6.0.1/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3' +services: + 1panel_wordpress: + image: wordpress:6.0.1 + container_name: 1panel_wordpress + ports: + - "8080:80" + restart: always + networks: + - 1panel + volumes: + - ./data:/var/www/html + environment: + WORDPRESS_DB_HOST: 1panel_mysql + WORDPRESS_DB_NAME: wpdb + WORDPRESS_DB_USER: root + WORDPRESS_DB_PASSWORD: Password@123 + WORDPRESS_DEBUG: 1 + +networks: + 1panel: + external: true \ No newline at end of file diff --git a/backend/app/api/v1/app.go b/backend/app/api/v1/app.go index fdc11bef7..ad30faab9 100644 --- a/backend/app/api/v1/app.go +++ b/backend/app/api/v1/app.go @@ -25,7 +25,7 @@ func (b *BaseApi) AppSearch(c *gin.Context) { } func (b *BaseApi) AppSync(c *gin.Context) { - if err := appService.Sync(); err != nil { + if err := appService.SyncAppList(); err != nil { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) return } @@ -106,3 +106,11 @@ func (b *BaseApi) InstallOperate(c *gin.Context) { helper.SuccessWithData(c, nil) } + +func (b *BaseApi) InstalledSync(c *gin.Context) { + if err := appService.SyncAllInstalled(); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, "") +} diff --git a/backend/app/dto/app.go b/backend/app/dto/app.go index 3630ce5e8..0cdc07ebc 100644 --- a/backend/app/dto/app.go +++ b/backend/app/dto/app.go @@ -88,9 +88,18 @@ var ( Down AppOperate = "down" Restart AppOperate = "restart" Delete AppOperate = "delete" + Sync AppOperate = "sync" ) type AppInstallOperate struct { InstallId uint `json:"installId" validate:"required"` Operate AppOperate `json:"operate" validate:"required"` } + +//type AppContainer struct { +// Names []string `json:"names"` +// Image string `json:"image"` +// Ports string `json:"ports"` +// Status string `json:"status"` +// State string `json:"state"` +//} diff --git a/backend/app/model/app_container.go b/backend/app/model/app_container.go new file mode 100644 index 000000000..18b0eb62c --- /dev/null +++ b/backend/app/model/app_container.go @@ -0,0 +1,9 @@ +package model + +type AppContainer struct { + BaseModel + ServiceName string `json:"serviceName"` + ContainerName string `json:"containerName"` + AppInstallId uint `json:"appInstallId"` + Image string `json:"image"` +} diff --git a/backend/app/model/app_install.go b/backend/app/model/app_install.go index 54eda70a2..946811a5e 100644 --- a/backend/app/model/app_install.go +++ b/backend/app/model/app_install.go @@ -1,15 +1,28 @@ package model +import ( + "github.com/1Panel-dev/1Panel/global" + "path" +) + type AppInstall struct { BaseModel - Name string `json:"name" gorm:"type:varchar(64);not null"` - ContainerName string `json:"containerName" gorm:"type:varchar(256);not null"` - Version string `json:"version" gorm:"type:varchar(256);not null"` - AppId uint `json:"appId" gorm:"type:integer;not null"` - AppDetailId uint `json:"appDetailId" gorm:"type:integer;not null"` - Params string `json:"params" gorm:"type:longtext;not null"` - Status string `json:"status" gorm:"type:varchar(256);not null"` - Description string `json:"description" gorm:"type:varchar(256);not null"` - Message string `json:"message" gorm:"type:longtext;not null"` - App App `json:"-"` + Name string `json:"name" gorm:"type:varchar(64);not null"` + Version string `json:"version" gorm:"type:varchar(256);not null"` + AppId uint `json:"appId" gorm:"type:integer;not null"` + AppDetailId uint `json:"appDetailId" gorm:"type:integer;not null"` + Params string `json:"params" gorm:"type:longtext;not null"` + Status string `json:"status" gorm:"type:varchar(256);not null"` + Description string `json:"description" gorm:"type:varchar(256);not null"` + Message string `json:"message" gorm:"type:longtext;not null"` + App App `json:"-"` + Containers []AppContainer `json:"containers"` +} + +func (i AppInstall) GetPath() string { + return path.Join(global.CONF.System.AppDir, i.App.Key, i.Name) +} + +func (i AppInstall) GetComposePath() string { + return path.Join(global.CONF.System.AppDir, i.App.Key, i.Name, "docker-compose.yml") } diff --git a/backend/app/repo/app_container.go b/backend/app/repo/app_container.go new file mode 100644 index 000000000..5fa529514 --- /dev/null +++ b/backend/app/repo/app_container.go @@ -0,0 +1,21 @@ +package repo + +import ( + "context" + "github.com/1Panel-dev/1Panel/app/model" + "github.com/1Panel-dev/1Panel/global" + "gorm.io/gorm" +) + +type AppContainerRepo struct { +} + +func (a AppContainerRepo) Create(container *model.AppContainer) error { + db := global.DB.Model(&model.AppContainer{}) + return db.Create(&container).Error +} + +func (a AppContainerRepo) BatchCreate(ctx context.Context, containers []*model.AppContainer) error { + db := ctx.Value("db").(*gorm.DB) + return db.Model(&model.AppContainer{}).Create(&containers).Error +} diff --git a/backend/app/repo/app_install.go b/backend/app/repo/app_install.go index 1d130428c..3feca3baf 100644 --- a/backend/app/repo/app_install.go +++ b/backend/app/repo/app_install.go @@ -13,7 +13,17 @@ func (a AppInstallRepo) GetBy(opts ...DBOption) ([]model.AppInstall, error) { db = opt(db) } var install []model.AppInstall - err := db.Preload("App").Find(&install).Error + err := db.Preload("App").Preload("Containers").Find(&install).Error + return install, err +} + +func (a AppInstallRepo) GetFirst(opts ...DBOption) (model.AppInstall, error) { + db := global.DB.Model(&model.AppInstall{}) + for _, opt := range opts { + db = opt(db) + } + var install model.AppInstall + err := db.Preload("App").Preload("Containers").First(&install).Error return install, err } @@ -43,6 +53,6 @@ func (a AppInstallRepo) Page(page, size int, opts ...DBOption) (int64, []model.A } count := int64(0) db = db.Count(&count) - err := db.Debug().Limit(size).Offset(size * (page - 1)).Preload("App").Find(&apps).Error + err := db.Debug().Limit(size).Offset(size * (page - 1)).Preload("App").Preload("Containers").Find(&apps).Error return count, apps, err } diff --git a/backend/app/repo/entry.go b/backend/app/repo/entry.go index e51d71269..908fa67cf 100644 --- a/backend/app/repo/entry.go +++ b/backend/app/repo/entry.go @@ -14,6 +14,7 @@ type RepoGroup struct { TagRepo AppDetailRepo AppInstallRepo + AppContainerRepo } var RepoGroupApp = new(RepoGroup) diff --git a/backend/app/service/app.go b/backend/app/service/app.go index dbd91a1ca..7977b8de1 100644 --- a/backend/app/service/app.go +++ b/backend/app/service/app.go @@ -11,14 +11,17 @@ import ( "github.com/1Panel-dev/1Panel/global" "github.com/1Panel-dev/1Panel/utils/common" "github.com/1Panel-dev/1Panel/utils/compose" + "github.com/1Panel-dev/1Panel/utils/docker" "github.com/1Panel-dev/1Panel/utils/files" "github.com/joho/godotenv" "golang.org/x/net/context" + "gopkg.in/yaml.v3" "os" "path" "reflect" "sort" "strconv" + "strings" ) type AppService struct { @@ -158,7 +161,8 @@ func (a AppService) Operate(req dto.AppInstallOperate) error { } install := appInstall[0] - dockerComposePath := path.Join(global.CONF.System.AppDir, install.App.Key, install.ContainerName, "docker-compose.yml") + dockerComposePath := install.GetComposePath() + switch req.Operate { case dto.Up: out, err := compose.Up(dockerComposePath) @@ -180,7 +184,7 @@ func (a AppService) Operate(req dto.AppInstallOperate) error { install.Status = constant.Running case dto.Delete: op := files.NewFileOp() - appDir := path.Join(global.CONF.System.AppDir, install.App.Key, install.ContainerName) + appDir := install.GetPath() dir, _ := os.Stat(appDir) if dir == nil { return appInstallRepo.Delete(commonRepo.WithByID(install.ID)) @@ -196,6 +200,11 @@ func (a AppService) Operate(req dto.AppInstallOperate) error { _ = op.DeleteDir(appDir) _ = appInstallRepo.Delete(commonRepo.WithByID(install.ID)) return nil + case dto.Sync: + if err := a.SyncInstalled(install.ID); err != nil { + return err + } + return nil default: return errors.New("operate not support") } @@ -233,30 +242,28 @@ func (a AppService) Install(name string, appDetailId uint, params map[string]int if err != nil { return err } - containerName := constant.ContainerPrefix + app.Key + "-" + common.RandStr(6) appInstall := model.AppInstall{ - Name: name, - AppId: appDetail.AppId, - AppDetailId: appDetail.ID, - Version: appDetail.Version, - Status: constant.Installing, - Params: string(paramByte), - ContainerName: containerName, - Message: "", + Name: name, + AppId: appDetail.AppId, + AppDetailId: appDetail.ID, + Version: appDetail.Version, + Status: constant.Installing, + Params: string(paramByte), } + resourceDir := path.Join(global.CONF.System.ResourceDir, "apps", app.Key, appDetail.Version) installDir := path.Join(global.CONF.System.AppDir, app.Key) - installAppDir := path.Join(installDir, appDetail.Version) - op := files.NewFileOp() - if err := op.Copy(resourceDir, installAppDir); err != nil { + installVersionDir := path.Join(installDir, appDetail.Version) + fileOp := files.NewFileOp() + if err := fileOp.Copy(resourceDir, installVersionDir); err != nil { return err } - containerNameDir := path.Join(installDir, containerName) - if err := op.Rename(installAppDir, containerNameDir); err != nil { + appDir := path.Join(installDir, name) + if err := fileOp.Rename(installVersionDir, appDir); err != nil { return err } - composeFilePath := path.Join(containerNameDir, "docker-compose.yml") - envPath := path.Join(containerNameDir, ".env") + composeFilePath := path.Join(appDir, "docker-compose.yml") + envPath := path.Join(appDir, ".env") envParams := make(map[string]string, len(params)) for k, v := range params { @@ -269,13 +276,58 @@ func (a AppService) Install(name string, appDetailId uint, params map[string]int envParams[k] = t.(string) } } - envParams["CONTAINER_NAME"] = containerName if err := godotenv.Write(envParams, envPath); err != nil { return err } + + fileContent, err := os.ReadFile(composeFilePath) + if err != nil { + return err + } + composeMap := make(map[string]interface{}) + if err := yaml.Unmarshal(fileContent, &composeMap); err != nil { + return err + } + servicesMap := composeMap["services"].(map[string]interface{}) + changeKeys := make(map[string]string, len(servicesMap)) + var appContainers []*model.AppContainer + for k, v := range servicesMap { + serviceName := k + "-" + common.RandStr(4) + changeKeys[k] = serviceName + value := v.(map[string]interface{}) + containerName := constant.ContainerPrefix + k + "-" + common.RandStr(4) + value["container_name"] = containerName + var image string + if i, ok := value["image"]; ok { + image = i.(string) + } + appContainers = append(appContainers, &model.AppContainer{ + ServiceName: serviceName, + ContainerName: containerName, + Image: image, + }) + } + for k, v := range changeKeys { + servicesMap[v] = servicesMap[k] + delete(servicesMap, k) + } + serviceByte, err := yaml.Marshal(servicesMap) + if err != nil { + return err + } + if err := fileOp.WriteFile(composeFilePath, strings.NewReader(string(serviceByte)), 0775); err != nil { + return err + } + if err := appInstallRepo.Create(&appInstall); err != nil { return err } + for _, c := range appContainers { + c.AppInstallId = appInstall.ID + } + if err := appContainerRepo.BatchCreate(context.WithValue(context.Background(), "db", global.DB), appContainers); err != nil { + return err + } go upApp(composeFilePath, appInstall) return nil } @@ -296,12 +348,96 @@ func upApp(composeFilePath string, appInstall model.AppInstall) { } } -func (a AppService) SyncInstalled() error { - +func (a AppService) SyncAllInstalled() error { + allList, err := appInstallRepo.GetBy() + if err != nil { + return err + } + go func() { + for _, i := range allList { + if err := a.SyncInstalled(i.ID); err != nil { + global.LOG.Errorf("sync install app[%s] error,mgs: %s", i.Name, err.Error()) + } + } + }() return nil } -func (a AppService) Sync() error { +func (a AppService) SyncInstalled(installId uint) error { + appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId)) + if err != nil { + return err + } + var containerNames []string + for _, a := range appInstall.Containers { + containerNames = append(containerNames, a.ContainerName) + } + cli, err := docker.NewClient() + if err != nil { + return err + } + containers, err := cli.ListContainersByName(containerNames) + if err != nil { + return err + } + var errorContainers []string + var notFoundContainers []string + + for _, n := range containers { + if n.State != "running" { + errorContainers = append(errorContainers, n.Names...) + } + } + for _, old := range containerNames { + exist := false + for _, new := range containers { + if common.ExistWithStrArray(old, new.Names) { + exist = true + break + } + } + if !exist { + notFoundContainers = append(notFoundContainers, old) + } + } + + if len(containers) == 0 { + appInstall.Status = constant.Error + appInstall.Message = "container is not found" + return appInstallRepo.Save(appInstall) + } + + if len(errorContainers) == 0 && len(notFoundContainers) == 0 { + appInstall.Status = constant.Running + return appInstallRepo.Save(appInstall) + } + if len(errorContainers) == len(containerNames) { + appInstall.Status = constant.Error + } + if len(notFoundContainers) == len(containerNames) { + appInstall.Status = constant.Stopped + } + + var errMsg strings.Builder + if len(errorContainers) > 0 { + errMsg.Write([]byte(string(rune(len(errorContainers))) + " error containers:")) + for _, e := range errorContainers { + errMsg.Write([]byte(e)) + } + errMsg.Write([]byte("\n")) + } + if len(notFoundContainers) > 0 { + errMsg.Write([]byte(string(rune(len(notFoundContainers))) + " not found containers:")) + for _, e := range notFoundContainers { + errMsg.Write([]byte(e)) + } + errMsg.Write([]byte("\n")) + } + appInstall.Message = errMsg.String() + return appInstallRepo.Save(appInstall) +} + +func (a AppService) SyncAppList() error { //TODO 从 oss 拉取最新列表 var appConfig model.AppConfig appConfig.OssPath = global.CONF.System.AppOss diff --git a/backend/app/service/entry.go b/backend/app/service/entry.go index d363a90db..6ac5d2434 100644 --- a/backend/app/service/entry.go +++ b/backend/app/service/entry.go @@ -30,5 +30,6 @@ var ( appTagRepo = repo.RepoGroupApp.AppTagRepo appDetailRepo = repo.RepoGroupApp.AppDetailRepo tagRepo = repo.RepoGroupApp.TagRepo - appInstallRepo = repo.AppInstallRepo{} + appInstallRepo = repo.RepoGroupApp.AppInstallRepo + appContainerRepo = repo.RepoGroupApp.AppContainerRepo ) diff --git a/backend/constant/app.go b/backend/constant/app.go index 3befe623d..92cb86bed 100644 --- a/backend/constant/app.go +++ b/backend/constant/app.go @@ -2,7 +2,7 @@ package constant const ( Running = "Running" - Warning = "Warning" + UnHealthy = "UnHealthy" Error = "Error" Stopped = "Stopped" Installing = "Installing" diff --git a/backend/go.mod b/backend/go.mod index 137dfc48e..418d0d9e2 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -50,12 +50,17 @@ require ( require ( github.com/KyleBanks/depth v1.2.1 // indirect + github.com/Microsoft/go-winio v0.6.0 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/andybalholm/brotli v1.0.4 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect github.com/dgraph-io/ristretto v0.1.0 // indirect + github.com/docker/distribution v2.8.1+incompatible // indirect + github.com/docker/docker v20.10.18+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/dsnet/compress v0.0.1 // indirect github.com/dustin/go-humanize v1.0.0 // indirect github.com/fatih/color v1.13.0 // indirect @@ -100,6 +105,8 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.0.2 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.2 // indirect github.com/pierrec/lz4/v4 v4.1.15 // indirect @@ -118,9 +125,10 @@ require ( go.opentelemetry.io/otel v1.0.0 // indirect go.opentelemetry.io/otel/trace v1.0.0 // indirect golang.org/x/image v0.0.0-20190802002840-cff245a6509b // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect - golang.org/x/tools v0.1.10 // indirect + golang.org/x/tools v0.1.12 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/ini.v1 v1.66.6 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect diff --git a/backend/go.sum b/backend/go.sum index 885b3ef37..8072eb0aa 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -41,6 +41,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= +github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= @@ -85,6 +87,14 @@ github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/Lu github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= +github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc= +github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= @@ -358,6 +368,10 @@ github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= @@ -528,6 +542,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -711,6 +727,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/backend/init/migration/migrations/init.go b/backend/init/migration/migrations/init.go index d6719995e..aaa5f6b87 100644 --- a/backend/init/migration/migrations/init.go +++ b/backend/init/migration/migrations/init.go @@ -150,6 +150,6 @@ var AddTableCronjob = &gormigrate.Migration{ var AddTableApp = &gormigrate.Migration{ ID: "20200921-add-table-app", Migrate: func(tx *gorm.DB) error { - return tx.AutoMigrate(&model.App{}, &model.AppDetail{}, &model.Tag{}, &model.AppTag{}, &model.AppConfig{}, &model.AppInstall{}) + return tx.AutoMigrate(&model.App{}, &model.AppDetail{}, &model.Tag{}, &model.AppTag{}, &model.AppConfig{}, &model.AppInstall{}, &model.AppContainer{}, &model.AppContainer{}) }, } diff --git a/backend/router/ro_app.go b/backend/router/ro_app.go index 1bf9098b8..a5b6566a7 100644 --- a/backend/router/ro_app.go +++ b/backend/router/ro_app.go @@ -22,5 +22,6 @@ func (a *AppRouter) InitAppRouter(Router *gin.RouterGroup) { appRouter.POST("/install", baseApi.InstallApp) appRouter.POST("/installed", baseApi.PageInstalled) appRouter.POST("/installed/op", baseApi.InstallOperate) + appRouter.POST("/installed/sync", baseApi.InstalledSync) } } diff --git a/backend/utils/common/common.go b/backend/utils/common/common.go index 249f5351b..d3ab545a9 100644 --- a/backend/utils/common/common.go +++ b/backend/utils/common/common.go @@ -71,3 +71,12 @@ func ScanPort(port string) bool { defer ln.Close() return false } + +func ExistWithStrArray(str string, arr []string) bool { + for _, a := range arr { + if strings.Contains(a, str) { + return true + } + } + return false +} diff --git a/backend/utils/docker/docker.go b/backend/utils/docker/docker.go new file mode 100644 index 000000000..655cf2363 --- /dev/null +++ b/backend/utils/docker/docker.go @@ -0,0 +1,49 @@ +package docker + +import ( + "context" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/client" +) + +type Client struct { + cli *client.Client +} + +func NewClient() (Client, error) { + cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + if err != nil { + return Client{}, err + } + + return Client{ + cli: cli, + }, nil +} + +func (c Client) ListAllContainers() ([]types.Container, error) { + var options types.ContainerListOptions + containers, err := c.cli.ContainerList(context.Background(), options) + if err != nil { + return nil, err + } + return containers, nil +} + +func (c Client) ListContainersByName(names []string) ([]types.Container, error) { + var options types.ContainerListOptions + options.All = true + if len(names) > 0 { + var array []filters.KeyValuePair + for _, n := range names { + array = append(array, filters.Arg("name", n)) + } + options.Filters = filters.NewArgs(array...) + } + containers, err := c.cli.ContainerList(context.Background(), options) + if err != nil { + return nil, err + } + return containers, nil +} diff --git a/backend/utils/files/file_op.go b/backend/utils/files/file_op.go index 7dffe82f8..82ca289a6 100644 --- a/backend/utils/files/file_op.go +++ b/backend/utils/files/file_op.go @@ -29,6 +29,10 @@ func NewFileOp() FileOp { } } +func (f FileOp) OpenFile(dst string) (fs.File, error) { + return f.Fs.Open(dst) +} + func (f FileOp) CreateDir(dst string, mode fs.FileMode) error { return f.Fs.MkdirAll(dst, mode) } diff --git a/frontend/src/api/interface/app.ts b/frontend/src/api/interface/app.ts index 76eafdd3d..1af5fc8ef 100644 --- a/frontend/src/api/interface/app.ts +++ b/frontend/src/api/interface/app.ts @@ -63,7 +63,6 @@ export namespace App { export interface AppInstalled extends CommonModel { name: string; - containerName: string; version: string; appId: string; appDetailId: string; @@ -72,9 +71,8 @@ export namespace App { description: string; message: string; appName: string; - total: number; - ready: number; icon: string; + constainers: any[]; } export interface AppInstalledOp { diff --git a/frontend/src/api/modules/app.ts b/frontend/src/api/modules/app.ts index 6e8fa80c2..b7ee43670 100644 --- a/frontend/src/api/modules/app.ts +++ b/frontend/src/api/modules/app.ts @@ -29,3 +29,7 @@ export const GetAppInstalled = (info: ReqPage) => { export const InstalledOp = (op: App.AppInstalledOp) => { return http.post('apps/installed/op', op); }; + +export const SyncInstalledApp = () => { + return http.post('apps/installed/sync', {}); +}; diff --git a/frontend/src/assets/apps/mysql.png b/frontend/src/assets/apps/mysql.png deleted file mode 100644 index 3fbbeed73..000000000 Binary files a/frontend/src/assets/apps/mysql.png and /dev/null differ diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 636eef503..3ba45f565 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -408,5 +408,6 @@ export default { description: '描述', delete: '删除', deleteWarn: '删除操作会把数据一并删除,此操作不可回滚,是否继续?', + syncSuccess: '同步成功', }, }; diff --git a/frontend/src/views/app-store/apps/index.vue b/frontend/src/views/app-store/apps/index.vue index 652a44ecb..5b6c5a1a3 100644 --- a/frontend/src/views/app-store/apps/index.vue +++ b/frontend/src/views/app-store/apps/index.vue @@ -1,53 +1,60 @@