mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 14:08:06 +08:00
feat: 增加已安装应用同步逻辑
This commit is contained in:
parent
5855e9b0d8
commit
69b34e07c9
BIN
apps/icons/halo.png
Normal file
BIN
apps/icons/halo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 66 KiB |
BIN
apps/icons/wordpress.png
Normal file
BIN
apps/icons/wordpress.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "0.1",
|
"version": "ddd",
|
||||||
"tags": [
|
"tags": [
|
||||||
{
|
{
|
||||||
"key": "WebSite",
|
"key": "WebSite",
|
||||||
@ -24,6 +24,8 @@
|
|||||||
"icon": "mysql.png",
|
"icon": "mysql.png",
|
||||||
"author": "Oracle",
|
"author": "Oracle",
|
||||||
"type": "internal",
|
"type": "internal",
|
||||||
|
"required": [""],
|
||||||
|
"crossVersionUpdate": false,
|
||||||
"source": "https://www.mysql.com"
|
"source": "https://www.mysql.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -35,6 +37,8 @@
|
|||||||
"icon": "nginx.png",
|
"icon": "nginx.png",
|
||||||
"author": "Nginx",
|
"author": "Nginx",
|
||||||
"type": "internal",
|
"type": "internal",
|
||||||
|
"required": [""],
|
||||||
|
"crossVersionUpdate": true,
|
||||||
"source": "http://nginx.org/"
|
"source": "http://nginx.org/"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
5
apps/nginx/1.23.1/READEME.md
Normal file
5
apps/nginx/1.23.1/READEME.md
Normal file
@ -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)
|
22
apps/wordpress/6.0.1/docker-compose.yml
Normal file
22
apps/wordpress/6.0.1/docker-compose.yml
Normal file
@ -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
|
@ -25,7 +25,7 @@ func (b *BaseApi) AppSearch(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseApi) AppSync(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)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -106,3 +106,11 @@ func (b *BaseApi) InstallOperate(c *gin.Context) {
|
|||||||
|
|
||||||
helper.SuccessWithData(c, nil)
|
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, "")
|
||||||
|
}
|
||||||
|
@ -88,9 +88,18 @@ var (
|
|||||||
Down AppOperate = "down"
|
Down AppOperate = "down"
|
||||||
Restart AppOperate = "restart"
|
Restart AppOperate = "restart"
|
||||||
Delete AppOperate = "delete"
|
Delete AppOperate = "delete"
|
||||||
|
Sync AppOperate = "sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AppInstallOperate struct {
|
type AppInstallOperate struct {
|
||||||
InstallId uint `json:"installId" validate:"required"`
|
InstallId uint `json:"installId" validate:"required"`
|
||||||
Operate AppOperate `json:"operate" 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"`
|
||||||
|
//}
|
||||||
|
9
backend/app/model/app_container.go
Normal file
9
backend/app/model/app_container.go
Normal file
@ -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"`
|
||||||
|
}
|
@ -1,9 +1,13 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/global"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
type AppInstall struct {
|
type AppInstall struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
Name string `json:"name" gorm:"type:varchar(64);not null"`
|
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"`
|
Version string `json:"version" gorm:"type:varchar(256);not null"`
|
||||||
AppId uint `json:"appId" gorm:"type:integer;not null"`
|
AppId uint `json:"appId" gorm:"type:integer;not null"`
|
||||||
AppDetailId uint `json:"appDetailId" gorm:"type:integer;not null"`
|
AppDetailId uint `json:"appDetailId" gorm:"type:integer;not null"`
|
||||||
@ -12,4 +16,13 @@ type AppInstall struct {
|
|||||||
Description string `json:"description" 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"`
|
Message string `json:"message" gorm:"type:longtext;not null"`
|
||||||
App App `json:"-"`
|
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")
|
||||||
}
|
}
|
||||||
|
21
backend/app/repo/app_container.go
Normal file
21
backend/app/repo/app_container.go
Normal file
@ -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
|
||||||
|
}
|
@ -13,7 +13,17 @@ func (a AppInstallRepo) GetBy(opts ...DBOption) ([]model.AppInstall, error) {
|
|||||||
db = opt(db)
|
db = opt(db)
|
||||||
}
|
}
|
||||||
var install []model.AppInstall
|
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
|
return install, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,6 +53,6 @@ func (a AppInstallRepo) Page(page, size int, opts ...DBOption) (int64, []model.A
|
|||||||
}
|
}
|
||||||
count := int64(0)
|
count := int64(0)
|
||||||
db = db.Count(&count)
|
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
|
return count, apps, err
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ type RepoGroup struct {
|
|||||||
TagRepo
|
TagRepo
|
||||||
AppDetailRepo
|
AppDetailRepo
|
||||||
AppInstallRepo
|
AppInstallRepo
|
||||||
|
AppContainerRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
var RepoGroupApp = new(RepoGroup)
|
var RepoGroupApp = new(RepoGroup)
|
||||||
|
@ -11,14 +11,17 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/global"
|
"github.com/1Panel-dev/1Panel/global"
|
||||||
"github.com/1Panel-dev/1Panel/utils/common"
|
"github.com/1Panel-dev/1Panel/utils/common"
|
||||||
"github.com/1Panel-dev/1Panel/utils/compose"
|
"github.com/1Panel-dev/1Panel/utils/compose"
|
||||||
|
"github.com/1Panel-dev/1Panel/utils/docker"
|
||||||
"github.com/1Panel-dev/1Panel/utils/files"
|
"github.com/1Panel-dev/1Panel/utils/files"
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AppService struct {
|
type AppService struct {
|
||||||
@ -158,7 +161,8 @@ func (a AppService) Operate(req dto.AppInstallOperate) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
install := appInstall[0]
|
install := appInstall[0]
|
||||||
dockerComposePath := path.Join(global.CONF.System.AppDir, install.App.Key, install.ContainerName, "docker-compose.yml")
|
dockerComposePath := install.GetComposePath()
|
||||||
|
|
||||||
switch req.Operate {
|
switch req.Operate {
|
||||||
case dto.Up:
|
case dto.Up:
|
||||||
out, err := compose.Up(dockerComposePath)
|
out, err := compose.Up(dockerComposePath)
|
||||||
@ -180,7 +184,7 @@ func (a AppService) Operate(req dto.AppInstallOperate) error {
|
|||||||
install.Status = constant.Running
|
install.Status = constant.Running
|
||||||
case dto.Delete:
|
case dto.Delete:
|
||||||
op := files.NewFileOp()
|
op := files.NewFileOp()
|
||||||
appDir := path.Join(global.CONF.System.AppDir, install.App.Key, install.ContainerName)
|
appDir := install.GetPath()
|
||||||
dir, _ := os.Stat(appDir)
|
dir, _ := os.Stat(appDir)
|
||||||
if dir == nil {
|
if dir == nil {
|
||||||
return appInstallRepo.Delete(commonRepo.WithByID(install.ID))
|
return appInstallRepo.Delete(commonRepo.WithByID(install.ID))
|
||||||
@ -196,6 +200,11 @@ func (a AppService) Operate(req dto.AppInstallOperate) error {
|
|||||||
_ = op.DeleteDir(appDir)
|
_ = op.DeleteDir(appDir)
|
||||||
_ = appInstallRepo.Delete(commonRepo.WithByID(install.ID))
|
_ = appInstallRepo.Delete(commonRepo.WithByID(install.ID))
|
||||||
return nil
|
return nil
|
||||||
|
case dto.Sync:
|
||||||
|
if err := a.SyncInstalled(install.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
default:
|
default:
|
||||||
return errors.New("operate not support")
|
return errors.New("operate not support")
|
||||||
}
|
}
|
||||||
@ -233,7 +242,6 @@ func (a AppService) Install(name string, appDetailId uint, params map[string]int
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
containerName := constant.ContainerPrefix + app.Key + "-" + common.RandStr(6)
|
|
||||||
appInstall := model.AppInstall{
|
appInstall := model.AppInstall{
|
||||||
Name: name,
|
Name: name,
|
||||||
AppId: appDetail.AppId,
|
AppId: appDetail.AppId,
|
||||||
@ -241,22 +249,21 @@ func (a AppService) Install(name string, appDetailId uint, params map[string]int
|
|||||||
Version: appDetail.Version,
|
Version: appDetail.Version,
|
||||||
Status: constant.Installing,
|
Status: constant.Installing,
|
||||||
Params: string(paramByte),
|
Params: string(paramByte),
|
||||||
ContainerName: containerName,
|
|
||||||
Message: "",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceDir := path.Join(global.CONF.System.ResourceDir, "apps", app.Key, appDetail.Version)
|
resourceDir := path.Join(global.CONF.System.ResourceDir, "apps", app.Key, appDetail.Version)
|
||||||
installDir := path.Join(global.CONF.System.AppDir, app.Key)
|
installDir := path.Join(global.CONF.System.AppDir, app.Key)
|
||||||
installAppDir := path.Join(installDir, appDetail.Version)
|
installVersionDir := path.Join(installDir, appDetail.Version)
|
||||||
op := files.NewFileOp()
|
fileOp := files.NewFileOp()
|
||||||
if err := op.Copy(resourceDir, installAppDir); err != nil {
|
if err := fileOp.Copy(resourceDir, installVersionDir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
containerNameDir := path.Join(installDir, containerName)
|
appDir := path.Join(installDir, name)
|
||||||
if err := op.Rename(installAppDir, containerNameDir); err != nil {
|
if err := fileOp.Rename(installVersionDir, appDir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
composeFilePath := path.Join(containerNameDir, "docker-compose.yml")
|
composeFilePath := path.Join(appDir, "docker-compose.yml")
|
||||||
envPath := path.Join(containerNameDir, ".env")
|
envPath := path.Join(appDir, ".env")
|
||||||
|
|
||||||
envParams := make(map[string]string, len(params))
|
envParams := make(map[string]string, len(params))
|
||||||
for k, v := range 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[k] = t.(string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
envParams["CONTAINER_NAME"] = containerName
|
|
||||||
if err := godotenv.Write(envParams, envPath); err != nil {
|
if err := godotenv.Write(envParams, envPath); err != nil {
|
||||||
return err
|
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 {
|
if err := appInstallRepo.Create(&appInstall); err != nil {
|
||||||
return err
|
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)
|
go upApp(composeFilePath, appInstall)
|
||||||
return nil
|
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
|
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 拉取最新列表
|
//TODO 从 oss 拉取最新列表
|
||||||
var appConfig model.AppConfig
|
var appConfig model.AppConfig
|
||||||
appConfig.OssPath = global.CONF.System.AppOss
|
appConfig.OssPath = global.CONF.System.AppOss
|
||||||
|
@ -30,5 +30,6 @@ var (
|
|||||||
appTagRepo = repo.RepoGroupApp.AppTagRepo
|
appTagRepo = repo.RepoGroupApp.AppTagRepo
|
||||||
appDetailRepo = repo.RepoGroupApp.AppDetailRepo
|
appDetailRepo = repo.RepoGroupApp.AppDetailRepo
|
||||||
tagRepo = repo.RepoGroupApp.TagRepo
|
tagRepo = repo.RepoGroupApp.TagRepo
|
||||||
appInstallRepo = repo.AppInstallRepo{}
|
appInstallRepo = repo.RepoGroupApp.AppInstallRepo
|
||||||
|
appContainerRepo = repo.RepoGroupApp.AppContainerRepo
|
||||||
)
|
)
|
||||||
|
@ -2,7 +2,7 @@ package constant
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
Running = "Running"
|
Running = "Running"
|
||||||
Warning = "Warning"
|
UnHealthy = "UnHealthy"
|
||||||
Error = "Error"
|
Error = "Error"
|
||||||
Stopped = "Stopped"
|
Stopped = "Stopped"
|
||||||
Installing = "Installing"
|
Installing = "Installing"
|
||||||
|
@ -50,12 +50,17 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
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/purell v1.1.1 // indirect
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||||
github.com/cespare/xxhash v1.1.0 // indirect
|
github.com/cespare/xxhash v1.1.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||||
github.com/dgraph-io/ristretto v0.1.0 // 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/dsnet/compress v0.0.1 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||||
github.com/fatih/color v1.13.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/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.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 v1.9.5 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.2 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.2 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.15 // 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 v1.0.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace 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/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/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // 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
|
google.golang.org/protobuf v1.28.0 // indirect
|
||||||
gopkg.in/ini.v1 v1.66.6 // indirect
|
gopkg.in/ini.v1 v1.66.6 // indirect
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||||
|
@ -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/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 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
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 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
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/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 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
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 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
|
||||||
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
|
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=
|
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/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 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
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/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 v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
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.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.2/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.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-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-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/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.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 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
|
||||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
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-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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
@ -150,6 +150,6 @@ var AddTableCronjob = &gormigrate.Migration{
|
|||||||
var AddTableApp = &gormigrate.Migration{
|
var AddTableApp = &gormigrate.Migration{
|
||||||
ID: "20200921-add-table-app",
|
ID: "20200921-add-table-app",
|
||||||
Migrate: func(tx *gorm.DB) error {
|
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{})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -22,5 +22,6 @@ func (a *AppRouter) InitAppRouter(Router *gin.RouterGroup) {
|
|||||||
appRouter.POST("/install", baseApi.InstallApp)
|
appRouter.POST("/install", baseApi.InstallApp)
|
||||||
appRouter.POST("/installed", baseApi.PageInstalled)
|
appRouter.POST("/installed", baseApi.PageInstalled)
|
||||||
appRouter.POST("/installed/op", baseApi.InstallOperate)
|
appRouter.POST("/installed/op", baseApi.InstallOperate)
|
||||||
|
appRouter.POST("/installed/sync", baseApi.InstalledSync)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,3 +71,12 @@ func ScanPort(port string) bool {
|
|||||||
defer ln.Close()
|
defer ln.Close()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExistWithStrArray(str string, arr []string) bool {
|
||||||
|
for _, a := range arr {
|
||||||
|
if strings.Contains(a, str) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
49
backend/utils/docker/docker.go
Normal file
49
backend/utils/docker/docker.go
Normal file
@ -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
|
||||||
|
}
|
@ -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 {
|
func (f FileOp) CreateDir(dst string, mode fs.FileMode) error {
|
||||||
return f.Fs.MkdirAll(dst, mode)
|
return f.Fs.MkdirAll(dst, mode)
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,6 @@ export namespace App {
|
|||||||
|
|
||||||
export interface AppInstalled extends CommonModel {
|
export interface AppInstalled extends CommonModel {
|
||||||
name: string;
|
name: string;
|
||||||
containerName: string;
|
|
||||||
version: string;
|
version: string;
|
||||||
appId: string;
|
appId: string;
|
||||||
appDetailId: string;
|
appDetailId: string;
|
||||||
@ -72,9 +71,8 @@ export namespace App {
|
|||||||
description: string;
|
description: string;
|
||||||
message: string;
|
message: string;
|
||||||
appName: string;
|
appName: string;
|
||||||
total: number;
|
|
||||||
ready: number;
|
|
||||||
icon: string;
|
icon: string;
|
||||||
|
constainers: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppInstalledOp {
|
export interface AppInstalledOp {
|
||||||
|
@ -29,3 +29,7 @@ export const GetAppInstalled = (info: ReqPage) => {
|
|||||||
export const InstalledOp = (op: App.AppInstalledOp) => {
|
export const InstalledOp = (op: App.AppInstalledOp) => {
|
||||||
return http.post<any>('apps/installed/op', op);
|
return http.post<any>('apps/installed/op', op);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const SyncInstalledApp = () => {
|
||||||
|
return http.post<any>('apps/installed/sync', {});
|
||||||
|
};
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 66 KiB |
@ -408,5 +408,6 @@ export default {
|
|||||||
description: '描述',
|
description: '描述',
|
||||||
delete: '删除',
|
delete: '删除',
|
||||||
deleteWarn: '删除操作会把数据一并删除,此操作不可回滚,是否继续?',
|
deleteWarn: '删除操作会把数据一并删除,此操作不可回滚,是否继续?',
|
||||||
|
syncSuccess: '同步成功',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div v-loading="loading">
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-input v-model="req.name" @blur="searchByName"></el-input>
|
<el-input v-model="req.name" @blur="searchByName"></el-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="11">
|
||||||
<el-select v-model="req.tags" multiple style="width: 100%" @change="changeTag">
|
<el-select v-model="req.tags" multiple style="width: 100%" @change="changeTag">
|
||||||
<el-option v-for="item in tags" :key="item.key" :label="item.name" :value="item.key"></el-option>
|
<el-option v-for="item in tags" :key="item.key" :label="item.name" :value="item.key"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col :span="1">
|
||||||
|
<el-button @click="sync">{{ $t('app.sync') }}</el-button>
|
||||||
|
</el-col>
|
||||||
<el-col v-for="(app, index) in apps" :key="index" :xs="8" :sm="8" :lg="4">
|
<el-col v-for="(app, index) in apps" :key="index" :xs="8" :sm="8" :lg="4">
|
||||||
<div @click="getAppDetail(app.id)">
|
<div @click="getAppDetail(app.id)">
|
||||||
<el-card :body-style="{ padding: '0px' }" class="a-card">
|
<el-card :body-style="{ padding: '0px' }" class="a-card">
|
||||||
@ -41,13 +45,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { App } from '@/api/interface/app';
|
import { App } from '@/api/interface/app';
|
||||||
import { onMounted, reactive, ref } from 'vue';
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
import router from '@/routers';
|
import router from '@/routers';
|
||||||
import { SearchApp } from '@/api/modules/app';
|
import { SearchApp, SyncApp } from '@/api/modules/app';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
|
||||||
let req = reactive({
|
let req = reactive({
|
||||||
name: '',
|
name: '',
|
||||||
@ -59,6 +66,7 @@ let req = reactive({
|
|||||||
let apps = ref<App.App[]>([]);
|
let apps = ref<App.App[]>([]);
|
||||||
let tags = ref<App.Tag[]>([]);
|
let tags = ref<App.Tag[]>([]);
|
||||||
const colorArr = ['#6495ED', '#54FF9F', '#BEBEBE', '#FFF68F', '#FFFF00', '#8B0000'];
|
const colorArr = ['#6495ED', '#54FF9F', '#BEBEBE', '#FFF68F', '#FFFF00', '#8B0000'];
|
||||||
|
let loading = ref(false);
|
||||||
|
|
||||||
const getColor = (index: number) => {
|
const getColor = (index: number) => {
|
||||||
return colorArr[index];
|
return colorArr[index];
|
||||||
@ -78,6 +86,17 @@ const getAppDetail = (id: number) => {
|
|||||||
router.push({ name: 'AppDetail', params });
|
router.push({ name: 'AppDetail', params });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sync = () => {
|
||||||
|
loading.value = true;
|
||||||
|
SyncApp()
|
||||||
|
.then(() => {
|
||||||
|
ElMessage.success(i18n.global.t('app.syncSuccess'));
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const changeTag = () => {
|
const changeTag = () => {
|
||||||
search(req);
|
search(req);
|
||||||
};
|
};
|
||||||
|
@ -21,25 +21,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { SyncApp } from '@/api/modules/app';
|
|
||||||
import LayoutContent from '@/layout/layout-content.vue';
|
import LayoutContent from '@/layout/layout-content.vue';
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const activeName = ref('all');
|
const activeName = ref('all');
|
||||||
|
|
||||||
const sync = () => {
|
|
||||||
SyncApp().then((res) => {
|
|
||||||
console.log(res);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const routerTo = (path: string) => {
|
const routerTo = (path: string) => {
|
||||||
router.push({ path: path });
|
router.push({ path: path });
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
sync();
|
|
||||||
const path = router.currentRoute.value.path;
|
const path = router.currentRoute.value.path;
|
||||||
if (path === '/apps/all') {
|
if (path === '/apps/all') {
|
||||||
activeName.value = 'all';
|
activeName.value = 'all';
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div style="float: right; margin-bottom: 5px">
|
||||||
|
<el-button @click="sync">{{ $t('app.sync') }}</el-button>
|
||||||
|
</div>
|
||||||
<ComplexTable :pagination-config="paginationConfig" :data="data" @search="search" v-loading="loading">
|
<ComplexTable :pagination-config="paginationConfig" :data="data" @search="search" v-loading="loading">
|
||||||
<el-table-column :label="$t('app.name')" prop="name"></el-table-column>
|
<el-table-column :label="$t('app.name')" prop="name"></el-table-column>
|
||||||
<!-- <el-table-column :label="$t('app.description')" prop="description"></el-table-column> -->
|
<!-- <el-table-column :label="$t('app.description')" prop="description"></el-table-column> -->
|
||||||
<el-table-column :label="$t('app.appName')" prop="appName"></el-table-column>
|
<el-table-column :label="$t('app.appName')" prop="appName"></el-table-column>
|
||||||
<el-table-column :label="$t('app.version')" prop="version"></el-table-column>
|
<el-table-column :label="$t('app.version')" prop="version"></el-table-column>
|
||||||
<el-table-column :label="$t('app.container')">
|
|
||||||
<template #default="{ row }">
|
|
||||||
{{ row.ready / row.total }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column :label="$t('app.status')">
|
<el-table-column :label="$t('app.status')">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-popover
|
<el-popover
|
||||||
@ -32,7 +30,7 @@
|
|||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
/>
|
/>
|
||||||
<fu-table-operations
|
<fu-table-operations
|
||||||
width="200px"
|
width="250px"
|
||||||
:ellipsis="10"
|
:ellipsis="10"
|
||||||
:buttons="buttons"
|
:buttons="buttons"
|
||||||
:label="$t('commons.table.operate')"
|
:label="$t('commons.table.operate')"
|
||||||
@ -52,7 +50,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { GetAppInstalled, InstalledOp } from '@/api/modules/app';
|
import { GetAppInstalled, InstalledOp, SyncInstalledApp } from '@/api/modules/app';
|
||||||
import { onMounted, reactive, ref } from 'vue';
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
import ComplexTable from '@/components/complex-table/index.vue';
|
import ComplexTable from '@/components/complex-table/index.vue';
|
||||||
import { dateFromat } from '@/utils/util';
|
import { dateFromat } from '@/utils/util';
|
||||||
@ -72,6 +70,18 @@ let operateReq = reactive({
|
|||||||
operate: '',
|
operate: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const sync = () => {
|
||||||
|
loading.value = true;
|
||||||
|
SyncInstalledApp()
|
||||||
|
.then(() => {
|
||||||
|
ElMessage.success(i18n.global.t('app.syncSuccess'));
|
||||||
|
search();
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const search = () => {
|
const search = () => {
|
||||||
const req = {
|
const req = {
|
||||||
page: paginationConfig.currentPage,
|
page: paginationConfig.currentPage,
|
||||||
@ -122,12 +132,21 @@ const getMsg = (op: string) => {
|
|||||||
case 'delete':
|
case 'delete':
|
||||||
tip = i18n.global.t('app.deleteWarn');
|
tip = i18n.global.t('app.deleteWarn');
|
||||||
break;
|
break;
|
||||||
|
case 'sync':
|
||||||
|
tip = i18n.global.t('app.sync');
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
return tip;
|
return tip;
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttons = [
|
const buttons = [
|
||||||
|
{
|
||||||
|
label: i18n.global.t('app.sync'),
|
||||||
|
click: (row: any) => {
|
||||||
|
openOperate(row, 'sync');
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: i18n.global.t('app.restart'),
|
label: i18n.global.t('app.restart'),
|
||||||
click: (row: any) => {
|
click: (row: any) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user