Compare commits
8 Commits
release-1.
...
dev
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3e6cd1cab1 | ||
![]() |
98df3806f5 | ||
![]() |
18e8af6234 | ||
![]() |
5bbda8f842 | ||
![]() |
85f8c1e634 | ||
![]() |
85c935ee46 | ||
![]() |
b4033471e7 | ||
![]() |
8dca519068 |
@@ -34,6 +34,28 @@ func (b *BaseApi) CreateRemoteDB(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, nil)
|
helper.SuccessWithData(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Database
|
||||||
|
// @Summary Check remote database
|
||||||
|
// @Description 检测远程数据库连接性
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.RemoteDBCreate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /databases/remote/check [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["name", "type"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"检测远程数据库 [name][type] 连接性","formatEN":"check if remote database [name][type] is connectable"}
|
||||||
|
func (b *BaseApi) CheckeRemoteDB(c *gin.Context) {
|
||||||
|
var req dto.RemoteDBCreate
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := global.VALID.Struct(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, remoteDBService.CheckeRemoteDB(req))
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags Database
|
// @Tags Database
|
||||||
// @Summary Page remote databases
|
// @Summary Page remote databases
|
||||||
// @Description 获取远程数据库列表分页
|
// @Description 获取远程数据库列表分页
|
||||||
|
@@ -341,6 +341,24 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
|
|||||||
install.Version = detail.Version
|
install.Version = detail.Version
|
||||||
install.AppDetailId = detailId
|
install.AppDetailId = detailId
|
||||||
|
|
||||||
|
images, err := getImages(install)
|
||||||
|
if err != nil {
|
||||||
|
upErr = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dockerCli, err := composeV2.NewClient()
|
||||||
|
if err != nil {
|
||||||
|
upErr = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, image := range images {
|
||||||
|
if err = dockerCli.PullImage(image, true); err != nil {
|
||||||
|
upErr = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
||||||
if out != "" {
|
if out != "" {
|
||||||
upErr = errors.New(out)
|
upErr = errors.New(out)
|
||||||
@@ -398,6 +416,26 @@ func getContainerNames(install model.AppInstall) ([]string, error) {
|
|||||||
return containerNames, nil
|
return containerNames, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getImages(install model.AppInstall) ([]string, error) {
|
||||||
|
envStr, err := coverEnvJsonToStr(install.Env)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
project, err := composeV2.GetComposeProject(install.Name, install.GetPath(), []byte(install.DockerCompose), []byte(envStr), true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
imagesMap := make(map[string]struct{})
|
||||||
|
for _, service := range project.AllServices() {
|
||||||
|
imagesMap[service.Image] = struct{}{}
|
||||||
|
}
|
||||||
|
var images []string
|
||||||
|
for k := range imagesMap {
|
||||||
|
images = append(images, k)
|
||||||
|
}
|
||||||
|
return images, nil
|
||||||
|
}
|
||||||
|
|
||||||
func coverEnvJsonToStr(envJson string) (string, error) {
|
func coverEnvJsonToStr(envJson string) (string, error) {
|
||||||
envMap := make(map[string]interface{})
|
envMap := make(map[string]interface{})
|
||||||
_ = json.Unmarshal([]byte(envJson), &envMap)
|
_ = json.Unmarshal([]byte(envJson), &envMap)
|
||||||
|
@@ -16,6 +16,7 @@ type RemoteDBService struct{}
|
|||||||
type IRemoteDBService interface {
|
type IRemoteDBService interface {
|
||||||
Get(name string) (dto.RemoteDBInfo, error)
|
Get(name string) (dto.RemoteDBInfo, error)
|
||||||
SearchWithPage(search dto.RemoteDBSearch) (int64, interface{}, error)
|
SearchWithPage(search dto.RemoteDBSearch) (int64, interface{}, error)
|
||||||
|
CheckeRemoteDB(req dto.RemoteDBCreate) bool
|
||||||
Create(req dto.RemoteDBCreate) error
|
Create(req dto.RemoteDBCreate) error
|
||||||
Update(req dto.RemoteDBUpdate) error
|
Update(req dto.RemoteDBUpdate) error
|
||||||
Delete(id uint) error
|
Delete(id uint) error
|
||||||
@@ -68,6 +69,20 @@ func (u *RemoteDBService) List(dbType string) ([]dto.RemoteDBOption, error) {
|
|||||||
return datas, err
|
return datas, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *RemoteDBService) CheckeRemoteDB(req dto.RemoteDBCreate) bool {
|
||||||
|
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
||||||
|
From: "remote",
|
||||||
|
Address: req.Address,
|
||||||
|
Port: req.Port,
|
||||||
|
Username: req.Username,
|
||||||
|
Password: req.Password,
|
||||||
|
Timeout: 6,
|
||||||
|
}); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (u *RemoteDBService) Create(req dto.RemoteDBCreate) error {
|
func (u *RemoteDBService) Create(req dto.RemoteDBCreate) error {
|
||||||
db, _ := remoteDBRepo.Get(commonRepo.WithByName(req.Name))
|
db, _ := remoteDBRepo.Get(commonRepo.WithByName(req.Name))
|
||||||
if db.ID != 0 {
|
if db.ID != 0 {
|
||||||
@@ -79,7 +94,7 @@ func (u *RemoteDBService) Create(req dto.RemoteDBCreate) error {
|
|||||||
Port: req.Port,
|
Port: req.Port,
|
||||||
Username: req.Username,
|
Username: req.Username,
|
||||||
Password: req.Password,
|
Password: req.Password,
|
||||||
Timeout: 300,
|
Timeout: 6,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -61,3 +61,18 @@ func WithMap(Key string, maps map[string]interface{}, err error) BusinessError {
|
|||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithNameAndErr(Key string, name string, err error) BusinessError {
|
||||||
|
paramMap := map[string]interface{}{}
|
||||||
|
if name != "" {
|
||||||
|
paramMap["name"] = name
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
paramMap["err"] = err.Error()
|
||||||
|
}
|
||||||
|
return BusinessError{
|
||||||
|
Msg: Key,
|
||||||
|
Map: paramMap,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -45,6 +45,7 @@ ErrImagePullTimeOut: 'Image pull timeout'
|
|||||||
ErrContainerNotFound: '{{ .name }} container does not exist'
|
ErrContainerNotFound: '{{ .name }} container does not exist'
|
||||||
ErrContainerMsg: '{{ .name }} container is abnormal, please check the log on the container page for details'
|
ErrContainerMsg: '{{ .name }} container is abnormal, please check the log on the container page for details'
|
||||||
ErrAppBackup: '{{ .name }} application backup failed err {{.err}}'
|
ErrAppBackup: '{{ .name }} application backup failed err {{.err}}'
|
||||||
|
ErrImagePull: '{{ .name }} image pull failed err {{.err}}'
|
||||||
|
|
||||||
#file
|
#file
|
||||||
ErrFileCanNotRead: "File can not read"
|
ErrFileCanNotRead: "File can not read"
|
||||||
|
@@ -45,6 +45,7 @@ ErrImagePullTimeOut: "鏡像拉取超時"
|
|||||||
ErrContainerNotFound: '{{ .name }} 容器不存在'
|
ErrContainerNotFound: '{{ .name }} 容器不存在'
|
||||||
ErrContainerMsg: '{{ .name }} 容器異常,具體請在容器頁面查看日誌'
|
ErrContainerMsg: '{{ .name }} 容器異常,具體請在容器頁面查看日誌'
|
||||||
ErrAppBackup: '{{ .name }} 應用備份失敗 err {{.err}}'
|
ErrAppBackup: '{{ .name }} 應用備份失敗 err {{.err}}'
|
||||||
|
ErrImagePull: '{{ .name }} 鏡像拉取失敗 err {{.err}}'
|
||||||
|
|
||||||
#file
|
#file
|
||||||
ErrFileCanNotRead: "此文件不支持預覽"
|
ErrFileCanNotRead: "此文件不支持預覽"
|
||||||
|
@@ -45,6 +45,7 @@ ErrImagePullTimeOut: '镜像拉取超时'
|
|||||||
ErrContainerNotFound: '{{ .name }} 容器不存在'
|
ErrContainerNotFound: '{{ .name }} 容器不存在'
|
||||||
ErrContainerMsg: '{{ .name }} 容器异常,具体请在容器页面查看日志'
|
ErrContainerMsg: '{{ .name }} 容器异常,具体请在容器页面查看日志'
|
||||||
ErrAppBackup: '{{ .name }} 应用备份失败 err {{.err}}'
|
ErrAppBackup: '{{ .name }} 应用备份失败 err {{.err}}'
|
||||||
|
ErrImagePull: '镜像拉取失败 {{.err}}'
|
||||||
|
|
||||||
#file
|
#file
|
||||||
ErrFileCanNotRead: "此文件不支持预览"
|
ErrFileCanNotRead: "此文件不支持预览"
|
||||||
|
@@ -43,6 +43,7 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) {
|
|||||||
cmdRouter.POST("/redis/conffile/update", baseApi.UpdateRedisConfByFile)
|
cmdRouter.POST("/redis/conffile/update", baseApi.UpdateRedisConfByFile)
|
||||||
cmdRouter.POST("/redis/persistence/update", baseApi.UpdateRedisPersistenceConf)
|
cmdRouter.POST("/redis/persistence/update", baseApi.UpdateRedisPersistenceConf)
|
||||||
|
|
||||||
|
cmdRouter.POST("/remote/check", baseApi.CheckeRemoteDB)
|
||||||
cmdRouter.POST("/remote", baseApi.CreateRemoteDB)
|
cmdRouter.POST("/remote", baseApi.CreateRemoteDB)
|
||||||
cmdRouter.GET("/remote/:name", baseApi.GetRemoteDB)
|
cmdRouter.GET("/remote/:name", baseApi.GetRemoteDB)
|
||||||
cmdRouter.GET("/remote/list/:type", baseApi.ListRemoteDB)
|
cmdRouter.GET("/remote/list/:type", baseApi.ListRemoteDB)
|
||||||
|
@@ -4,11 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"github.com/compose-spec/compose-go/loader"
|
"github.com/compose-spec/compose-go/loader"
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
"github.com/docker/cli/cli/command"
|
|
||||||
"github.com/docker/cli/cli/flags"
|
|
||||||
"github.com/docker/compose/v2/pkg/api"
|
"github.com/docker/compose/v2/pkg/api"
|
||||||
"github.com/docker/compose/v2/pkg/compose"
|
|
||||||
"github.com/docker/docker/client"
|
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -21,24 +17,6 @@ type ComposeService struct {
|
|||||||
project *types.Project
|
project *types.Project
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewComposeService(ops ...command.DockerCliOption) (*ComposeService, error) {
|
|
||||||
apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ops = append(ops, command.WithAPIClient(apiClient), command.WithDefaultContextStoreConfig())
|
|
||||||
cli, err := command.NewDockerCli(ops...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
cliOp := flags.NewClientOptions()
|
|
||||||
if err := cli.Initialize(cliOp); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
service := compose.NewComposeService(cli)
|
|
||||||
return &ComposeService{service, nil}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ComposeService) SetProject(project *types.Project) {
|
func (s *ComposeService) SetProject(project *types.Project) {
|
||||||
s.project = project
|
s.project = project
|
||||||
for i, s := range project.Services {
|
for i, s := range project.Services {
|
||||||
|
@@ -72,6 +72,22 @@ func (c Client) DeleteImage(imageID string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c Client) PullImage(imageName string, force bool) error {
|
||||||
|
if !force {
|
||||||
|
exist, err := c.CheckImageExist(imageName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if exist {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := c.cli.ImagePull(context.Background(), imageName, types.ImagePullOptions{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c Client) GetImageIDByName(imageName string) (string, error) {
|
func (c Client) GetImageIDByName(imageName string) (string, error) {
|
||||||
filter := filters.NewArgs()
|
filter := filters.NewArgs()
|
||||||
filter.Add("reference", imageName)
|
filter.Add("reference", imageName)
|
||||||
@@ -87,6 +103,18 @@ func (c Client) GetImageIDByName(imageName string) (string, error) {
|
|||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c Client) CheckImageExist(imageName string) (bool, error) {
|
||||||
|
filter := filters.NewArgs()
|
||||||
|
filter.Add("reference", imageName)
|
||||||
|
list, err := c.cli.ImageList(context.Background(), types.ImageListOptions{
|
||||||
|
Filters: filter,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return len(list) > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c Client) NetworkExist(name string) bool {
|
func (c Client) NetworkExist(name string) bool {
|
||||||
var options types.NetworkListOptions
|
var options types.NetworkListOptions
|
||||||
options.Filters = filters.NewArgs(filters.Arg("name", name))
|
options.Filters = filters.NewArgs(filters.Arg("name", name))
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
package mysql
|
package mysql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
@@ -38,9 +40,16 @@ func NewMysqlClient(conn client.DBInfo) (MysqlClient, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := db.Ping(); err != nil {
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(conn.Timeout)*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
if err := db.PingContext(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
return nil, buserr.New(constant.ErrExecTimeOut)
|
||||||
|
}
|
||||||
|
|
||||||
return client.NewRemote(client.Remote{
|
return client.NewRemote(client.Remote{
|
||||||
Client: db,
|
Client: db,
|
||||||
From: conn.From,
|
From: conn.From,
|
||||||
|
@@ -4410,6 +4410,49 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/databases/remote/check": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "检测远程数据库连接性",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Database"
|
||||||
|
],
|
||||||
|
"summary": "Check remote database",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.RemoteDBCreate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFuntions": [],
|
||||||
|
"bodyKeys": [
|
||||||
|
"name",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"formatEN": "check if remote database [name][type] is connectable",
|
||||||
|
"formatZH": "检测远程数据库 [name][type] 连接性",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/databases/remote/del": {
|
"/databases/remote/del": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -11621,7 +11664,6 @@ const docTemplate = `{
|
|||||||
"dto.ChangeDBInfo": {
|
"dto.ChangeDBInfo": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"from",
|
|
||||||
"value"
|
"value"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -12923,13 +12965,15 @@ const docTemplate = `{
|
|||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"maxLength": 256
|
||||||
},
|
},
|
||||||
"protocol": {
|
"protocol": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"username": {
|
"username": {
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"maxLength": 256
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -13776,7 +13820,8 @@ const docTemplate = `{
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"maxLength": 256
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@@ -13817,7 +13862,8 @@ const docTemplate = `{
|
|||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"maxLength": 256
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
@@ -4403,6 +4403,49 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/databases/remote/check": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "检测远程数据库连接性",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Database"
|
||||||
|
],
|
||||||
|
"summary": "Check remote database",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.RemoteDBCreate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFuntions": [],
|
||||||
|
"bodyKeys": [
|
||||||
|
"name",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"formatEN": "check if remote database [name][type] is connectable",
|
||||||
|
"formatZH": "检测远程数据库 [name][type] 连接性",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/databases/remote/del": {
|
"/databases/remote/del": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -11614,7 +11657,6 @@
|
|||||||
"dto.ChangeDBInfo": {
|
"dto.ChangeDBInfo": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"from",
|
|
||||||
"value"
|
"value"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -12916,13 +12958,15 @@
|
|||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"maxLength": 256
|
||||||
},
|
},
|
||||||
"protocol": {
|
"protocol": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"username": {
|
"username": {
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"maxLength": 256
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -13769,7 +13813,8 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"maxLength": 256
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@@ -13810,7 +13855,8 @@
|
|||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"maxLength": 256
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
@@ -127,7 +127,6 @@ definitions:
|
|||||||
value:
|
value:
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- from
|
|
||||||
- value
|
- value
|
||||||
type: object
|
type: object
|
||||||
dto.ChangeHostGroup:
|
dto.ChangeHostGroup:
|
||||||
@@ -1000,10 +999,12 @@ definitions:
|
|||||||
id:
|
id:
|
||||||
type: integer
|
type: integer
|
||||||
password:
|
password:
|
||||||
|
maxLength: 256
|
||||||
type: string
|
type: string
|
||||||
protocol:
|
protocol:
|
||||||
type: string
|
type: string
|
||||||
username:
|
username:
|
||||||
|
maxLength: 256
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
dto.ImageSave:
|
dto.ImageSave:
|
||||||
@@ -1565,6 +1566,7 @@ definitions:
|
|||||||
- remote
|
- remote
|
||||||
type: string
|
type: string
|
||||||
name:
|
name:
|
||||||
|
maxLength: 256
|
||||||
type: string
|
type: string
|
||||||
password:
|
password:
|
||||||
type: string
|
type: string
|
||||||
@@ -1599,6 +1601,7 @@ definitions:
|
|||||||
id:
|
id:
|
||||||
type: integer
|
type: integer
|
||||||
name:
|
name:
|
||||||
|
maxLength: 256
|
||||||
type: string
|
type: string
|
||||||
password:
|
password:
|
||||||
type: string
|
type: string
|
||||||
@@ -6648,6 +6651,34 @@ paths:
|
|||||||
summary: Get remote databases
|
summary: Get remote databases
|
||||||
tags:
|
tags:
|
||||||
- Database
|
- Database
|
||||||
|
/databases/remote/check:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 检测远程数据库连接性
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.RemoteDBCreate'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Check remote database
|
||||||
|
tags:
|
||||||
|
- Database
|
||||||
|
x-panel-log:
|
||||||
|
BeforeFuntions: []
|
||||||
|
bodyKeys:
|
||||||
|
- name
|
||||||
|
- type
|
||||||
|
formatEN: check if remote database [name][type] is connectable
|
||||||
|
formatZH: 检测远程数据库 [name][type] 连接性
|
||||||
|
paramKeys: []
|
||||||
/databases/remote/del:
|
/databases/remote/del:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
@@ -100,11 +100,14 @@ export const searchRemoteDBs = (params: Database.SearchRemoteDBPage) => {
|
|||||||
export const listRemoteDBs = (type: string) => {
|
export const listRemoteDBs = (type: string) => {
|
||||||
return http.get<Array<Database.RemoteDBOption>>(`/databases/remote/list/${type}`);
|
return http.get<Array<Database.RemoteDBOption>>(`/databases/remote/list/${type}`);
|
||||||
};
|
};
|
||||||
|
export const checkRemoteDB = (params: Database.RemoteDBCreate) => {
|
||||||
|
return http.post<boolean>(`/databases/remote/check`, params, 40000);
|
||||||
|
};
|
||||||
export const addRemoteDB = (params: Database.RemoteDBCreate) => {
|
export const addRemoteDB = (params: Database.RemoteDBCreate) => {
|
||||||
return http.post(`/databases/remote`, params);
|
return http.post(`/databases/remote`, params, 40000);
|
||||||
};
|
};
|
||||||
export const editRemoteDB = (params: Database.RemoteDBUpdate) => {
|
export const editRemoteDB = (params: Database.RemoteDBUpdate) => {
|
||||||
return http.post(`/databases/remote/update`, params);
|
return http.post(`/databases/remote/update`, params, 40000);
|
||||||
};
|
};
|
||||||
export const deleteRemoteDB = (id: number) => {
|
export const deleteRemoteDB = (id: number) => {
|
||||||
return http.post(`/databases/remote/del`, { id: id });
|
return http.post(`/databases/remote/del`, { id: id });
|
||||||
|
@@ -3,23 +3,6 @@
|
|||||||
<div class="complex-table__header" v-if="$slots.header || header">
|
<div class="complex-table__header" v-if="$slots.header || header">
|
||||||
<slot name="header">{{ header }}</slot>
|
<slot name="header">{{ header }}</slot>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="$slots.toolbar && !searchConfig" style="margin-bottom: 10px">
|
|
||||||
<slot name="toolbar"></slot>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<template v-if="searchConfig">
|
|
||||||
<fu-filter-bar v-bind="searchConfig" @exec="search">
|
|
||||||
<template #tl>
|
|
||||||
<slot name="toolbar"></slot>
|
|
||||||
</template>
|
|
||||||
<template #default>
|
|
||||||
<slot name="complex"></slot>
|
|
||||||
</template>
|
|
||||||
<template #buttons>
|
|
||||||
<slot name="buttons"></slot>
|
|
||||||
</template>
|
|
||||||
</fu-filter-bar>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<div class="complex-table__body">
|
<div class="complex-table__body">
|
||||||
<fu-table v-bind="$attrs" ref="tableRef" @selection-change="handleSelectionChange">
|
<fu-table v-bind="$attrs" ref="tableRef" @selection-change="handleSelectionChange">
|
||||||
@@ -30,13 +13,14 @@
|
|||||||
</fu-table>
|
</fu-table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="complex-table__pagination" v-if="$slots.pagination || paginationConfig">
|
<div class="complex-table__pagination" v-if="props.paginationConfig">
|
||||||
<slot name="pagination">
|
<slot name="pagination">
|
||||||
<fu-table-pagination
|
<fu-table-pagination
|
||||||
v-model:current-page="paginationConfig.currentPage"
|
v-model:current-page="paginationConfig.currentPage"
|
||||||
v-model:page-size="paginationConfig.pageSize"
|
v-model:page-size="paginationConfig.pageSize"
|
||||||
v-bind="paginationConfig"
|
:total="paginationConfig.total"
|
||||||
@change="search"
|
@size-change="sizeChange"
|
||||||
|
@current-change="currentChange"
|
||||||
:small="mobile"
|
:small="mobile"
|
||||||
:layout="mobile ? 'total, prev, pager, next' : 'total, sizes, prev, pager, next, jumper'"
|
:layout="mobile ? 'total, prev, pager, next' : 'total, sizes, prev, pager, next, jumper'"
|
||||||
/>
|
/>
|
||||||
@@ -49,15 +33,15 @@ import { ref, computed } from 'vue';
|
|||||||
import { GlobalStore } from '@/store';
|
import { GlobalStore } from '@/store';
|
||||||
|
|
||||||
defineOptions({ name: 'ComplexTable' });
|
defineOptions({ name: 'ComplexTable' });
|
||||||
defineProps({
|
const props = defineProps({
|
||||||
header: String,
|
header: String,
|
||||||
searchConfig: Object,
|
|
||||||
paginationConfig: {
|
paginationConfig: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
required: false,
|
||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const emit = defineEmits(['search', 'update:selects']);
|
const emit = defineEmits(['search', 'update:selects', 'update:paginationConfig']);
|
||||||
|
|
||||||
const globalStore = GlobalStore();
|
const globalStore = GlobalStore();
|
||||||
|
|
||||||
@@ -65,13 +49,15 @@ const mobile = computed(() => {
|
|||||||
return globalStore.isMobile();
|
return globalStore.isMobile();
|
||||||
});
|
});
|
||||||
|
|
||||||
const condition = ref({});
|
|
||||||
const tableRef = ref();
|
const tableRef = ref();
|
||||||
function search(conditions: any, e: any) {
|
|
||||||
if (conditions) {
|
function currentChange() {
|
||||||
condition.value = conditions;
|
emit('search');
|
||||||
}
|
}
|
||||||
emit('search', condition.value, e);
|
|
||||||
|
function sizeChange() {
|
||||||
|
props.paginationConfig.currentPage = 1;
|
||||||
|
emit('search');
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSelectionChange(row: any) {
|
function handleSelectionChange(row: any) {
|
||||||
|
@@ -13,20 +13,7 @@ const terminalElement = ref<HTMLDivElement | null>(null);
|
|||||||
const fitAddon = new FitAddon();
|
const fitAddon = new FitAddon();
|
||||||
const termReady = ref(false);
|
const termReady = ref(false);
|
||||||
const webSocketReady = ref(false);
|
const webSocketReady = ref(false);
|
||||||
const term = ref(
|
const term = ref();
|
||||||
new Terminal({
|
|
||||||
lineHeight: 1.2,
|
|
||||||
fontSize: 12,
|
|
||||||
fontFamily: "Monaco, Menlo, Consolas, 'Courier New', monospace",
|
|
||||||
theme: {
|
|
||||||
background: '#000000',
|
|
||||||
},
|
|
||||||
cursorBlink: true,
|
|
||||||
cursorStyle: 'underline',
|
|
||||||
scrollback: 100,
|
|
||||||
tabStopWidth: 4,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
const terminalSocket = ref<WebSocket>();
|
const terminalSocket = ref<WebSocket>();
|
||||||
const heartbeatTimer = ref<number>();
|
const heartbeatTimer = ref<number>();
|
||||||
const latency = ref(0);
|
const latency = ref(0);
|
||||||
@@ -56,6 +43,21 @@ const acceptParams = (props: WsProps) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const newTerm = () => {
|
||||||
|
term.value = new Terminal({
|
||||||
|
lineHeight: 1.2,
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: "Monaco, Menlo, Consolas, 'Courier New', monospace",
|
||||||
|
theme: {
|
||||||
|
background: '#000000',
|
||||||
|
},
|
||||||
|
cursorBlink: true,
|
||||||
|
cursorStyle: 'underline',
|
||||||
|
scrollback: 100,
|
||||||
|
tabStopWidth: 4,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const init = (endpoint: string, args: string) => {
|
const init = (endpoint: string, args: string) => {
|
||||||
if (initTerminal(true)) {
|
if (initTerminal(true)) {
|
||||||
initWebSocket(endpoint, args);
|
initWebSocket(endpoint, args);
|
||||||
@@ -78,11 +80,13 @@ function onClose(isKeepShow: boolean = false) {
|
|||||||
term.value.dispose();
|
term.value.dispose();
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
terminalElement.value.innerHTML = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// terminal 相关代码 start
|
// terminal 相关代码 start
|
||||||
|
|
||||||
const initTerminal = (online: boolean = false): boolean => {
|
const initTerminal = (online: boolean = false): boolean => {
|
||||||
|
newTerm();
|
||||||
if (terminalElement.value) {
|
if (terminalElement.value) {
|
||||||
term.value.open(terminalElement.value);
|
term.value.open(terminalElement.value);
|
||||||
term.value.loadAddon(fitAddon);
|
term.value.loadAddon(fitAddon);
|
||||||
|
@@ -479,7 +479,10 @@ const message = {
|
|||||||
rename: 'Rename',
|
rename: 'Rename',
|
||||||
remove: 'Remove',
|
remove: 'Remove',
|
||||||
containerPrune: 'Container prune',
|
containerPrune: 'Container prune',
|
||||||
containerPruneHelper: 'Remove all stopped containers. Do you want to continue?',
|
containerPruneHelper1: 'Cleaning containers will delete all containers that are in a stopped state.',
|
||||||
|
containerPruneHelper2:
|
||||||
|
'If the containers are from the app store, after performing the cleanup, you need to go to the [Installed] list in the [App Store] and click the [Rebuild] button to reinstall them.',
|
||||||
|
containerPruneHelper3: 'This operation cannot be rolled back. Do you want to continue?',
|
||||||
imagePrune: 'Image prune',
|
imagePrune: 'Image prune',
|
||||||
imagePruneSome: 'Clean unlabeled',
|
imagePruneSome: 'Clean unlabeled',
|
||||||
imagePruneSomeHelper: 'Remove all unused and unlabeled container images。',
|
imagePruneSomeHelper: 'Remove all unused and unlabeled container images。',
|
||||||
@@ -533,7 +536,7 @@ const message = {
|
|||||||
exposePort: 'Expose port',
|
exposePort: 'Expose port',
|
||||||
exposeAll: 'Expose all',
|
exposeAll: 'Expose all',
|
||||||
cmd: 'Command',
|
cmd: 'Command',
|
||||||
cmdHelper: 'Example: echo "hello"',
|
cmdHelper: "Separate multiple commands with ' ' as delimiter, such as 'nginx' '-g' 'daemon off;'",
|
||||||
autoRemove: 'Auto remove',
|
autoRemove: 'Auto remove',
|
||||||
cpuQuota: 'NacosCPU',
|
cpuQuota: 'NacosCPU',
|
||||||
memoryLimit: 'Memory',
|
memoryLimit: 'Memory',
|
||||||
@@ -1684,6 +1687,14 @@ const message = {
|
|||||||
restartHelper:
|
restartHelper:
|
||||||
'Initialization will restart the service, causing all the original daemon processes to close',
|
'Initialization will restart the service, causing all the original daemon processes to close',
|
||||||
msg: 'Message',
|
msg: 'Message',
|
||||||
|
RUNNING: 'Running',
|
||||||
|
STOPPED: 'Stopped',
|
||||||
|
STOPPING: 'Stopping',
|
||||||
|
STARTING: 'Starting',
|
||||||
|
FATAL: 'Failed to start',
|
||||||
|
BACKOFF: 'Start exception',
|
||||||
|
statusCode: 'Status code',
|
||||||
|
manage: 'Management',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@@ -469,7 +469,10 @@ const message = {
|
|||||||
rename: '重命名',
|
rename: '重命名',
|
||||||
remove: '刪除',
|
remove: '刪除',
|
||||||
containerPrune: '清理容器',
|
containerPrune: '清理容器',
|
||||||
containerPruneHelper: '清理容器 將刪除所有處於停止狀態的容器,該操作無法回滾,是否繼續?',
|
containerPruneHelper1: '清理容器 將刪除所有處於停止狀態的容器。',
|
||||||
|
containerPruneHelper2:
|
||||||
|
'若容器來自於應用商店,在執行清理操作後,您需要前往 [應用商店] 的 [已安裝] 列表,點擊 [重建] 按鈕進行重新安裝。',
|
||||||
|
containerPruneHelper3: '該操作無法回滾,是否繼續?',
|
||||||
imagePrune: '清理鏡像',
|
imagePrune: '清理鏡像',
|
||||||
imagePruneSome: '未標簽鏡像',
|
imagePruneSome: '未標簽鏡像',
|
||||||
imagePruneSomeHelper: '清理標簽為 none 且未被任何容器使用的鏡像。',
|
imagePruneSomeHelper: '清理標簽為 none 且未被任何容器使用的鏡像。',
|
||||||
@@ -519,7 +522,7 @@ const message = {
|
|||||||
exposePort: '暴露端口',
|
exposePort: '暴露端口',
|
||||||
exposeAll: '暴露所有',
|
exposeAll: '暴露所有',
|
||||||
cmd: '啟動命令',
|
cmd: '啟動命令',
|
||||||
cmdHelper: '例:echo "hello"',
|
cmdHelper: "多個命令間請用 ' ' 分隔開,如 'nginx' '-g' 'daemon off;'",
|
||||||
autoRemove: '容器退出後自動刪除容器',
|
autoRemove: '容器退出後自動刪除容器',
|
||||||
cpuQuota: 'CPU 限製',
|
cpuQuota: 'CPU 限製',
|
||||||
memoryLimit: '內存限製',
|
memoryLimit: '內存限製',
|
||||||
@@ -1596,6 +1599,14 @@ const message = {
|
|||||||
serviceNameHelper: 'systemctl 管理的 Supervisor 服務名稱,一般為 supervisor 或 supervisord',
|
serviceNameHelper: 'systemctl 管理的 Supervisor 服務名稱,一般為 supervisor 或 supervisord',
|
||||||
restartHelper: '初始化會重啟服務,導致原有的守護進程全部關閉',
|
restartHelper: '初始化會重啟服務,導致原有的守護進程全部關閉',
|
||||||
msg: '信息',
|
msg: '信息',
|
||||||
|
RUNNING: '運行中',
|
||||||
|
STOPPED: '已停止',
|
||||||
|
STOPPING: '停止中',
|
||||||
|
STARTING: '啟動中',
|
||||||
|
FATAL: '啟動失敗',
|
||||||
|
BACKOFF: '啟動異常',
|
||||||
|
statusCode: '狀態碼',
|
||||||
|
manage: '管理',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@@ -469,7 +469,10 @@ const message = {
|
|||||||
rename: '重命名',
|
rename: '重命名',
|
||||||
remove: '删除',
|
remove: '删除',
|
||||||
containerPrune: '清理容器',
|
containerPrune: '清理容器',
|
||||||
containerPruneHelper: '清理容器 将删除所有处于停止状态的容器,该操作无法回滚,是否继续?',
|
containerPruneHelper1: '清理容器 将删除所有处于停止状态的容器。',
|
||||||
|
containerPruneHelper2:
|
||||||
|
'若容器来自于应用商店,在执行清理操作后,您需要前往 [应用商店] 的 [已安装] 列表,点击 [重建] 按钮进行重新安装。',
|
||||||
|
containerPruneHelper3: '该操作无法回滚,是否继续?',
|
||||||
imagePrune: '清理镜像',
|
imagePrune: '清理镜像',
|
||||||
imagePruneSome: '未标签镜像',
|
imagePruneSome: '未标签镜像',
|
||||||
imagePruneSomeHelper: '清理标签为 none 且未被任何容器使用的镜像。',
|
imagePruneSomeHelper: '清理标签为 none 且未被任何容器使用的镜像。',
|
||||||
@@ -519,7 +522,7 @@ const message = {
|
|||||||
exposePort: '暴露端口',
|
exposePort: '暴露端口',
|
||||||
exposeAll: '暴露所有',
|
exposeAll: '暴露所有',
|
||||||
cmd: '启动命令',
|
cmd: '启动命令',
|
||||||
cmdHelper: '例:echo "hello"',
|
cmdHelper: "多个命令间请用 ' ' 分隔开,如 'nginx' '-g' 'daemon off;'",
|
||||||
autoRemove: '容器退出后自动删除容器',
|
autoRemove: '容器退出后自动删除容器',
|
||||||
cpuQuota: 'CPU 限制',
|
cpuQuota: 'CPU 限制',
|
||||||
memoryLimit: '内存限制',
|
memoryLimit: '内存限制',
|
||||||
@@ -1598,6 +1601,14 @@ const message = {
|
|||||||
serviceNameHelper: 'systemctl 管理的 Supervisor 服务名称,一般为 supervisor、supervisord',
|
serviceNameHelper: 'systemctl 管理的 Supervisor 服务名称,一般为 supervisor、supervisord',
|
||||||
restartHelper: '初始化会重启服务,导致原有的守护进程全部关闭',
|
restartHelper: '初始化会重启服务,导致原有的守护进程全部关闭',
|
||||||
msg: '信息',
|
msg: '信息',
|
||||||
|
RUNNING: '运行中',
|
||||||
|
STOPPED: '已停止',
|
||||||
|
STOPPING: '停止中',
|
||||||
|
STARTING: '启动中',
|
||||||
|
FATAL: '启动失败',
|
||||||
|
BACKOFF: '启动异常',
|
||||||
|
statusCode: '状态码',
|
||||||
|
manage: '管理',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@@ -157,6 +157,7 @@
|
|||||||
</LayoutContent>
|
</LayoutContent>
|
||||||
|
|
||||||
<CodemirrorDialog ref="mydetail" />
|
<CodemirrorDialog ref="mydetail" />
|
||||||
|
<PruneDialog @search="search" ref="dialogPruneRef" />
|
||||||
|
|
||||||
<ReNameDialog @search="search" ref="dialogReNameRef" />
|
<ReNameDialog @search="search" ref="dialogReNameRef" />
|
||||||
<ContainerLogDialog ref="dialogContainerLogRef" />
|
<ContainerLogDialog ref="dialogContainerLogRef" />
|
||||||
@@ -172,6 +173,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import Tooltip from '@/components/tooltip/index.vue';
|
import Tooltip from '@/components/tooltip/index.vue';
|
||||||
import TableSetting from '@/components/table-setting/index.vue';
|
import TableSetting from '@/components/table-setting/index.vue';
|
||||||
|
import PruneDialog from '@/views/container/container/prune/index.vue';
|
||||||
import ReNameDialog from '@/views/container/container/rename/index.vue';
|
import ReNameDialog from '@/views/container/container/rename/index.vue';
|
||||||
import OperateDialog from '@/views/container/container/operate/index.vue';
|
import OperateDialog from '@/views/container/container/operate/index.vue';
|
||||||
import UpgraeDialog from '@/views/container/container/upgrade/index.vue';
|
import UpgraeDialog from '@/views/container/container/upgrade/index.vue';
|
||||||
@@ -185,7 +187,6 @@ import { reactive, onMounted, ref, computed } from 'vue';
|
|||||||
import {
|
import {
|
||||||
containerListStats,
|
containerListStats,
|
||||||
containerOperator,
|
containerOperator,
|
||||||
containerPrune,
|
|
||||||
inspect,
|
inspect,
|
||||||
loadContainerInfo,
|
loadContainerInfo,
|
||||||
loadDockerStatus,
|
loadDockerStatus,
|
||||||
@@ -196,7 +197,6 @@ import { ElMessageBox } from 'element-plus';
|
|||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import router from '@/routers';
|
import router from '@/routers';
|
||||||
import { MsgSuccess, MsgWarning } from '@/utils/message';
|
import { MsgSuccess, MsgWarning } from '@/utils/message';
|
||||||
import { computeSize } from '@/utils/util';
|
|
||||||
import { GlobalStore } from '@/store';
|
import { GlobalStore } from '@/store';
|
||||||
const globalStore = GlobalStore();
|
const globalStore = GlobalStore();
|
||||||
|
|
||||||
@@ -264,6 +264,7 @@ const mydetail = ref();
|
|||||||
|
|
||||||
const dialogContainerLogRef = ref();
|
const dialogContainerLogRef = ref();
|
||||||
const dialogReNameRef = ref();
|
const dialogReNameRef = ref();
|
||||||
|
const dialogPruneRef = ref();
|
||||||
|
|
||||||
const search = async (column?: any) => {
|
const search = async (column?: any) => {
|
||||||
let filterItem = props.filters ? props.filters : '';
|
let filterItem = props.filters ? props.filters : '';
|
||||||
@@ -360,31 +361,7 @@ const onInspect = async (id: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onClean = () => {
|
const onClean = () => {
|
||||||
ElMessageBox.confirm(i18n.global.t('container.containerPruneHelper'), i18n.global.t('container.containerPrune'), {
|
dialogPruneRef.value!.acceptParams();
|
||||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
|
||||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
|
||||||
type: 'info',
|
|
||||||
}).then(async () => {
|
|
||||||
loading.value = true;
|
|
||||||
let params = {
|
|
||||||
pruneType: 'container',
|
|
||||||
withTagAll: false,
|
|
||||||
};
|
|
||||||
await containerPrune(params)
|
|
||||||
.then((res) => {
|
|
||||||
loading.value = false;
|
|
||||||
MsgSuccess(
|
|
||||||
i18n.global.t('container.cleanSuccessWithSpace', [
|
|
||||||
res.data.deletedNumber,
|
|
||||||
computeSize(res.data.spaceReclaimed),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
search();
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
loading.value = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkStatus = (operation: string, row: Container.ContainerInfo | null) => {
|
const checkStatus = (operation: string, row: Container.ContainerInfo | null) => {
|
||||||
|
@@ -110,7 +110,8 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('container.cmd')" prop="cmdStr">
|
<el-form-item :label="$t('container.cmd')" prop="cmdStr">
|
||||||
<el-input :placeholder="$t('container.cmdHelper')" v-model="dialogData.rowData!.cmdStr" />
|
<el-input v-model="dialogData.rowData!.cmdStr" />
|
||||||
|
<span class="input-help">{{ $t('container.cmdHelper') }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="autoRemove">
|
<el-form-item prop="autoRemove">
|
||||||
<el-checkbox v-model="dialogData.rowData!.autoRemove">
|
<el-checkbox v-model="dialogData.rowData!.autoRemove">
|
||||||
|
71
frontend/src/views/container/container/prune/index.vue
Normal file
71
frontend/src/views/container/container/prune/index.vue
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog v-model="dialogVisiable" :title="$t('container.containerPrune')" :destroy-on-close="true" width="30%">
|
||||||
|
<div>
|
||||||
|
<ul class="help-ul">
|
||||||
|
<li lineClass style="color: red">{{ $t('container.containerPruneHelper1') }}</li>
|
||||||
|
<li class="lineClass">{{ $t('container.containerPruneHelper2') }}</li>
|
||||||
|
<li class="lineClass">{{ $t('container.containerPruneHelper3') }}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button :disabled="loading" @click="dialogVisiable = false">
|
||||||
|
{{ $t('commons.button.cancel') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button :disabled="loading" type="primary" @click="onClean()">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { containerPrune } from '@/api/modules/container';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { computeSize } from '@/utils/util';
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const dialogVisiable = ref<boolean>(false);
|
||||||
|
|
||||||
|
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||||
|
|
||||||
|
const onClean = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
let params = {
|
||||||
|
pruneType: 'container',
|
||||||
|
withTagAll: false,
|
||||||
|
};
|
||||||
|
await containerPrune(params)
|
||||||
|
.then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
MsgSuccess(
|
||||||
|
i18n.global.t('container.cleanSuccessWithSpace', [
|
||||||
|
res.data.deletedNumber,
|
||||||
|
computeSize(res.data.spaceReclaimed),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
dialogVisiable.value = false;
|
||||||
|
emit('search');
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const acceptParams = (): void => {
|
||||||
|
dialogVisiable.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.lineClass {
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -43,7 +43,7 @@
|
|||||||
<el-button v-if="!terminalOpen" @click="initTerm(formRef)">
|
<el-button v-if="!terminalOpen" @click="initTerm(formRef)">
|
||||||
{{ $t('commons.button.conn') }}
|
{{ $t('commons.button.conn') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-else @click="handleClose()">{{ $t('commons.button.disconn') }}</el-button>
|
<el-button v-else @click="onClose()">{{ $t('commons.button.disconn') }}</el-button>
|
||||||
<Terminal style="height: calc(100vh - 302px)" ref="terminalRef"></Terminal>
|
<Terminal style="height: calc(100vh - 302px)" ref="terminalRef"></Terminal>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
@@ -99,10 +99,14 @@ const initTerm = (formEl: FormInstance | undefined) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleClose() {
|
const onClose = () => {
|
||||||
terminalRef.value?.onClose();
|
terminalRef.value?.onClose();
|
||||||
terminalVisiable.value = false;
|
|
||||||
terminalOpen.value = false;
|
terminalOpen.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
onClose();
|
||||||
|
terminalVisiable.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
:back="handleClose"
|
:back="handleClose"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<el-form ref="formRef" label-position="top" :model="dialogData.rowData" :rules="rules">
|
<el-form ref="formRef" v-loading="loading" label-position="top" :model="dialogData.rowData" :rules="rules">
|
||||||
<el-row type="flex" justify="center">
|
<el-row type="flex" justify="center">
|
||||||
<el-col :span="22">
|
<el-col :span="22">
|
||||||
<el-form-item :label="$t('commons.table.name')" prop="name">
|
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
<el-tag v-else>{{ dialogData.rowData!.name }}</el-tag>
|
<el-tag v-else>{{ dialogData.rowData!.name }}</el-tag>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('database.version')" prop="version">
|
<el-form-item :label="$t('database.version')" prop="version">
|
||||||
<el-select v-model="dialogData.rowData!.version">
|
<el-select @change="isOK = false" v-model="dialogData.rowData!.version">
|
||||||
<el-option value="5.6" label="5.6" />
|
<el-option value="5.6" label="5.6" />
|
||||||
<el-option value="5.7" label="5.7" />
|
<el-option value="5.7" label="5.7" />
|
||||||
<el-option value="8.0" label="8.0" />
|
<el-option value="8.0" label="8.0" />
|
||||||
@@ -28,17 +28,23 @@
|
|||||||
<span class="input-help">{{ $t('database.versionHelper') }}</span>
|
<span class="input-help">{{ $t('database.versionHelper') }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('database.address')" prop="address">
|
<el-form-item :label="$t('database.address')" prop="address">
|
||||||
<el-input clearable v-model.trim="dialogData.rowData!.address" />
|
<el-input @change="isOK = false" clearable v-model.trim="dialogData.rowData!.address" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('commons.table.port')" prop="port">
|
<el-form-item :label="$t('commons.table.port')" prop="port">
|
||||||
<el-input clearable v-model.number="dialogData.rowData!.port" />
|
<el-input @change="isOK = false" clearable v-model.number="dialogData.rowData!.port" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('commons.login.username')" prop="username">
|
<el-form-item :label="$t('commons.login.username')" prop="username">
|
||||||
<el-input clearable v-model.trim="dialogData.rowData!.username" />
|
<el-input @change="isOK = false" clearable v-model.trim="dialogData.rowData!.username" />
|
||||||
<span class="input-help">{{ $t('database.userHelper') }}</span>
|
<span class="input-help">{{ $t('database.userHelper') }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('commons.login.password')" prop="password">
|
<el-form-item :label="$t('commons.login.password')" prop="password">
|
||||||
<el-input type="password" clearable show-password v-model.trim="dialogData.rowData!.password" />
|
<el-input
|
||||||
|
@change="isOK = false"
|
||||||
|
type="password"
|
||||||
|
clearable
|
||||||
|
show-password
|
||||||
|
v-model.trim="dialogData.rowData!.password"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('commons.table.description')" prop="description">
|
<el-form-item :label="$t('commons.table.description')" prop="description">
|
||||||
<el-input clearable v-model.trim="dialogData.rowData!.description" />
|
<el-input clearable v-model.trim="dialogData.rowData!.description" />
|
||||||
@@ -49,7 +55,10 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="drawerVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button @click="drawerVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
<el-button type="primary" @click="onSubmit(formRef)">
|
<el-button @click="onSubmit(formRef, 'check')">
|
||||||
|
{{ $t('terminal.testConn') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" :disabled="!isOK" @click="onSubmit(formRef, dialogData.title)">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
@@ -63,9 +72,9 @@ import i18n from '@/lang';
|
|||||||
import { ElForm } from 'element-plus';
|
import { ElForm } from 'element-plus';
|
||||||
import { Database } from '@/api/interface/database';
|
import { Database } from '@/api/interface/database';
|
||||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||||
import { MsgSuccess } from '@/utils/message';
|
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||||
import { Rules } from '@/global/form-rules';
|
import { Rules } from '@/global/form-rules';
|
||||||
import { addRemoteDB, editRemoteDB } from '@/api/modules/database';
|
import { addRemoteDB, checkRemoteDB, editRemoteDB } from '@/api/modules/database';
|
||||||
|
|
||||||
interface DialogProps {
|
interface DialogProps {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -77,6 +86,9 @@ const drawerVisiable = ref(false);
|
|||||||
const dialogData = ref<DialogProps>({
|
const dialogData = ref<DialogProps>({
|
||||||
title: '',
|
title: '',
|
||||||
});
|
});
|
||||||
|
const isOK = ref(false);
|
||||||
|
const loading = ref();
|
||||||
|
|
||||||
const acceptParams = (params: DialogProps): void => {
|
const acceptParams = (params: DialogProps): void => {
|
||||||
dialogData.value = params;
|
dialogData.value = params;
|
||||||
title.value = i18n.global.t('database.' + dialogData.value.title + 'RemoteDB');
|
title.value = i18n.global.t('database.' + dialogData.value.title + 'RemoteDB');
|
||||||
@@ -91,7 +103,7 @@ const handleClose = () => {
|
|||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
name: [Rules.requiredInput],
|
name: [Rules.requiredInput],
|
||||||
version: [Rules.requiredSelect],
|
version: [Rules.requiredSelect],
|
||||||
address: [Rules.ip],
|
address: [Rules.host],
|
||||||
port: [Rules.port],
|
port: [Rules.port],
|
||||||
username: [Rules.requiredInput],
|
username: [Rules.requiredInput],
|
||||||
password: [Rules.requiredInput],
|
password: [Rules.requiredInput],
|
||||||
@@ -100,12 +112,12 @@ const rules = reactive({
|
|||||||
type FormInstance = InstanceType<typeof ElForm>;
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
const onSubmit = async (formEl: FormInstance | undefined) => {
|
const onSubmit = async (formEl: FormInstance | undefined, operation: string) => {
|
||||||
if (!formEl) return;
|
if (!formEl) return;
|
||||||
formEl.validate(async (valid) => {
|
formEl.validate(async (valid) => {
|
||||||
if (!valid) return;
|
if (!valid) return;
|
||||||
if (dialogData.value.title === 'create') {
|
|
||||||
let param = {
|
let param = {
|
||||||
|
id: dialogData.value.rowData.id,
|
||||||
name: dialogData.value.rowData.name,
|
name: dialogData.value.rowData.name,
|
||||||
type: 'mysql',
|
type: 'mysql',
|
||||||
version: dialogData.value.rowData.version,
|
version: dialogData.value.rowData.version,
|
||||||
@@ -116,24 +128,49 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
|||||||
password: dialogData.value.rowData.password,
|
password: dialogData.value.rowData.password,
|
||||||
description: dialogData.value.rowData.description,
|
description: dialogData.value.rowData.description,
|
||||||
};
|
};
|
||||||
await addRemoteDB(param);
|
loading.value = true;
|
||||||
|
|
||||||
|
if (operation === 'check') {
|
||||||
|
await checkRemoteDB(param)
|
||||||
|
.then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
if (res.data) {
|
||||||
|
isOK.value = true;
|
||||||
|
MsgSuccess(i18n.global.t('terminal.connTestOk'));
|
||||||
|
} else {
|
||||||
|
MsgError(i18n.global.t('terminal.connTestFailed'));
|
||||||
}
|
}
|
||||||
if (dialogData.value.title === 'edit') {
|
})
|
||||||
let param = {
|
.catch(() => {
|
||||||
id: dialogData.value.rowData.id,
|
loading.value = false;
|
||||||
version: dialogData.value.rowData.version,
|
MsgError(i18n.global.t('terminal.connTestFailed'));
|
||||||
address: dialogData.value.rowData.address,
|
});
|
||||||
port: dialogData.value.rowData.port,
|
|
||||||
username: dialogData.value.rowData.username,
|
|
||||||
password: dialogData.value.rowData.password,
|
|
||||||
description: dialogData.value.rowData.description,
|
|
||||||
};
|
|
||||||
await editRemoteDB(param);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (operation === 'create') {
|
||||||
|
await addRemoteDB(param)
|
||||||
|
.then(() => {
|
||||||
|
loading.value = true;
|
||||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
emit('search');
|
emit('search');
|
||||||
drawerVisiable.value = false;
|
drawerVisiable.value = false;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (operation === 'edit') {
|
||||||
|
await editRemoteDB(param)
|
||||||
|
.then(() => {
|
||||||
|
loading.value = false;
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
emit('search');
|
||||||
|
drawerVisiable.value = false;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -39,6 +39,39 @@
|
|||||||
prop="numprocs"
|
prop="numprocs"
|
||||||
width="100px"
|
width="100px"
|
||||||
></el-table-column>
|
></el-table-column>
|
||||||
|
<el-table-column :label="$t('tool.supervisor.manage')" width="100px">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div v-if="row.status && row.status.length > 0">
|
||||||
|
<el-button
|
||||||
|
v-if="checkStatus(row.status) === 'RUNNING'"
|
||||||
|
link
|
||||||
|
type="success"
|
||||||
|
:icon="VideoPlay"
|
||||||
|
@click="operate('stop', row.name)"
|
||||||
|
>
|
||||||
|
{{ $t('commons.status.running') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-else-if="checkStatus(row.status) === 'WARNING'"
|
||||||
|
link
|
||||||
|
type="warning"
|
||||||
|
:icon="RefreshRight"
|
||||||
|
@click="operate('restart', row.name)"
|
||||||
|
>
|
||||||
|
{{ $t('commons.status.unhealthy') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-else
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
:icon="VideoPause"
|
||||||
|
@click="operate('start', row.name)"
|
||||||
|
>
|
||||||
|
{{ $t('commons.status.stopped') }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column :label="$t('commons.table.status')" width="100px">
|
<el-table-column :label="$t('commons.table.status')" width="100px">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div v-if="row.status">
|
<div v-if="row.status">
|
||||||
@@ -48,7 +81,7 @@
|
|||||||
{{ $t('website.check') }}
|
{{ $t('website.check') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" link v-else>
|
<el-button type="primary" link v-else>
|
||||||
<span>{{ row.status[0].status }}</span>
|
<span>{{ $t('tool.supervisor.' + row.status[0].status) }}</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<el-table :data="row.status">
|
<el-table :data="row.status">
|
||||||
@@ -60,7 +93,7 @@
|
|||||||
/>
|
/>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
property="status"
|
property="status"
|
||||||
:label="$t('commons.table.status')"
|
:label="$t('tool.supervisor.statusCode')"
|
||||||
width="100px"
|
width="100px"
|
||||||
/>
|
/>
|
||||||
<el-table-column property="PID" label="PID" width="100px" />
|
<el-table-column property="PID" label="PID" width="100px" />
|
||||||
@@ -85,7 +118,7 @@
|
|||||||
:buttons="buttons"
|
:buttons="buttons"
|
||||||
:label="$t('commons.table.operate')"
|
:label="$t('commons.table.operate')"
|
||||||
:fixed="mobile ? false : 'right'"
|
:fixed="mobile ? false : 'right'"
|
||||||
width="350px"
|
width="250px"
|
||||||
fix
|
fix
|
||||||
/>
|
/>
|
||||||
</ComplexTable>
|
</ComplexTable>
|
||||||
@@ -110,6 +143,7 @@ import { GlobalStore } from '@/store';
|
|||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { HostTool } from '@/api/interface/host-tool';
|
import { HostTool } from '@/api/interface/host-tool';
|
||||||
import { MsgSuccess } from '@/utils/message';
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
import { VideoPlay, VideoPause, RefreshRight } from '@element-plus/icons-vue';
|
||||||
const globalStore = GlobalStore();
|
const globalStore = GlobalStore();
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
@@ -170,6 +204,20 @@ const mobile = computed(() => {
|
|||||||
return globalStore.isMobile();
|
return globalStore.isMobile();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const checkStatus = (status: HostTool.ProcessStatus[]): string => {
|
||||||
|
if (!status || status.length === 0) return 'STOPPED';
|
||||||
|
|
||||||
|
const statusCounts = status.reduce((acc, curr) => {
|
||||||
|
acc[curr.status] = (acc[curr.status] || 0) + 1;
|
||||||
|
return acc;
|
||||||
|
}, {} as Record<string, number>);
|
||||||
|
|
||||||
|
if (statusCounts['STARTING']) return 'STARTING';
|
||||||
|
if (statusCounts['RUNNING'] === status.length) return 'RUNNING';
|
||||||
|
if (statusCounts['RUNNING'] > 0) return 'WARNING';
|
||||||
|
return 'STOPPED';
|
||||||
|
};
|
||||||
|
|
||||||
const operate = async (operation: string, name: string) => {
|
const operate = async (operation: string, name: string) => {
|
||||||
try {
|
try {
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm(
|
||||||
@@ -224,42 +272,11 @@ const buttons = [
|
|||||||
getFile(row.name, 'out.log');
|
getFile(row.name, 'out.log');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: i18n.global.t('app.start'),
|
|
||||||
click: function (row: HostTool.SupersivorProcess) {
|
|
||||||
operate('start', row.name);
|
|
||||||
},
|
|
||||||
disabled: (row: any) => {
|
|
||||||
if (row.status == undefined) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return row.status && row.status[0].status == 'RUNNING';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: i18n.global.t('app.stop'),
|
|
||||||
click: function (row: HostTool.SupersivorProcess) {
|
|
||||||
operate('stop', row.name);
|
|
||||||
},
|
|
||||||
disabled: (row: any) => {
|
|
||||||
if (row.status == undefined) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return row.status && row.status[0].status != 'RUNNING';
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.restart'),
|
label: i18n.global.t('commons.button.restart'),
|
||||||
click: function (row: HostTool.SupersivorProcess) {
|
click: function (row: HostTool.SupersivorProcess) {
|
||||||
operate('restart', row.name);
|
operate('restart', row.name);
|
||||||
},
|
},
|
||||||
disabled: (row: any): boolean => {
|
|
||||||
if (row.status == undefined) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return row.status && row.status[0].status != 'RUNNING';
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.delete'),
|
label: i18n.global.t('commons.button.delete'),
|
||||||
|
Reference in New Issue
Block a user