2023-07-20 09:51:57 +00:00
|
|
|
package service
|
|
|
|
|
|
|
|
import (
|
2023-07-27 08:07:27 +00:00
|
|
|
"context"
|
2023-09-01 15:10:14 +00:00
|
|
|
"fmt"
|
2023-10-08 06:52:14 +00:00
|
|
|
"os"
|
|
|
|
"path"
|
2023-07-27 08:07:27 +00:00
|
|
|
|
2024-01-02 09:08:13 +00:00
|
|
|
"github.com/1Panel-dev/1Panel/backend/utils/postgresql"
|
2024-05-15 03:56:40 +00:00
|
|
|
pg_client "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
|
|
|
|
redis_client "github.com/1Panel-dev/1Panel/backend/utils/redis"
|
2024-01-02 09:08:13 +00:00
|
|
|
|
2023-07-20 09:51:57 +00:00
|
|
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
2023-09-16 05:16:15 +00:00
|
|
|
"github.com/1Panel-dev/1Panel/backend/buserr"
|
2023-07-20 09:51:57 +00:00
|
|
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
2023-10-08 06:52:14 +00:00
|
|
|
"github.com/1Panel-dev/1Panel/backend/global"
|
2023-09-01 15:10:14 +00:00
|
|
|
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
|
2023-07-20 09:51:57 +00:00
|
|
|
"github.com/1Panel-dev/1Panel/backend/utils/mysql"
|
|
|
|
"github.com/1Panel-dev/1Panel/backend/utils/mysql/client"
|
|
|
|
"github.com/jinzhu/copier"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
2023-08-29 02:50:15 +00:00
|
|
|
type DatabaseService struct{}
|
2023-07-20 09:51:57 +00:00
|
|
|
|
2023-08-29 02:50:15 +00:00
|
|
|
type IDatabaseService interface {
|
|
|
|
Get(name string) (dto.DatabaseInfo, error)
|
|
|
|
SearchWithPage(search dto.DatabaseSearch) (int64, interface{}, error)
|
|
|
|
CheckDatabase(req dto.DatabaseCreate) bool
|
|
|
|
Create(req dto.DatabaseCreate) error
|
|
|
|
Update(req dto.DatabaseUpdate) error
|
2023-10-08 06:52:14 +00:00
|
|
|
DeleteCheck(id uint) ([]string, error)
|
|
|
|
Delete(req dto.DatabaseDelete) error
|
2023-08-29 02:50:15 +00:00
|
|
|
List(dbType string) ([]dto.DatabaseOption, error)
|
2024-01-07 14:52:41 +00:00
|
|
|
LoadItems(dbType string) ([]dto.DatabaseItem, error)
|
2023-07-20 09:51:57 +00:00
|
|
|
}
|
|
|
|
|
2023-08-29 02:50:15 +00:00
|
|
|
func NewIDatabaseService() IDatabaseService {
|
|
|
|
return &DatabaseService{}
|
2023-07-20 09:51:57 +00:00
|
|
|
}
|
|
|
|
|
2023-08-29 02:50:15 +00:00
|
|
|
func (u *DatabaseService) SearchWithPage(search dto.DatabaseSearch) (int64, interface{}, error) {
|
|
|
|
total, dbs, err := databaseRepo.Page(search.Page, search.PageSize,
|
2023-09-01 04:28:12 +00:00
|
|
|
databaseRepo.WithTypeList(search.Type),
|
2023-07-20 09:51:57 +00:00
|
|
|
commonRepo.WithLikeName(search.Info),
|
2024-06-03 06:07:44 +00:00
|
|
|
commonRepo.WithOrderRuleBy(search.OrderBy, search.Order),
|
2023-08-29 02:50:15 +00:00
|
|
|
databaseRepo.WithoutByFrom("local"),
|
2023-07-20 09:51:57 +00:00
|
|
|
)
|
2023-08-29 02:50:15 +00:00
|
|
|
var datas []dto.DatabaseInfo
|
2023-07-20 09:51:57 +00:00
|
|
|
for _, db := range dbs {
|
2023-08-29 02:50:15 +00:00
|
|
|
var item dto.DatabaseInfo
|
2023-07-20 09:51:57 +00:00
|
|
|
if err := copier.Copy(&item, &db); err != nil {
|
|
|
|
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
|
|
|
}
|
|
|
|
datas = append(datas, item)
|
|
|
|
}
|
|
|
|
return total, datas, err
|
|
|
|
}
|
|
|
|
|
2023-08-29 02:50:15 +00:00
|
|
|
func (u *DatabaseService) Get(name string) (dto.DatabaseInfo, error) {
|
|
|
|
var data dto.DatabaseInfo
|
|
|
|
remote, err := databaseRepo.Get(commonRepo.WithByName(name))
|
2023-07-27 08:07:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return data, err
|
|
|
|
}
|
|
|
|
if err := copier.Copy(&data, &remote); err != nil {
|
|
|
|
return data, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
|
|
|
}
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
2023-08-29 02:50:15 +00:00
|
|
|
func (u *DatabaseService) List(dbType string) ([]dto.DatabaseOption, error) {
|
2023-09-01 04:28:12 +00:00
|
|
|
dbs, err := databaseRepo.GetList(databaseRepo.WithTypeList(dbType))
|
2024-05-30 07:13:21 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-08-29 02:50:15 +00:00
|
|
|
var datas []dto.DatabaseOption
|
2023-07-20 09:51:57 +00:00
|
|
|
for _, db := range dbs {
|
2023-08-29 02:50:15 +00:00
|
|
|
var item dto.DatabaseOption
|
2023-07-20 09:51:57 +00:00
|
|
|
if err := copier.Copy(&item, &db); err != nil {
|
|
|
|
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
|
|
|
}
|
2023-08-29 02:50:15 +00:00
|
|
|
item.Database = db.Name
|
2023-07-20 09:51:57 +00:00
|
|
|
datas = append(datas, item)
|
|
|
|
}
|
|
|
|
return datas, err
|
|
|
|
}
|
|
|
|
|
2024-01-07 14:52:41 +00:00
|
|
|
func (u *DatabaseService) LoadItems(dbType string) ([]dto.DatabaseItem, error) {
|
|
|
|
dbs, err := databaseRepo.GetList(databaseRepo.WithTypeList(dbType))
|
|
|
|
var datas []dto.DatabaseItem
|
|
|
|
for _, db := range dbs {
|
|
|
|
if dbType == "postgresql" {
|
|
|
|
items, _ := postgresqlRepo.List(postgresqlRepo.WithByPostgresqlName(db.Name))
|
|
|
|
for _, item := range items {
|
|
|
|
var dItem dto.DatabaseItem
|
|
|
|
if err := copier.Copy(&dItem, &item); err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
dItem.Database = db.Name
|
|
|
|
datas = append(datas, dItem)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
items, _ := mysqlRepo.List(mysqlRepo.WithByMysqlName(db.Name))
|
|
|
|
for _, item := range items {
|
|
|
|
var dItem dto.DatabaseItem
|
|
|
|
if err := copier.Copy(&dItem, &item); err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
dItem.Database = db.Name
|
|
|
|
datas = append(datas, dItem)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return datas, err
|
|
|
|
}
|
|
|
|
|
2023-08-29 02:50:15 +00:00
|
|
|
func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
2023-12-28 08:29:18 +00:00
|
|
|
switch req.Type {
|
|
|
|
case constant.AppPostgresql:
|
2024-05-15 03:56:40 +00:00
|
|
|
_, err := postgresql.NewPostgresqlClient(pg_client.DBInfo{
|
2023-12-28 08:29:18 +00:00
|
|
|
From: "remote",
|
|
|
|
Address: req.Address,
|
|
|
|
Port: req.Port,
|
|
|
|
Username: req.Username,
|
|
|
|
Password: req.Password,
|
2024-01-02 09:08:13 +00:00
|
|
|
Timeout: 6,
|
2023-12-28 08:29:18 +00:00
|
|
|
})
|
|
|
|
return err == nil
|
2024-05-15 03:56:40 +00:00
|
|
|
case constant.AppRedis:
|
|
|
|
_, err := redis_client.NewRedisClient(redis_client.DBInfo{
|
|
|
|
Address: req.Address,
|
|
|
|
Port: req.Port,
|
|
|
|
Password: req.Password,
|
|
|
|
})
|
|
|
|
return err == nil
|
2023-12-28 08:29:18 +00:00
|
|
|
case "mysql", "mariadb":
|
|
|
|
_, err := mysql.NewMysqlClient(client.DBInfo{
|
|
|
|
From: "remote",
|
|
|
|
Address: req.Address,
|
|
|
|
Port: req.Port,
|
|
|
|
Username: req.Username,
|
|
|
|
Password: req.Password,
|
|
|
|
|
|
|
|
SSL: req.SSL,
|
|
|
|
RootCert: req.RootCert,
|
|
|
|
ClientKey: req.ClientKey,
|
|
|
|
ClientCert: req.ClientCert,
|
|
|
|
SkipVerify: req.SkipVerify,
|
|
|
|
Timeout: 6,
|
|
|
|
})
|
|
|
|
return err == nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
2023-08-11 14:20:15 +00:00
|
|
|
}
|
|
|
|
|
2023-08-29 02:50:15 +00:00
|
|
|
func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
|
2023-09-16 05:16:15 +00:00
|
|
|
db, _ := databaseRepo.Get(commonRepo.WithByName(req.Name))
|
2023-07-20 09:51:57 +00:00
|
|
|
if db.ID != 0 {
|
2023-09-16 05:16:15 +00:00
|
|
|
if db.From == "local" {
|
|
|
|
return buserr.New(constant.ErrLocalExist)
|
|
|
|
}
|
2023-07-20 09:51:57 +00:00
|
|
|
return constant.ErrRecordExist
|
|
|
|
}
|
2023-12-28 08:29:18 +00:00
|
|
|
switch req.Type {
|
|
|
|
case constant.AppPostgresql:
|
2024-05-15 03:56:40 +00:00
|
|
|
if _, err := postgresql.NewPostgresqlClient(pg_client.DBInfo{
|
2023-12-28 08:29:18 +00:00
|
|
|
From: "remote",
|
|
|
|
Address: req.Address,
|
|
|
|
Port: req.Port,
|
|
|
|
Username: req.Username,
|
|
|
|
Password: req.Password,
|
2024-01-02 09:08:13 +00:00
|
|
|
Timeout: 6,
|
2023-12-28 08:29:18 +00:00
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-05-15 03:56:40 +00:00
|
|
|
case constant.AppRedis:
|
|
|
|
if _, err := redis_client.NewRedisClient(redis_client.DBInfo{
|
|
|
|
Address: req.Address,
|
|
|
|
Port: req.Port,
|
|
|
|
Password: req.Password,
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-12-28 08:29:18 +00:00
|
|
|
case "mysql", "mariadb":
|
|
|
|
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
|
|
|
From: "remote",
|
|
|
|
Address: req.Address,
|
|
|
|
Port: req.Port,
|
|
|
|
Username: req.Username,
|
|
|
|
Password: req.Password,
|
|
|
|
|
|
|
|
SSL: req.SSL,
|
|
|
|
RootCert: req.RootCert,
|
|
|
|
ClientKey: req.ClientKey,
|
|
|
|
ClientCert: req.ClientCert,
|
|
|
|
SkipVerify: req.SkipVerify,
|
|
|
|
Timeout: 6,
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return errors.New("database type not supported")
|
2023-07-20 09:51:57 +00:00
|
|
|
}
|
2023-12-28 08:29:18 +00:00
|
|
|
|
2023-07-20 09:51:57 +00:00
|
|
|
if err := copier.Copy(&db, &req); err != nil {
|
|
|
|
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
|
|
|
}
|
2023-08-29 14:22:18 +00:00
|
|
|
if err := databaseRepo.Create(context.Background(), &db); err != nil {
|
2023-07-20 09:51:57 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-10-08 06:52:14 +00:00
|
|
|
func (u *DatabaseService) DeleteCheck(id uint) ([]string, error) {
|
|
|
|
var appInUsed []string
|
|
|
|
apps, _ := appInstallResourceRepo.GetBy(databaseRepo.WithByFrom("remote"), appInstallResourceRepo.WithLinkId(id))
|
|
|
|
for _, app := range apps {
|
|
|
|
appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId))
|
|
|
|
if appInstall.ID != 0 {
|
|
|
|
appInUsed = append(appInUsed, appInstall.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return appInUsed, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *DatabaseService) Delete(req dto.DatabaseDelete) error {
|
|
|
|
db, _ := databaseRepo.Get(commonRepo.WithByID(req.ID))
|
2023-07-20 09:51:57 +00:00
|
|
|
if db.ID == 0 {
|
|
|
|
return constant.ErrRecordNotFound
|
|
|
|
}
|
2023-10-08 06:52:14 +00:00
|
|
|
|
|
|
|
if req.DeleteBackup {
|
|
|
|
uploadDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/uploads/database/%s/%s", db.Type, db.Name))
|
|
|
|
if _, err := os.Stat(uploadDir); err == nil {
|
|
|
|
_ = os.RemoveAll(uploadDir)
|
|
|
|
}
|
|
|
|
localDir, err := loadLocalDir()
|
|
|
|
if err != nil && !req.ForceDelete {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
backupDir := path.Join(localDir, fmt.Sprintf("database/%s/%s", db.Type, db.Name))
|
|
|
|
if _, err := os.Stat(backupDir); err == nil {
|
|
|
|
_ = os.RemoveAll(backupDir)
|
|
|
|
}
|
|
|
|
_ = backupRepo.DeleteRecord(context.Background(), commonRepo.WithByType(db.Type), commonRepo.WithByName(db.Name))
|
|
|
|
global.LOG.Infof("delete database %s-%s backups successful", db.Type, db.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := databaseRepo.Delete(context.Background(), commonRepo.WithByID(req.ID)); err != nil && !req.ForceDelete {
|
2023-07-27 08:07:27 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
if db.From != "local" {
|
2024-01-10 12:59:34 +00:00
|
|
|
if db.Type == "mysql" || db.Type == "mariadb" {
|
|
|
|
if err := mysqlRepo.Delete(context.Background(), mysqlRepo.WithByMysqlName(db.Name)); err != nil && !req.ForceDelete {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
2024-01-11 05:53:50 +00:00
|
|
|
if err := postgresqlRepo.Delete(context.Background(), postgresqlRepo.WithByPostgresqlName(db.Name)); err != nil && !req.ForceDelete {
|
2024-01-10 12:59:34 +00:00
|
|
|
return err
|
|
|
|
}
|
2023-07-27 08:07:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2023-07-20 09:51:57 +00:00
|
|
|
}
|
|
|
|
|
2023-08-29 02:50:15 +00:00
|
|
|
func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
2023-12-28 08:29:18 +00:00
|
|
|
switch req.Type {
|
|
|
|
case constant.AppPostgresql:
|
2024-05-15 03:56:40 +00:00
|
|
|
if _, err := postgresql.NewPostgresqlClient(pg_client.DBInfo{
|
2023-12-28 08:29:18 +00:00
|
|
|
From: "remote",
|
|
|
|
Address: req.Address,
|
|
|
|
Port: req.Port,
|
|
|
|
Username: req.Username,
|
|
|
|
Password: req.Password,
|
2024-01-02 09:08:13 +00:00
|
|
|
Timeout: 300,
|
2023-12-28 08:29:18 +00:00
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-05-29 14:13:11 +00:00
|
|
|
case constant.AppRedis:
|
|
|
|
if _, err := redis_client.NewRedisClient(redis_client.DBInfo{
|
|
|
|
Address: req.Address,
|
|
|
|
Port: req.Port,
|
|
|
|
Password: req.Password,
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-12-28 08:29:18 +00:00
|
|
|
case "mysql", "mariadb":
|
|
|
|
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
|
|
|
From: "remote",
|
|
|
|
Address: req.Address,
|
|
|
|
Port: req.Port,
|
|
|
|
Username: req.Username,
|
|
|
|
Password: req.Password,
|
|
|
|
|
|
|
|
SSL: req.SSL,
|
|
|
|
RootCert: req.RootCert,
|
|
|
|
ClientKey: req.ClientKey,
|
|
|
|
ClientCert: req.ClientCert,
|
|
|
|
SkipVerify: req.SkipVerify,
|
|
|
|
Timeout: 300,
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return errors.New("database type not supported")
|
2023-07-20 09:51:57 +00:00
|
|
|
}
|
|
|
|
|
2023-09-01 15:10:14 +00:00
|
|
|
pass, err := encrypt.StringEncrypt(req.Password)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("decrypt database password failed, err: %v", err)
|
|
|
|
}
|
|
|
|
|
2023-07-20 09:51:57 +00:00
|
|
|
upMap := make(map[string]interface{})
|
2023-09-06 13:20:16 +00:00
|
|
|
upMap["type"] = req.Type
|
2023-07-20 09:51:57 +00:00
|
|
|
upMap["version"] = req.Version
|
|
|
|
upMap["address"] = req.Address
|
|
|
|
upMap["port"] = req.Port
|
|
|
|
upMap["username"] = req.Username
|
2023-09-01 15:10:14 +00:00
|
|
|
upMap["password"] = pass
|
2023-12-28 08:29:18 +00:00
|
|
|
upMap["description"] = req.Description
|
2023-11-29 02:46:07 +00:00
|
|
|
upMap["ssl"] = req.SSL
|
|
|
|
upMap["client_key"] = req.ClientKey
|
|
|
|
upMap["client_cert"] = req.ClientCert
|
|
|
|
upMap["root_cert"] = req.RootCert
|
|
|
|
upMap["skip_verify"] = req.SkipVerify
|
2023-08-29 02:50:15 +00:00
|
|
|
return databaseRepo.Update(req.ID, upMap)
|
2023-07-20 09:51:57 +00:00
|
|
|
}
|