mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-11-24 11:09:16 +08:00
feat: Mysql 远程数据库支持 SSL (#3091)
This commit is contained in:
parent
86bc75ff28
commit
10848fb249
@ -1,6 +1,8 @@
|
||||
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"
|
||||
@ -21,6 +23,14 @@ func (b *BaseApi) CreateDatabase(c *gin.Context) {
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if req.SSL {
|
||||
key, _ := base64.StdEncoding.DecodeString(req.ClientKey)
|
||||
req.ClientKey = string(key)
|
||||
cert, _ := base64.StdEncoding.DecodeString(req.ClientCert)
|
||||
req.ClientCert = string(cert)
|
||||
ca, _ := base64.StdEncoding.DecodeString(req.RootCert)
|
||||
req.RootCert = string(ca)
|
||||
}
|
||||
|
||||
if err := databaseService.Create(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
@ -43,6 +53,14 @@ func (b *BaseApi) CheckDatabase(c *gin.Context) {
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if req.SSL {
|
||||
clientKey, _ := base64.StdEncoding.DecodeString(req.ClientKey)
|
||||
req.ClientKey = string(clientKey)
|
||||
clientCert, _ := base64.StdEncoding.DecodeString(req.ClientCert)
|
||||
req.ClientCert = string(clientCert)
|
||||
rootCert, _ := base64.StdEncoding.DecodeString(req.RootCert)
|
||||
req.RootCert = string(rootCert)
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, databaseService.CheckDatabase(req))
|
||||
}
|
||||
@ -173,6 +191,14 @@ func (b *BaseApi) UpdateDatabase(c *gin.Context) {
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if req.SSL {
|
||||
cKey, _ := base64.StdEncoding.DecodeString(req.ClientKey)
|
||||
req.ClientKey = string(cKey)
|
||||
cCert, _ := base64.StdEncoding.DecodeString(req.ClientCert)
|
||||
req.ClientCert = string(cCert)
|
||||
ca, _ := base64.StdEncoding.DecodeString(req.RootCert)
|
||||
req.RootCert = string(ca)
|
||||
}
|
||||
|
||||
if err := databaseService.Update(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
|
@ -227,17 +227,24 @@ type DatabaseSearch struct {
|
||||
}
|
||||
|
||||
type DatabaseInfo struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Name string `json:"name" validate:"max=256"`
|
||||
From string `json:"from"`
|
||||
Type string `json:"type"`
|
||||
Version string `json:"version"`
|
||||
Address string `json:"address"`
|
||||
Port uint `json:"port"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Description string `json:"description"`
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Name string `json:"name" validate:"max=256"`
|
||||
From string `json:"from"`
|
||||
Type string `json:"type"`
|
||||
Version string `json:"version"`
|
||||
Address string `json:"address"`
|
||||
Port uint `json:"port"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
|
||||
SSL bool `json:"ssl"`
|
||||
RootCert string `json:"rootCert"`
|
||||
ClientKey string `json:"clientKey"`
|
||||
ClientCert string `json:"clientCert"`
|
||||
SkipVerify bool `json:"skipVerify"`
|
||||
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type DatabaseOption struct {
|
||||
@ -250,25 +257,39 @@ type DatabaseOption struct {
|
||||
}
|
||||
|
||||
type DatabaseCreate struct {
|
||||
Name string `json:"name" validate:"required,max=256"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Version string `json:"version" validate:"required"`
|
||||
Address string `json:"address"`
|
||||
Port uint `json:"port"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Name string `json:"name" validate:"required,max=256"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Version string `json:"version" validate:"required"`
|
||||
Address string `json:"address"`
|
||||
Port uint `json:"port"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
|
||||
SSL bool `json:"ssl"`
|
||||
RootCert string `json:"rootCert"`
|
||||
ClientKey string `json:"clientKey"`
|
||||
ClientCert string `json:"clientCert"`
|
||||
SkipVerify bool `json:"skipVerify"`
|
||||
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type DatabaseUpdate struct {
|
||||
ID uint `json:"id"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
Version string `json:"version" validate:"required"`
|
||||
Address string `json:"address"`
|
||||
Port uint `json:"port"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
ID uint `json:"id"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
Version string `json:"version" validate:"required"`
|
||||
Address string `json:"address"`
|
||||
Port uint `json:"port"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
|
||||
SSL bool `json:"ssl"`
|
||||
RootCert string `json:"rootCert"`
|
||||
ClientKey string `json:"clientKey"`
|
||||
ClientCert string `json:"clientCert"`
|
||||
SkipVerify bool `json:"skipVerify"`
|
||||
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
|
@ -11,5 +11,12 @@ type Database struct {
|
||||
Port uint `json:"port" gorm:"type:decimal;not null"`
|
||||
Username string `json:"username" gorm:"type:varchar(64)"`
|
||||
Password string `json:"password" gorm:"type:varchar(64)"`
|
||||
Description string `json:"description" gorm:"type:varchar(256);"`
|
||||
|
||||
SSL bool `json:"ssl"`
|
||||
RootCert string `json:"rootCert" gorm:"type:longText"`
|
||||
ClientKey string `json:"clientKey" gorm:"type:longText"`
|
||||
ClientCert string `json:"clientCert" gorm:"type:longText"`
|
||||
SkipVerify bool `json:"skipVerify"`
|
||||
|
||||
Description string `json:"description" gorm:"type:varchar(256);"`
|
||||
}
|
||||
|
@ -84,7 +84,13 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: 6,
|
||||
|
||||
SSL: req.SSL,
|
||||
RootCert: req.RootCert,
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
Timeout: 6,
|
||||
}); err != nil {
|
||||
return false
|
||||
}
|
||||
@ -105,7 +111,13 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: 6,
|
||||
|
||||
SSL: req.SSL,
|
||||
RootCert: req.RootCert,
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
Timeout: 6,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -172,7 +184,14 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: 300,
|
||||
|
||||
SSL: req.SSL,
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
RootCert: req.RootCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -189,6 +208,10 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
||||
upMap["port"] = req.Port
|
||||
upMap["username"] = req.Username
|
||||
upMap["password"] = pass
|
||||
upMap["description"] = req.Description
|
||||
upMap["ssl"] = req.SSL
|
||||
upMap["client_key"] = req.ClientKey
|
||||
upMap["client_cert"] = req.ClientCert
|
||||
upMap["root_cert"] = req.RootCert
|
||||
upMap["skip_verify"] = req.SkipVerify
|
||||
return databaseRepo.Update(req.ID, upMap)
|
||||
}
|
||||
|
@ -641,6 +641,11 @@ func LoadMysqlClientByFrom(database string) (mysql.MysqlClient, string, error) {
|
||||
dbInfo.Port = databaseItem.Port
|
||||
dbInfo.Username = databaseItem.Username
|
||||
dbInfo.Password = databaseItem.Password
|
||||
dbInfo.SSL = databaseItem.SSL
|
||||
dbInfo.ClientKey = databaseItem.ClientKey
|
||||
dbInfo.ClientCert = databaseItem.ClientCert
|
||||
dbInfo.RootCert = databaseItem.RootCert
|
||||
dbInfo.SkipVerify = databaseItem.SkipVerify
|
||||
version = databaseItem.Version
|
||||
|
||||
} else {
|
||||
|
@ -57,6 +57,7 @@ func Init() {
|
||||
migrations.UpdateWebsiteSSL,
|
||||
migrations.AddWebsiteCA,
|
||||
migrations.AddDockerSockPath,
|
||||
migrations.AddDatabaseSSL,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
@ -45,3 +45,13 @@ var AddDockerSockPath = &gormigrate.Migration{
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var AddDatabaseSSL = &gormigrate.Migration{
|
||||
ID: "20231126-add-database-ssl",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.Database{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
@ -35,7 +35,12 @@ func NewMysqlClient(conn client.DBInfo) (MysqlClient, error) {
|
||||
if strings.Contains(conn.Address, ":") {
|
||||
conn.Address = fmt.Sprintf("[%s]", conn.Address)
|
||||
}
|
||||
connArgs := fmt.Sprintf("%s:%s@tcp(%s:%d)/?charset=utf8", conn.Username, conn.Password, conn.Address, conn.Port)
|
||||
|
||||
tlsItem, err := client.ConnWithSSL(conn.SSL, conn.SkipVerify, conn.ClientKey, conn.ClientCert, conn.RootCert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
connArgs := fmt.Sprintf("%s:%s@tcp(%s:%d)/?charset=utf8%s", conn.Username, conn.Password, conn.Address, conn.Port, tlsItem)
|
||||
db, err := sql.Open("mysql", connArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -57,5 +62,11 @@ func NewMysqlClient(conn client.DBInfo) (MysqlClient, error) {
|
||||
Password: conn.Password,
|
||||
Address: conn.Address,
|
||||
Port: conn.Port,
|
||||
|
||||
SSL: conn.SSL,
|
||||
RootCert: conn.RootCert,
|
||||
ClientKey: conn.ClientKey,
|
||||
ClientCert: conn.ClientCert,
|
||||
SkipVerify: conn.SkipVerify,
|
||||
}), nil
|
||||
}
|
||||
|
@ -1,9 +1,13 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
type DBInfo struct {
|
||||
@ -14,6 +18,12 @@ type DBInfo struct {
|
||||
Username string `json:"userName"`
|
||||
Password string `json:"password"`
|
||||
|
||||
SSL bool `json:"ssl"`
|
||||
RootCert string `json:"rootCert"`
|
||||
ClientKey string `json:"clientKey"`
|
||||
ClientCert string `json:"clientCert"`
|
||||
SkipVerify bool `json:"skipVerify"`
|
||||
|
||||
Timeout uint `json:"timeout"` // second
|
||||
}
|
||||
|
||||
@ -114,3 +124,45 @@ func randomPassword(user string) string {
|
||||
}
|
||||
return passwdItem + "@" + common.RandStrAndNum(8)
|
||||
}
|
||||
|
||||
func VerifyPeerCertFunc(pool *x509.CertPool) func([][]byte, [][]*x509.Certificate) error {
|
||||
return func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
|
||||
if len(rawCerts) == 0 {
|
||||
return errors.New("no certificates available to verify")
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(rawCerts[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts := x509.VerifyOptions{Roots: pool}
|
||||
if _, err = cert.Verify(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func ConnWithSSL(ssl, skipVerify bool, clientKey, clientCert, rootCert string) (string, error) {
|
||||
if !ssl {
|
||||
return "", nil
|
||||
}
|
||||
pool := x509.NewCertPool()
|
||||
if ok := pool.AppendCertsFromPEM([]byte(rootCert)); !ok {
|
||||
return "", errors.New("unable to append root cert to pool")
|
||||
}
|
||||
cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := mysql.RegisterTLSConfig("cloudsql", &tls.Config{
|
||||
RootCAs: pool,
|
||||
Certificates: []tls.Certificate{cert},
|
||||
InsecureSkipVerify: skipVerify,
|
||||
VerifyPeerCertificate: VerifyPeerCertFunc(pool),
|
||||
}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "&tls=cloudsql", nil
|
||||
}
|
||||
|
@ -23,6 +23,12 @@ type Remote struct {
|
||||
Password string
|
||||
Address string
|
||||
Port uint
|
||||
|
||||
SSL bool
|
||||
RootCert string
|
||||
ClientKey string
|
||||
ClientCert string
|
||||
SkipVerify bool
|
||||
}
|
||||
|
||||
func NewRemote(db Remote) *Remote {
|
||||
@ -224,7 +230,12 @@ func (r *Remote) Backup(info BackupInfo) error {
|
||||
}
|
||||
}
|
||||
fileNameItem := info.TargetDir + "/" + strings.TrimSuffix(info.FileName, ".gz")
|
||||
dns := fmt.Sprintf("%s:%s@tcp(%s:%v)/%s?charset=%s&parseTime=true&loc=Asia%sShanghai", r.User, r.Password, r.Address, r.Port, info.Name, info.Format, "%2F")
|
||||
|
||||
tlsItem, err := ConnWithSSL(r.SSL, r.SkipVerify, r.ClientKey, r.ClientCert, r.RootCert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dns := fmt.Sprintf("%s:%s@tcp(%s:%v)/%s?charset=%s&parseTime=true&loc=Asia%sShanghai%s", r.User, r.Password, r.Address, r.Port, info.Name, info.Format, "%2F", tlsItem)
|
||||
|
||||
f, _ := os.OpenFile(fileNameItem, os.O_RDWR|os.O_CREATE, 0755)
|
||||
defer f.Close()
|
||||
@ -254,7 +265,12 @@ func (r *Remote) Recover(info RecoverInfo) error {
|
||||
_, _ = gzipCmd.CombinedOutput()
|
||||
}()
|
||||
}
|
||||
dns := fmt.Sprintf("%s:%s@tcp(%s:%v)/%s?charset=%s&parseTime=true&loc=Asia%sShanghai", r.User, r.Password, r.Address, r.Port, info.Name, info.Format, "%2F")
|
||||
tlsItem, err := ConnWithSSL(r.SSL, r.SkipVerify, r.ClientKey, r.ClientCert, r.RootCert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dns := fmt.Sprintf("%s:%s@tcp(%s:%v)/%s?charset=%s&parseTime=true&loc=Asia%sShanghai%s", r.User, r.Password, r.Address, r.Port, info.Name, info.Format, "%2F", tlsItem)
|
||||
|
||||
f, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -213,6 +213,13 @@ export namespace Database {
|
||||
port: number;
|
||||
username: string;
|
||||
password: string;
|
||||
|
||||
ssl: boolean;
|
||||
rootCert: string;
|
||||
clientKey: string;
|
||||
clientCert: string;
|
||||
skipVerify: boolean;
|
||||
|
||||
description: string;
|
||||
}
|
||||
export interface SearchDatabasePage {
|
||||
@ -239,6 +246,13 @@ export namespace Database {
|
||||
port: number;
|
||||
username: string;
|
||||
password: string;
|
||||
|
||||
ssl: boolean;
|
||||
rootCert: string;
|
||||
clientKey: string;
|
||||
clientCert: string;
|
||||
skipVerify: boolean;
|
||||
|
||||
description: string;
|
||||
}
|
||||
export interface DatabaseUpdate {
|
||||
@ -248,6 +262,13 @@ export namespace Database {
|
||||
port: number;
|
||||
username: string;
|
||||
password: string;
|
||||
|
||||
ssl: boolean;
|
||||
rootCert: string;
|
||||
clientKey: string;
|
||||
clientCert: string;
|
||||
skipVerify: boolean;
|
||||
|
||||
description: string;
|
||||
}
|
||||
export interface DatabaseDelete {
|
||||
|
@ -101,13 +101,34 @@ export const listDatabases = (type: string) => {
|
||||
return http.get<Array<Database.DatabaseOption>>(`/databases/db/list/${type}`);
|
||||
};
|
||||
export const checkDatabase = (params: Database.DatabaseCreate) => {
|
||||
return http.post<boolean>(`/databases/db/check`, params, TimeoutEnum.T_40S);
|
||||
let request = deepCopy(params) as Database.DatabaseCreate;
|
||||
if (request.ssl) {
|
||||
request.clientKey = Base64.encode(request.clientKey);
|
||||
request.clientCert = Base64.encode(request.clientCert);
|
||||
request.rootCert = Base64.encode(request.rootCert);
|
||||
}
|
||||
|
||||
return http.post<boolean>(`/databases/db/check`, request, TimeoutEnum.T_40S);
|
||||
};
|
||||
export const addDatabase = (params: Database.DatabaseCreate) => {
|
||||
return http.post(`/databases/db`, params, TimeoutEnum.T_40S);
|
||||
let request = deepCopy(params) as Database.DatabaseCreate;
|
||||
if (request.ssl) {
|
||||
request.clientKey = Base64.encode(request.clientKey);
|
||||
request.clientCert = Base64.encode(request.clientCert);
|
||||
request.rootCert = Base64.encode(request.rootCert);
|
||||
}
|
||||
|
||||
return http.post(`/databases/db`, request, TimeoutEnum.T_40S);
|
||||
};
|
||||
export const editDatabase = (params: Database.DatabaseUpdate) => {
|
||||
return http.post(`/databases/db/update`, params, TimeoutEnum.T_40S);
|
||||
let request = deepCopy(params) as Database.DatabaseCreate;
|
||||
if (request.ssl) {
|
||||
request.clientKey = Base64.encode(request.clientKey);
|
||||
request.clientCert = Base64.encode(request.clientCert);
|
||||
request.rootCert = Base64.encode(request.rootCert);
|
||||
}
|
||||
|
||||
return http.post(`/databases/db/update`, request, TimeoutEnum.T_40S);
|
||||
};
|
||||
export const deleteCheckDatabase = (id: number) => {
|
||||
return http.post<Array<string>>(`/databases/db/del/check`, { id: id });
|
||||
|
@ -391,6 +391,11 @@ const message = {
|
||||
address: 'DB address',
|
||||
version: 'DB version',
|
||||
userHelper: 'The root user or a database user with root privileges can access the remote database.',
|
||||
ssl: 'Use SSL',
|
||||
clientKey: 'Client Private Key',
|
||||
clientCert: 'Client Certificate',
|
||||
caCert: 'CA Certificate',
|
||||
skipVerify: 'Ignore Certificate Validity Check',
|
||||
|
||||
formatHelper:
|
||||
'The current database character set is {0}, the character set inconsistency may cause recovery failure',
|
||||
|
@ -383,6 +383,11 @@ const message = {
|
||||
address: '數據庫地址',
|
||||
version: '數據庫版本',
|
||||
userHelper: 'root 用戶或者擁有 root 權限的數據庫用戶',
|
||||
ssl: '使用 SSL',
|
||||
clientKey: '客户端私钥',
|
||||
clientCert: '客户端证书',
|
||||
caCert: 'CA 证书',
|
||||
skipVerify: '忽略校验证书可用性检测',
|
||||
|
||||
formatHelper: '當前資料庫字符集為 {0},字符集不一致可能導致恢復失敗',
|
||||
selectFile: '選擇文件',
|
||||
|
@ -383,6 +383,11 @@ const message = {
|
||||
address: '数据库地址',
|
||||
version: '数据库版本',
|
||||
userHelper: 'root 用户或者拥有 root 权限的数据库用户',
|
||||
ssl: '使用 SSL',
|
||||
clientKey: '客户端私钥',
|
||||
clientCert: '客户端证书',
|
||||
caCert: 'CA 证书',
|
||||
skipVerify: '忽略校验证书可用性检测',
|
||||
|
||||
formatHelper: '当前数据库字符集为 {0},字符集不一致可能导致恢复失败',
|
||||
selectFile: '选择文件',
|
||||
|
@ -54,6 +54,46 @@
|
||||
v-model.trim="dialogData.rowData!.password"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox
|
||||
@change="isOK = false"
|
||||
v-model="dialogData.rowData!.ssl"
|
||||
:label="$t('database.ssl')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div v-if="dialogData.rowData!.ssl">
|
||||
<el-form-item :label="$t('database.clientKey')" prop="clientKey">
|
||||
<el-input
|
||||
type="textarea"
|
||||
@change="isOK = false"
|
||||
clearable
|
||||
v-model="dialogData.rowData!.clientKey"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('database.clientCert')" prop="clientCert">
|
||||
<el-input
|
||||
type="textarea"
|
||||
@change="isOK = false"
|
||||
clearable
|
||||
v-model="dialogData.rowData!.clientCert"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('database.caCert')" prop="rootCert">
|
||||
<el-input
|
||||
type="textarea"
|
||||
@change="isOK = false"
|
||||
clearable
|
||||
v-model="dialogData.rowData!.rootCert"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox
|
||||
@change="isOK = false"
|
||||
v-model="dialogData.rowData!.skipVerify"
|
||||
:label="$t('database.skipVerify')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item :label="$t('commons.table.description')" prop="description">
|
||||
<el-input clearable v-model.trim="dialogData.rowData!.description" />
|
||||
</el-form-item>
|
||||
@ -128,6 +168,10 @@ const rules = reactive({
|
||||
port: [Rules.port],
|
||||
username: [Rules.requiredInput],
|
||||
password: [Rules.requiredInput],
|
||||
|
||||
clientKey: [Rules.requiredInput],
|
||||
clientCert: [Rules.requiredInput],
|
||||
caCert: [Rules.requiredInput],
|
||||
});
|
||||
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
|
@ -43,7 +43,7 @@
|
||||
</span>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6" align="center">
|
||||
<el-popover placement="bottom" :width="160" trigger="hover">
|
||||
<el-popover placement="bottom" :width="160" trigger="hover" v-if="chartsOption['memory']">
|
||||
<el-tag style="font-weight: 500">{{ $t('home.mem') }}:</el-tag>
|
||||
<el-tag class="tagClass">
|
||||
{{ $t('home.total') }}: {{ formatNumber(currentInfo.memoryTotal / 1024 / 1024) }} MB
|
||||
@ -57,10 +57,8 @@
|
||||
<el-tag class="tagClass">
|
||||
{{ $t('home.percent') }}: {{ formatNumber(currentInfo.memoryUsedPercent) }}%
|
||||
</el-tag>
|
||||
<div v-if="currentInfo.swapMemoryTotal">
|
||||
<el-row :gutter="5" class="mt-2">
|
||||
<el-tag style="font-weight: 500">{{ $t('home.swapMem') }}:</el-tag>
|
||||
</el-row>
|
||||
<div v-if="currentInfo.swapMemoryTotal" class="mt-2">
|
||||
<el-tag style="font-weight: 500">{{ $t('home.swapMem') }}:</el-tag>
|
||||
<el-tag class="tagClass">
|
||||
{{ $t('home.total') }}: {{ formatNumber(currentInfo.swapMemoryTotal / 1024 / 1024) }} MB
|
||||
</el-tag>
|
||||
|
Loading…
Reference in New Issue
Block a user