fix: 解决删除网站导致的数据库锁库问题

This commit is contained in:
zhengkunwang223 2022-12-21 15:54:34 +08:00
parent 94ebe4952a
commit 0a8bf98ee9
12 changed files with 66 additions and 57 deletions

View file

@ -1,6 +1,7 @@
package v1 package v1
import ( import (
"context"
"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/request" "github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/constant"
@ -63,7 +64,7 @@ func (b *BaseApi) InstallApp(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return return
} }
install, err := appService.Install(req.Name, req.AppDetailId, req.Params) install, err := appService.Install(context.Background(), req)
if err != nil { if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return

View file

@ -1,6 +1,7 @@
package v1 package v1
import ( import (
"context"
"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"
@ -18,7 +19,7 @@ func (b *BaseApi) CreateMysql(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return return
} }
if err := mysqlService.Create(req); err != nil { if _, err := mysqlService.Create(context.Background(), req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return
} }

View file

@ -1,6 +1,7 @@
package repo package repo
import ( import (
"context"
"github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
"gorm.io/gorm" "gorm.io/gorm"
@ -17,7 +18,7 @@ type IBackupRepo interface {
CreateRecord(record *model.BackupRecord) error CreateRecord(record *model.BackupRecord) error
Update(id uint, vars map[string]interface{}) error Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error Delete(opts ...DBOption) error
DeleteRecord(opts ...DBOption) error DeleteRecord(ctx context.Context, opts ...DBOption) error
WithByDetailName(detailName string) DBOption WithByDetailName(detailName string) DBOption
} }
@ -114,10 +115,6 @@ func (u *BackupRepo) Delete(opts ...DBOption) error {
return db.Delete(&model.BackupAccount{}).Error return db.Delete(&model.BackupAccount{}).Error
} }
func (u *BackupRepo) DeleteRecord(opts ...DBOption) error { func (u *BackupRepo) DeleteRecord(ctx context.Context, opts ...DBOption) error {
db := global.DB return getTx(ctx, opts...).Delete(&model.BackupRecord{}).Error
for _, opt := range opts {
db = opt(db)
}
return db.Delete(&model.BackupRecord{}).Error
} }

View file

@ -1,6 +1,7 @@
package service package service
import ( import (
"context"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"github.com/1Panel-dev/1Panel/backend/buserr" "github.com/1Panel-dev/1Panel/backend/buserr"
@ -25,7 +26,6 @@ type AppService struct {
} }
func (a AppService) PageApp(req request.AppSearch) (interface{}, error) { func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
var opts []repo.DBOption var opts []repo.DBOption
opts = append(opts, commonRepo.WithOrderBy("name")) opts = append(opts, commonRepo.WithOrderBy("name"))
if req.Name != "" { if req.Name != "" {
@ -139,22 +139,21 @@ func (a AppService) GetAppDetail(appId uint, version string) (response.AppDetail
return appDetailDTO, nil return appDetailDTO, nil
} }
func (a AppService) Install(name string, appDetailId uint, params map[string]interface{}) (*model.AppInstall, error) { func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (*model.AppInstall, error) {
if list, _ := appInstallRepo.GetBy(commonRepo.WithByName(req.Name)); len(list) > 0 {
if list, _ := appInstallRepo.GetBy(commonRepo.WithByName(name)); len(list) > 0 {
return nil, buserr.New(constant.ErrNameIsExist) return nil, buserr.New(constant.ErrNameIsExist)
} }
httpPort, err := checkPort("PANEL_APP_PORT_HTTP", params) httpPort, err := checkPort("PANEL_APP_PORT_HTTP", req.Params)
if err != nil { if err != nil {
return nil, err return nil, err
} }
httpsPort, err := checkPort("PANEL_APP_PORT_HTTPS", params) httpsPort, err := checkPort("PANEL_APP_PORT_HTTPS", req.Params)
if err != nil { if err != nil {
return nil, err return nil, err
} }
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(appDetailId)) appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(req.AppDetailId))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -166,16 +165,16 @@ func (a AppService) Install(name string, appDetailId uint, params map[string]int
if err := checkRequiredAndLimit(app); err != nil { if err := checkRequiredAndLimit(app); err != nil {
return nil, err return nil, err
} }
if err := copyAppData(app.Key, appDetail.Version, name, params); err != nil { if err := copyAppData(app.Key, appDetail.Version, req.Name, req.Params); err != nil {
return nil, err return nil, err
} }
paramByte, err := json.Marshal(params) paramByte, err := json.Marshal(req.Params)
if err != nil { if err != nil {
return nil, err return nil, err
} }
appInstall := model.AppInstall{ appInstall := model.AppInstall{
Name: name, Name: req.Name,
AppId: appDetail.AppId, AppId: appDetail.AppId,
AppDetailId: appDetail.ID, AppDetailId: appDetail.ID,
Version: appDetail.Version, Version: appDetail.Version,
@ -216,12 +215,12 @@ func (a AppService) Install(name string, appDetailId uint, params map[string]int
return nil, err return nil, err
} }
tx, ctx := getTxAndContext() tx, ctx := getTxByContext(ctx)
if err := appInstallRepo.Create(ctx, &appInstall); err != nil { if err := appInstallRepo.Create(ctx, &appInstall); err != nil {
tx.Rollback() tx.Rollback()
return nil, err return nil, err
} }
if err := createLink(ctx, app, &appInstall, params); err != nil { if err := createLink(ctx, app, &appInstall, req.Params); err != nil {
tx.Rollback() tx.Rollback()
return nil, err return nil, err
} }
@ -484,20 +483,16 @@ func (a AppService) SyncAppList() error {
return err return err
} }
} }
if err := appTagRepo.DeleteAll(ctx); err != nil { if err := appTagRepo.DeleteAll(ctx); err != nil {
tx.Rollback() tx.Rollback()
return err return err
} }
if len(appTags) > 0 { if len(appTags) > 0 {
if err := appTagRepo.BatchCreate(ctx, appTags); err != nil { if err := appTagRepo.BatchCreate(ctx, appTags); err != nil {
tx.Rollback() tx.Rollback()
return err return err
} }
} }
tx.Commit() tx.Commit()
return nil return nil
} }

View file

@ -130,29 +130,25 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
if err != nil { if err != nil {
return err return err
} }
var database model.DatabaseMysql var createMysql dto.MysqlDBCreate
database.Name = dbConfig.DbName createMysql.Name = dbConfig.DbName
database.Username = dbConfig.DbUser createMysql.Username = dbConfig.DbUser
database.Password = dbConfig.Password createMysql.Format = "utf8mb4"
database.MysqlName = dbInstall.Name createMysql.Permission = "%"
database.Format = "utf8mb4" createMysql.Password = dbConfig.Password
database.Permission = "127.0.0.1" mysqldb, err := NewIMysqlService().Create(ctx, createMysql)
if err := mysqlRepo.Create(ctx, &database); err != nil { if err != nil {
return err return err
} }
var installResource model.AppInstallResource var installResource model.AppInstallResource
installResource.ResourceId = database.ID installResource.ResourceId = mysqldb.ID
installResource.AppInstallId = appInstall.ID installResource.AppInstallId = appInstall.ID
installResource.LinkId = dbInstall.ID installResource.LinkId = dbInstall.ID
installResource.Key = dbInstall.App.Key installResource.Key = dbInstall.App.Key
if err := appInstallResourceRepo.Create(ctx, &installResource); err != nil { if err := appInstallResourceRepo.Create(ctx, &installResource); err != nil {
return err return err
} }
if err := execDockerCommand(database, dbInstall, Add); err != nil {
return err
}
} }
return nil return nil
} }

