mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-18 22:22:59 +08:00
feat: 删除wordpress级联删除datastore
This commit is contained in:
parent
ad50907158
commit
9c7552d037
@ -3,6 +3,7 @@ package model
|
||||
type Database struct {
|
||||
BaseModel
|
||||
AppContainerId uint `json:"appContainerId" gorm:"type:integer;not null"`
|
||||
AppInstallId uint `json:"appInstallId" gorm:"type:integer;not null"`
|
||||
Key string `json:"key" gorm:"type:varchar(64);not null"`
|
||||
Dbname string `json:"dbname" gorm:"type:varchar(256);not null"`
|
||||
Username string `json:"username" gorm:"type:varchar(256);not null"`
|
||||
|
@ -63,8 +63,8 @@ func (a AppInstallRepo) DeleteBy(opts ...DBOption) error {
|
||||
return db.Delete(&model.AppInstall{}).Error
|
||||
}
|
||||
|
||||
func (a AppInstallRepo) Delete(install model.AppInstall) error {
|
||||
db := global.DB
|
||||
func (a AppInstallRepo) Delete(ctx context.Context, install model.AppInstall) error {
|
||||
db := ctx.Value("db").(*gorm.DB).Model(&model.AppInstall{})
|
||||
return db.Delete(&install).Error
|
||||
}
|
||||
|
||||
|
@ -3,45 +3,54 @@ package repo
|
||||
import (
|
||||
"context"
|
||||
"github.com/1Panel-dev/1Panel/app/model"
|
||||
"github.com/1Panel-dev/1Panel/global"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type DatabaseRepo struct {
|
||||
}
|
||||
|
||||
func (d DatabaseRepo) ByAppInstallId(installId uint) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("app_install_id = ?", installId)
|
||||
}
|
||||
}
|
||||
|
||||
func (d DatabaseRepo) Create(ctx context.Context, database *model.Database) error {
|
||||
db := ctx.Value("db").(*gorm.DB).Model(&model.Database{})
|
||||
return db.Create(&database).Error
|
||||
}
|
||||
|
||||
//func (a DatabaseRepo) BatchCreate(ctx context.Context, tags []*model.AppTag) error {
|
||||
// db := ctx.Value("db").(*gorm.DB)
|
||||
// return db.Create(&tags).Error
|
||||
//}
|
||||
func (d DatabaseRepo) DeleteBy(ctx context.Context, opts ...DBOption) error {
|
||||
db := ctx.Value("db").(*gorm.DB).Model(&model.Database{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
return db.Delete(&model.Database{}).Error
|
||||
}
|
||||
|
||||
//func (d DatabaseRepo) DeleteBy(ctx context.Context, appIds []uint) error {
|
||||
// db := ctx.Value("db").(*gorm.DB)
|
||||
// return db.Where("app_id in (?)", appIds).Delete(&model.AppTag{}).Error
|
||||
//}
|
||||
func (d DatabaseRepo) GetBy(opts ...DBOption) ([]model.Database, error) {
|
||||
db := global.DB.Model(model.Database{})
|
||||
var databases []model.Database
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(&databases).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return databases, nil
|
||||
}
|
||||
|
||||
//
|
||||
//func (a AppTagRepo) DeleteAll(ctx context.Context) error {
|
||||
// db := ctx.Value("db").(*gorm.DB)
|
||||
// return db.Where("1 = 1").Delete(&model.AppTag{}).Error
|
||||
//}
|
||||
//
|
||||
//func (a AppTagRepo) GetByAppId(appId uint) ([]model.AppTag, error) {
|
||||
// var appTags []model.AppTag
|
||||
// if err := global.DB.Where("app_id = ?", appId).Find(&appTags).Error; err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// return appTags, nil
|
||||
//}
|
||||
//
|
||||
//func (a AppTagRepo) GetByTagIds(tagIds []uint) ([]model.AppTag, error) {
|
||||
// var appTags []model.AppTag
|
||||
// if err := global.DB.Where("tag_id in (?)", tagIds).Find(&appTags).Error; err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// return appTags, nil
|
||||
//}
|
||||
func (d DatabaseRepo) GetFirst(opts ...DBOption) (model.Database, error) {
|
||||
db := global.DB.Model(model.Database{})
|
||||
var database model.Database
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(&database).Error
|
||||
if err != nil {
|
||||
return database, err
|
||||
}
|
||||
return database, nil
|
||||
}
|
||||
|
@ -10,14 +10,13 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/constant"
|
||||
"github.com/1Panel-dev/1Panel/global"
|
||||
"github.com/1Panel-dev/1Panel/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/utils/files"
|
||||
"github.com/joho/godotenv"
|
||||
"golang.org/x/net/context"
|
||||
"gopkg.in/yaml.v3"
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
@ -180,17 +179,51 @@ func (a AppService) Operate(req dto.AppInstallOperate) error {
|
||||
op := files.NewFileOp()
|
||||
appDir := install.GetPath()
|
||||
dir, _ := os.Stat(appDir)
|
||||
|
||||
tx := global.DB.Begin()
|
||||
ctx := context.WithValue(context.Background(), "db", tx)
|
||||
if dir == nil {
|
||||
return appInstallRepo.Delete(install)
|
||||
if err := appInstallRepo.Delete(ctx, install); err != nil {
|
||||
return err
|
||||
}
|
||||
tx.Commit()
|
||||
return nil
|
||||
}
|
||||
out, err := compose.Down(dockerComposePath)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return handleErr(install, err, out)
|
||||
}
|
||||
if err := op.DeleteDir(appDir); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
return appInstallRepo.Delete(install)
|
||||
if err := appInstallRepo.Delete(ctx, install); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
database, _ := dataBaseRepo.GetFirst(dataBaseRepo.ByAppInstallId(install.ID))
|
||||
if reflect.DeepEqual(database, model.Database{}) {
|
||||
tx.Commit()
|
||||
return nil
|
||||
}
|
||||
if err := dataBaseRepo.DeleteBy(ctx, dataBaseRepo.ByAppInstallId(install.ID)); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
container, err := appContainerRepo.GetFirst(commonRepo.WithByID(database.AppContainerId))
|
||||
if err != nil {
|
||||
tx.Commit()
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := execDockerCommand(database, container, Delete); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
tx.Commit()
|
||||
return nil
|
||||
case dto.Sync:
|
||||
if err := a.SyncInstalled(install.ID); err != nil {
|
||||
return err
|
||||
@ -275,32 +308,8 @@ func (a AppService) Install(name string, appDetailId uint, params map[string]int
|
||||
Params: string(paramByte),
|
||||
}
|
||||
|
||||
resourceDir := path.Join(global.CONF.System.ResourceDir, "apps", app.Key, appDetail.Version)
|
||||
installDir := path.Join(global.CONF.System.AppDir, app.Key)
|
||||
installVersionDir := path.Join(installDir, appDetail.Version)
|
||||
fileOp := files.NewFileOp()
|
||||
if err := fileOp.Copy(resourceDir, installVersionDir); err != nil {
|
||||
return err
|
||||
}
|
||||
appDir := path.Join(installDir, name)
|
||||
if err := fileOp.Rename(installVersionDir, appDir); err != nil {
|
||||
return err
|
||||
}
|
||||
composeFilePath := path.Join(appDir, "docker-compose.yml")
|
||||
envPath := path.Join(appDir, ".env")
|
||||
|
||||
envParams := make(map[string]string, len(params))
|
||||
for k, v := range params {
|
||||
switch t := v.(type) {
|
||||
case string:
|
||||
envParams[k] = t
|
||||
case float64:
|
||||
envParams[k] = strconv.FormatFloat(t, 'f', -1, 32)
|
||||
default:
|
||||
envParams[k] = t.(string)
|
||||
}
|
||||
}
|
||||
if err := godotenv.Write(envParams, envPath); err != nil {
|
||||
composeFilePath, err := copyAppData(app.Key, appDetail.Version, name, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -353,20 +362,11 @@ func (a AppService) Install(name string, appDetailId uint, params map[string]int
|
||||
containerName := constant.ContainerPrefix + k + "-" + common.RandStr(4)
|
||||
value["container_name"] = containerName
|
||||
servicePort := 0
|
||||
if portArray, ok := value["ports"].([]interface{}); ok {
|
||||
for _, p := range portArray {
|
||||
if pStr, ok := p.(string); ok {
|
||||
start := strings.Index(pStr, "{")
|
||||
end := strings.Index(pStr, "}")
|
||||
if start > -1 && end > -1 {
|
||||
portS := pStr[start+1 : end]
|
||||
if v, ok := envParams[portS]; ok {
|
||||
portN, _ := strconv.Atoi(v)
|
||||
servicePort = portN
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
port, ok := params["PANEL_APP_PORT"]
|
||||
if ok {
|
||||
portN := int(math.Ceil(port.(float64)))
|
||||
servicePort = portN
|
||||
}
|
||||
|
||||
appContainers = append(appContainers, &model.AppContainer{
|
||||
@ -384,6 +384,7 @@ func (a AppService) Install(name string, appDetailId uint, params map[string]int
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
if err := fileOp.WriteFile(composeFilePath, strings.NewReader(string(composeByte)), 0775); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -423,19 +424,14 @@ func (a AppService) Install(name string, appDetailId uint, params map[string]int
|
||||
database.Dbname = dbConfig.DbName
|
||||
database.Username = dbConfig.DbUser
|
||||
database.Password = dbConfig.Password
|
||||
database.AppInstallId = appInstall.ID
|
||||
database.Key = app.Key
|
||||
if err := dataBaseRepo.Create(ctx, &database); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
var auth dto.AuthParam
|
||||
json.Unmarshal([]byte(container.Auth), &auth)
|
||||
execConfig := dto.ContainerExec{
|
||||
ContainerName: container.ContainerName,
|
||||
Auth: auth,
|
||||
DbParam: dbConfig,
|
||||
}
|
||||
_, err = cmd.Exec(getSqlStr(app.Key, execConfig))
|
||||
if err != nil {
|
||||
|
||||
if err := execDockerCommand(database, container, Add); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
@ -752,7 +748,6 @@ func (a AppService) SyncAppList() error {
|
||||
}
|
||||
|
||||
func syncCanUpdate() {
|
||||
|
||||
apps, err := appRepo.GetBy()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("sync update app error: %s", err.Error())
|
||||
@ -787,78 +782,3 @@ func syncCanUpdate() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getApps(oldApps []model.App, items []dto.AppDefine) map[string]model.App {
|
||||
apps := make(map[string]model.App, len(oldApps))
|
||||
for _, old := range oldApps {
|
||||
old.Status = constant.AppTakeDown
|
||||
apps[old.Key] = old
|
||||
}
|
||||
for _, item := range items {
|
||||
app, ok := apps[item.Key]
|
||||
if !ok {
|
||||
app = model.App{}
|
||||
}
|
||||
app.Name = item.Name
|
||||
app.Key = item.Key
|
||||
app.ShortDesc = item.ShortDesc
|
||||
app.Author = item.Author
|
||||
app.Source = item.Source
|
||||
app.Type = item.Type
|
||||
app.CrossVersionUpdate = item.CrossVersionUpdate
|
||||
app.Required = item.GetRequired()
|
||||
app.Status = constant.AppNormal
|
||||
apps[item.Key] = app
|
||||
}
|
||||
return apps
|
||||
}
|
||||
|
||||
func getAppDetails(details []model.AppDetail, versions []string) map[string]model.AppDetail {
|
||||
appDetails := make(map[string]model.AppDetail, len(details))
|
||||
for _, old := range details {
|
||||
old.Status = constant.AppTakeDown
|
||||
appDetails[old.Version] = old
|
||||
}
|
||||
|
||||
for _, v := range versions {
|
||||
detail, ok := appDetails[v]
|
||||
if ok {
|
||||
detail.Status = constant.AppNormal
|
||||
appDetails[v] = detail
|
||||
} else {
|
||||
appDetails[v] = model.AppDetail{
|
||||
Version: v,
|
||||
Status: constant.AppNormal,
|
||||
}
|
||||
}
|
||||
}
|
||||
return appDetails
|
||||
}
|
||||
|
||||
func upApp(composeFilePath string, appInstall model.AppInstall) {
|
||||
out, err := compose.Up(composeFilePath)
|
||||
if err != nil {
|
||||
if out != "" {
|
||||
appInstall.Message = out
|
||||
} else {
|
||||
appInstall.Message = err.Error()
|
||||
}
|
||||
appInstall.Status = constant.Error
|
||||
_ = appInstallRepo.Save(appInstall)
|
||||
} else {
|
||||
appInstall.Status = constant.Running
|
||||
_ = appInstallRepo.Save(appInstall)
|
||||
}
|
||||
}
|
||||
|
||||
func getSqlStr(key string, exec dto.ContainerExec) string {
|
||||
var str string
|
||||
param := exec.DbParam
|
||||
switch key {
|
||||
case "mysql":
|
||||
str = fmt.Sprintf("docker exec -i %s mysql -uroot -p%s -e \"CREATE USER '%s'@'%%' IDENTIFIED BY '%s';\" -e \"create database %s;\" -e \"GRANT ALL ON %s.* TO '%s'@'%%';\"",
|
||||
exec.ContainerName, exec.Auth.RootPassword, param.DbUser, param.Password, param.DbName, param.DbName, param.DbUser)
|
||||
}
|
||||
fmt.Println(str)
|
||||
return str
|
||||
}
|
||||
|
154
backend/app/service/app_utils.go
Normal file
154
backend/app/service/app_utils.go
Normal file
@ -0,0 +1,154 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/app/model"
|
||||
"github.com/1Panel-dev/1Panel/constant"
|
||||
"github.com/1Panel-dev/1Panel/global"
|
||||
"github.com/1Panel-dev/1Panel/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/utils/files"
|
||||
"github.com/joho/godotenv"
|
||||
"path"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type DatabaseOp string
|
||||
|
||||
var (
|
||||
Add DatabaseOp = "add"
|
||||
Delete DatabaseOp = "delete"
|
||||
)
|
||||
|
||||
func execDockerCommand(database model.Database, container model.AppContainer, op DatabaseOp) error {
|
||||
var auth dto.AuthParam
|
||||
var dbConfig dto.AppDatabase
|
||||
dbConfig.Password = database.Password
|
||||
dbConfig.DbUser = database.Username
|
||||
dbConfig.DbName = database.Dbname
|
||||
json.Unmarshal([]byte(container.Auth), &auth)
|
||||
execConfig := dto.ContainerExec{
|
||||
ContainerName: container.ContainerName,
|
||||
Auth: auth,
|
||||
DbParam: dbConfig,
|
||||
}
|
||||
_, err := cmd.Exec(getSqlStr(database.Key, op, execConfig))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSqlStr(key string, operate DatabaseOp, exec dto.ContainerExec) string {
|
||||
var str string
|
||||
param := exec.DbParam
|
||||
switch key {
|
||||
case "mysql":
|
||||
if operate == Add {
|
||||
str = fmt.Sprintf("docker exec -i %s mysql -uroot -p%s -e \"CREATE USER '%s'@'%%' IDENTIFIED BY '%s';\" -e \"create database %s;\" -e \"GRANT ALL ON %s.* TO '%s'@'%%';\"",
|
||||
exec.ContainerName, exec.Auth.RootPassword, param.DbUser, param.Password, param.DbName, param.DbName, param.DbUser)
|
||||
}
|
||||
if operate == Delete {
|
||||
str = fmt.Sprintf("docker exec -i %s mysql -uroot -p%s -e \"drop database %s;\" -e \"drop user %s;\" ",
|
||||
exec.ContainerName, exec.Auth.RootPassword, param.DbName, param.DbUser)
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func copyAppData(key, version, installName string, params map[string]interface{}) (composeFilePath string, err error) {
|
||||
resourceDir := path.Join(global.CONF.System.ResourceDir, "apps", key, version)
|
||||
installDir := path.Join(global.CONF.System.AppDir, key)
|
||||
installVersionDir := path.Join(installDir, version)
|
||||
fileOp := files.NewFileOp()
|
||||
if err = fileOp.Copy(resourceDir, installVersionDir); err != nil {
|
||||
return
|
||||
}
|
||||
appDir := path.Join(installDir, installName)
|
||||
if err = fileOp.Rename(installVersionDir, appDir); err != nil {
|
||||
return
|
||||
}
|
||||
composeFilePath = path.Join(appDir, "docker-compose.yml")
|
||||
envPath := path.Join(appDir, ".env")
|
||||
|
||||
envParams := make(map[string]string, len(params))
|
||||
for k, v := range params {
|
||||
switch t := v.(type) {
|
||||
case string:
|
||||
envParams[k] = t
|
||||
case float64:
|
||||
envParams[k] = strconv.FormatFloat(t, 'f', -1, 32)
|
||||
default:
|
||||
envParams[k] = t.(string)
|
||||
}
|
||||
}
|
||||
if err = godotenv.Write(envParams, envPath); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func upApp(composeFilePath string, appInstall model.AppInstall) {
|
||||
out, err := compose.Up(composeFilePath)
|
||||
if err != nil {
|
||||
if out != "" {
|
||||
appInstall.Message = out
|
||||
} else {
|
||||
appInstall.Message = err.Error()
|
||||
}
|
||||
appInstall.Status = constant.Error
|
||||
_ = appInstallRepo.Save(appInstall)
|
||||
} else {
|
||||
appInstall.Status = constant.Running
|
||||
_ = appInstallRepo.Save(appInstall)
|
||||
}
|
||||
}
|
||||
|
||||
func getAppDetails(details []model.AppDetail, versions []string) map[string]model.AppDetail {
|
||||
appDetails := make(map[string]model.AppDetail, len(details))
|
||||
for _, old := range details {
|
||||
old.Status = constant.AppTakeDown
|
||||
appDetails[old.Version] = old
|
||||
}
|
||||
|
||||
for _, v := range versions {
|
||||
detail, ok := appDetails[v]
|
||||
if ok {
|
||||
detail.Status = constant.AppNormal
|
||||
appDetails[v] = detail
|
||||
} else {
|
||||
appDetails[v] = model.AppDetail{
|
||||
Version: v,
|
||||
Status: constant.AppNormal,
|
||||
}
|
||||
}
|
||||
}
|
||||
return appDetails
|
||||
}
|
||||
|
||||
func getApps(oldApps []model.App, items []dto.AppDefine) map[string]model.App {
|
||||
apps := make(map[string]model.App, len(oldApps))
|
||||
for _, old := range oldApps {
|
||||
old.Status = constant.AppTakeDown
|
||||
apps[old.Key] = old
|
||||
}
|
||||
for _, item := range items {
|
||||
app, ok := apps[item.Key]
|
||||
if !ok {
|
||||
app = model.App{}
|
||||
}
|
||||
app.Name = item.Name
|
||||
app.Key = item.Key
|
||||
app.ShortDesc = item.ShortDesc
|
||||
app.Author = item.Author
|
||||
app.Source = item.Source
|
||||
app.Type = item.Type
|
||||
app.CrossVersionUpdate = item.CrossVersionUpdate
|
||||
app.Required = item.GetRequired()
|
||||
app.Status = constant.AppNormal
|
||||
apps[item.Key] = app
|
||||
}
|
||||
return apps
|
||||
}
|
Loading…
Reference in New Issue
Block a user