mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-11-24 02:59:16 +08:00
feat: 创建网站、应用任务化
This commit is contained in:
parent
865b6cba3f
commit
0e5d6e825e
1
.gitignore
vendored
1
.gitignore
vendored
@ -48,6 +48,7 @@ agent/utils/xpack/xpack_xpack.go
|
|||||||
core/xpack
|
core/xpack
|
||||||
core/router/entry_xpack.go
|
core/router/entry_xpack.go
|
||||||
core/server/init_xpack.go
|
core/server/init_xpack.go
|
||||||
|
xpack
|
||||||
|
|
||||||
.history/
|
.history/
|
||||||
dist/
|
dist/
|
||||||
|
@ -161,14 +161,11 @@ func (b *BaseApi) InstallApp(c *gin.Context) {
|
|||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tx, ctx := helper.GetTxAndContext()
|
install, err := appService.Install(req)
|
||||||
install, err := appService.Install(ctx, req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tx.Commit()
|
|
||||||
helper.SuccessWithData(c, install)
|
helper.SuccessWithData(c, install)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package v1
|
package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/api/v1/helper"
|
"github.com/1Panel-dev/1Panel/agent/app/api/v1/helper"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||||
@ -78,14 +76,6 @@ func (b *BaseApi) CreateWebsite(c *gin.Context) {
|
|||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(req.FtpPassword) != 0 {
|
|
||||||
pass, err := base64.StdEncoding.DecodeString(req.FtpPassword)
|
|
||||||
if err != nil {
|
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.FtpPassword = string(pass)
|
|
||||||
}
|
|
||||||
err := websiteService.CreateWebsite(req)
|
err := websiteService.CreateWebsite(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
@ -128,6 +128,7 @@ type FileReadByLineReq struct {
|
|||||||
ID uint `json:"ID"`
|
ID uint `json:"ID"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Latest bool `json:"latest"`
|
Latest bool `json:"latest"`
|
||||||
|
TaskID string `json:"taskID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileExistReq struct {
|
type FileExistReq struct {
|
||||||
|
1
agent/app/dto/request/task.go
Normal file
1
agent/app/dto/request/task.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package request
|
@ -31,6 +31,7 @@ type WebsiteCreate struct {
|
|||||||
FtpPassword string `json:"ftpPassword"`
|
FtpPassword string `json:"ftpPassword"`
|
||||||
|
|
||||||
RuntimeID uint `json:"runtimeID"`
|
RuntimeID uint `json:"runtimeID"`
|
||||||
|
TaskID string `json:"taskID"`
|
||||||
RuntimeConfig
|
RuntimeConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
agent/app/dto/response/task.go
Normal file
1
agent/app/dto/response/task.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package response
|
17
agent/app/model/task.go
Normal file
17
agent/app/model/task.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Task struct {
|
||||||
|
ID string `gorm:"primarykey;" json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
LogFile string `json:"logFile"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
ErrorMsg string `json:"errorMsg"`
|
||||||
|
OperationLogID uint `json:"operationLogID"`
|
||||||
|
ResourceID uint `json:"resourceID"`
|
||||||
|
CurrentStep string `json:"currentStep"`
|
||||||
|
EndAt time.Time `json:"endAt"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
}
|
55
agent/app/repo/task.go
Normal file
55
agent/app/repo/task.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TaskRepo struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type ITaskRepo interface {
|
||||||
|
Create(ctx context.Context, task *model.Task) error
|
||||||
|
GetFirst(opts ...DBOption) (model.Task, error)
|
||||||
|
Page(page, size int, opts ...DBOption) (int64, []model.Task, error)
|
||||||
|
Update(ctx context.Context, task *model.Task) error
|
||||||
|
|
||||||
|
WithByID(id string) DBOption
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewITaskRepo() ITaskRepo {
|
||||||
|
return &TaskRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TaskRepo) WithByID(id string) DBOption {
|
||||||
|
return func(g *gorm.DB) *gorm.DB {
|
||||||
|
return g.Where("id = ?", id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TaskRepo) Create(ctx context.Context, task *model.Task) error {
|
||||||
|
return getTx(ctx).Create(&task).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TaskRepo) GetFirst(opts ...DBOption) (model.Task, error) {
|
||||||
|
var task model.Task
|
||||||
|
db := getDb(opts...).Model(&model.Task{})
|
||||||
|
if err := db.First(&task).Error; err != nil {
|
||||||
|
return task, err
|
||||||
|
}
|
||||||
|
return task, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TaskRepo) Page(page, size int, opts ...DBOption) (int64, []model.Task, error) {
|
||||||
|
var tasks []model.Task
|
||||||
|
db := getDb(opts...).Model(&model.Task{})
|
||||||
|
count := int64(0)
|
||||||
|
db = db.Count(&count)
|
||||||
|
err := db.Limit(size).Offset(size * (page - 1)).Find(&tasks).Error
|
||||||
|
return count, tasks, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TaskRepo) Update(ctx context.Context, task *model.Task) error {
|
||||||
|
return getTx(ctx).Save(&task).Error
|
||||||
|
}
|
@ -5,18 +5,12 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
|
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||||
"github.com/1Panel-dev/1Panel/agent/global"
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
@ -27,7 +21,14 @@ import (
|
|||||||
http2 "github.com/1Panel-dev/1Panel/agent/utils/http"
|
http2 "github.com/1Panel-dev/1Panel/agent/utils/http"
|
||||||
httpUtil "github.com/1Panel-dev/1Panel/agent/utils/http"
|
httpUtil "github.com/1Panel-dev/1Panel/agent/utils/http"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||||
|
"github.com/google/uuid"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AppService struct {
|
type AppService struct {
|
||||||
@ -38,7 +39,7 @@ type IAppService interface {
|
|||||||
GetAppTags() ([]response.TagDTO, error)
|
GetAppTags() ([]response.TagDTO, error)
|
||||||
GetApp(key string) (*response.AppDTO, error)
|
GetApp(key string) (*response.AppDTO, error)
|
||||||
GetAppDetail(appId uint, version, appType string) (response.AppDetailDTO, error)
|
GetAppDetail(appId uint, version, appType string) (response.AppDetailDTO, error)
|
||||||
Install(ctx context.Context, req request.AppInstallCreate) (*model.AppInstall, error)
|
Install(req request.AppInstallCreate) (*model.AppInstall, error)
|
||||||
SyncAppListFromRemote() error
|
SyncAppListFromRemote() error
|
||||||
GetAppUpdate() (*response.AppUpdateRes, error)
|
GetAppUpdate() (*response.AppUpdateRes, error)
|
||||||
GetAppDetailByID(id uint) (*response.AppDetailDTO, error)
|
GetAppDetailByID(id uint) (*response.AppDetailDTO, error)
|
||||||
@ -295,7 +296,7 @@ func (a AppService) GetIgnoredApp() ([]response.IgnoredApp, error) {
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (appInstall *model.AppInstall, err error) {
|
func (a AppService) Install(req request.AppInstallCreate) (appInstall *model.AppInstall, err error) {
|
||||||
if err = docker.CreateDefaultDockerNetwork(); err != nil {
|
if err = docker.CreateDefaultDockerNetwork(); err != nil {
|
||||||
err = buserr.WithDetail(constant.Err1PanelNetworkFailed, err.Error(), nil)
|
err = buserr.WithDetail(constant.Err1PanelNetworkFailed, err.Error(), nil)
|
||||||
return
|
return
|
||||||
@ -423,14 +424,6 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
|||||||
}
|
}
|
||||||
appInstall.DockerCompose = string(composeByte)
|
appInstall.DockerCompose = string(composeByte)
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
hErr := handleAppInstallErr(ctx, appInstall)
|
|
||||||
if hErr != nil {
|
|
||||||
global.LOG.Errorf("delete app dir error %s", hErr.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
if hostName, ok := req.Params["PANEL_DB_HOST"]; ok {
|
if hostName, ok := req.Params["PANEL_DB_HOST"]; ok {
|
||||||
database, _ := databaseRepo.Get(commonRepo.WithByName(hostName.(string)))
|
database, _ := databaseRepo.Get(commonRepo.WithByName(hostName.(string)))
|
||||||
if !reflect.DeepEqual(database, model.Database{}) {
|
if !reflect.DeepEqual(database, model.Database{}) {
|
||||||
@ -445,29 +438,48 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
|||||||
}
|
}
|
||||||
appInstall.Env = string(paramByte)
|
appInstall.Env = string(paramByte)
|
||||||
|
|
||||||
if err = appInstallRepo.Create(ctx, appInstall); err != nil {
|
if err = appInstallRepo.Create(context.Background(), appInstall); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = createLink(ctx, app, appInstall, req.Params); err != nil {
|
|
||||||
return
|
taskID := uuid.New().String()
|
||||||
}
|
installTask, err := task.NewTaskWithOps(appInstall.Name, task.TaskCreate, task.TaskScopeApp, taskID)
|
||||||
go func() {
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = createLink(context.Background(), installTask, app, appInstall, req.Params); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
installApp := func(t *task.Task) error {
|
||||||
|
if err = copyData(t, app, appDetail, appInstall, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = runScript(t, appInstall, "init"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
upApp(t, appInstall, req.PullImage)
|
||||||
|
updateToolApp(appInstall)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
handleAppStatus := func() {
|
||||||
appInstall.Status = constant.UpErr
|
appInstall.Status = constant.UpErr
|
||||||
appInstall.Message = err.Error()
|
appInstall.Message = installTask.Task.ErrorMsg
|
||||||
|
_ = appInstallRepo.Save(context.Background(), appInstall)
|
||||||
|
}
|
||||||
|
|
||||||
|
installTask.AddSubTask(task.GetTaskName(appInstall.Name, task.TaskInstall, task.TaskScopeApp), installApp, handleAppStatus)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if taskErr := installTask.Execute(); taskErr != nil {
|
||||||
|
appInstall.Status = constant.InstallErr
|
||||||
|
appInstall.Message = taskErr.Error()
|
||||||
_ = appInstallRepo.Save(context.Background(), appInstall)
|
_ = appInstallRepo.Save(context.Background(), appInstall)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
if err = copyData(app, appDetail, appInstall, req); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = runScript(appInstall, "init"); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
upApp(appInstall, req.PullImage)
|
|
||||||
}()
|
|
||||||
go updateToolApp(appInstall)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -130,9 +131,13 @@ var ToolKeys = map[string]uint{
|
|||||||
"minio": 9001,
|
"minio": 9001,
|
||||||
}
|
}
|
||||||
|
|
||||||
func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall, params map[string]interface{}) error {
|
func createLink(ctx context.Context, installTask *task.Task, app model.App, appInstall *model.AppInstall, params map[string]interface{}) error {
|
||||||
|
deleteAppLink := func() {
|
||||||
|
_ = deleteLink(ctx, appInstall, true, true, true)
|
||||||
|
}
|
||||||
var dbConfig dto.AppDatabase
|
var dbConfig dto.AppDatabase
|
||||||
if DatabaseKeys[app.Key] > 0 {
|
if DatabaseKeys[app.Key] > 0 {
|
||||||
|
handleDataBaseApp := func(task *task.Task) error {
|
||||||
database := &model.Database{
|
database := &model.Database{
|
||||||
AppInstallID: appInstall.ID,
|
AppInstallID: appInstall.ID,
|
||||||
Name: appInstall.Name,
|
Name: appInstall.Name,
|
||||||
@ -199,9 +204,9 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
|
|||||||
database.Password = password.(string)
|
database.Password = password.(string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := databaseRepo.Create(ctx, database); err != nil {
|
return databaseRepo.Create(ctx, database)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
installTask.AddSubTask(i18n.GetMsgByKey("HandleDatabaseApp"), handleDataBaseApp, deleteAppLink)
|
||||||
}
|
}
|
||||||
if ToolKeys[app.Key] > 0 {
|
if ToolKeys[app.Key] > 0 {
|
||||||
if app.Key == "minio" {
|
if app.Key == "minio" {
|
||||||
@ -231,6 +236,7 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(dbConfig, dto.AppDatabase{}) && dbConfig.ServiceName != "" {
|
if !reflect.DeepEqual(dbConfig, dto.AppDatabase{}) && dbConfig.ServiceName != "" {
|
||||||
|
createAppDataBase := func(rootTask *task.Task) error {
|
||||||
hostName := params["PANEL_DB_HOST_NAME"]
|
hostName := params["PANEL_DB_HOST_NAME"]
|
||||||
if hostName == nil || hostName.(string) == "" {
|
if hostName == nil || hostName.(string) == "" {
|
||||||
return nil
|
return nil
|
||||||
@ -289,7 +295,6 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
|
|||||||
resourceId = mysqldb.ID
|
resourceId = mysqldb.ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
var installResource model.AppInstallResource
|
var installResource model.AppInstallResource
|
||||||
installResource.ResourceId = resourceId
|
installResource.ResourceId = resourceId
|
||||||
@ -301,25 +306,9 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
|
|||||||
}
|
}
|
||||||
installResource.Key = database.Type
|
installResource.Key = database.Type
|
||||||
installResource.From = database.From
|
installResource.From = database.From
|
||||||
if err := appInstallResourceRepo.Create(ctx, &installResource); err != nil {
|
return appInstallResourceRepo.Create(ctx, &installResource)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
installTask.AddSubTask(task.GetTaskName(dbConfig.DbName, task.TaskCreate, task.TaskScopeDatabase), createAppDataBase, deleteAppLink)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleAppInstallErr(ctx context.Context, install *model.AppInstall) error {
|
|
||||||
op := files.NewFileOp()
|
|
||||||
appDir := install.GetPath()
|
|
||||||
dir, _ := os.Stat(appDir)
|
|
||||||
if dir != nil {
|
|
||||||
_, _ = compose.Down(install.GetComposePath())
|
|
||||||
if err := op.DeleteDir(appDir); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := deleteLink(ctx, install, true, true, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -333,7 +322,8 @@ func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete b
|
|||||||
if err != nil && !forceDelete {
|
if err != nil && !forceDelete {
|
||||||
return handleErr(install, err, out)
|
return handleErr(install, err, out)
|
||||||
}
|
}
|
||||||
if err = runScript(&install, "uninstall"); err != nil {
|
//TODO use task
|
||||||
|
if err = runScript(nil, &install, "uninstall"); err != nil {
|
||||||
_, _ = compose.Up(install.GetComposePath())
|
_, _ = compose.Up(install.GetComposePath())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -652,7 +642,8 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if upErr = runScript(&install, "upgrade"); upErr != nil {
|
//TODO use task
|
||||||
|
if upErr = runScript(nil, &install, "upgrade"); upErr != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -800,7 +791,7 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyData(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall, req request.AppInstallCreate) (err error) {
|
func copyData(task *task.Task, app model.App, appDetail model.AppDetail, appInstall *model.AppInstall, req request.AppInstallCreate) (err error) {
|
||||||
fileOp := files.NewFileOp()
|
fileOp := files.NewFileOp()
|
||||||
appResourceDir := path.Join(constant.AppResourceDir, app.Resource)
|
appResourceDir := path.Join(constant.AppResourceDir, app.Resource)
|
||||||
|
|
||||||
@ -853,7 +844,7 @@ func copyData(app model.App, appDetail model.AppDetail, appInstall *model.AppIns
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func runScript(appInstall *model.AppInstall, operate string) error {
|
func runScript(task *task.Task, appInstall *model.AppInstall, operate string) error {
|
||||||
workDir := appInstall.GetPath()
|
workDir := appInstall.GetPath()
|
||||||
scriptPath := ""
|
scriptPath := ""
|
||||||
switch operate {
|
switch operate {
|
||||||
@ -867,15 +858,17 @@ func runScript(appInstall *model.AppInstall, operate string) error {
|
|||||||
if !files.NewFileOp().Stat(scriptPath) {
|
if !files.NewFileOp().Stat(scriptPath) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
logStr := i18n.GetWithName("ExecShell", operate)
|
||||||
|
task.LogStart(logStr)
|
||||||
out, err := cmd.ExecScript(scriptPath, workDir)
|
out, err := cmd.ExecScript(scriptPath, workDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if out != "" {
|
if out != "" {
|
||||||
errMsg := fmt.Sprintf("run script %s error %s", scriptPath, out)
|
err = errors.New(out)
|
||||||
global.LOG.Error(errMsg)
|
|
||||||
return errors.New(errMsg)
|
|
||||||
}
|
}
|
||||||
|
task.LogFailedWithErr(logStr, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
task.LogSuccess(logStr)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -905,15 +898,25 @@ func checkContainerNameIsExist(containerName, appDir string) (bool, error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func upApp(appInstall *model.AppInstall, pullImages bool) {
|
func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) {
|
||||||
upProject := func(appInstall *model.AppInstall) (err error) {
|
upProject := func(appInstall *model.AppInstall) (err error) {
|
||||||
var (
|
var (
|
||||||
out string
|
out string
|
||||||
errMsg string
|
errMsg string
|
||||||
)
|
)
|
||||||
if pullImages && appInstall.App.Type != "php" {
|
if pullImages && appInstall.App.Type != "php" {
|
||||||
out, err = compose.Pull(appInstall.GetComposePath())
|
projectName := strings.ToLower(appInstall.Name)
|
||||||
|
envByte, err := files.NewFileOp().GetContent(appInstall.GetEnvPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
images, err := composeV2.GetDockerComposeImages(projectName, envByte, []byte(appInstall.DockerCompose))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, image := range images {
|
||||||
|
task.Log(i18n.GetWithName("PullImageStart", image))
|
||||||
|
if out, err = cmd.ExecWithTimeOut("docker pull "+image, 20*time.Minute); err != nil {
|
||||||
if out != "" {
|
if out != "" {
|
||||||
if strings.Contains(out, "no such host") {
|
if strings.Contains(out, "no such host") {
|
||||||
errMsg = i18n.GetMsgByKey("ErrNoSuchHost") + ":"
|
errMsg = i18n.GetMsgByKey("ErrNoSuchHost") + ":"
|
||||||
@ -921,22 +924,33 @@ func upApp(appInstall *model.AppInstall, pullImages bool) {
|
|||||||
if strings.Contains(out, "timeout") {
|
if strings.Contains(out, "timeout") {
|
||||||
errMsg = i18n.GetMsgByKey("ErrImagePullTimeOut") + ":"
|
errMsg = i18n.GetMsgByKey("ErrImagePullTimeOut") + ":"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
appInstall.Message = errMsg + out
|
appInstall.Message = errMsg + out
|
||||||
}
|
task.LogFailedWithErr(i18n.GetMsgByKey("PullImage"), err)
|
||||||
return err
|
return err
|
||||||
|
} else {
|
||||||
|
task.Log(i18n.GetMsgByKey("PullImageSuccess"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
logStr := fmt.Sprintf("%s %s", i18n.GetMsgByKey("Run"), i18n.GetMsgByKey("App"))
|
||||||
|
task.Log(logStr)
|
||||||
out, err = compose.Up(appInstall.GetComposePath())
|
out, err = compose.Up(appInstall.GetComposePath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if out != "" {
|
if out != "" {
|
||||||
appInstall.Message = errMsg + out
|
appInstall.Message = errMsg + out
|
||||||
|
err = errors.New(out)
|
||||||
}
|
}
|
||||||
|
task.LogFailedWithErr(logStr, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
task.LogSuccess(logStr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := upProject(appInstall); err != nil {
|
if err := upProject(appInstall); err != nil {
|
||||||
|
if appInstall.Message == "" {
|
||||||
|
appInstall.Message = err.Error()
|
||||||
|
}
|
||||||
appInstall.Status = constant.UpErr
|
appInstall.Status = constant.UpErr
|
||||||
} else {
|
} else {
|
||||||
appInstall.Status = constant.Running
|
appInstall.Status = constant.Running
|
||||||
@ -944,14 +958,13 @@ func upApp(appInstall *model.AppInstall, pullImages bool) {
|
|||||||
exist, _ := appInstallRepo.GetFirst(commonRepo.WithByID(appInstall.ID))
|
exist, _ := appInstallRepo.GetFirst(commonRepo.WithByID(appInstall.ID))
|
||||||
if exist.ID > 0 {
|
if exist.ID > 0 {
|
||||||
containerNames, err := getContainerNames(*appInstall)
|
containerNames, err := getContainerNames(*appInstall)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(containerNames) > 0 {
|
if len(containerNames) > 0 {
|
||||||
appInstall.ContainerName = strings.Join(containerNames, ",")
|
appInstall.ContainerName = strings.Join(containerNames, ",")
|
||||||
}
|
}
|
||||||
_ = appInstallRepo.Save(context.Background(), appInstall)
|
_ = appInstallRepo.Save(context.Background(), appInstall)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func rebuildApp(appInstall model.AppInstall) error {
|
func rebuildApp(appInstall model.AppInstall) error {
|
||||||
|
@ -43,4 +43,6 @@ var (
|
|||||||
phpExtensionsRepo = repo.NewIPHPExtensionsRepo()
|
phpExtensionsRepo = repo.NewIPHPExtensionsRepo()
|
||||||
|
|
||||||
favoriteRepo = repo.NewIFavoriteRepo()
|
favoriteRepo = repo.NewIFavoriteRepo()
|
||||||
|
|
||||||
|
taskRepo = repo.NewITaskRepo()
|
||||||
)
|
)
|
||||||
|
@ -466,7 +466,13 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
|
|||||||
return nil, fmt.Errorf("handle ungzip file %s failed, err: %v", fileGzPath, err)
|
return nil, fmt.Errorf("handle ungzip file %s failed, err: %v", fileGzPath, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "image-pull", "image-push", "image-build", "compose-create":
|
case constant.TypeTask:
|
||||||
|
task, err := taskRepo.GetFirst(taskRepo.WithByID(req.TaskID))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
logFilePath = task.LogFile
|
||||||
|
case constant.TypeImagePull, constant.TypeImagePush, constant.TypeImageBuild, constant.TypeComposeCreate:
|
||||||
logFilePath = path.Join(global.CONF.System.TmpDir, fmt.Sprintf("docker_logs/%s", req.Name))
|
logFilePath = path.Join(global.CONF.System.TmpDir, fmt.Sprintf("docker_logs/%s", req.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,10 +5,12 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -206,6 +208,13 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
|||||||
if exist, _ := websiteRepo.GetBy(websiteRepo.WithAlias(alias)); len(exist) > 0 {
|
if exist, _ := websiteRepo.GetBy(websiteRepo.WithAlias(alias)); len(exist) > 0 {
|
||||||
return buserr.New(constant.ErrAliasIsExist)
|
return buserr.New(constant.ErrAliasIsExist)
|
||||||
}
|
}
|
||||||
|
if len(create.FtpPassword) != 0 {
|
||||||
|
pass, err := base64.StdEncoding.DecodeString(create.FtpPassword)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
create.FtpPassword = string(pass)
|
||||||
|
}
|
||||||
|
|
||||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -249,23 +258,15 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
|||||||
runtime *model.Runtime
|
runtime *model.Runtime
|
||||||
)
|
)
|
||||||
|
|
||||||
defer func() {
|
createTask, err := task.NewTaskWithOps(create.PrimaryDomain, task.TaskCreate, task.TaskScopeWebsite, create.TaskID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if website.AppInstallID > 0 {
|
return err
|
||||||
req := request.AppInstalledOperate{
|
|
||||||
InstallId: website.AppInstallID,
|
|
||||||
Operate: constant.Delete,
|
|
||||||
ForceDelete: true,
|
|
||||||
}
|
}
|
||||||
if err := NewIAppInstalledService().Operate(req); err != nil {
|
|
||||||
global.LOG.Errorf(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
var proxy string
|
var proxy string
|
||||||
|
|
||||||
switch create.Type {
|
switch create.Type {
|
||||||
|
|
||||||
case constant.Deployment:
|
case constant.Deployment:
|
||||||
if create.AppType == constant.NewApp {
|
if create.AppType == constant.NewApp {
|
||||||
var (
|
var (
|
||||||
@ -276,13 +277,10 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
|||||||
req.AppDetailId = create.AppInstall.AppDetailId
|
req.AppDetailId = create.AppInstall.AppDetailId
|
||||||
req.Params = create.AppInstall.Params
|
req.Params = create.AppInstall.Params
|
||||||
req.AppContainerConfig = create.AppInstall.AppContainerConfig
|
req.AppContainerConfig = create.AppInstall.AppContainerConfig
|
||||||
tx, installCtx := getTxAndContext()
|
install, err = NewIAppService().Install(req)
|
||||||
install, err = NewIAppService().Install(installCtx, req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
tx.Commit()
|
|
||||||
appInstall = install
|
appInstall = install
|
||||||
website.AppInstallID = install.ID
|
website.AppInstallID = install.ID
|
||||||
website.Proxy = fmt.Sprintf("127.0.0.1:%d", appInstall.HttpPort)
|
website.Proxy = fmt.Sprintf("127.0.0.1:%d", appInstall.HttpPort)
|
||||||
@ -292,9 +290,13 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
configApp := func(t *task.Task) error {
|
||||||
appInstall = &install
|
appInstall = &install
|
||||||
website.AppInstallID = appInstall.ID
|
website.AppInstallID = appInstall.ID
|
||||||
website.Proxy = fmt.Sprintf("127.0.0.1:%d", appInstall.HttpPort)
|
website.Proxy = fmt.Sprintf("127.0.0.1:%d", appInstall.HttpPort)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
createTask.AddSubTask(i18n.GetMsgByKey("ConfigApp"), configApp, nil)
|
||||||
}
|
}
|
||||||
case constant.Runtime:
|
case constant.Runtime:
|
||||||
runtime, err = runtimeRepo.GetFirst(commonRepo.WithByID(create.RuntimeID))
|
runtime, err = runtimeRepo.GetFirst(commonRepo.WithByID(create.RuntimeID))
|
||||||
@ -302,9 +304,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
website.RuntimeID = runtime.ID
|
website.RuntimeID = runtime.ID
|
||||||
switch runtime.Type {
|
if runtime.Type == constant.RuntimePHP {
|
||||||
case constant.RuntimePHP:
|
|
||||||
if runtime.Resource == constant.ResourceAppstore {
|
|
||||||
var (
|
var (
|
||||||
req request.AppInstallCreate
|
req request.AppInstallCreate
|
||||||
install *model.AppInstall
|
install *model.AppInstall
|
||||||
@ -316,13 +316,10 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
|||||||
req.Params["IMAGE_NAME"] = runtime.Image
|
req.Params["IMAGE_NAME"] = runtime.Image
|
||||||
req.AppContainerConfig = create.AppInstall.AppContainerConfig
|
req.AppContainerConfig = create.AppInstall.AppContainerConfig
|
||||||
req.Params["PANEL_WEBSITE_DIR"] = path.Join(nginxInstall.GetPath(), "/www")
|
req.Params["PANEL_WEBSITE_DIR"] = path.Join(nginxInstall.GetPath(), "/www")
|
||||||
tx, installCtx := getTxAndContext()
|
install, err = NewIAppService().Install(req)
|
||||||
install, err = NewIAppService().Install(installCtx, req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
tx.Commit()
|
|
||||||
website.AppInstallID = install.ID
|
website.AppInstallID = install.ID
|
||||||
appInstall = install
|
appInstall = install
|
||||||
website.Proxy = fmt.Sprintf("127.0.0.1:%d", appInstall.HttpPort)
|
website.Proxy = fmt.Sprintf("127.0.0.1:%d", appInstall.HttpPort)
|
||||||
@ -339,25 +336,35 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
|||||||
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
|
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
|
||||||
website.Proxy = fmt.Sprintf("127.0.0.1:%d", runtime.Port)
|
website.Proxy = fmt.Sprintf("127.0.0.1:%d", runtime.Port)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err = configDefaultNginx(website, domains, appInstall, runtime); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(create.FtpUser) != 0 && len(create.FtpPassword) != 0 {
|
if len(create.FtpUser) != 0 && len(create.FtpPassword) != 0 {
|
||||||
|
createFtpUser := func(t *task.Task) error {
|
||||||
indexDir := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "index")
|
indexDir := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "index")
|
||||||
itemID, err := NewIFtpService().Create(dto.FtpCreate{User: create.FtpUser, Password: create.FtpPassword, Path: indexDir})
|
itemID, err := NewIFtpService().Create(dto.FtpCreate{User: create.FtpUser, Password: create.FtpPassword, Path: indexDir})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("create ftp for website failed, err: %v", err)
|
createTask.Log(fmt.Sprintf("create ftp for website failed, err: %v", err))
|
||||||
}
|
}
|
||||||
website.FtpID = itemID
|
website.FtpID = itemID
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
deleteFtpUser := func() {
|
||||||
|
if website.FtpID > 0 {
|
||||||
|
req := dto.BatchDeleteReq{Ids: []uint{website.FtpID}}
|
||||||
|
if err = NewIFtpService().Delete(req); err != nil {
|
||||||
|
createTask.Log(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
createTask.AddSubTask(i18n.GetWithName("ConfigFTP", create.FtpUser), createFtpUser, deleteFtpUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configNginx := func(t *task.Task) error {
|
||||||
|
if err = configDefaultNginx(website, domains, appInstall, runtime); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err = createWafConfig(website, domains); err != nil {
|
if err = createWafConfig(website, domains); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tx, ctx := helper.GetTxAndContext()
|
tx, ctx := helper.GetTxAndContext()
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
if err = websiteRepo.Create(ctx, website); err != nil {
|
if err = websiteRepo.Create(ctx, website); err != nil {
|
||||||
@ -371,6 +378,15 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
|||||||
}
|
}
|
||||||
tx.Commit()
|
tx.Commit()
|
||||||
return nil
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteWebsite := func() {
|
||||||
|
_ = deleteWebsiteFolder(nginxInstall, website)
|
||||||
|
}
|
||||||
|
|
||||||
|
createTask.AddSubTask(i18n.GetMsgByKey("ConfigOpenresty"), configNginx, deleteWebsite)
|
||||||
|
|
||||||
|
return createTask.Execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w WebsiteService) OpWebsite(req request.WebsiteOp) error {
|
func (w WebsiteService) OpWebsite(req request.WebsiteOp) error {
|
||||||
|
@ -220,10 +220,9 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := createWebsiteFolder(nginxInstall, website, runtime); err != nil {
|
if err = createWebsiteFolder(nginxInstall, website, runtime); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
nginxFileName := website.Alias + ".conf"
|
nginxFileName := website.Alias + ".conf"
|
||||||
configPath := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name, "conf", "conf.d", nginxFileName)
|
configPath := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name, "conf", "conf.d", nginxFileName)
|
||||||
nginxContent := string(nginx_conf.WebsiteDefault)
|
nginxContent := string(nginx_conf.WebsiteDefault)
|
||||||
@ -284,15 +283,13 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
|
|||||||
}
|
}
|
||||||
|
|
||||||
config.FilePath = configPath
|
config.FilePath = configPath
|
||||||
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := opNginx(nginxInstall.ContainerName, constant.NginxCheck); err != nil {
|
if err = opNginx(nginxInstall.ContainerName, constant.NginxCheck); err != nil {
|
||||||
_ = deleteWebsiteFolder(nginxInstall, website)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {
|
if err = opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {
|
||||||
_ = deleteWebsiteFolder(nginxInstall, website)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -3,28 +3,35 @@ package task
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||||
|
"github.com/google/uuid"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ActionFunc func() error
|
type ActionFunc func(*Task) error
|
||||||
type RollbackFunc func()
|
type RollbackFunc func()
|
||||||
|
|
||||||
type Task struct {
|
type Task struct {
|
||||||
Name string
|
Name string
|
||||||
|
TaskID string
|
||||||
Logger *log.Logger
|
Logger *log.Logger
|
||||||
SubTasks []*SubTask
|
SubTasks []*SubTask
|
||||||
Rollbacks []RollbackFunc
|
Rollbacks []RollbackFunc
|
||||||
logFile *os.File
|
logFile *os.File
|
||||||
|
taskRepo repo.ITaskRepo
|
||||||
|
Task *model.Task
|
||||||
|
ParentID string
|
||||||
}
|
}
|
||||||
|
|
||||||
type SubTask struct {
|
type SubTask struct {
|
||||||
|
RootTask *Task
|
||||||
Name string
|
Name string
|
||||||
Retry int
|
Retry int
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
@ -33,51 +40,107 @@ type SubTask struct {
|
|||||||
Error error
|
Error error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTask(name string, taskType string) (*Task, error) {
|
const (
|
||||||
logPath := path.Join(constant.LogDir, taskType)
|
TaskInstall = "TaskInstall"
|
||||||
//TODO 增加插入到日志表的逻辑
|
TaskUninstall = "TaskUninstall"
|
||||||
|
TaskCreate = "TaskCreate"
|
||||||
|
TaskDelete = "TaskDelete"
|
||||||
|
TaskUpgrade = "TaskUpgrade"
|
||||||
|
TaskUpdate = "TaskUpdate"
|
||||||
|
TaskRestart = "TaskRestart"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TaskScopeWebsite = "Website"
|
||||||
|
TaskScopeApp = "App"
|
||||||
|
TaskScopeRuntime = "Runtime"
|
||||||
|
TaskScopeDatabase = "Database"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TaskSuccess = "Success"
|
||||||
|
TaskFailed = "Failed"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetTaskName(resourceName, operate, scope string) string {
|
||||||
|
return fmt.Sprintf("%s%s [%s]", i18n.GetMsgByKey(operate), i18n.GetMsgByKey(scope), resourceName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTaskWithOps(resourceName, operate, scope, taskID string) (*Task, error) {
|
||||||
|
return NewTask(GetTaskName(resourceName, operate, scope), scope, taskID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChildTask(name, taskType, parentTaskID string) (*Task, error) {
|
||||||
|
task, err := NewTask(name, taskType, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
task.ParentID = parentTaskID
|
||||||
|
return task, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTask(name, taskType, taskID string) (*Task, error) {
|
||||||
|
if taskID == "" {
|
||||||
|
taskID = uuid.New().String()
|
||||||
|
}
|
||||||
|
logDir := path.Join(constant.LogDir, taskType)
|
||||||
|
if _, err := os.Stat(logDir); os.IsNotExist(err) {
|
||||||
|
if err = os.MkdirAll(logDir, 0755); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create log directory: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logPath := path.Join(constant.LogDir, taskType, taskID+".log")
|
||||||
file, err := os.OpenFile(logPath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0666)
|
file, err := os.OpenFile(logPath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to open log file: %w", err)
|
return nil, fmt.Errorf("failed to open log file: %w", err)
|
||||||
}
|
}
|
||||||
logger := log.New(file, "", log.LstdFlags)
|
logger := log.New(file, "", log.LstdFlags)
|
||||||
return &Task{Name: name, logFile: file, Logger: logger}, nil
|
taskModel := &model.Task{
|
||||||
|
ID: taskID,
|
||||||
|
Name: name,
|
||||||
|
Type: taskType,
|
||||||
|
LogFile: logPath,
|
||||||
|
Status: constant.StatusRunning,
|
||||||
|
}
|
||||||
|
taskRepo := repo.NewITaskRepo()
|
||||||
|
task := &Task{Name: name, logFile: file, Logger: logger, taskRepo: taskRepo, Task: taskModel}
|
||||||
|
return task, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Task) AddSubTask(name string, action ActionFunc, rollback RollbackFunc) {
|
func (t *Task) AddSubTask(name string, action ActionFunc, rollback RollbackFunc) {
|
||||||
subTask := &SubTask{Name: name, Retry: 0, Timeout: 10 * time.Minute, Action: action, Rollback: rollback}
|
subTask := &SubTask{RootTask: t, Name: name, Retry: 0, Timeout: 10 * time.Minute, Action: action, Rollback: rollback}
|
||||||
t.SubTasks = append(t.SubTasks, subTask)
|
t.SubTasks = append(t.SubTasks, subTask)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Task) AddSubTaskWithOps(name string, action ActionFunc, rollback RollbackFunc, retry int, timeout time.Duration) {
|
func (t *Task) AddSubTaskWithOps(name string, action ActionFunc, rollback RollbackFunc, retry int, timeout time.Duration) {
|
||||||
subTask := &SubTask{Name: name, Retry: retry, Timeout: timeout, Action: action, Rollback: rollback}
|
subTask := &SubTask{RootTask: t, Name: name, Retry: retry, Timeout: timeout, Action: action, Rollback: rollback}
|
||||||
t.SubTasks = append(t.SubTasks, subTask)
|
t.SubTasks = append(t.SubTasks, subTask)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SubTask) Execute(logger *log.Logger) bool {
|
func (s *SubTask) Execute() error {
|
||||||
logger.Printf(i18n.GetWithName("SubTaskStart", s.Name))
|
s.RootTask.Log(s.Name)
|
||||||
|
var err error
|
||||||
for i := 0; i < s.Retry+1; i++ {
|
for i := 0; i < s.Retry+1; i++ {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
logger.Printf(i18n.GetWithName("TaskRetry", strconv.Itoa(i)))
|
s.RootTask.Log(i18n.GetWithName("TaskRetry", strconv.Itoa(i)))
|
||||||
}
|
}
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
done := make(chan error)
|
done := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
done <- s.Action()
|
done <- s.Action(s.RootTask)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
logger.Printf(i18n.GetWithName("TaskTimeout", s.Name))
|
s.RootTask.Log(i18n.GetWithName("TaskTimeout", s.Name))
|
||||||
case err := <-done:
|
case err = <-done:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Error = err
|
s.RootTask.Log(i18n.GetWithNameAndErr("SubTaskFailed", s.Name, err))
|
||||||
logger.Printf(i18n.GetWithNameAndErr("TaskFailed", s.Name, err))
|
|
||||||
} else {
|
} else {
|
||||||
logger.Printf(i18n.GetWithName("TaskSuccess", s.Name))
|
s.RootTask.Log(i18n.GetWithName("SubTaskSuccess", s.Name))
|
||||||
return true
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,29 +151,77 @@ func (s *SubTask) Execute(logger *log.Logger) bool {
|
|||||||
}
|
}
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
}
|
}
|
||||||
if s.Error != nil {
|
return err
|
||||||
s.Error = fmt.Errorf(i18n.GetWithName("TaskFailed", s.Name))
|
}
|
||||||
}
|
|
||||||
return false
|
func (t *Task) updateTask(task *model.Task) {
|
||||||
|
_ = t.taskRepo.Update(context.Background(), task)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Task) Execute() error {
|
func (t *Task) Execute() error {
|
||||||
t.Logger.Printf(i18n.GetWithName("TaskStart", t.Name))
|
if err := t.taskRepo.Create(context.Background(), t.Task); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
var err error
|
var err error
|
||||||
|
t.Log(i18n.GetWithName("TaskStart", t.Name))
|
||||||
for _, subTask := range t.SubTasks {
|
for _, subTask := range t.SubTasks {
|
||||||
if subTask.Execute(t.Logger) {
|
t.Task.CurrentStep = subTask.Name
|
||||||
|
t.updateTask(t.Task)
|
||||||
|
if err = subTask.Execute(); err == nil {
|
||||||
if subTask.Rollback != nil {
|
if subTask.Rollback != nil {
|
||||||
t.Rollbacks = append(t.Rollbacks, subTask.Rollback)
|
t.Rollbacks = append(t.Rollbacks, subTask.Rollback)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = subTask.Error
|
t.Task.ErrorMsg = err.Error()
|
||||||
|
t.Task.Status = constant.StatusFailed
|
||||||
for _, rollback := range t.Rollbacks {
|
for _, rollback := range t.Rollbacks {
|
||||||
rollback()
|
rollback()
|
||||||
}
|
}
|
||||||
|
t.updateTask(t.Task)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.Logger.Printf(i18n.GetWithName("TaskEnd", t.Name))
|
if t.Task.Status == constant.Running {
|
||||||
|
t.Task.Status = constant.StatusSuccess
|
||||||
|
t.Log(i18n.GetWithName("TaskSuccess", t.Name))
|
||||||
|
} else {
|
||||||
|
t.Log(i18n.GetWithName("TaskFailed", t.Name))
|
||||||
|
}
|
||||||
|
t.Log("[TASK-END]")
|
||||||
|
t.Task.EndAt = time.Now()
|
||||||
|
t.updateTask(t.Task)
|
||||||
_ = t.logFile.Close()
|
_ = t.logFile.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Task) DeleteLogFile() {
|
||||||
|
_ = os.Remove(t.Task.LogFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) LogWithStatus(msg string, err error) {
|
||||||
|
if err != nil {
|
||||||
|
t.Logger.Printf(i18n.GetWithNameAndErr("FailedStatus", msg, err))
|
||||||
|
} else {
|
||||||
|
t.Logger.Printf(i18n.GetWithName("SuccessStatus", msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) Log(msg string) {
|
||||||
|
t.Logger.Printf(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) LogFailed(msg string) {
|
||||||
|
t.Logger.Printf(msg + i18n.GetMsgByKey("Failed"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) LogFailedWithErr(msg string, err error) {
|
||||||
|
t.Logger.Printf(fmt.Sprintf("%s %s : %s", msg, i18n.GetMsgByKey("Failed"), err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) LogSuccess(msg string) {
|
||||||
|
t.Logger.Printf(msg + i18n.GetMsgByKey("Success"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) LogStart(msg string) {
|
||||||
|
t.Logger.Printf(fmt.Sprintf("%s%s", i18n.GetMsgByKey("Start"), msg))
|
||||||
|
}
|
||||||
|
@ -14,6 +14,7 @@ const (
|
|||||||
SyncSuccess = "SyncSuccess"
|
SyncSuccess = "SyncSuccess"
|
||||||
Paused = "Paused"
|
Paused = "Paused"
|
||||||
UpErr = "UpErr"
|
UpErr = "UpErr"
|
||||||
|
InstallErr = "InstallErr"
|
||||||
|
|
||||||
ContainerPrefix = "1Panel-"
|
ContainerPrefix = "1Panel-"
|
||||||
|
|
||||||
|
@ -11,6 +11,11 @@ const (
|
|||||||
TypePhp = "php"
|
TypePhp = "php"
|
||||||
TypeSSL = "ssl"
|
TypeSSL = "ssl"
|
||||||
TypeSystem = "system"
|
TypeSystem = "system"
|
||||||
|
TypeTask = "task"
|
||||||
|
TypeImagePull = "image-pull"
|
||||||
|
TypeImagePush = "image-push"
|
||||||
|
TypeImageBuild = "image-build"
|
||||||
|
TypeComposeCreate = "compose-create"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
6
agent/constant/task.go
Normal file
6
agent/constant/task.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package constant
|
||||||
|
|
||||||
|
const (
|
||||||
|
TaskInstall = "installApp"
|
||||||
|
TaskCreateWebsite = "createWebsite"
|
||||||
|
)
|
@ -198,10 +198,36 @@ ErrXpackNotActive: "This section is a professional edition feature, please synch
|
|||||||
ErrXpackOutOfDate: "The current license has expired, please re-import the license in Panel Settings-License interface"
|
ErrXpackOutOfDate: "The current license has expired, please re-import the license in Panel Settings-License interface"
|
||||||
|
|
||||||
#task
|
#task
|
||||||
TaskStart: "{{.name}} started [START]"
|
TaskStart: "{{.name}} Start [START]"
|
||||||
TaskEnd: "{{.name}} ended [COMPLETED]"
|
TaskEnd: "{{.name}} End [COMPLETED]"
|
||||||
TaskFailed: "{{.name}} failed: {{.err}}"
|
TaskFailed: "{{.name}} Failed"
|
||||||
TaskTimeout: "{{.name}} timed out"
|
TaskTimeout: "{{.name}} Timeout"
|
||||||
TaskSuccess: "{{.name}} succeeded"
|
TaskSuccess: "{{.name}} Success"
|
||||||
TaskRetry: "Start {{.name}} retry"
|
TaskRetry: "Starting {{.name}} Retry"
|
||||||
SubTaskStart: "Start {{.name}}"
|
SubTaskSuccess: "{{ .name }} Success"
|
||||||
|
SubTaskFailed: "{{ .name }} Failed: {{ .err }}"
|
||||||
|
TaskInstall: "Install"
|
||||||
|
TaskUninstall: "Uninstall"
|
||||||
|
TaskCreate: "Create"
|
||||||
|
TaskDelete: "Delete"
|
||||||
|
TaskUpgrade: "Upgrade"
|
||||||
|
TaskUpdate: "Update"
|
||||||
|
TaskRestart: "Restart"
|
||||||
|
Website: "Website"
|
||||||
|
App: "App"
|
||||||
|
Runtime: "Runtime"
|
||||||
|
Database: "Database"
|
||||||
|
ConfigFTP: "Create FTP User {{ .name }}"
|
||||||
|
ConfigOpenresty: "Create Openresty Configuration File"
|
||||||
|
InstallAppSuccess: "App {{ .name }} Installed Successfully"
|
||||||
|
ConfigRuntime: "Configure Runtime"
|
||||||
|
ConfigApp: "Configure App"
|
||||||
|
SuccessStatus: "{{ .name }} Success"
|
||||||
|
FailedStatus: "{{ .name }} Failed {{.err}}"
|
||||||
|
HandleLink: "Handle App Link"
|
||||||
|
HandleDatabaseApp: "Handle App Parameters"
|
||||||
|
ExecShell: "Execute {{ .name }} Script"
|
||||||
|
PullImage: "Pull Image"
|
||||||
|
Start: "Start"
|
||||||
|
Run: "Run"
|
||||||
|
|
||||||
|
@ -202,8 +202,33 @@ ErrXpackOutOfDate: "當前許可證已過期,請重新在 面板設置-許可
|
|||||||
#task
|
#task
|
||||||
TaskStart: "{{.name}} 開始 [START]"
|
TaskStart: "{{.name}} 開始 [START]"
|
||||||
TaskEnd: "{{.name}} 結束 [COMPLETED]"
|
TaskEnd: "{{.name}} 結束 [COMPLETED]"
|
||||||
TaskFailed: "{{.name}} 失敗: {{.err}}"
|
TaskFailed: "{{.name}} 失敗"
|
||||||
TaskTimeout: "{{.name}} 逾時"
|
TaskTimeout: "{{.name}} 超時"
|
||||||
TaskSuccess: "{{.name}} 成功"
|
TaskSuccess: "{{.name}} 成功"
|
||||||
TaskRetry: "開始第 {{.name}} 次重試"
|
TaskRetry: "開始第 {{.name}} 次重試"
|
||||||
SubTaskStart: "開始 {{.name}}"
|
SubTaskSuccess: "{{ .name }} 成功"
|
||||||
|
SubTaskFailed: "{{ .name }} 失敗: {{ .err }}"
|
||||||
|
TaskInstall: "安裝"
|
||||||
|
TaskUninstall: "卸載"
|
||||||
|
TaskCreate: "創建"
|
||||||
|
TaskDelete: "刪除"
|
||||||
|
TaskUpgrade: "升級"
|
||||||
|
TaskUpdate: "更新"
|
||||||
|
TaskRestart: "重啟"
|
||||||
|
Website: "網站"
|
||||||
|
App: "應用"
|
||||||
|
Runtime: "運行環境"
|
||||||
|
Database: "數據庫"
|
||||||
|
ConfigFTP: "創建 FTP 用戶 {{ .name }}"
|
||||||
|
ConfigOpenresty: "創建 Openresty 配置文件"
|
||||||
|
InstallAppSuccess: "應用 {{ .name }} 安裝成功"
|
||||||
|
ConfigRuntime: "配置運行環境"
|
||||||
|
ConfigApp: "配置應用"
|
||||||
|
SuccessStatus: "{{ .name }} 成功"
|
||||||
|
FailedStatus: "{{ .name }} 失敗 {{.err}}"
|
||||||
|
HandleLink: "處理應用關聯"
|
||||||
|
HandleDatabaseApp: "處理應用參數"
|
||||||
|
ExecShell: "執行 {{ .name }} 腳本"
|
||||||
|
PullImage: "拉取鏡像"
|
||||||
|
Start: "開始"
|
||||||
|
Run: "啟動"
|
||||||
|
@ -203,8 +203,33 @@ ErrXpackOutOfDate: "当前许可证已过期,请重新在 面板设置-许可
|
|||||||
#task
|
#task
|
||||||
TaskStart: "{{.name}} 开始 [START]"
|
TaskStart: "{{.name}} 开始 [START]"
|
||||||
TaskEnd: "{{.name}} 结束 [COMPLETED]"
|
TaskEnd: "{{.name}} 结束 [COMPLETED]"
|
||||||
TaskFailed: "{{.name}} 失败: {{.err}}"
|
TaskFailed: "{{.name}} 失败"
|
||||||
TaskTimeout: "{{.name}} 超时"
|
TaskTimeout: "{{.name}} 超时"
|
||||||
TaskSuccess: "{{.name}} 成功"
|
TaskSuccess: "{{.name}} 成功"
|
||||||
TaskRetry: "开始第 {{.name}} 次重试"
|
TaskRetry: "开始第 {{.name}} 次重试"
|
||||||
SubTaskStart: "开始 {{.name}}"
|
SubTaskSuccess: "{{ .name }} 成功"
|
||||||
|
SubTaskFailed: "{{ .name }} 失败: {{ .err }}"
|
||||||
|
TaskInstall: "安装"
|
||||||
|
TaskUninstall: "卸载"
|
||||||
|
TaskCreate: "创建"
|
||||||
|
TaskDelete: "删除"
|
||||||
|
TaskUpgrade: "升级"
|
||||||
|
TaskUpdate: "更新"
|
||||||
|
TaskRestart: "重启"
|
||||||
|
Website: "网站"
|
||||||
|
App: "应用"
|
||||||
|
Runtime: "运行环境"
|
||||||
|
Database: "数据库"
|
||||||
|
ConfigFTP: "创建 FTP 用户 {{ .name }}"
|
||||||
|
ConfigOpenresty: "创建 Openresty 配置文件"
|
||||||
|
InstallAppSuccess: "应用 {{ .name }} 安装成功"
|
||||||
|
ConfigRuntime: "配置运行环境"
|
||||||
|
ConfigApp: "配置应用"
|
||||||
|
SuccessStatus: "{{ .name }} 成功"
|
||||||
|
FailedStatus: "{{ .name }} 失败 {{.err}}"
|
||||||
|
HandleLink: "处理应用关联"
|
||||||
|
HandleDatabaseApp: "处理应用参数"
|
||||||
|
ExecShell: "执行 {{ .name }} 脚本"
|
||||||
|
PullImage: "拉取镜像"
|
||||||
|
Start: "开始"
|
||||||
|
Run: "启动"
|
@ -17,6 +17,7 @@ func Init() {
|
|||||||
migrations.InitDefaultGroup,
|
migrations.InitDefaultGroup,
|
||||||
migrations.InitDefaultCA,
|
migrations.InitDefaultCA,
|
||||||
migrations.InitPHPExtensions,
|
migrations.InitPHPExtensions,
|
||||||
|
migrations.AddTask,
|
||||||
})
|
})
|
||||||
if err := m.Migrate(); err != nil {
|
if err := m.Migrate(); err != nil {
|
||||||
global.LOG.Error(err)
|
global.LOG.Error(err)
|
||||||
|
@ -253,3 +253,11 @@ var InitPHPExtensions = &gormigrate.Migration{
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var AddTask = &gormigrate.Migration{
|
||||||
|
ID: "20240724-add-task",
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
return tx.AutoMigrate(
|
||||||
|
&model.Task{})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/cron"
|
"github.com/1Panel-dev/1Panel/agent/cron"
|
||||||
"github.com/1Panel-dev/1Panel/agent/global"
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||||
"github.com/1Panel-dev/1Panel/agent/init/app"
|
"github.com/1Panel-dev/1Panel/agent/init/app"
|
||||||
"github.com/1Panel-dev/1Panel/agent/init/business"
|
"github.com/1Panel-dev/1Panel/agent/init/business"
|
||||||
@ -40,25 +39,23 @@ func Start() {
|
|||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Handler: rootRouter,
|
Handler: rootRouter,
|
||||||
}
|
}
|
||||||
if len(global.CurrentNode) == 0 || global.CurrentNode == "127.0.0.1" {
|
//ln, err := net.Listen("tcp4", "0.0.0.0:9998")
|
||||||
_ = os.Remove("/tmp/agent.sock")
|
//if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
//}
|
||||||
|
//type tcpKeepAliveListener struct {
|
||||||
|
// *net.TCPListener
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//global.LOG.Info("listen at http://0.0.0.0:9998")
|
||||||
|
//if err := server.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}); err != nil {
|
||||||
|
// panic(err)
|
||||||
|
//}
|
||||||
|
|
||||||
|
os.Remove("/tmp/agent.sock")
|
||||||
listener, err := net.Listen("unix", "/tmp/agent.sock")
|
listener, err := net.Listen("unix", "/tmp/agent.sock")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
_ = server.Serve(listener)
|
server.Serve(listener)
|
||||||
} else {
|
|
||||||
server.Addr = "0.0.0.0:9999"
|
|
||||||
type tcpKeepAliveListener struct {
|
|
||||||
*net.TCPListener
|
|
||||||
}
|
|
||||||
ln, err := net.Listen("tcp4", "0.0.0.0:9999")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
global.LOG.Info("listen at http://0.0.0.0:9999")
|
|
||||||
if err := server.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ type ILogService interface {
|
|||||||
CreateLoginLog(operation model.LoginLog) error
|
CreateLoginLog(operation model.LoginLog) error
|
||||||
PageLoginLog(search dto.SearchLgLogWithPage) (int64, interface{}, error)
|
PageLoginLog(search dto.SearchLgLogWithPage) (int64, interface{}, error)
|
||||||
|
|
||||||
CreateOperationLog(operation model.OperationLog) error
|
CreateOperationLog(operation *model.OperationLog) error
|
||||||
PageOperationLog(search dto.SearchOpLogWithPage) (int64, interface{}, error)
|
PageOperationLog(search dto.SearchOpLogWithPage) (int64, interface{}, error)
|
||||||
|
|
||||||
CleanLogs(logtype string) error
|
CleanLogs(logtype string) error
|
||||||
@ -92,8 +92,8 @@ func (u *LogService) PageLoginLog(req dto.SearchLgLogWithPage) (int64, interface
|
|||||||
return total, dtoOps, err
|
return total, dtoOps, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *LogService) CreateOperationLog(operation model.OperationLog) error {
|
func (u *LogService) CreateOperationLog(operation *model.OperationLog) error {
|
||||||
return logRepo.CreateOperationLog(&operation)
|
return logRepo.CreateOperationLog(operation)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *LogService) PageOperationLog(req dto.SearchOpLogWithPage) (int64, interface{}, error) {
|
func (u *LogService) PageOperationLog(req dto.SearchOpLogWithPage) (int64, interface{}, error) {
|
||||||
|
@ -27,7 +27,7 @@ func OperationLog() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
source := loadLogInfo(c.Request.URL.Path)
|
source := loadLogInfo(c.Request.URL.Path)
|
||||||
record := model.OperationLog{
|
record := &model.OperationLog{
|
||||||
Source: source,
|
Source: source,
|
||||||
IP: c.ClientIP(),
|
IP: c.ClientIP(),
|
||||||
Method: strings.ToLower(c.Request.Method),
|
Method: strings.ToLower(c.Request.Method),
|
||||||
|
@ -22,7 +22,7 @@ func Proxy() gin.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
currentNode := c.Request.Header.Get("CurrentNode")
|
currentNode := c.Request.Header.Get("CurrentNode")
|
||||||
if currentNode == "127.0.0.1" {
|
if len(currentNode) == 0 || currentNode == "127.0.0.1" {
|
||||||
sockPath := "/tmp/agent.sock"
|
sockPath := "/tmp/agent.sock"
|
||||||
if _, err := os.Stat(sockPath); err != nil {
|
if _, err := os.Stat(sockPath); err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrProxy, err)
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrProxy, err)
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
"qs": "^6.12.1",
|
"qs": "^6.12.1",
|
||||||
"screenfull": "^6.0.2",
|
"screenfull": "^6.0.2",
|
||||||
"unplugin-vue-define-options": "^0.7.3",
|
"unplugin-vue-define-options": "^0.7.3",
|
||||||
|
"uuid": "^10.0.0",
|
||||||
"vue": "^3.4.27",
|
"vue": "^3.4.27",
|
||||||
"vue-clipboard3": "^2.0.0",
|
"vue-clipboard3": "^2.0.0",
|
||||||
"vue-codemirror": "^6.1.1",
|
"vue-codemirror": "^6.1.1",
|
||||||
|
@ -174,6 +174,7 @@ export namespace File {
|
|||||||
name?: string;
|
name?: string;
|
||||||
page: number;
|
page: number;
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
|
taskID?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Favorite extends CommonModel {
|
export interface Favorite extends CommonModel {
|
||||||
|
@ -79,6 +79,7 @@ export namespace Website {
|
|||||||
proxyType: string;
|
proxyType: string;
|
||||||
ftpUser: string;
|
ftpUser: string;
|
||||||
ftpPassword: string;
|
ftpPassword: string;
|
||||||
|
taskID: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WebSiteUpdateReq {
|
export interface WebSiteUpdateReq {
|
||||||
|
@ -19,7 +19,7 @@ export const CreateWebsite = (req: Website.WebSiteCreateReq) => {
|
|||||||
if (request.ftpPassword) {
|
if (request.ftpPassword) {
|
||||||
request.ftpPassword = Base64.encode(request.ftpPassword);
|
request.ftpPassword = Base64.encode(request.ftpPassword);
|
||||||
}
|
}
|
||||||
return http.post<any>(`/websites`, request);
|
return http.post<any>(`/websites`, request, TimeoutEnum.T_10M);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const OpWebsite = (req: Website.WebSiteOp) => {
|
export const OpWebsite = (req: Website.WebSiteOp) => {
|
||||||
|
@ -246,7 +246,7 @@ const initCodemirror = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
let hljsDom = scrollerElement.value.querySelector('.hljs') as HTMLElement;
|
let hljsDom = scrollerElement.value.querySelector('.hljs') as HTMLElement;
|
||||||
hljsDom.style['min-height'] = '500px';
|
hljsDom.style['min-height'] = '100px';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
212
frontend/src/components/task-log/index.vue
Normal file
212
frontend/src/components/task-log/index.vue
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="open"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
:show-close="showClose"
|
||||||
|
:before-close="handleClose"
|
||||||
|
class="task-log-dialog"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<highlightjs ref="editorRef" language="JavaScript" :autodetect="false" :code="content"></highlightjs>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { nextTick, onUnmounted, reactive, ref } from 'vue';
|
||||||
|
import { ReadByLine } from '@/api/modules/files';
|
||||||
|
|
||||||
|
const editorRef = ref();
|
||||||
|
|
||||||
|
const data = ref({
|
||||||
|
enable: false,
|
||||||
|
content: '',
|
||||||
|
path: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
let timer: NodeJS.Timer | null = null;
|
||||||
|
const tailLog = ref(false);
|
||||||
|
const content = ref('');
|
||||||
|
const end = ref(false);
|
||||||
|
const lastContent = ref('');
|
||||||
|
const scrollerElement = ref<HTMLElement | null>(null);
|
||||||
|
const minPage = ref(1);
|
||||||
|
const maxPage = ref(1);
|
||||||
|
const open = ref(false);
|
||||||
|
const taskID = ref('');
|
||||||
|
const showClose = ref(false);
|
||||||
|
|
||||||
|
const readReq = reactive({
|
||||||
|
taskID: '',
|
||||||
|
type: 'task',
|
||||||
|
page: 1,
|
||||||
|
pageSize: 500,
|
||||||
|
latest: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const stopSignals = ['[TASK-END]'];
|
||||||
|
|
||||||
|
const acceptParams = (id: string, closeShow: boolean) => {
|
||||||
|
if (closeShow) {
|
||||||
|
showClose.value = closeShow;
|
||||||
|
}
|
||||||
|
taskID.value = id;
|
||||||
|
open.value = true;
|
||||||
|
initCodemirror();
|
||||||
|
init();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getContent = (pre: boolean) => {
|
||||||
|
readReq.taskID = taskID.value;
|
||||||
|
if (readReq.page < 1) {
|
||||||
|
readReq.page = 1;
|
||||||
|
}
|
||||||
|
ReadByLine(readReq).then((res) => {
|
||||||
|
if (!end.value && res.data.end) {
|
||||||
|
lastContent.value = content.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.data.content = res.data.content.replace(/\\u(\w{4})/g, function (match, grp) {
|
||||||
|
return String.fromCharCode(parseInt(grp, 16));
|
||||||
|
});
|
||||||
|
data.value = res.data;
|
||||||
|
if (res.data.content != '') {
|
||||||
|
if (stopSignals.some((signal) => res.data.content.endsWith(signal))) {
|
||||||
|
onCloseLog();
|
||||||
|
}
|
||||||
|
if (end.value) {
|
||||||
|
if (lastContent.value == '') {
|
||||||
|
content.value = res.data.content;
|
||||||
|
} else {
|
||||||
|
content.value = pre
|
||||||
|
? res.data.content + '\n' + lastContent.value
|
||||||
|
: lastContent.value + '\n' + res.data.content;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (content.value == '') {
|
||||||
|
content.value = res.data.content;
|
||||||
|
} else {
|
||||||
|
content.value = pre
|
||||||
|
? res.data.content + '\n' + content.value
|
||||||
|
: content.value + '\n' + res.data.content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end.value = res.data.end;
|
||||||
|
nextTick(() => {
|
||||||
|
if (pre) {
|
||||||
|
if (scrollerElement.value.scrollHeight > 2000) {
|
||||||
|
scrollerElement.value.scrollTop = 2000;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scrollerElement.value.scrollTop = scrollerElement.value.scrollHeight;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (readReq.latest) {
|
||||||
|
readReq.page = res.data.total;
|
||||||
|
readReq.latest = false;
|
||||||
|
maxPage.value = res.data.total;
|
||||||
|
minPage.value = res.data.total;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeTail = (fromOutSide: boolean) => {
|
||||||
|
if (fromOutSide) {
|
||||||
|
tailLog.value = !tailLog.value;
|
||||||
|
}
|
||||||
|
if (tailLog.value) {
|
||||||
|
timer = setInterval(() => {
|
||||||
|
getContent(false);
|
||||||
|
}, 1000 * 3);
|
||||||
|
} else {
|
||||||
|
onCloseLog();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
onCloseLog();
|
||||||
|
open.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCloseLog = async () => {
|
||||||
|
tailLog.value = false;
|
||||||
|
clearInterval(Number(timer));
|
||||||
|
timer = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
function isScrolledToBottom(element: HTMLElement): boolean {
|
||||||
|
return element.scrollTop + element.clientHeight + 1 >= element.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isScrolledToTop(element: HTMLElement): boolean {
|
||||||
|
return element.scrollTop === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
|
tailLog.value = true;
|
||||||
|
if (tailLog.value) {
|
||||||
|
changeTail(false);
|
||||||
|
}
|
||||||
|
readReq.latest = true;
|
||||||
|
getContent(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const initCodemirror = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
if (editorRef.value) {
|
||||||
|
scrollerElement.value = editorRef.value.$el as HTMLElement;
|
||||||
|
scrollerElement.value.addEventListener('scroll', function () {
|
||||||
|
if (isScrolledToBottom(scrollerElement.value)) {
|
||||||
|
readReq.page = maxPage.value;
|
||||||
|
getContent(false);
|
||||||
|
}
|
||||||
|
if (isScrolledToTop(scrollerElement.value)) {
|
||||||
|
readReq.page = minPage.value - 1;
|
||||||
|
if (readReq.page < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
minPage.value = readReq.page;
|
||||||
|
getContent(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let hljsDom = scrollerElement.value.querySelector('.hljs') as HTMLElement;
|
||||||
|
hljsDom.style['min-height'] = '100px';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
onCloseLog();
|
||||||
|
});
|
||||||
|
|
||||||
|
defineExpose({ acceptParams, handleClose });
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.task-log-dialog {
|
||||||
|
--dialog-max-height: 80vh;
|
||||||
|
--dialog-header-height: 50px;
|
||||||
|
--dialog-padding: 20px;
|
||||||
|
.el-dialog {
|
||||||
|
max-width: 60%;
|
||||||
|
max-height: var(--dialog-max-height);
|
||||||
|
margin-top: 5vh !important;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.el-dialog__body {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: var(--dialog-padding);
|
||||||
|
}
|
||||||
|
.log-container {
|
||||||
|
height: calc(var(--dialog-max-height) - var(--dialog-header-height) - var(--dialog-padding) * 2);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.log-file {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -42,7 +42,7 @@ const GlobalStore = defineStore({
|
|||||||
|
|
||||||
errStatus: '',
|
errStatus: '',
|
||||||
|
|
||||||
currentNode: '',
|
currentNode: '127.0.0.1',
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
isDarkTheme: (state) =>
|
isDarkTheme: (state) =>
|
||||||
|
@ -40,7 +40,6 @@ import { reactive, ref } from 'vue';
|
|||||||
import { Rules } from '@/global/form-rules';
|
import { Rules } from '@/global/form-rules';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { ElForm } from 'element-plus';
|
import { ElForm } from 'element-plus';
|
||||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
|
||||||
import { MsgSuccess } from '@/utils/message';
|
import { MsgSuccess } from '@/utils/message';
|
||||||
import { Host } from '@/api/interface/host';
|
import { Host } from '@/api/interface/host';
|
||||||
import { operateForwardRule } from '@/api/modules/host';
|
import { operateForwardRule } from '@/api/modules/host';
|
||||||
|
@ -163,7 +163,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, onBeforeUnmount, reactive, ref, shallowRef } from 'vue';
|
import { onBeforeUnmount, reactive, ref } from 'vue';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { ElMessageBox } from 'element-plus';
|
import { ElMessageBox } from 'element-plus';
|
||||||
import { MsgSuccess } from '@/utils/message';
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
@ -13,10 +13,6 @@
|
|||||||
<el-button type="primary" @click="openCreate">
|
<el-button type="primary" @click="openCreate">
|
||||||
{{ $t('runtime.create') }}
|
{{ $t('runtime.create') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<el-button type="primary" plain @click="onOpenBuildCache()">
|
|
||||||
{{ $t('container.cleanBuildCache') }}
|
|
||||||
</el-button>
|
|
||||||
</template>
|
</template>
|
||||||
<template #main>
|
<template #main>
|
||||||
<ComplexTable :pagination-config="paginationConfig" :data="items" @search="search()" :heightDiff="350">
|
<ComplexTable :pagination-config="paginationConfig" :data="items" @search="search()" :heightDiff="350">
|
||||||
@ -115,8 +111,6 @@ import { Promotion } from '@element-plus/icons-vue';
|
|||||||
import PortJumpDialog from '@/components/port-jump/index.vue';
|
import PortJumpDialog from '@/components/port-jump/index.vue';
|
||||||
import AppResources from '@/views/website/runtime/php/check/index.vue';
|
import AppResources from '@/views/website/runtime/php/check/index.vue';
|
||||||
import { ElMessageBox } from 'element-plus';
|
import { ElMessageBox } from 'element-plus';
|
||||||
import { containerPrune } from '@/api/modules/container';
|
|
||||||
import { MsgSuccess } from '@/utils/message';
|
|
||||||
|
|
||||||
let timer: NodeJS.Timer | null = null;
|
let timer: NodeJS.Timer | null = null;
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
@ -221,29 +215,6 @@ const openDelete = async (row: Runtime.Runtime) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onOpenBuildCache = () => {
|
|
||||||
ElMessageBox.confirm(i18n.global.t('container.delBuildCacheHelper'), i18n.global.t('container.cleanBuildCache'), {
|
|
||||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
|
||||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
|
||||||
type: 'info',
|
|
||||||
}).then(async () => {
|
|
||||||
loading.value = true;
|
|
||||||
let params = {
|
|
||||||
pruneType: 'buildcache',
|
|
||||||
withTagAll: false,
|
|
||||||
};
|
|
||||||
await containerPrune(params)
|
|
||||||
.then((res) => {
|
|
||||||
loading.value = false;
|
|
||||||
MsgSuccess(i18n.global.t('container.cleanSuccess', [res.data.deletedNumber]));
|
|
||||||
search();
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
loading.value = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const openLog = (row: any) => {
|
const openLog = (row: any) => {
|
||||||
composeLogRef.value.acceptParams({ compose: row.path + '/docker-compose.yml', resource: row.name });
|
composeLogRef.value.acceptParams({ compose: row.path + '/docker-compose.yml', resource: row.name });
|
||||||
};
|
};
|
||||||
|
@ -13,10 +13,6 @@
|
|||||||
<el-button type="primary" @click="openCreate">
|
<el-button type="primary" @click="openCreate">
|
||||||
{{ $t('runtime.create') }}
|
{{ $t('runtime.create') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<el-button type="primary" plain @click="onOpenBuildCache()">
|
|
||||||
{{ $t('container.cleanBuildCache') }}
|
|
||||||
</el-button>
|
|
||||||
</template>
|
</template>
|
||||||
<template #main>
|
<template #main>
|
||||||
<ComplexTable :pagination-config="paginationConfig" :data="items" @search="search()" :heightDiff="350">
|
<ComplexTable :pagination-config="paginationConfig" :data="items" @search="search()" :heightDiff="350">
|
||||||
@ -115,8 +111,6 @@ import { Promotion } from '@element-plus/icons-vue';
|
|||||||
import PortJumpDialog from '@/components/port-jump/index.vue';
|
import PortJumpDialog from '@/components/port-jump/index.vue';
|
||||||
import AppResources from '@/views/website/runtime/php/check/index.vue';
|
import AppResources from '@/views/website/runtime/php/check/index.vue';
|
||||||
import { ElMessageBox } from 'element-plus';
|
import { ElMessageBox } from 'element-plus';
|
||||||
import { containerPrune } from '@/api/modules/container';
|
|
||||||
import { MsgSuccess } from '@/utils/message';
|
|
||||||
|
|
||||||
let timer: NodeJS.Timer | null = null;
|
let timer: NodeJS.Timer | null = null;
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
@ -221,29 +215,6 @@ const openDelete = (row: Runtime.Runtime) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onOpenBuildCache = () => {
|
|
||||||
ElMessageBox.confirm(i18n.global.t('container.delBuildCacheHelper'), i18n.global.t('container.cleanBuildCache'), {
|
|
||||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
|
||||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
|
||||||
type: 'info',
|
|
||||||
}).then(async () => {
|
|
||||||
loading.value = true;
|
|
||||||
let params = {
|
|
||||||
pruneType: 'buildcache',
|
|
||||||
withTagAll: false,
|
|
||||||
};
|
|
||||||
await containerPrune(params)
|
|
||||||
.then((res) => {
|
|
||||||
loading.value = false;
|
|
||||||
MsgSuccess(i18n.global.t('container.cleanSuccess', [res.data.deletedNumber]));
|
|
||||||
search();
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
loading.value = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const openLog = (row: any) => {
|
const openLog = (row: any) => {
|
||||||
composeLogRef.value.acceptParams({ compose: row.path + '/docker-compose.yml', resource: row.name });
|
composeLogRef.value.acceptParams({ compose: row.path + '/docker-compose.yml', resource: row.name });
|
||||||
};
|
};
|
||||||
|
@ -13,10 +13,6 @@
|
|||||||
<el-button type="primary" @click="openCreate">
|
<el-button type="primary" @click="openCreate">
|
||||||
{{ $t('runtime.create') }}
|
{{ $t('runtime.create') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<el-button type="primary" plain @click="onOpenBuildCache()">
|
|
||||||
{{ $t('container.cleanBuildCache') }}
|
|
||||||
</el-button>
|
|
||||||
</template>
|
</template>
|
||||||
<template #main>
|
<template #main>
|
||||||
<ComplexTable :pagination-config="paginationConfig" :data="items" @search="search()" :heightDiff="350">
|
<ComplexTable :pagination-config="paginationConfig" :data="items" @search="search()" :heightDiff="350">
|
||||||
@ -117,8 +113,6 @@ import { Promotion } from '@element-plus/icons-vue';
|
|||||||
import PortJumpDialog from '@/components/port-jump/index.vue';
|
import PortJumpDialog from '@/components/port-jump/index.vue';
|
||||||
import AppResources from '@/views/website/runtime/php/check/index.vue';
|
import AppResources from '@/views/website/runtime/php/check/index.vue';
|
||||||
import { ElMessageBox } from 'element-plus';
|
import { ElMessageBox } from 'element-plus';
|
||||||
import { containerPrune } from '@/api/modules/container';
|
|
||||||
import { MsgSuccess } from '@/utils/message';
|
|
||||||
|
|
||||||
let timer: NodeJS.Timer | null = null;
|
let timer: NodeJS.Timer | null = null;
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
@ -237,29 +231,6 @@ const openDelete = async (row: Runtime.Runtime) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onOpenBuildCache = () => {
|
|
||||||
ElMessageBox.confirm(i18n.global.t('container.delBuildCacheHelper'), i18n.global.t('container.cleanBuildCache'), {
|
|
||||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
|
||||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
|
||||||
type: 'info',
|
|
||||||
}).then(async () => {
|
|
||||||
loading.value = true;
|
|
||||||
let params = {
|
|
||||||
pruneType: 'buildcache',
|
|
||||||
withTagAll: false,
|
|
||||||
};
|
|
||||||
await containerPrune(params)
|
|
||||||
.then((res) => {
|
|
||||||
loading.value = false;
|
|
||||||
MsgSuccess(i18n.global.t('container.cleanSuccess', [res.data.deletedNumber]));
|
|
||||||
search();
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
loading.value = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const openLog = (row: any) => {
|
const openLog = (row: any) => {
|
||||||
composeLogRef.value.acceptParams({ compose: row.path + '/docker-compose.yml', resource: row.name });
|
composeLogRef.value.acceptParams({ compose: row.path + '/docker-compose.yml', resource: row.name });
|
||||||
};
|
};
|
||||||
|
@ -346,6 +346,7 @@
|
|||||||
{{ $t('runtime.openrestyWarn') }}
|
{{ $t('runtime.openrestyWarn') }}
|
||||||
</span>
|
</span>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
<TaskLog ref="taskLog" />
|
||||||
</DrawerPro>
|
</DrawerPro>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -365,6 +366,8 @@ import { Group } from '@/api/interface/group';
|
|||||||
import { SearchRuntimes } from '@/api/modules/runtime';
|
import { SearchRuntimes } from '@/api/modules/runtime';
|
||||||
import { Runtime } from '@/api/interface/runtime';
|
import { Runtime } from '@/api/interface/runtime';
|
||||||
import { getRandomStr } from '@/utils/util';
|
import { getRandomStr } from '@/utils/util';
|
||||||
|
import TaskLog from '@/components/task-log/index.vue';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
const websiteForm = ref<FormInstance>();
|
const websiteForm = ref<FormInstance>();
|
||||||
const website = ref({
|
const website = ref({
|
||||||
@ -402,6 +405,7 @@ const website = ref({
|
|||||||
proxyProtocol: 'http://',
|
proxyProtocol: 'http://',
|
||||||
proxyAddress: '',
|
proxyAddress: '',
|
||||||
runtimeType: 'php',
|
runtimeType: 'php',
|
||||||
|
taskID: '',
|
||||||
});
|
});
|
||||||
const rules = ref<any>({
|
const rules = ref<any>({
|
||||||
primaryDomain: [Rules.domainWithPort],
|
primaryDomain: [Rules.domainWithPort],
|
||||||
@ -453,6 +457,7 @@ const runtimeReq = ref<Runtime.RuntimeReq>({
|
|||||||
const runtimes = ref<Runtime.RuntimeDTO[]>([]);
|
const runtimes = ref<Runtime.RuntimeDTO[]>([]);
|
||||||
const versionExist = ref(true);
|
const versionExist = ref(true);
|
||||||
const em = defineEmits(['close']);
|
const em = defineEmits(['close']);
|
||||||
|
const taskLog = ref();
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
open.value = false;
|
open.value = false;
|
||||||
@ -612,6 +617,10 @@ function isSubsetOfStrArray(primaryDomain: string, otherDomains: string): boolea
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const openTaskLog = (taskID: string) => {
|
||||||
|
taskLog.value.acceptParams(taskID);
|
||||||
|
};
|
||||||
|
|
||||||
const submit = async (formEl: FormInstance | undefined) => {
|
const submit = async (formEl: FormInstance | undefined) => {
|
||||||
if (!formEl) return;
|
if (!formEl) return;
|
||||||
await formEl.validate((valid) => {
|
await formEl.validate((valid) => {
|
||||||
@ -638,6 +647,8 @@ const submit = async (formEl: FormInstance | undefined) => {
|
|||||||
website.value.ftpUser = '';
|
website.value.ftpUser = '';
|
||||||
website.value.ftpPassword = '';
|
website.value.ftpPassword = '';
|
||||||
}
|
}
|
||||||
|
const taskID = uuidv4();
|
||||||
|
website.value.taskID = taskID;
|
||||||
CreateWebsite(website.value)
|
CreateWebsite(website.value)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
|
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
|
||||||
@ -646,6 +657,7 @@ const submit = async (formEl: FormInstance | undefined) => {
|
|||||||
.finally(() => {
|
.finally(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
});
|
});
|
||||||
|
openTaskLog(taskID);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
Loading…
Reference in New Issue
Block a user