View file

@ -1,6 +1,7 @@
package service package service
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
@ -169,7 +170,7 @@ func (u *BackupService) BatchDeleteRecord(ids []uint) error {
} }
} }
} }
return backupRepo.DeleteRecord(commonRepo.WithIdsIn(ids)) return backupRepo.DeleteRecord(context.Background(), commonRepo.WithIdsIn(ids))
} }
func (u *BackupService) Update(id uint, upMap map[string]interface{}) error { func (u *BackupService) Update(id uint, upMap map[string]interface{}) error {

View file

@ -1,6 +1,7 @@
package service package service
import ( import (
"context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -184,7 +185,7 @@ func (u *CronjobService) HandleRmExpired(backType, baseDir, backupDir string, cr
dbCopies++ dbCopies++
if dbCopies > cronjob.RetainCopies { if dbCopies > cronjob.RetainCopies {
_ = os.Remove(baseDir + "/" + backupDir + "/" + files[i].Name()) _ = os.Remove(baseDir + "/" + backupDir + "/" + files[i].Name())
_ = backupRepo.DeleteRecord(backupRepo.WithByFileName(files[i].Name())) _ = backupRepo.DeleteRecord(context.Background(), backupRepo.WithByFileName(files[i].Name()))
} }
} }
} }

View file

