mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 08:00:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86ba53d6ec | ||
|
|
0ca559f1e1 | ||
|
|
1fd31fffd6 | ||
|
|
c62c19a49e | ||
|
|
aa8816f3ec | ||
|
|
920f1112fa | ||
|
|
4c2302dc94 | ||
|
|
efebb26b5e | ||
|
|
22fe2a6d51 | ||
|
|
e17b80cff4 | ||
|
|
1d6f1b0ef3 | ||
|
|
dac0c81d96 | ||
|
|
e555dcb903 |
@@ -1,8 +1,6 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
@@ -30,44 +28,6 @@ func (b *BaseApi) CreateHost(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if req.AuthMode == "password" && len(req.Password) != 0 {
|
||||
password, err := base64.StdEncoding.DecodeString(req.Password)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
passwordItem, err := encrypt.StringEncrypt(string(password))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.Password = passwordItem
|
||||
req.PrivateKey = ""
|
||||
req.PassPhrase = ""
|
||||
}
|
||||
if req.AuthMode == "key" && len(req.PrivateKey) != 0 {
|
||||
privateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
keyItem, err := encrypt.StringEncrypt(string(privateKey))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.Password = keyItem
|
||||
|
||||
if len(req.PassPhrase) != 0 {
|
||||
pass, err := encrypt.StringEncrypt(req.PassPhrase)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.PassPhrase = pass
|
||||
}
|
||||
req.Password = ""
|
||||
}
|
||||
|
||||
host, err := hostService.Create(req)
|
||||
if err != nil {
|
||||
@@ -216,40 +176,30 @@ func (b *BaseApi) UpdateHost(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if req.AuthMode == "password" && len(req.Password) != 0 {
|
||||
password, err := base64.StdEncoding.DecodeString(req.Password)
|
||||
var err error
|
||||
if len(req.Password) != 0 && req.AuthMode == "password" {
|
||||
req.Password, err = hostService.EncryptHost(req.Password)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
passwordItem, err := encrypt.StringEncrypt(string(password))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.Password = passwordItem
|
||||
req.PrivateKey = ""
|
||||
req.PassPhrase = ""
|
||||
}
|
||||
if req.AuthMode == "key" && len(req.PrivateKey) != 0 {
|
||||
privateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)
|
||||
if len(req.PrivateKey) != 0 && req.AuthMode == "key" {
|
||||
req.PrivateKey, err = hostService.EncryptHost(req.PrivateKey)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
keyItem, err := encrypt.StringEncrypt(string(privateKey))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.PrivateKey = keyItem
|
||||
|
||||
if len(req.PassPhrase) != 0 {
|
||||
pass, err := encrypt.StringEncrypt(req.PassPhrase)
|
||||
req.PassPhrase, err = encrypt.StringEncrypt(req.PassPhrase)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.PassPhrase = pass
|
||||
}
|
||||
req.Password = ""
|
||||
}
|
||||
|
||||
upMap := make(map[string]interface{})
|
||||
|
||||
@@ -61,7 +61,12 @@ func NewIAppInstalledService() IAppInstallService {
|
||||
}
|
||||
|
||||
func (a *AppInstallService) Page(req request.AppInstalledSearch) (int64, []response.AppInstalledDTO, error) {
|
||||
var opts []repo.DBOption
|
||||
var (
|
||||
opts []repo.DBOption
|
||||
total int64
|
||||
installs []model.AppInstall
|
||||
err error
|
||||
)
|
||||
|
||||
if req.Name != "" {
|
||||
opts = append(opts, commonRepo.WithLikeName(req.Name))
|
||||
@@ -87,15 +92,25 @@ func (a *AppInstallService) Page(req request.AppInstalledSearch) (int64, []respo
|
||||
opts = append(opts, appInstallRepo.WithAppIdsIn(appIds))
|
||||
}
|
||||
|
||||
total, installs, err := appInstallRepo.Page(req.Page, req.PageSize, opts...)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
if req.Update {
|
||||
installs, err = appInstallRepo.ListBy(opts...)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
} else {
|
||||
total, installs, err = appInstallRepo.Page(req.Page, req.PageSize, opts...)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
installDTOs, err := handleInstalled(installs, req.Update)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if req.Update {
|
||||
total = int64(len(installDTOs))
|
||||
}
|
||||
|
||||
return total, installDTOs, nil
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/docker/docker/api/types"
|
||||
@@ -552,6 +553,9 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, container, since, tail string, follow bool) error {
|
||||
if cmd.CheckIllegal(container, since, tail) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
command := fmt.Sprintf("docker logs %s", container)
|
||||
if tail != "0" {
|
||||
command += " -n " + tail
|
||||
|
||||
@@ -14,8 +14,10 @@ import (
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/docker/docker/api/types"
|
||||
@@ -127,6 +129,9 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
||||
}
|
||||
|
||||
func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) {
|
||||
if cmd.CheckIllegal(req.Path) {
|
||||
return false, buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
composeItem, _ := composeRepo.GetRecord(commonRepo.WithByName(req.Name))
|
||||
if composeItem.ID != 0 {
|
||||
return false, constant.ErrRecordExist
|
||||
@@ -143,6 +148,9 @@ func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) {
|
||||
}
|
||||
|
||||
func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error) {
|
||||
if cmd.CheckIllegal(req.Name, req.Path) {
|
||||
return "", buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
if err := u.loadPath(&req); err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -177,6 +185,9 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error)
|
||||
}
|
||||
|
||||
func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
|
||||
if cmd.CheckIllegal(req.Path, req.Operation) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
if _, err := os.Stat(req.Path); err != nil {
|
||||
return fmt.Errorf("load file with path %s failed, %v", req.Path, err)
|
||||
}
|
||||
@@ -195,6 +206,9 @@ func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
|
||||
}
|
||||
|
||||
func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
|
||||
if cmd.CheckIllegal(req.Name, req.Path) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
if _, err := os.Stat(req.Path); err != nil {
|
||||
return fmt.Errorf("load file with path %s failed, %v", req.Path, err)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
@@ -77,6 +78,10 @@ var formatMap = map[string]string{
|
||||
}
|
||||
|
||||
func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error) {
|
||||
if cmd.CheckIllegal(req.Name, req.Username, req.Password, req.Format, req.Permission) {
|
||||
return nil, buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
|
||||
if req.Username == "root" {
|
||||
return nil, errors.New("Cannot set root as user name")
|
||||
}
|
||||
@@ -184,6 +189,10 @@ func (u *MysqlService) Delete(ctx context.Context, req dto.MysqlDBDelete) error
|
||||
}
|
||||
|
||||
func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error {
|
||||
if cmd.CheckIllegal(info.Value) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
|
||||
var (
|
||||
mysql model.DatabaseMysql
|
||||
err error
|
||||
@@ -253,6 +262,9 @@ func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error {
|
||||
}
|
||||
|
||||
func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error {
|
||||
if cmd.CheckIllegal(info.Value) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
var (
|
||||
mysql model.DatabaseMysql
|
||||
err error
|
||||
|
||||
@@ -304,7 +304,6 @@ func OperateFirewallPort(oldPorts, newPorts []int) error {
|
||||
return err
|
||||
}
|
||||
for _, port := range newPorts {
|
||||
|
||||
if err := client.Port(fireClient.FireInfo{Port: strconv.Itoa(port), Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ type IHostService interface {
|
||||
Create(hostDto dto.HostOperate) (*dto.HostInfo, error)
|
||||
Update(id uint, upMap map[string]interface{}) error
|
||||
Delete(id []uint) error
|
||||
|
||||
EncryptHost(itemVal string) (string, error)
|
||||
}
|
||||
|
||||
func NewIHostService() IHostService {
|
||||
@@ -220,6 +222,28 @@ func (u *HostService) SearchForTree(search dto.SearchForTree) ([]dto.HostTree, e
|
||||
}
|
||||
|
||||
func (u *HostService) Create(req dto.HostOperate) (*dto.HostInfo, error) {
|
||||
var err error
|
||||
if len(req.Password) != 0 && req.AuthMode == "password" {
|
||||
req.Password, err = u.EncryptHost(req.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.PrivateKey = ""
|
||||
req.PassPhrase = ""
|
||||
}
|
||||
if len(req.PrivateKey) != 0 && req.AuthMode == "key" {
|
||||
req.PrivateKey, err = u.EncryptHost(req.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(req.PassPhrase) != 0 {
|
||||
req.PassPhrase, err = encrypt.StringEncrypt(req.PassPhrase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
req.Password = ""
|
||||
}
|
||||
var host model.Host
|
||||
if err := copier.Copy(&host, &req); err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
@@ -290,3 +314,12 @@ func (u *HostService) Delete(ids []uint) error {
|
||||
func (u *HostService) Update(id uint, upMap map[string]interface{}) error {
|
||||
return hostRepo.Update(id, upMap)
|
||||
}
|
||||
|
||||
func (u *HostService) EncryptHost(itemVal string) (string, error) {
|
||||
privateKey, err := base64.StdEncoding.DecodeString(itemVal)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
keyItem, err := encrypt.StringEncrypt(string(privateKey))
|
||||
return keyItem, err
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ func (u *ImageRepoService) List() ([]dto.ImageRepoOption, error) {
|
||||
|
||||
func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
|
||||
if cmd.CheckIllegal(req.Username, req.Password, req.DownloadUrl) {
|
||||
return buserr.New(constant.ErrRepoConn)
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
imageRepo, _ := imageRepoRepo.Get(commonRepo.WithByName(req.Name))
|
||||
if imageRepo.ID != 0 {
|
||||
@@ -148,7 +148,7 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
|
||||
return errors.New("The default value cannot be deleted !")
|
||||
}
|
||||
if cmd.CheckIllegal(req.Username, req.Password, req.DownloadUrl) {
|
||||
return buserr.New(constant.ErrRepoConn)
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
@@ -146,6 +147,9 @@ func (u *SSHService) UpdateByFile(value string) error {
|
||||
}
|
||||
|
||||
func (u *SSHService) GenerateSSH(req dto.GenerateSSH) error {
|
||||
if cmd.CheckIllegal(req.EncryptionMode, req.Password) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
return fmt.Errorf("load current user failed, err: %v", err)
|
||||
|
||||
@@ -42,6 +42,7 @@ var (
|
||||
ErrTypePasswordExpired = "ErrPasswordExpired"
|
||||
ErrNameIsExist = "ErrNameIsExist"
|
||||
ErrDemoEnvironment = "ErrDemoEnvironment"
|
||||
ErrCmdIllegal = "ErrCmdIllegal"
|
||||
)
|
||||
|
||||
// app
|
||||
@@ -107,7 +108,6 @@ var (
|
||||
ErrInUsed = "ErrInUsed"
|
||||
ErrObjectInUsed = "ErrObjectInUsed"
|
||||
ErrPortRules = "ErrPortRules"
|
||||
ErrRepoConn = "ErrRepoConn"
|
||||
)
|
||||
|
||||
// runtime
|
||||
|
||||
@@ -13,6 +13,7 @@ ErrNotSupportType: "The system does not support the current type: {{ .detail }}"
|
||||
ErrNameIsExist: "Name is already exist"
|
||||
ErrDemoEnvironment: "Demo server, prohibit this operation!"
|
||||
ErrCmdTimeout: "Command execution timed out!"
|
||||
ErrCmdIllegal: "The command contains illegal characters. Please modify and try again!"
|
||||
|
||||
#app
|
||||
ErrPortInUsed: "{{ .detail }} port already in use"
|
||||
@@ -83,7 +84,6 @@ ErrTypeOfRedis: "The recovery file type does not match the current persistence m
|
||||
#container
|
||||
ErrInUsed: "{{ .detail }} is in use and cannot be deleted"
|
||||
ErrObjectInUsed: "This object is in use and cannot be deleted"
|
||||
ErrRepoConn: "The repository information contains illegal characters"
|
||||
ErrPortRules: "The number of ports does not match, please re-enter!"
|
||||
|
||||
#runtime
|
||||
|
||||
@@ -13,6 +13,7 @@ ErrNotSupportType: "系統暫不支持當前類型: {{ .detail }}"
|
||||
ErrNameIsExist: "名稱已存在"
|
||||
ErrDemoEnvironment: "演示伺服器,禁止此操作!"
|
||||
ErrCmdTimeout: "指令執行超時!"
|
||||
ErrCmdIllegal: "執行命令中存在不合法字符,請修改後重試!"
|
||||
|
||||
#app
|
||||
ErrPortInUsed: "{{ .detail }} 端口已被佔用!"
|
||||
@@ -83,7 +84,6 @@ ErrTypeOfRedis: "恢復文件類型與當前持久化方式不符,請修改後
|
||||
#container
|
||||
ErrInUsed: "{{ .detail }} 正被使用,無法刪除"
|
||||
ErrObjectInUsed: "該對象正被使用,無法刪除"
|
||||
ErrRepoConn: "倉庫資訊中存在不合法的字符"
|
||||
ErrPortRules: "端口數目不匹配,請重新輸入!"
|
||||
|
||||
#runtime
|
||||
|
||||
@@ -13,6 +13,7 @@ ErrNotSupportType: "系统暂不支持当前类型: {{ .detail }}"
|
||||
ErrNameIsExist: "名称已存在"
|
||||
ErrDemoEnvironment: "演示服务器,禁止此操作!"
|
||||
ErrCmdTimeout: "命令执行超时!"
|
||||
ErrCmdIllegal: "执行命令中存在不合法字符,请修改后重试!"
|
||||
|
||||
#app
|
||||
ErrPortInUsed: "{{ .detail }} 端口已被占用!"
|
||||
@@ -83,7 +84,6 @@ ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后
|
||||
#container
|
||||
ErrInUsed: "{{ .detail }} 正被使用,无法删除"
|
||||
ErrObjectInUsed: "该对象正被使用,无法删除"
|
||||
ErrRepoConn: "仓库信息中存在不合法的字符"
|
||||
ErrPortRules: "端口数目不匹配,请重新输入!"
|
||||
|
||||
#runtime
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
)
|
||||
|
||||
@@ -114,6 +116,10 @@ func (f *Firewall) ListAddress() ([]FireInfo, error) {
|
||||
}
|
||||
|
||||
func (f *Firewall) Port(port FireInfo, operation string) error {
|
||||
if cmd.CheckIllegal(operation, port.Protocol, port.Port) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
|
||||
stdout, err := cmd.Execf("firewall-cmd --zone=public --%s-port=%s/%s --permanent", operation, port.Port, port.Protocol)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s port failed, err: %s", operation, stdout)
|
||||
@@ -122,6 +128,9 @@ func (f *Firewall) Port(port FireInfo, operation string) error {
|
||||
}
|
||||
|
||||
func (f *Firewall) RichRules(rule FireInfo, operation string) error {
|
||||
if cmd.CheckIllegal(operation, rule.Address, rule.Protocol, rule.Port, rule.Strategy) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
ruleStr := ""
|
||||
if strings.Contains(rule.Address, "-") {
|
||||
std, err := cmd.Execf("firewall-cmd --permanent --new-ipset=%s --type=hash:ip", rule.Address)
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
)
|
||||
|
||||
@@ -131,6 +133,9 @@ func (f *Ufw) Port(port FireInfo, operation string) error {
|
||||
default:
|
||||
return fmt.Errorf("unsupport strategy %s", port.Strategy)
|
||||
}
|
||||
if cmd.CheckIllegal(port.Protocol, port.Port) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
|
||||
command := fmt.Sprintf("%s %s %s", f.CmdStr, port.Strategy, port.Port)
|
||||
if operation == "remove" {
|
||||
@@ -156,6 +161,10 @@ func (f *Ufw) RichRules(rule FireInfo, operation string) error {
|
||||
return fmt.Errorf("unsupport strategy %s", rule.Strategy)
|
||||
}
|
||||
|
||||
if cmd.CheckIllegal(operation, rule.Protocol, rule.Address, rule.Port) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
|
||||
ruleStr := fmt.Sprintf("%s %s ", f.CmdStr, rule.Strategy)
|
||||
if operation == "remove" {
|
||||
ruleStr = fmt.Sprintf("%s delete %s ", f.CmdStr, rule.Strategy)
|
||||
|
||||
@@ -31,9 +31,60 @@ const props = defineProps({
|
||||
option: {
|
||||
type: Object,
|
||||
required: true,
|
||||
}, // option: { title , xDatas, yDatas, formatStr }
|
||||
}, // option: { title , xDatas, yDatas, formatStr, yAxis, grid, tooltip}
|
||||
});
|
||||
|
||||
const seriesStyle = [
|
||||
{
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(0, 94, 235, .5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(0, 94, 235, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
{
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(27, 143, 60, .5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(27, 143, 60, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
{
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(249, 199, 79, .5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(249, 199, 79, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
{
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(255, 173, 177, 0.5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(255, 173, 177, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
];
|
||||
|
||||
function initChart() {
|
||||
let itemChart = echarts?.getInstanceByDom(document.getElementById(props.id) as HTMLElement);
|
||||
// 如果不存在,就进行初始化
|
||||
@@ -45,44 +96,32 @@ function initChart() {
|
||||
|
||||
const series = [];
|
||||
if (props.option?.yDatas?.length) {
|
||||
series.push({
|
||||
name: props.option?.yDatas[0]?.name,
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(0, 94, 235, .5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(0, 94, 235, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
data: props.option?.yDatas[0]?.data,
|
||||
showSymbol: false,
|
||||
});
|
||||
if (props.option?.yDatas?.length > 1) {
|
||||
props.option?.yDatas.forEach((item: any, index: number) => {
|
||||
series.push({
|
||||
name: props.option?.yDatas[1]?.name,
|
||||
name: item?.name,
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(27, 143, 60, .5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(27, 143, 60, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
data: props.option?.yDatas[1]?.data,
|
||||
areaStyle: seriesStyle[index],
|
||||
data: item?.data,
|
||||
showSymbol: false,
|
||||
yAxisIndex: item.yAxisIndex ? 1 : null,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
const yAxis = [];
|
||||
if (props.option.yAxis && props.option.yAxis.length > 0) {
|
||||
props.option.yAxis.forEach((item: any) => {
|
||||
yAxis.push({
|
||||
splitLine: {
|
||||
show: true,
|
||||
//分隔辅助线
|
||||
lineStyle: {
|
||||
type: 'dashed', //线的类型 虚线0
|
||||
opacity: theme === 'dark' ? 0.1 : 1, //透明度
|
||||
},
|
||||
},
|
||||
...item,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 把配置和数据放这里
|
||||
@@ -96,7 +135,7 @@ function initChart() {
|
||||
],
|
||||
zlevel: 1,
|
||||
z: 1,
|
||||
tooltip: {
|
||||
tooltip: props.option.tooltip || {
|
||||
trigger: 'axis',
|
||||
formatter: function (datas: any) {
|
||||
let res = datas[0].name + '<br/>';
|
||||
@@ -127,7 +166,7 @@ function initChart() {
|
||||
return res;
|
||||
},
|
||||
},
|
||||
grid: { left: '7%', right: '7%', bottom: '20%' },
|
||||
grid: props.option.grid || { left: '7%', right: '7%', bottom: '20%' },
|
||||
legend: {
|
||||
right: 10,
|
||||
itemWidth: 8,
|
||||
@@ -137,26 +176,27 @@ function initChart() {
|
||||
icon: 'circle',
|
||||
},
|
||||
xAxis: { data: props.option.xDatas, boundaryGap: false },
|
||||
yAxis: {
|
||||
name: '( ' + props.option.formatStr + ' )',
|
||||
splitLine: {
|
||||
//分隔辅助线
|
||||
lineStyle: {
|
||||
type: 'dashed', //线的类型 虚线0
|
||||
opacity: theme === 'dark' ? 0.1 : 1, //透明度
|
||||
},
|
||||
},
|
||||
},
|
||||
yAxis: props.option.yAxis
|
||||
? yAxis
|
||||
: {
|
||||
name: '( ' + props.option.formatStr + ' )',
|
||||
splitLine: {
|
||||
//分隔辅助线
|
||||
lineStyle: {
|
||||
type: 'dashed', //线的类型 虚线0
|
||||
opacity: theme === 'dark' ? 0.1 : 1, //透明度
|
||||
},
|
||||
},
|
||||
},
|
||||
series: series,
|
||||
dataZoom: [{ startValue: props?.option.xDatas[0], show: props.dataZoom }],
|
||||
};
|
||||
// 渲染数据
|
||||
itemChart.setOption(option, true);
|
||||
}
|
||||
|
||||
window.onresize = function () {
|
||||
//自适应大小
|
||||
itemChart.resize();
|
||||
};
|
||||
function changeChartSize() {
|
||||
echarts.getInstanceByDom(document.getElementById(props.id) as HTMLElement)?.resize();
|
||||
}
|
||||
|
||||
watch(
|
||||
@@ -173,11 +213,13 @@ watch(
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', changeChartSize);
|
||||
});
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
echarts.getInstanceByDom(document.getElementById(props.id) as HTMLElement).dispose();
|
||||
window.removeEventListener('resize', changeChartSize);
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -376,7 +376,7 @@ const checkDisableFunctions = (rule: any, value: any, callback: any) => {
|
||||
if (value === '' || typeof value === 'undefined' || value == null) {
|
||||
callback(new Error(i18n.global.t('commons.rule.disableFunction')));
|
||||
} else {
|
||||
const reg = /^[a-zA-Z,]+$/;
|
||||
const reg = /^[a-zA-Z_,]+$/;
|
||||
if (!reg.test(value) && value !== '') {
|
||||
callback(new Error(i18n.global.t('commons.rule.disableFunction')));
|
||||
} else {
|
||||
|
||||
@@ -179,7 +179,7 @@ const message = {
|
||||
appName: 'Support English, numbers, - and _, length 2-30, and cannot start and end with -_',
|
||||
containerName:
|
||||
'Supports letters, numbers, underscores, hyphens and dots, cannot end with hyphen- or dot.1-127',
|
||||
disableFunction: 'Only support letters and,',
|
||||
disableFunction: 'Only support letters ,underscores,and,',
|
||||
leechExts: 'Only support letters, numbers and,',
|
||||
paramSimple: 'Support lowercase letters and numbers, length 1-128',
|
||||
},
|
||||
@@ -763,6 +763,7 @@ const message = {
|
||||
fold: 'All contract',
|
||||
batchInput: 'Batch input',
|
||||
quickCommand: 'Quick command',
|
||||
quickCommandHelper: 'Frequently used command list for quick selection at the bottom of the terminal interface',
|
||||
groupDeleteHelper:
|
||||
'After the group is removed, all connections in the group will be migrated to the default group. Confirm the information',
|
||||
command: 'Command',
|
||||
|
||||
@@ -177,7 +177,7 @@ const message = {
|
||||
nginxDoc: '僅支持英文大小寫,數字,和.',
|
||||
appName: '支持英文、數字、-和_,長度2-30,並且不能以-_開頭和結尾',
|
||||
containerName: '支持字母、數字、下劃線、連字符和點,不能以連字符-或點.結尾,長度1-127',
|
||||
disableFunction: '僅支持字母和,',
|
||||
disableFunction: '僅支持字母、下劃線和,',
|
||||
leechExts: '僅支持字母數字和,',
|
||||
paramSimple: '支持小寫字母和數字,長度 1-128',
|
||||
},
|
||||
@@ -737,6 +737,7 @@ const message = {
|
||||
fold: '全部收縮',
|
||||
batchInput: '批量輸入',
|
||||
quickCommand: '快速命令',
|
||||
quickCommandHelper: '常用命令列表,用於在終端界面底部快速選擇',
|
||||
groupDeleteHelper: '移除組後,組內所有連接將遷移到 default 組內,是否確認',
|
||||
command: '命令',
|
||||
addHost: '添加主機',
|
||||
@@ -887,6 +888,12 @@ const message = {
|
||||
belong: '歸屬地',
|
||||
local: '內網',
|
||||
remote: '外網',
|
||||
config: '配置',
|
||||
session: '會話',
|
||||
loginTime: '登錄時間',
|
||||
loginIP: '登錄IP',
|
||||
disconnect: '斷開',
|
||||
stopSSHWarn: '是否斷開此SSH連接',
|
||||
},
|
||||
setting: {
|
||||
all: '全部',
|
||||
|
||||
@@ -177,7 +177,7 @@ const message = {
|
||||
nginxDoc: '仅支持英文大小写,数字,和.',
|
||||
appName: '支持英文、数字、-和_,长度2-30,并且不能以-_开头和结尾',
|
||||
containerName: '支持字母、数字、下划线、连字符和点,不能以连字符-或点.结尾,长度1-127',
|
||||
disableFunction: '仅支持字母和,',
|
||||
disableFunction: '仅支持字母、下划线和,',
|
||||
leechExts: '仅支持字母数字和,',
|
||||
paramSimple: '支持小写字母和数字,长度1-128',
|
||||
},
|
||||
@@ -737,6 +737,7 @@ const message = {
|
||||
fold: '全部收缩',
|
||||
batchInput: '批量输入',
|
||||
quickCommand: '快速命令',
|
||||
quickCommandHelper: '常用命令列表,用于在终端界面底部快速选择',
|
||||
groupDeleteHelper: '移除组后,组内所有连接将迁移到 default 组内,是否确认',
|
||||
command: '命令',
|
||||
addHost: '添加主机',
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
html {
|
||||
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, 微软雅黑, Arial, sans-serif;
|
||||
}
|
||||
.flx-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -122,8 +125,6 @@
|
||||
font-size: 12px;
|
||||
word-break: break-all;
|
||||
color: #8f959e;
|
||||
transform: scale(0.9);
|
||||
transform-origin: left;
|
||||
width: 110%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@@ -287,17 +287,16 @@ onMounted(() => {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.page-button {
|
||||
float: right;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 768px) and (max-width: 1200px) {
|
||||
.app-col-12 {
|
||||
max-width: 50%;
|
||||
flex: 0 0 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.page-button {
|
||||
float: right;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -91,3 +91,9 @@
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.page-button {
|
||||
float: right;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
@@ -234,6 +234,15 @@
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="page-button" v-if="mode === 'installed'">
|
||||
<fu-table-pagination
|
||||
v-model:current-page="paginationConfig.currentPage"
|
||||
v-model:page-size="paginationConfig.pageSize"
|
||||
v-bind="paginationConfig"
|
||||
@change="search"
|
||||
:layout="'total, sizes, prev, pager, next, jumper'"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</LayoutContent>
|
||||
<Backups ref="backupRef" @close="search" />
|
||||
@@ -300,7 +309,7 @@ const tags = ref<App.Tag[]>([]);
|
||||
const activeTag = ref('all');
|
||||
const searchReq = reactive({
|
||||
page: 1,
|
||||
pageSize: 15,
|
||||
pageSize: 20,
|
||||
name: '',
|
||||
tags: [],
|
||||
update: false,
|
||||
|
||||
@@ -439,12 +439,12 @@ const checkPortValid = () => {
|
||||
}
|
||||
for (const port of dialogData.value.rowData!.exposedPorts) {
|
||||
if (port.host.indexOf(':') !== -1) {
|
||||
port.hostIP = port.host.split(':')[0];
|
||||
port.hostIP = port.host.substring(0, port.host.lastIndexOf(':'));
|
||||
if (checkIpV4V6(port.hostIP)) {
|
||||
MsgError(i18n.global.t('firewall.addressFormatError'));
|
||||
return false;
|
||||
}
|
||||
port.hostPort = port.host.split(':')[1];
|
||||
port.hostPort = port.host.substring(port.host.lastIndexOf(':') + 1);
|
||||
} else {
|
||||
port.hostPort = port.host;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-scrollbar max-height="500px">
|
||||
<el-scrollbar height="525px" class="moz-height">
|
||||
<div class="h-app-card" v-for="(app, index) in apps" :key="index">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="5">
|
||||
@@ -88,6 +88,7 @@ defineExpose({
|
||||
cursor: pointer;
|
||||
padding: 10px 15px;
|
||||
margin-right: 10px;
|
||||
line-height: 18px;
|
||||
|
||||
.h-app-content {
|
||||
padding-left: 15px;
|
||||
@@ -118,4 +119,11 @@ defineExpose({
|
||||
border: 0;
|
||||
border-top: var(--panel-border);
|
||||
}
|
||||
|
||||
/* FOR MOZILLA */
|
||||
@-moz-document url-prefix() {
|
||||
.moz-height {
|
||||
height: 524px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
|
||||
<div v-if="chartOption === 'io'" style="margin-top: 40px" class="mobile-monitor-chart">
|
||||
<v-charts
|
||||
height="360px"
|
||||
height="383px"
|
||||
id="ioChart"
|
||||
type="line"
|
||||
:option="chartsOption['ioChart']"
|
||||
@@ -136,7 +136,7 @@
|
||||
</div>
|
||||
<div v-if="chartOption === 'network'" style="margin-top: 40px" class="mobile-monitor-chart">
|
||||
<v-charts
|
||||
height="360px"
|
||||
height="383px"
|
||||
id="networkChart"
|
||||
type="line"
|
||||
:option="chartsOption['networkChart']"
|
||||
@@ -151,56 +151,58 @@
|
||||
<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
|
||||
<CardWithHeader :header="$t('home.systemInfo')">
|
||||
<template #body>
|
||||
<el-descriptions :column="1" class="h-systemInfo">
|
||||
<el-descriptions-item class-name="system-content">
|
||||
<template #label>
|
||||
<span class="system-label">
|
||||
{{ $t('home.hostname') }}
|
||||
</span>
|
||||
</template>
|
||||
{{ baseInfo.hostname }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item class-name="system-content">
|
||||
<template #label>
|
||||
<span class="system-label">
|
||||
{{ $t('home.platformVersion') }}
|
||||
</span>
|
||||
</template>
|
||||
{{ baseInfo.platform }}-{{ baseInfo.platformVersion }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item class-name="system-content">
|
||||
<template #label>
|
||||
<span class="system-label">
|
||||
{{ $t('home.kernelVersion') }}
|
||||
</span>
|
||||
</template>
|
||||
{{ baseInfo.kernelVersion }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item class-name="system-content">
|
||||
<template #label>
|
||||
<span class="system-label">
|
||||
{{ $t('home.kernelArch') }}
|
||||
</span>
|
||||
</template>
|
||||
{{ baseInfo.kernelArch }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item class-name="system-content">
|
||||
<template #label>
|
||||
<span class="system-label">
|
||||
{{ $t('home.uptime') }}
|
||||
</span>
|
||||
</template>
|
||||
{{ currentInfo.timeSinceUptime }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item class-name="system-content">
|
||||
<template #label>
|
||||
<span class="system-label">
|
||||
{{ $t('home.runningTime') }}
|
||||
</span>
|
||||
</template>
|
||||
{{ loadUpTime(currentInfo.uptime) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-scrollbar>
|
||||
<el-descriptions :column="1" class="h-systemInfo">
|
||||
<el-descriptions-item class-name="system-content">
|
||||
<template #label>
|
||||
<span class="system-label">
|
||||
{{ $t('home.hostname') }}
|
||||
</span>
|
||||
</template>
|
||||
{{ baseInfo.hostname }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item class-name="system-content">
|
||||
<template #label>
|
||||
<span class="system-label">
|
||||
{{ $t('home.platformVersion') }}
|
||||
</span>
|
||||
</template>
|
||||
{{ baseInfo.platform }}-{{ baseInfo.platformVersion }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item class-name="system-content">
|
||||
<template #label>
|
||||
<span class="system-label">
|
||||
{{ $t('home.kernelVersion') }}
|
||||
</span>
|
||||
</template>
|
||||
{{ baseInfo.kernelVersion }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item class-name="system-content">
|
||||
<template #label>
|
||||
<span class="system-label">
|
||||
{{ $t('home.kernelArch') }}
|
||||
</span>
|
||||
</template>
|
||||
{{ baseInfo.kernelArch }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item class-name="system-content">
|
||||
<template #label>
|
||||
<span class="system-label">
|
||||
{{ $t('home.uptime') }}
|
||||
</span>
|
||||
</template>
|
||||
{{ currentInfo.timeSinceUptime }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item class-name="system-content">
|
||||
<template #label>
|
||||
<span class="system-label">
|
||||
{{ $t('home.runningTime') }}
|
||||
</span>
|
||||
</template>
|
||||
{{ loadUpTime(currentInfo.uptime) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
</CardWithHeader>
|
||||
|
||||
@@ -563,6 +565,12 @@ onBeforeUnmount(() => {
|
||||
|
||||
.h-systemInfo {
|
||||
margin-left: 18px;
|
||||
height: 216px;
|
||||
}
|
||||
@-moz-document url-prefix() {
|
||||
.h-systemInfo {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.system-label {
|
||||
|
||||
@@ -37,12 +37,21 @@
|
||||
></el-date-picker>
|
||||
</div>
|
||||
</template>
|
||||
<div id="loadLoadChart" class="chart"></div>
|
||||
<div class="chart">
|
||||
<v-charts
|
||||
height="400px"
|
||||
id="loadLoadChart"
|
||||
type="line"
|
||||
:option="chartsOption['loadLoadChart']"
|
||||
v-if="chartsOption['loadLoadChart']"
|
||||
:dataZoom="true"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20" style="margin-top: 20px">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
||||
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
|
||||
<el-card style="overflow: inherit">
|
||||
<template #header>
|
||||
<div :class="mobile ? 'flx-wrap' : 'flx-justify-between'">
|
||||
@@ -60,10 +69,19 @@
|
||||
></el-date-picker>
|
||||
</div>
|
||||
</template>
|
||||
<div id="loadCPUChart" class="chart"></div>
|
||||
<div class="chart">
|
||||
<v-charts
|
||||
height="400px"
|
||||
id="loadCPUChart"
|
||||
type="line"
|
||||
:option="chartsOption['loadCPUChart']"
|
||||
v-if="chartsOption['loadCPUChart']"
|
||||
:dataZoom="true"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
||||
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
|
||||
<el-card style="overflow: inherit">
|
||||
<template #header>
|
||||
<div :class="mobile ? 'flx-wrap' : 'flx-justify-between'">
|
||||
@@ -81,12 +99,21 @@
|
||||
></el-date-picker>
|
||||
</div>
|
||||
</template>
|
||||
<div id="loadMemoryChart" class="chart"></div>
|
||||
<div class="chart">
|
||||
<v-charts
|
||||
height="400px"
|
||||
id="loadMemoryChart"
|
||||
type="line"
|
||||
:option="chartsOption['loadMemoryChart']"
|
||||
v-if="chartsOption['loadMemoryChart']"
|
||||
:dataZoom="true"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20" style="margin-top: 20px">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
||||
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
|
||||
<el-card style="overflow: inherit">
|
||||
<template #header>
|
||||
<div :class="mobile ? 'flx-wrap' : 'flx-justify-between'">
|
||||
@@ -104,10 +131,19 @@
|
||||
></el-date-picker>
|
||||
</div>
|
||||
</template>
|
||||
<div id="loadIOChart" class="chart"></div>
|
||||
<div class="chart">
|
||||
<v-charts
|
||||
height="400px"
|
||||
id="loadIOChart"
|
||||
type="line"
|
||||
:option="chartsOption['loadIOChart']"
|
||||
v-if="chartsOption['loadIOChart']"
|
||||
:dataZoom="true"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
||||
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
|
||||
<el-card style="overflow: inherit">
|
||||
<template #header>
|
||||
<div :class="mobile ? 'flx-wrap' : 'flx-justify-between'">
|
||||
@@ -148,7 +184,16 @@
|
||||
></el-date-picker>
|
||||
</div>
|
||||
</template>
|
||||
<div id="loadNetworkChart" class="chart"></div>
|
||||
<div class="chart">
|
||||
<v-charts
|
||||
height="400px"
|
||||
id="loadNetworkChart"
|
||||
type="line"
|
||||
:option="chartsOption['loadNetworkChart']"
|
||||
v-if="chartsOption['loadNetworkChart']"
|
||||
:dataZoom="true"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -156,8 +201,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, onBeforeUnmount, computed } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import { ref, reactive, onMounted, computed } from 'vue';
|
||||
import { loadMonitor, getNetworkOptions } from '@/api/modules/monitor';
|
||||
import { Monitor } from '@/api/interface/monitor';
|
||||
import { computeSizeFromKBs, dateFormatWithoutYear } from '@/utils/util';
|
||||
@@ -181,6 +225,8 @@ const timeRangeIO = ref<[Date, Date]>([new Date(new Date().setHours(0, 0, 0, 0))
|
||||
const timeRangeNetwork = ref<[Date, Date]>([new Date(new Date().setHours(0, 0, 0, 0)), new Date()]);
|
||||
const networkChoose = ref();
|
||||
const netOptions = ref();
|
||||
const chartsOption = ref({ loadLoadChart: null, loadCPUChart: null, loadMemoryChart: null, loadNetworkChart: null });
|
||||
|
||||
const shortcuts = [
|
||||
{
|
||||
text: i18n.global.t('monitor.today'),
|
||||
@@ -287,50 +333,34 @@ const search = async (param: string) => {
|
||||
return item.cpu.toFixed(2);
|
||||
});
|
||||
cpuData = cpuData.length === 0 ? loadEmptyData() : cpuData;
|
||||
let yDatasOfCpu = {
|
||||
name: 'CPU',
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(0, 94, 235, .5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(0, 94, 235, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
data: cpuData,
|
||||
showSymbol: false,
|
||||
chartsOption.value['loadCPUChart'] = {
|
||||
xDatas: baseDate,
|
||||
yDatas: [
|
||||
{
|
||||
name: 'CPU',
|
||||
data: cpuData,
|
||||
},
|
||||
],
|
||||
|
||||
formatStr: '%',
|
||||
};
|
||||
initCharts('loadCPUChart', baseDate, yDatasOfCpu, 'CPU', '%');
|
||||
}
|
||||
if (param === 'memory' || param === 'all') {
|
||||
let memoryData = item.value.map(function (item: any) {
|
||||
return item.memory.toFixed(2);
|
||||
});
|
||||
memoryData = memoryData.length === 0 ? loadEmptyData() : memoryData;
|
||||
let yDatasOfMem = {
|
||||
name: i18n.global.t('monitor.memory'),
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(0, 94, 235, .5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(0, 94, 235, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
data: memoryData,
|
||||
showSymbol: false,
|
||||
chartsOption.value['loadMemoryChart'] = {
|
||||
xDatas: baseDate,
|
||||
yDatas: [
|
||||
{
|
||||
name: i18n.global.t('monitor.memory'),
|
||||
data: memoryData,
|
||||
},
|
||||
],
|
||||
|
||||
formatStr: '%',
|
||||
};
|
||||
initCharts('loadMemoryChart', baseDate, yDatasOfMem, i18n.global.t('monitor.memory'), '%');
|
||||
}
|
||||
if (param === 'load' || param === 'all') {
|
||||
initLoadCharts(item);
|
||||
@@ -348,47 +378,30 @@ const search = async (param: string) => {
|
||||
return item.up.toFixed(2);
|
||||
});
|
||||
networkUp = networkUp.length === 0 ? loadEmptyData() : networkUp;
|
||||
let yDatasOfUp = {
|
||||
name: i18n.global.t('monitor.up'),
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(0, 94, 235, .5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(0, 94, 235, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
data: networkUp,
|
||||
showSymbol: false,
|
||||
};
|
||||
let networkOut = item.value.map(function (item: any) {
|
||||
return item.down.toFixed(2);
|
||||
});
|
||||
networkOut = networkOut.length === 0 ? loadEmptyData() : networkOut;
|
||||
let yDatasOfDown = {
|
||||
name: i18n.global.t('monitor.down'),
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(27, 143, 60, .5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(27, 143, 60, 0)',
|
||||
},
|
||||
]),
|
||||
|
||||
chartsOption.value['loadNetworkChart'] = {
|
||||
xDatas: networkDate,
|
||||
yDatas: [
|
||||
{
|
||||
name: i18n.global.t('monitor.up'),
|
||||
data: networkUp,
|
||||
},
|
||||
{
|
||||
name: i18n.global.t('monitor.down'),
|
||||
data: networkOut,
|
||||
},
|
||||
],
|
||||
grid: {
|
||||
left: getSideWidth(true),
|
||||
right: getSideWidth(true),
|
||||
bottom: '20%',
|
||||
},
|
||||
data: networkOut,
|
||||
showSymbol: false,
|
||||
formatStr: 'KB/s',
|
||||
};
|
||||
initCharts('loadNetworkChart', networkDate, [yDatasOfUp, yDatasOfDown], 'KB/s', 'KB/s');
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -401,47 +414,6 @@ const loadNetworkOptions = async () => {
|
||||
search('all');
|
||||
};
|
||||
|
||||
function initCharts(chartName: string, xDatas: any, yDatas: any, yTitle: string, formatStr: string) {
|
||||
const lineChart = echarts.init(document.getElementById(chartName) as HTMLElement);
|
||||
const option = {
|
||||
zlevel: 1,
|
||||
z: 1,
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: function (datas: any) {
|
||||
let res = datas[0].name + '<br/>';
|
||||
if (chartName !== 'loadNetworkChart') {
|
||||
for (const item of datas) {
|
||||
res += item.marker + ' ' + item.seriesName + ':' + item.data + formatStr + '<br/>';
|
||||
}
|
||||
} else {
|
||||
for (const item of datas) {
|
||||
res += item.marker + ' ' + item.seriesName + ':' + computeSizeFromKBs(item.data) + '<br/>';
|
||||
}
|
||||
}
|
||||
return res;
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
data: chartName === 'loadNetworkChart' && [i18n.global.t('monitor.up'), i18n.global.t('monitor.down')],
|
||||
textStyle: {
|
||||
color: '#646A73',
|
||||
},
|
||||
icon: 'circle',
|
||||
},
|
||||
grid: {
|
||||
left: getSideWidth(chartName == 'loadNetworkChart'),
|
||||
right: getSideWidth(chartName == 'loadNetworkChart'),
|
||||
bottom: '20%',
|
||||
},
|
||||
xAxis: { data: xDatas },
|
||||
yAxis: { name: '( ' + formatStr + ' )', axisLabel: { fontSize: chartName == 'loadNetworkChart' ? 10 : 12 } },
|
||||
dataZoom: [{ startValue: zoomStart.value }],
|
||||
series: yDatas,
|
||||
};
|
||||
lineChart.setOption(option, true);
|
||||
}
|
||||
|
||||
function initLoadCharts(item: Monitor.MonitorData) {
|
||||
let itemLoadDate = item.date.length === 0 ? loadEmptyDate(timeRangeLoad.value) : item.date;
|
||||
let loadDate = itemLoadDate.map(function (item: any) {
|
||||
@@ -463,37 +435,27 @@ function initLoadCharts(item: Monitor.MonitorData) {
|
||||
return item.loadUsage.toFixed(2);
|
||||
});
|
||||
loadUsage = loadUsage.length === 0 ? loadEmptyData() : loadUsage;
|
||||
|
||||
const lineChart = echarts.init(document.getElementById('loadLoadChart') as HTMLElement);
|
||||
const option = {
|
||||
zlevel: 1,
|
||||
z: 1,
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: function (datas: any) {
|
||||
let res = datas[0].name + '<br/>';
|
||||
for (const item of datas) {
|
||||
res += item.marker + ' ' + item.seriesName + ':' + item.data + '%' + '<br/>';
|
||||
}
|
||||
return res;
|
||||
chartsOption.value['loadLoadChart'] = {
|
||||
xDatas: loadDate,
|
||||
yDatas: [
|
||||
{
|
||||
name: '1 ' + i18n.global.t('commons.units.minute'),
|
||||
data: load1Data,
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
data: [
|
||||
'1 ' + i18n.global.t('commons.units.minute'),
|
||||
'5 ' + i18n.global.t('commons.units.minute'),
|
||||
'15 ' + i18n.global.t('commons.units.minute'),
|
||||
i18n.global.t('monitor.resourceUsage'),
|
||||
],
|
||||
textStyle: {
|
||||
color: '#646A73',
|
||||
{
|
||||
name: '5 ' + i18n.global.t('commons.units.minute'),
|
||||
data: load5Data,
|
||||
},
|
||||
icon: 'circle',
|
||||
},
|
||||
grid: { left: '7%', right: '7%', bottom: '20%' },
|
||||
xAxis: {
|
||||
data: loadDate,
|
||||
},
|
||||
{
|
||||
name: '15 ' + i18n.global.t('commons.units.minute'),
|
||||
data: load15Data,
|
||||
},
|
||||
{
|
||||
name: i18n.global.t('monitor.resourceUsage'),
|
||||
data: loadUsage,
|
||||
yAxisIndex: 1,
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{ type: 'value', name: i18n.global.t('monitor.loadDetail') + ' ( % )' },
|
||||
{
|
||||
@@ -503,84 +465,8 @@ function initLoadCharts(item: Monitor.MonitorData) {
|
||||
alignTicks: true,
|
||||
},
|
||||
],
|
||||
dataZoom: [{ startValue: zoomStart.value }],
|
||||
series: [
|
||||
{
|
||||
name: '1 ' + i18n.global.t('commons.units.minute'),
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(0, 94, 235, .5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(0, 94, 235, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
showSymbol: false,
|
||||
data: load1Data,
|
||||
},
|
||||
{
|
||||
name: '5 ' + i18n.global.t('commons.units.minute'),
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(27, 143, 60, .5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(27, 143, 60, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
showSymbol: false,
|
||||
data: load5Data,
|
||||
},
|
||||
{
|
||||
name: '15 ' + i18n.global.t('commons.units.minute'),
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(249, 199, 79, .5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(249, 199, 79, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
showSymbol: false,
|
||||
data: load15Data,
|
||||
},
|
||||
{
|
||||
name: i18n.global.t('monitor.resourceUsage'),
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(255, 173, 177, 0.5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(255, 173, 177, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
showSymbol: false,
|
||||
data: loadUsage,
|
||||
yAxisIndex: 1,
|
||||
},
|
||||
],
|
||||
formatStr: '%',
|
||||
};
|
||||
lineChart.setOption(option, true);
|
||||
}
|
||||
|
||||
function initIOCharts(item: Monitor.MonitorData) {
|
||||
@@ -604,11 +490,28 @@ function initIOCharts(item: Monitor.MonitorData) {
|
||||
return item.time;
|
||||
});
|
||||
ioTime = ioTime.length === 0 ? loadEmptyData() : ioTime;
|
||||
|
||||
const lineChart = echarts.init(document.getElementById('loadIOChart') as HTMLElement);
|
||||
const option = {
|
||||
zlevel: 1,
|
||||
z: 1,
|
||||
chartsOption.value['loadIOChart'] = {
|
||||
xDatas: ioDate,
|
||||
yDatas: [
|
||||
{
|
||||
name: i18n.global.t('monitor.read'),
|
||||
data: ioRead,
|
||||
},
|
||||
{
|
||||
name: i18n.global.t('monitor.write'),
|
||||
data: ioWrite,
|
||||
},
|
||||
{
|
||||
name: i18n.global.t('monitor.readWriteCount'),
|
||||
data: ioCount,
|
||||
yAxisIndex: 1,
|
||||
},
|
||||
{
|
||||
name: i18n.global.t('monitor.readWriteTime'),
|
||||
data: ioTime,
|
||||
yAxisIndex: 1,
|
||||
},
|
||||
],
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: function (datas: any) {
|
||||
@@ -639,22 +542,7 @@ function initIOCharts(item: Monitor.MonitorData) {
|
||||
return res;
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
data: [
|
||||
i18n.global.t('monitor.read'),
|
||||
i18n.global.t('monitor.write'),
|
||||
i18n.global.t('monitor.readWriteCount'),
|
||||
i18n.global.t('monitor.readWriteTime'),
|
||||
],
|
||||
textStyle: {
|
||||
color: '#646A73',
|
||||
},
|
||||
icon: 'circle',
|
||||
},
|
||||
grid: { left: getSideWidth(true), right: getSideWidth(true), bottom: '20%' },
|
||||
xAxis: {
|
||||
data: ioDate,
|
||||
},
|
||||
yAxis: [
|
||||
{ type: 'value', name: '( KB/s )', axisLabel: { fontSize: 10 } },
|
||||
{
|
||||
@@ -666,85 +554,7 @@ function initIOCharts(item: Monitor.MonitorData) {
|
||||
},
|
||||
},
|
||||
],
|
||||
dataZoom: [{ startValue: zoomStart.value }],
|
||||
series: [
|
||||
{
|
||||
name: i18n.global.t('monitor.read'),
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(0, 94, 235, .5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(0, 94, 235, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
showSymbol: false,
|
||||
data: ioRead,
|
||||
},
|
||||
{
|
||||
name: i18n.global.t('monitor.write'),
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(27, 143, 60, .5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(27, 143, 60, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
showSymbol: false,
|
||||
data: ioWrite,
|
||||
},
|
||||
{
|
||||
name: i18n.global.t('monitor.readWriteCount'),
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(249, 199, 79, .5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(249, 199, 79, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
showSymbol: false,
|
||||
data: ioCount,
|
||||
yAxisIndex: 1,
|
||||
},
|
||||
{
|
||||
name: i18n.global.t('monitor.readWriteTime'),
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(255, 173, 177, 0.5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(255, 173, 177, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
showSymbol: false,
|
||||
data: ioTime,
|
||||
yAxisIndex: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
lineChart.setOption(option, true);
|
||||
}
|
||||
|
||||
function loadEmptyDate(timeRange: any) {
|
||||
@@ -763,30 +573,9 @@ function getSideWidth(b: boolean) {
|
||||
return !b || document.body.clientWidth > 1600 ? '7%' : '10%';
|
||||
}
|
||||
|
||||
function changeChartSize() {
|
||||
echarts.getInstanceByDom(document.getElementById('loadLoadChart') as HTMLElement)?.resize();
|
||||
echarts.getInstanceByDom(document.getElementById('loadCPUChart') as HTMLElement)?.resize();
|
||||
echarts.getInstanceByDom(document.getElementById('loadMemoryChart') as HTMLElement)?.resize();
|
||||
echarts.getInstanceByDom(document.getElementById('loadIOChart') as HTMLElement)?.resize();
|
||||
echarts.getInstanceByDom(document.getElementById('loadNetworkChart') as HTMLElement)?.resize();
|
||||
}
|
||||
|
||||
function disposeChart() {
|
||||
echarts.getInstanceByDom(document.getElementById('loadLoadChart') as HTMLElement)?.dispose();
|
||||
echarts.getInstanceByDom(document.getElementById('loadCPUChart') as HTMLElement)?.dispose();
|
||||
echarts.getInstanceByDom(document.getElementById('loadMemoryChart') as HTMLElement)?.dispose();
|
||||
echarts.getInstanceByDom(document.getElementById('loadIOChart') as HTMLElement)?.dispose();
|
||||
echarts.getInstanceByDom(document.getElementById('loadNetworkChart') as HTMLElement)?.dispose();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
zoomStart.value = dateFormatWithoutYear(new Date(new Date().setHours(0, 0, 0, 0)));
|
||||
loadNetworkOptions();
|
||||
window.addEventListener('resize', changeChartSize);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
disposeChart();
|
||||
window.removeEventListener('resize', changeChartSize);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<LayoutContent v-loading="loading" :title="$t('terminal.quickCommand')">
|
||||
<template #prompt>
|
||||
<el-alert type="info" :title="$t('terminal.quickCommandHelper')" :closable="false" />
|
||||
</template>
|
||||
<template #toolbar>
|
||||
<el-button type="primary" @click="onCreate()">
|
||||
{{ $t('commons.button.create') }}{{ $t('terminal.quickCommand') }}
|
||||
|
||||
@@ -332,9 +332,9 @@ const onConnTerminal = async (title: string, wsID: number, isLocal?: boolean) =>
|
||||
for (const tab of terminalTabs.value) {
|
||||
if (tab.title.indexOf('@127.0.0.1:') !== -1 || tab.title === i18n.global.t('terminal.localhost')) {
|
||||
onReconnect(tab);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
terminalTabs.value.push({
|
||||
index: tabIndex,
|
||||
@@ -344,7 +344,7 @@ const onConnTerminal = async (title: string, wsID: number, isLocal?: boolean) =>
|
||||
latency: 0,
|
||||
});
|
||||
terminalValue.value = tabIndex;
|
||||
if (!res.data && isLocal) {
|
||||
if (!res.data && title === i18n.global.t('terminal.localhost')) {
|
||||
dialogRef.value!.acceptParams({ isLocal: true });
|
||||
}
|
||||
nextTick(() => {
|
||||
|
||||
Reference in New Issue
Block a user