mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-12 00:16:37 +08:00
feat: Mysql 远程数据库支持 SSL (#3091)
This commit is contained in:
parent
86bc75ff28
commit
10848fb249
17 changed files with 313 additions and 42 deletions
|
@ -1,6 +1,8 @@
|
||||||
package v1
|
package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"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 {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
return
|
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 {
|
if err := databaseService.Create(req); err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
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 {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
return
|
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))
|
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 {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
return
|
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 {
|
if err := databaseService.Update(req); err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
|
|
@ -237,6 +237,13 @@ type DatabaseInfo struct {
|
||||||
Port uint `json:"port"`
|
Port uint `json:"port"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Password string `json:"password"`
|
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"`
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,6 +265,13 @@ type DatabaseCreate struct {
|
||||||
Port uint `json:"port"`
|
Port uint `json:"port"`
|
||||||
Username string `json:"username" validate:"required"`
|
Username string `json:"username" validate:"required"`
|
||||||
Password string `json:"password" 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"`
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,6 +283,13 @@ type DatabaseUpdate struct {
|
||||||
Port uint `json:"port"`
|
Port uint `json:"port"`
|
||||||
Username string `json:"username" validate:"required"`
|
Username string `json:"username" validate:"required"`
|
||||||
Password string `json:"password" 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"`
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,5 +11,12 @@ type Database struct {
|
||||||
Port uint `json:"port" gorm:"type:decimal;not null"`
|
Port uint `json:"port" gorm:"type:decimal;not null"`
|
||||||
Username string `json:"username" gorm:"type:varchar(64)"`
|
Username string `json:"username" gorm:"type:varchar(64)"`
|
||||||
Password string `json:"password" gorm:"type:varchar(64)"`
|
Password string `json:"password" gorm:"type:varchar(64)"`
|
||||||
|
|
||||||
|
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);"`
|
Description string `json:"description" gorm:"type:varchar(256);"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,12 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
||||||
Port: req.Port,
|
Port: req.Port,
|
||||||
Username: req.Username,
|
Username: req.Username,
|
||||||
Password: req.Password,
|
Password: req.Password,
|
||||||
|
|
||||||
|
SSL: req.SSL,
|
||||||
|
RootCert: req.RootCert,
|
||||||
|
ClientKey: req.ClientKey,
|
||||||
|
ClientCert: req.ClientCert,
|
||||||
|
SkipVerify: req.SkipVerify,
|
||||||
Timeout: 6,
|
Timeout: 6,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return false
|
return false
|
||||||
|
@ -105,6 +111,12 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
|
||||||
Port: req.Port,
|
Port: req.Port,
|
||||||
Username: req.Username,
|
Username: req.Username,
|
||||||
Password: req.Password,
|
Password: req.Password,
|
||||||
|
|
||||||
|
SSL: req.SSL,
|
||||||
|
RootCert: req.RootCert,
|
||||||
|
ClientKey: req.ClientKey,
|
||||||
|
ClientCert: req.ClientCert,
|
||||||
|
SkipVerify: req.SkipVerify,
|
||||||
Timeout: 6,
|
Timeout: 6,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -172,6 +184,13 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
||||||
Port: req.Port,
|
Port: req.Port,
|
||||||
Username: req.Username,
|
Username: req.Username,
|
||||||
Password: req.Password,
|
Password: req.Password,
|
||||||
|
|
||||||
|
SSL: req.SSL,
|
||||||
|
ClientKey: req.ClientKey,
|
||||||
|
ClientCert: req.ClientCert,
|
||||||
|
RootCert: req.RootCert,
|
||||||
|
SkipVerify: req.SkipVerify,
|
||||||
|
|
||||||
Timeout: 300,
|
Timeout: 300,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -189,6 +208,10 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
||||||
upMap["port"] = req.Port
|
upMap["port"] = req.Port
|
||||||
upMap["username"] = req.Username
|
upMap["username"] = req.Username
|
||||||
upMap["password"] = pass
|
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)
|
return databaseRepo.Update(req.ID, upMap)
|
||||||
}
|
}
|
||||||
|
|
|
@ -641,6 +641,11 @@ func LoadMysqlClientByFrom(database string) (mysql.MysqlClient, string, error) {
|
||||||
dbInfo.Port = databaseItem.Port
|
dbInfo.Port = databaseItem.Port
|
||||||
dbInfo.Username = databaseItem.Username
|
dbInfo.Username = databaseItem.Username
|
||||||
dbInfo.Password = databaseItem.Password
|
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
|
version = databaseItem.Version
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -57,6 +57,7 @@ func Init() {
|
||||||
migrations.UpdateWebsiteSSL,
|
migrations.UpdateWebsiteSSL,
|
||||||
migrations.AddWebsiteCA,
|
migrations.AddWebsiteCA,
|
||||||
migrations.AddDockerSockPath,
|
migrations.AddDockerSockPath,
|
||||||
|
migrations.AddDatabaseSSL,
|
||||||
})
|
})
|
||||||
if err := m.Migrate(); err != nil {
|
if err := m.Migrate(); err != nil {
|
||||||
global.LOG.Error(err)
|
global.LOG.Error(err)
|
||||||
|
|
|
@ -45,3 +45,13 @@ var AddDockerSockPath = &gormigrate.Migration{
|
||||||
return nil
|
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, ":") {
|
if strings.Contains(conn.Address, ":") {
|
||||||
conn.Address = fmt.Sprintf("[%s]", 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)
|
db, err := sql.Open("mysql", connArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -57,5 +62,11 @@ func NewMysqlClient(conn client.DBInfo) (MysqlClient, error) {
|
||||||
Password: conn.Password,
|
Password: conn.Password,
|
||||||
Address: conn.Address,
|
Address: conn.Address,
|
||||||
Port: conn.Port,
|
Port: conn.Port,
|
||||||
|
|
||||||
|
SSL: conn.SSL,
|
||||||
|
RootCert: conn.RootCert,
|
||||||
|
ClientKey: conn.ClientKey,
|
||||||
|
ClientCert: conn.ClientCert,
|
||||||
|
SkipVerify: conn.SkipVerify,
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||||
|
"github.com/go-sql-driver/mysql"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DBInfo struct {
|
type DBInfo struct {
|
||||||
|
@ -14,6 +18,12 @@ type DBInfo struct {
|
||||||
Username string `json:"userName"`
|
Username string `json:"userName"`
|
||||||
Password string `json:"password"`
|
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
|
Timeout uint `json:"timeout"` // second
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,3 +124,45 @@ func randomPassword(user string) string {
|
||||||
}
|
}
|
||||||
return passwdItem + "@" + common.RandStrAndNum(8)
|
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
|
Password string
|
||||||
Address string
|
Address string
|
||||||
Port uint
|
Port uint
|
||||||
|
|
||||||
|
SSL bool
|
||||||
|
RootCert string
|
||||||
|
ClientKey string
|
||||||
|
ClientCert string
|
||||||
|
SkipVerify bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRemote(db Remote) *Remote {
|
func NewRemote(db Remote) *Remote {
|
||||||
|
@ -224,7 +230,12 @@ func (r *Remote) Backup(info BackupInfo) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fileNameItem := info.TargetDir + "/" + strings.TrimSuffix(info.FileName, ".gz")
|
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)
|
f, _ := os.OpenFile(fileNameItem, os.O_RDWR|os.O_CREATE, 0755)
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
@ -254,7 +265,12 @@ func (r *Remote) Recover(info RecoverInfo) error {
|
||||||
_, _ = gzipCmd.CombinedOutput()
|
_, _ = 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)
|
f, err := os.Open(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -213,6 +213,13 @@ export namespace Database {
|
||||||
port: number;
|
port: number;
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
|
|
||||||
|
ssl: boolean;
|
||||||
|
rootCert: string;
|
||||||
|
clientKey: string;
|
||||||
|
clientCert: string;
|
||||||
|
skipVerify: boolean;
|
||||||
|
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
export interface SearchDatabasePage {
|
export interface SearchDatabasePage {
|
||||||
|
@ -239,6 +246,13 @@ export namespace Database {
|
||||||
port: number;
|
port: number;
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
|
|
||||||
|
ssl: boolean;
|
||||||
|
rootCert: string;
|
||||||
|
clientKey: string;
|
||||||
|
clientCert: string;
|
||||||
|
skipVerify: boolean;
|
||||||
|
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
export interface DatabaseUpdate {
|
export interface DatabaseUpdate {
|
||||||
|
@ -248,6 +262,13 @@ export namespace Database {
|
||||||
port: number;
|
port: number;
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
|
|
||||||
|
ssl: boolean;
|
||||||
|
rootCert: string;
|
||||||
|
clientKey: string;
|
||||||
|
clientCert: string;
|
||||||
|
skipVerify: boolean;
|
||||||
|
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
export interface DatabaseDelete {
|
export interface DatabaseDelete {
|
||||||
|
|
|
@ -101,13 +101,34 @@ export const listDatabases = (type: string) => {
|
||||||
return http.get<Array<Database.DatabaseOption>>(`/databases/db/list/${type}`);
|
return http.get<Array<Database.DatabaseOption>>(`/databases/db/list/${type}`);
|
||||||
};
|
};
|
||||||
export const checkDatabase = (params: Database.DatabaseCreate) => {
|
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) => {
|
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) => {
|
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) => {
|
export const deleteCheckDatabase = (id: number) => {
|
||||||
return http.post<Array<string>>(`/databases/db/del/check`, { id: id });
|
return http.post<Array<string>>(`/databases/db/del/check`, { id: id });
|
||||||
|
|
|
@ -391,6 +391,11 @@ const message = {
|
||||||
address: 'DB address',
|
address: 'DB address',
|
||||||
version: 'DB version',
|
version: 'DB version',
|
||||||
userHelper: 'The root user or a database user with root privileges can access the remote database.',
|
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:
|
formatHelper:
|
||||||
'The current database character set is {0}, the character set inconsistency may cause recovery failure',
|
'The current database character set is {0}, the character set inconsistency may cause recovery failure',
|
||||||
|
|
|
@ -383,6 +383,11 @@ const message = {
|
||||||
address: '數據庫地址',
|
address: '數據庫地址',
|
||||||
version: '數據庫版本',
|
version: '數據庫版本',
|
||||||
userHelper: 'root 用戶或者擁有 root 權限的數據庫用戶',
|
userHelper: 'root 用戶或者擁有 root 權限的數據庫用戶',
|
||||||
|
ssl: '使用 SSL',
|
||||||
|
clientKey: '客户端私钥',
|
||||||
|
clientCert: '客户端证书',
|
||||||
|
caCert: 'CA 证书',
|
||||||
|
skipVerify: '忽略校验证书可用性检测',
|
||||||
|
|
||||||
formatHelper: '當前資料庫字符集為 {0},字符集不一致可能導致恢復失敗',
|
formatHelper: '當前資料庫字符集為 {0},字符集不一致可能導致恢復失敗',
|
||||||
selectFile: '選擇文件',
|
selectFile: '選擇文件',
|
||||||
|
|
|
@ -383,6 +383,11 @@ const message = {
|
||||||
address: '数据库地址',
|
address: '数据库地址',
|
||||||
version: '数据库版本',
|
version: '数据库版本',
|
||||||
userHelper: 'root 用户或者拥有 root 权限的数据库用户',
|
userHelper: 'root 用户或者拥有 root 权限的数据库用户',
|
||||||
|
ssl: '使用 SSL',
|
||||||
|
clientKey: '客户端私钥',
|
||||||
|
clientCert: '客户端证书',
|
||||||
|
caCert: 'CA 证书',
|
||||||
|
skipVerify: '忽略校验证书可用性检测',
|
||||||
|
|
||||||
formatHelper: '当前数据库字符集为 {0},字符集不一致可能导致恢复失败',
|
formatHelper: '当前数据库字符集为 {0},字符集不一致可能导致恢复失败',
|
||||||
selectFile: '选择文件',
|
selectFile: '选择文件',
|
||||||
|
|
|
@ -54,6 +54,46 @@
|
||||||
v-model.trim="dialogData.rowData!.password"
|
v-model.trim="dialogData.rowData!.password"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</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-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" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
@ -128,6 +168,10 @@ const rules = reactive({
|
||||||
port: [Rules.port],
|
port: [Rules.port],
|
||||||
username: [Rules.requiredInput],
|
username: [Rules.requiredInput],
|
||||||
password: [Rules.requiredInput],
|
password: [Rules.requiredInput],
|
||||||
|
|
||||||
|
clientKey: [Rules.requiredInput],
|
||||||
|
clientCert: [Rules.requiredInput],
|
||||||
|
caCert: [Rules.requiredInput],
|
||||||
});
|
});
|
||||||
|
|
||||||
type FormInstance = InstanceType<typeof ElForm>;
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
</span>
|
</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6" align="center">
|
<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 style="font-weight: 500">{{ $t('home.mem') }}:</el-tag>
|
||||||
<el-tag class="tagClass">
|
<el-tag class="tagClass">
|
||||||
{{ $t('home.total') }}: {{ formatNumber(currentInfo.memoryTotal / 1024 / 1024) }} MB
|
{{ $t('home.total') }}: {{ formatNumber(currentInfo.memoryTotal / 1024 / 1024) }} MB
|
||||||
|
@ -57,10 +57,8 @@
|
||||||
<el-tag class="tagClass">
|
<el-tag class="tagClass">
|
||||||
{{ $t('home.percent') }}: {{ formatNumber(currentInfo.memoryUsedPercent) }}%
|
{{ $t('home.percent') }}: {{ formatNumber(currentInfo.memoryUsedPercent) }}%
|
||||||
</el-tag>
|
</el-tag>
|
||||||
<div v-if="currentInfo.swapMemoryTotal">
|
<div v-if="currentInfo.swapMemoryTotal" class="mt-2">
|
||||||
<el-row :gutter="5" class="mt-2">
|
|
||||||
<el-tag style="font-weight: 500">{{ $t('home.swapMem') }}:</el-tag>
|
<el-tag style="font-weight: 500">{{ $t('home.swapMem') }}:</el-tag>
|
||||||
</el-row>
|
|
||||||
<el-tag class="tagClass">
|
<el-tag class="tagClass">
|
||||||
{{ $t('home.total') }}: {{ formatNumber(currentInfo.swapMemoryTotal / 1024 / 1024) }} MB
|
{{ $t('home.total') }}: {{ formatNumber(currentInfo.swapMemoryTotal / 1024 / 1024) }} MB
|
||||||
</el-tag>
|
</el-tag>
|
||||||
|
|
Loading…
Add table
Reference in a new issue