@ -31,7 +31,7 @@ type MysqlService struct{}
type IMysqlService interface { type IMysqlService interface {
SearchWithPage(search dto.PageInfo) (int64, interface{}, error) SearchWithPage(search dto.PageInfo) (int64, interface{}, error)
ListDBName() ([]string, error) ListDBName() ([]string, error)
Create(mysqlDto dto.MysqlDBCreate) error Create(ctx context.Context, mysqlDto dto.MysqlDBCreate) (*model.DatabaseMysql, error)
ChangeAccess(info dto.ChangeDBInfo) error ChangeAccess(info dto.ChangeDBInfo) error
ChangePassword(info dto.ChangeDBInfo) error ChangePassword(info dto.ChangeDBInfo) error
UpdateVariables(updatas []dto.MysqlVariablesUpdate) error UpdateVariables(updatas []dto.MysqlVariablesUpdate) error
@ -153,41 +153,41 @@ func (u *MysqlService) ListDBName() ([]string, error) {
return dbNames, err return dbNames, err
} }
func (u *MysqlService) Create(mysqlDto dto.MysqlDBCreate) error { func (u *MysqlService) Create(ctx context.Context, mysqlDto dto.MysqlDBCreate) (*model.DatabaseMysql, error) {
if mysqlDto.Username == "root" { if mysqlDto.Username == "root" {
return errors.New("Cannot set root as user name") return nil, errors.New("Cannot set root as user name")
} }
app, err := appInstallRepo.LoadBaseInfo("mysql", "") app, err := appInstallRepo.LoadBaseInfo("mysql", "")
if err != nil { if err != nil {
return err return nil, err
} }
mysql, _ := mysqlRepo.Get(commonRepo.WithByName(mysqlDto.Name)) mysql, _ := mysqlRepo.Get(commonRepo.WithByName(mysqlDto.Name))
if mysql.ID != 0 { if mysql.ID != 0 {
return constant.ErrRecordExist return nil, constant.ErrRecordExist
} }
if err := copier.Copy(&mysql, &mysqlDto); err != nil { if err := copier.Copy(&mysql, &mysqlDto); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error()) return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
} }
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("create database if not exists `%s` character set=%s", mysqlDto.Name, mysqlDto.Format)); err != nil { if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("create database if not exists `%s` character set=%s", mysqlDto.Name, mysqlDto.Format)); err != nil {
return err return nil,err
} }
tmpPermission := mysqlDto.Permission tmpPermission := mysqlDto.Permission
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("create user if not exists '%s'@'%s' identified by '%s';", mysqlDto.Username, tmpPermission, mysqlDto.Password)); err != nil { if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("create user if not exists '%s'@'%s' identified by '%s';", mysqlDto.Username, tmpPermission, mysqlDto.Password)); err != nil {
return err return nil, err
} }
grantStr := fmt.Sprintf("grant all privileges on `%s`.* to '%s'@'%s'", mysqlDto.Name, mysqlDto.Username, tmpPermission) grantStr := fmt.Sprintf("grant all privileges on `%s`.* to '%s'@'%s'", mysqlDto.Name, mysqlDto.Username, tmpPermission)
if app.Version == "5.7.39" { if app.Version == "5.7.39" {
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, mysqlDto.Password) grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, mysqlDto.Password)
} }
if err := excuteSql(app.ContainerName, app.Password, grantStr); err != nil { if err := excuteSql(app.ContainerName, app.Password, grantStr); err != nil {
return err return nil, err
} }
mysql.MysqlName = app.Name mysql.MysqlName = app.Name
if err := mysqlRepo.Create(context.TODO(), &mysql); err != nil { if err := mysqlRepo.Create(ctx, &mysql); err != nil {
return err return nil, err
} }
return nil return &mysql, nil
} }
func (u *MysqlService) Backup(db dto.BackupDB) error { func (u *MysqlService) Backup(db dto.BackupDB) error {
@ -281,7 +281,7 @@ func (u *MysqlService) Delete(id uint) error {
if _, err := os.Stat(backupDir); err == nil { if _, err := os.Stat(backupDir); err == nil {
_ = os.RemoveAll(backupDir) _ = os.RemoveAll(backupDir)
} }
_ = backupRepo.DeleteRecord(commonRepo.WithByType("database-mysql"), commonRepo.WithByName(app.Name), backupRepo.WithByDetailName(db.Name)) _ = backupRepo.DeleteRecord(context.Background(), commonRepo.WithByType("database-mysql"), commonRepo.WithByName(app.Name), backupRepo.WithByDetailName(db.Name))
_ = mysqlRepo.Delete(context.Background(), commonRepo.WithByID(db.ID)) _ = mysqlRepo.Delete(context.Background(), commonRepo.WithByID(db.ID))
return nil return nil

View file

@ -12,3 +12,13 @@ func getTxAndContext() (tx *gorm.DB, ctx context.Context) {
ctx = context.WithValue(context.Background(), constant.DB, tx) ctx = context.WithValue(context.Background(), constant.DB, tx)
return return
} }
func getTxByContext(ctx context.Context) (*gorm.DB, context.Context) {
tx, ok := ctx.Value(constant.DB).(*gorm.DB)
if ok {
return tx, ctx
}
tx = global.DB.Begin()
ctx = context.WithValue(context.Background(), constant.DB, tx)
return tx, ctx
}

View file

@ -95,7 +95,11 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) error {
switch create.Type { switch create.Type {
case constant.Deployment: case constant.Deployment:
if create.AppType == constant.NewApp { if create.AppType == constant.NewApp {
install, err := ServiceGroupApp.Install(create.AppInstall.Name, create.AppInstall.AppDetailId, create.AppInstall.Params) var req request.AppInstallCreate
req.Name = create.AppInstall.Name
req.AppDetailId = create.AppInstall.AppDetailId
req.Params = create.AppInstall.Params
install, err := ServiceGroupApp.Install(context.Background(), req)
if err != nil { if err != nil {
return err return err
} }
@ -275,9 +279,10 @@ func (w WebsiteService) DeleteWebsite(req request.WebsiteDelete) error {
for _, b := range backups { for _, b := range backups {
_ = fileOp.DeleteDir(b.FileDir) _ = fileOp.DeleteDir(b.FileDir)
} }
} if err := backupRepo.DeleteRecord(ctx, backupRepo.WithByType("website-"+website.Type), commonRepo.WithByName(website.PrimaryDomain)); err != nil {
if err := backupRepo.DeleteRecord(backupRepo.WithByType("website-"+website.Type), commonRepo.WithByName(website.PrimaryDomain)); err != nil { tx.Rollback()
return err return err
}
} }
} }

View file

@ -75,6 +75,7 @@ const acceptParams = async (app: App.AppInstalled) => {
deleteBackup: false, deleteBackup: false,
forceDelete: false, forceDelete: false,
}; };
deleteInfo.value = '';
deleteReq.value.installId = app.id; deleteReq.value.installId = app.id;
deleteHelper.value = i18n.global.t('website.deleteConfirmHelper', [app.name]); deleteHelper.value = i18n.global.t('website.deleteConfirmHelper', [app.name]);
appInstallName.value = app.name; appInstallName.value = app.name;

View file

@ -90,6 +90,7 @@ const acceptParams = async (website: Website.Website) => {
deleteBackup: false, deleteBackup: false,
forceDelete: false, forceDelete: false,
}; };
deleteInfo.value = '';
deleteReq.value.id = website.id; deleteReq.value.id = website.id;
websiteName.value = website.primaryDomain; websiteName.value = website.primaryDomain;
deleteHelper.value = i18n.global.t('website.deleteConfirmHelper', [website.primaryDomain]); deleteHelper.value = i18n.global.t('website.deleteConfirmHelper', [website.primaryDomain]);