mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-03-14 01:34:47 +08:00
feat: 运行环境增加 Node.js 管理 (#2390)
Refs https://github.com/1Panel-dev/1Panel/issues/397
This commit is contained in:
parent
38dadf6056
commit
1130a70052
@ -70,7 +70,7 @@ func (b *BaseApi) DeleteRuntime(c *gin.Context) {
|
|||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := runtimeService.Delete(req.ID)
|
err := runtimeService.Delete(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
@ -121,3 +121,25 @@ func (b *BaseApi) GetRuntime(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
helper.SuccessWithData(c, res)
|
helper.SuccessWithData(c, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Runtime
|
||||||
|
// @Summary Get Node package scripts
|
||||||
|
// @Description 获取 Node 项目的 scripts
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.NodePackageReq true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /runtimes/node/package [post]
|
||||||
|
func (b *BaseApi) GetNodePackageRunScript(c *gin.Context) {
|
||||||
|
var req request.NodePackageReq
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := runtimeService.GetNodePackageRunScript(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, res)
|
||||||
|
}
|
||||||
|
@ -18,10 +18,18 @@ type RuntimeCreate struct {
|
|||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Source string `json:"source"`
|
Source string `json:"source"`
|
||||||
|
CodeDir string `json:"codeDir"`
|
||||||
|
NodeConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodeConfig struct {
|
||||||
|
Install bool `json:"install"`
|
||||||
|
Clean bool `json:"clean"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuntimeDelete struct {
|
type RuntimeDelete struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
|
ForceDelete bool `json:"forceDelete"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuntimeUpdate struct {
|
type RuntimeUpdate struct {
|
||||||
@ -32,4 +40,10 @@ type RuntimeUpdate struct {
|
|||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Rebuild bool `json:"rebuild"`
|
Rebuild bool `json:"rebuild"`
|
||||||
Source string `json:"source"`
|
Source string `json:"source"`
|
||||||
|
CodeDir string `json:"codeDir"`
|
||||||
|
NodeConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodePackageReq struct {
|
||||||
|
CodeDir string `json:"codeDir"`
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,45 @@
|
|||||||
package response
|
package response
|
||||||
|
|
||||||
import "github.com/1Panel-dev/1Panel/backend/app/model"
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type RuntimeRes struct {
|
type RuntimeDTO struct {
|
||||||
model.Runtime
|
ID uint `json:"id"`
|
||||||
AppParams []AppParam `json:"appParams"`
|
Name string `json:"name"`
|
||||||
AppID uint `json:"appId"`
|
Resource string `json:"resource"`
|
||||||
Source string `json:"source"`
|
AppDetailID uint `json:"appDetailID"`
|
||||||
|
AppID uint `json:"appID"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Image string `json:"image"`
|
||||||
|
Params map[string]interface{} `json:"params"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
CodeDir string `json:"codeDir"`
|
||||||
|
AppParams []AppParam `json:"appParams"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PackageScripts struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Script string `json:"script"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRuntimeDTO(runtime model.Runtime) RuntimeDTO {
|
||||||
|
return RuntimeDTO{
|
||||||
|
ID: runtime.ID,
|
||||||
|
Name: runtime.Name,
|
||||||
|
Resource: runtime.Resource,
|
||||||
|
AppDetailID: runtime.AppDetailID,
|
||||||
|
Status: runtime.Status,
|
||||||
|
Type: runtime.Type,
|
||||||
|
Image: runtime.Image,
|
||||||
|
Message: runtime.Message,
|
||||||
|
CreatedAt: runtime.CreatedAt,
|
||||||
|
CodeDir: runtime.CodeDir,
|
||||||
|
Version: runtime.Version,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
type Runtime struct {
|
type Runtime struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
Name string `gorm:"type:varchar;not null" json:"name"`
|
Name string `gorm:"type:varchar;not null" json:"name"`
|
||||||
@ -14,4 +19,21 @@ type Runtime struct {
|
|||||||
Status string `gorm:"type:varchar;not null" json:"status"`
|
Status string `gorm:"type:varchar;not null" json:"status"`
|
||||||
Resource string `gorm:"type:varchar;not null" json:"resource"`
|
Resource string `gorm:"type:varchar;not null" json:"resource"`
|
||||||
Message string `gorm:"type:longtext;" json:"message"`
|
Message string `gorm:"type:longtext;" json:"message"`
|
||||||
|
CodeDir string `gorm:"type:varchar;" json:"codeDir"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Runtime) GetComposePath() string {
|
||||||
|
return path.Join(r.GetPath(), "docker-compose.yml")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Runtime) GetEnvPath() string {
|
||||||
|
return path.Join(r.GetPath(), ".env")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Runtime) GetPath() string {
|
||||||
|
return path.Join(constant.RuntimeDir, r.Type, r.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Runtime) GetLogPath() string {
|
||||||
|
return path.Join(r.GetPath(), "build.log")
|
||||||
}
|
}
|
||||||
|
@ -176,36 +176,39 @@ func (a AppService) GetAppDetail(appId uint, version, appType string) (response.
|
|||||||
return appDetailDTO, err
|
return appDetailDTO, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buildPath := path.Join(versionPath, "build")
|
switch app.Type {
|
||||||
paramsPath := path.Join(buildPath, "config.json")
|
case constant.RuntimePHP:
|
||||||
if !fileOp.Stat(paramsPath) {
|
buildPath := path.Join(versionPath, "build")
|
||||||
return appDetailDTO, buserr.New(constant.ErrFileNotExist)
|
paramsPath := path.Join(buildPath, "config.json")
|
||||||
}
|
if !fileOp.Stat(paramsPath) {
|
||||||
param, err := fileOp.GetContent(paramsPath)
|
return appDetailDTO, buserr.New(constant.ErrFileNotExist)
|
||||||
if err != nil {
|
}
|
||||||
return appDetailDTO, err
|
param, err := fileOp.GetContent(paramsPath)
|
||||||
}
|
if err != nil {
|
||||||
paramMap := make(map[string]interface{})
|
return appDetailDTO, err
|
||||||
if err := json.Unmarshal(param, ¶mMap); err != nil {
|
}
|
||||||
return appDetailDTO, err
|
paramMap := make(map[string]interface{})
|
||||||
}
|
if err := json.Unmarshal(param, ¶mMap); err != nil {
|
||||||
appDetailDTO.Params = paramMap
|
return appDetailDTO, err
|
||||||
composePath := path.Join(buildPath, "docker-compose.yml")
|
}
|
||||||
if !fileOp.Stat(composePath) {
|
appDetailDTO.Params = paramMap
|
||||||
return appDetailDTO, buserr.New(constant.ErrFileNotExist)
|
composePath := path.Join(buildPath, "docker-compose.yml")
|
||||||
}
|
if !fileOp.Stat(composePath) {
|
||||||
compose, err := fileOp.GetContent(composePath)
|
return appDetailDTO, buserr.New(constant.ErrFileNotExist)
|
||||||
if err != nil {
|
}
|
||||||
return appDetailDTO, err
|
compose, err := fileOp.GetContent(composePath)
|
||||||
}
|
if err != nil {
|
||||||
composeMap := make(map[string]interface{})
|
return appDetailDTO, err
|
||||||
if err := yaml.Unmarshal(compose, &composeMap); err != nil {
|
}
|
||||||
return appDetailDTO, err
|
composeMap := make(map[string]interface{})
|
||||||
}
|
if err := yaml.Unmarshal(compose, &composeMap); err != nil {
|
||||||
if service, ok := composeMap["services"]; ok {
|
return appDetailDTO, err
|
||||||
servicesMap := service.(map[string]interface{})
|
}
|
||||||
for k := range servicesMap {
|
if service, ok := composeMap["services"]; ok {
|
||||||
appDetailDTO.Image = k
|
servicesMap := service.(map[string]interface{})
|
||||||
|
for k := range servicesMap {
|
||||||
|
appDetailDTO.Image = k
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -3,7 +3,6 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||||
@ -12,24 +11,26 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/subosito/gotenv"
|
"github.com/subosito/gotenv"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type RuntimeService struct {
|
type RuntimeService struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type IRuntimeService interface {
|
type IRuntimeService interface {
|
||||||
Page(req request.RuntimeSearch) (int64, []response.RuntimeRes, error)
|
Page(req request.RuntimeSearch) (int64, []response.RuntimeDTO, error)
|
||||||
Create(create request.RuntimeCreate) error
|
Create(create request.RuntimeCreate) error
|
||||||
Delete(id uint) error
|
Delete(delete request.RuntimeDelete) error
|
||||||
Update(req request.RuntimeUpdate) error
|
Update(req request.RuntimeUpdate) error
|
||||||
Get(id uint) (res *response.RuntimeRes, err error)
|
Get(id uint) (res *response.RuntimeDTO, err error)
|
||||||
|
GetNodePackageRunScript(req request.NodePackageReq) ([]response.PackageScripts, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRuntimeService() IRuntimeService {
|
func NewRuntimeService() IRuntimeService {
|
||||||
@ -37,24 +38,35 @@ func NewRuntimeService() IRuntimeService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
|
func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
|
||||||
exist, _ := runtimeRepo.GetFirst(runtimeRepo.WithName(create.Name))
|
exist, _ := runtimeRepo.GetFirst(runtimeRepo.WithName(create.Name), commonRepo.WithByType(create.Type))
|
||||||
if exist != nil {
|
if exist != nil {
|
||||||
return buserr.New(constant.ErrNameIsExist)
|
return buserr.New(constant.ErrNameIsExist)
|
||||||
}
|
}
|
||||||
if create.Resource == constant.ResourceLocal {
|
fileOp := files.NewFileOp()
|
||||||
runtime := &model.Runtime{
|
|
||||||
Name: create.Name,
|
switch create.Type {
|
||||||
Resource: create.Resource,
|
case constant.RuntimePHP:
|
||||||
Type: create.Type,
|
if create.Resource == constant.ResourceLocal {
|
||||||
Version: create.Version,
|
runtime := &model.Runtime{
|
||||||
Status: constant.RuntimeNormal,
|
Name: create.Name,
|
||||||
|
Resource: create.Resource,
|
||||||
|
Type: create.Type,
|
||||||
|
Version: create.Version,
|
||||||
|
Status: constant.RuntimeNormal,
|
||||||
|
}
|
||||||
|
return runtimeRepo.Create(context.Background(), runtime)
|
||||||
}
|
}
|
||||||
return runtimeRepo.Create(context.Background(), runtime)
|
exist, _ = runtimeRepo.GetFirst(runtimeRepo.WithImage(create.Image))
|
||||||
}
|
if exist != nil {
|
||||||
exist, _ = runtimeRepo.GetFirst(runtimeRepo.WithImage(create.Image))
|
return buserr.New(constant.ErrImageExist)
|
||||||
if exist != nil {
|
}
|
||||||
return buserr.New(constant.ErrImageExist)
|
case constant.RuntimeNode:
|
||||||
|
if !fileOp.Stat(create.CodeDir) {
|
||||||
|
return buserr.New(constant.ErrPathNotFound)
|
||||||
|
}
|
||||||
|
create.Install = true
|
||||||
}
|
}
|
||||||
|
|
||||||
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(create.AppDetailID))
|
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(create.AppDetailID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -63,64 +75,39 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fileOp := files.NewFileOp()
|
|
||||||
appVersionDir := path.Join(constant.AppResourceDir, app.Resource, app.Key, appDetail.Version)
|
appVersionDir := path.Join(constant.AppResourceDir, app.Resource, app.Key, appDetail.Version)
|
||||||
if !fileOp.Stat(appVersionDir) || appDetail.Update {
|
if !fileOp.Stat(appVersionDir) || appDetail.Update {
|
||||||
if err := downloadApp(app, appDetail, nil); err != nil {
|
if err := downloadApp(app, appDetail, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buildDir := path.Join(appVersionDir, "build")
|
|
||||||
if !fileOp.Stat(buildDir) {
|
|
||||||
return buserr.New(constant.ErrDirNotFound)
|
|
||||||
}
|
|
||||||
runtimeDir := path.Join(constant.RuntimeDir, create.Type)
|
|
||||||
tempDir := filepath.Join(runtimeDir, fmt.Sprintf("%d", time.Now().UnixNano()))
|
|
||||||
if err = fileOp.CopyDir(buildDir, tempDir); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
oldDir := path.Join(tempDir, "build")
|
|
||||||
newNameDir := path.Join(runtimeDir, create.Name)
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
_ = fileOp.DeleteDir(newNameDir)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
if oldDir != newNameDir {
|
|
||||||
if err = fileOp.Rename(oldDir, newNameDir); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = fileOp.DeleteDir(tempDir); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
composeContent, envContent, forms, err := handleParams(create.Image, create.Type, newNameDir, create.Source, create.Params)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
runtime := &model.Runtime{
|
runtime := &model.Runtime{
|
||||||
Name: create.Name,
|
Name: create.Name,
|
||||||
DockerCompose: string(composeContent),
|
AppDetailID: create.AppDetailID,
|
||||||
Env: string(envContent),
|
Type: create.Type,
|
||||||
AppDetailID: create.AppDetailID,
|
Image: create.Image,
|
||||||
Type: create.Type,
|
Resource: create.Resource,
|
||||||
Image: create.Image,
|
Version: create.Version,
|
||||||
Resource: create.Resource,
|
|
||||||
Status: constant.RuntimeBuildIng,
|
|
||||||
Version: create.Version,
|
|
||||||
Params: string(forms),
|
|
||||||
}
|
}
|
||||||
if err = runtimeRepo.Create(context.Background(), runtime); err != nil {
|
|
||||||
return
|
switch create.Type {
|
||||||
|
case constant.RuntimePHP:
|
||||||
|
if err = handlePHP(create, runtime, fileOp, appVersionDir); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case constant.RuntimeNode:
|
||||||
|
if err = handleNode(create, runtime, fileOp, appVersionDir); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
go buildRuntime(runtime, "", false)
|
return runtimeRepo.Create(context.Background(), runtime)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.RuntimeRes, error) {
|
func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.RuntimeDTO, error) {
|
||||||
var (
|
var (
|
||||||
opts []repo.DBOption
|
opts []repo.DBOption
|
||||||
res []response.RuntimeRes
|
res []response.RuntimeDTO
|
||||||
)
|
)
|
||||||
if req.Name != "" {
|
if req.Name != "" {
|
||||||
opts = append(opts, commonRepo.WithLikeName(req.Name))
|
opts = append(opts, commonRepo.WithLikeName(req.Name))
|
||||||
@ -128,116 +115,158 @@ func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.Runt
|
|||||||
if req.Status != "" {
|
if req.Status != "" {
|
||||||
opts = append(opts, runtimeRepo.WithStatus(req.Status))
|
opts = append(opts, runtimeRepo.WithStatus(req.Status))
|
||||||
}
|
}
|
||||||
|
if req.Type != "" {
|
||||||
|
opts = append(opts, commonRepo.WithByType(req.Type))
|
||||||
|
}
|
||||||
total, runtimes, err := runtimeRepo.Page(req.Page, req.PageSize, opts...)
|
total, runtimes, err := runtimeRepo.Page(req.Page, req.PageSize, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
for _, runtime := range runtimes {
|
for _, runtime := range runtimes {
|
||||||
res = append(res, response.RuntimeRes{
|
runtimeDTO := response.NewRuntimeDTO(runtime)
|
||||||
Runtime: runtime,
|
runtimeDTO.Params = make(map[string]interface{})
|
||||||
})
|
envs, err := gotenv.Unmarshal(runtime.Env)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
for k, v := range envs {
|
||||||
|
runtimeDTO.Params[k] = v
|
||||||
|
}
|
||||||
|
res = append(res, runtimeDTO)
|
||||||
}
|
}
|
||||||
return total, res, nil
|
return total, res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuntimeService) Delete(id uint) error {
|
func (r *RuntimeService) Delete(runtimeDelete request.RuntimeDelete) error {
|
||||||
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(id))
|
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(runtimeDelete.ID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
website, _ := websiteRepo.GetFirst(websiteRepo.WithRuntimeID(id))
|
website, _ := websiteRepo.GetFirst(websiteRepo.WithRuntimeID(runtimeDelete.ID))
|
||||||
if website.ID > 0 {
|
if website.ID > 0 {
|
||||||
return buserr.New(constant.ErrDelWithWebsite)
|
return buserr.New(constant.ErrDelWithWebsite)
|
||||||
}
|
}
|
||||||
if runtime.Resource == constant.ResourceAppstore {
|
if runtime.Resource == constant.ResourceAppstore {
|
||||||
client, err := docker.NewClient()
|
projectDir := runtime.GetPath()
|
||||||
if err != nil {
|
switch runtime.Type {
|
||||||
return err
|
case constant.RuntimePHP:
|
||||||
}
|
client, err := docker.NewClient()
|
||||||
imageID, err := client.GetImageIDByName(runtime.Image)
|
if err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
imageID, err := client.GetImageIDByName(runtime.Image)
|
||||||
if imageID != "" {
|
if err != nil {
|
||||||
if err := client.DeleteImage(imageID); err != nil {
|
return err
|
||||||
global.LOG.Errorf("delete image id [%s] error %v", imageID, err)
|
}
|
||||||
|
if imageID != "" {
|
||||||
|
if err := client.DeleteImage(imageID); err != nil {
|
||||||
|
global.LOG.Errorf("delete image id [%s] error %v", imageID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case constant.RuntimeNode:
|
||||||
|
if out, err := compose.Down(runtime.GetComposePath()); err != nil && !runtimeDelete.ForceDelete {
|
||||||
|
if out != "" {
|
||||||
|
return errors.New(out)
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runtimeDir := path.Join(constant.RuntimeDir, runtime.Type, runtime.Name)
|
if err := files.NewFileOp().DeleteDir(projectDir); err != nil && !runtimeDelete.ForceDelete {
|
||||||
if err := files.NewFileOp().DeleteDir(runtimeDir); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return runtimeRepo.DeleteBy(commonRepo.WithByID(id))
|
return runtimeRepo.DeleteBy(commonRepo.WithByID(runtimeDelete.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuntimeService) Get(id uint) (*response.RuntimeRes, error) {
|
func (r *RuntimeService) Get(id uint) (*response.RuntimeDTO, error) {
|
||||||
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(id))
|
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
res := &response.RuntimeRes{}
|
|
||||||
res.Runtime = *runtime
|
res := response.NewRuntimeDTO(*runtime)
|
||||||
if runtime.Resource == constant.ResourceLocal {
|
if runtime.Resource == constant.ResourceLocal {
|
||||||
return res, nil
|
return &res, nil
|
||||||
}
|
}
|
||||||
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(runtime.AppDetailID))
|
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(runtime.AppDetailID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
res.AppID = appDetail.AppId
|
res.AppID = appDetail.AppId
|
||||||
var (
|
switch runtime.Type {
|
||||||
appForm dto.AppForm
|
case constant.RuntimePHP:
|
||||||
appParams []response.AppParam
|
var (
|
||||||
)
|
appForm dto.AppForm
|
||||||
if err := json.Unmarshal([]byte(runtime.Params), &appForm); err != nil {
|
appParams []response.AppParam
|
||||||
return nil, err
|
)
|
||||||
}
|
if err := json.Unmarshal([]byte(runtime.Params), &appForm); err != nil {
|
||||||
envs, err := gotenv.Unmarshal(runtime.Env)
|
return nil, err
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
envs, err := gotenv.Unmarshal(runtime.Env)
|
||||||
}
|
if err != nil {
|
||||||
if v, ok := envs["CONTAINER_PACKAGE_URL"]; ok {
|
return nil, err
|
||||||
res.Source = v
|
}
|
||||||
}
|
if v, ok := envs["CONTAINER_PACKAGE_URL"]; ok {
|
||||||
for _, form := range appForm.FormFields {
|
res.Source = v
|
||||||
if v, ok := envs[form.EnvKey]; ok {
|
}
|
||||||
appParam := response.AppParam{
|
for _, form := range appForm.FormFields {
|
||||||
Edit: false,
|
if v, ok := envs[form.EnvKey]; ok {
|
||||||
Key: form.EnvKey,
|
appParam := response.AppParam{
|
||||||
Rule: form.Rule,
|
Edit: false,
|
||||||
Type: form.Type,
|
Key: form.EnvKey,
|
||||||
Required: form.Required,
|
Rule: form.Rule,
|
||||||
}
|
Type: form.Type,
|
||||||
if form.Edit {
|
Required: form.Required,
|
||||||
appParam.Edit = true
|
}
|
||||||
}
|
if form.Edit {
|
||||||
appParam.LabelZh = form.LabelZh
|
appParam.Edit = true
|
||||||
appParam.LabelEn = form.LabelEn
|
}
|
||||||
appParam.Multiple = form.Multiple
|
appParam.LabelZh = form.LabelZh
|
||||||
appParam.Value = v
|
appParam.LabelEn = form.LabelEn
|
||||||
if form.Type == "select" {
|
appParam.Multiple = form.Multiple
|
||||||
if form.Multiple {
|
appParam.Value = v
|
||||||
if v == "" {
|
if form.Type == "select" {
|
||||||
appParam.Value = []string{}
|
if form.Multiple {
|
||||||
|
if v == "" {
|
||||||
|
appParam.Value = []string{}
|
||||||
|
} else {
|
||||||
|
appParam.Value = strings.Split(v, ",")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
appParam.Value = strings.Split(v, ",")
|
for _, fv := range form.Values {
|
||||||
}
|
if fv.Value == v {
|
||||||
} else {
|
appParam.ShowValue = fv.Label
|
||||||
for _, fv := range form.Values {
|
break
|
||||||
if fv.Value == v {
|
}
|
||||||
appParam.ShowValue = fv.Label
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
appParam.Values = form.Values
|
||||||
}
|
}
|
||||||
appParam.Values = form.Values
|
appParams = append(appParams, appParam)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.AppParams = appParams
|
||||||
|
case constant.RuntimeNode:
|
||||||
|
res.Params = make(map[string]interface{})
|
||||||
|
envs, err := gotenv.Unmarshal(runtime.Env)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for k, v := range envs {
|
||||||
|
switch k {
|
||||||
|
case "NODE_APP_PORT", "PANEL_APP_PORT_HTTP":
|
||||||
|
port, err := strconv.Atoi(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res.Params[k] = port
|
||||||
|
default:
|
||||||
|
res.Params[k] = v
|
||||||
}
|
}
|
||||||
appParams = append(appParams, appParam)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.AppParams = appParams
|
|
||||||
return res, nil
|
return &res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
||||||
@ -245,36 +274,86 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
oldImage := runtime.Image
|
|
||||||
if runtime.Resource == constant.ResourceLocal {
|
if runtime.Resource == constant.ResourceLocal {
|
||||||
runtime.Version = req.Version
|
runtime.Version = req.Version
|
||||||
return runtimeRepo.Save(runtime)
|
return runtimeRepo.Save(runtime)
|
||||||
}
|
}
|
||||||
exist, _ := runtimeRepo.GetFirst(runtimeRepo.WithImage(req.Name), runtimeRepo.WithNotId(req.ID))
|
oldImage := runtime.Image
|
||||||
if exist != nil {
|
switch runtime.Type {
|
||||||
return buserr.New(constant.ErrImageExist)
|
case constant.RuntimePHP:
|
||||||
|
exist, _ := runtimeRepo.GetFirst(runtimeRepo.WithImage(req.Name), runtimeRepo.WithNotId(req.ID))
|
||||||
|
if exist != nil {
|
||||||
|
return buserr.New(constant.ErrImageExist)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
runtimeDir := path.Join(constant.RuntimeDir, runtime.Type, runtime.Name)
|
|
||||||
composeContent, envContent, _, err := handleParams(req.Image, runtime.Type, runtimeDir, req.Source, req.Params)
|
projectDir := path.Join(constant.RuntimeDir, runtime.Type, runtime.Name)
|
||||||
|
create := request.RuntimeCreate{
|
||||||
|
Image: req.Image,
|
||||||
|
Type: runtime.Type,
|
||||||
|
Source: req.Source,
|
||||||
|
Params: req.Params,
|
||||||
|
CodeDir: req.CodeDir,
|
||||||
|
Version: req.Version,
|
||||||
|
}
|
||||||
|
composeContent, envContent, _, err := handleParams(create, projectDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
runtime.Image = req.Image
|
|
||||||
runtime.Env = string(envContent)
|
runtime.Env = string(envContent)
|
||||||
runtime.DockerCompose = string(composeContent)
|
runtime.DockerCompose = string(composeContent)
|
||||||
runtime.Status = constant.RuntimeBuildIng
|
|
||||||
_ = runtimeRepo.Save(runtime)
|
switch runtime.Type {
|
||||||
client, err := docker.NewClient()
|
case constant.RuntimePHP:
|
||||||
if err != nil {
|
runtime.Image = req.Image
|
||||||
return err
|
runtime.Status = constant.RuntimeBuildIng
|
||||||
|
_ = runtimeRepo.Save(runtime)
|
||||||
|
client, err := docker.NewClient()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
imageID, err := client.GetImageIDByName(oldImage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go buildRuntime(runtime, imageID, req.Rebuild)
|
||||||
|
case constant.RuntimeNode:
|
||||||
|
runtime.Version = req.Version
|
||||||
|
runtime.CodeDir = req.CodeDir
|
||||||
|
runtime.Status = constant.RuntimeReCreating
|
||||||
|
_ = runtimeRepo.Save(runtime)
|
||||||
|
go reCreateRuntime(runtime)
|
||||||
}
|
}
|
||||||
imageID, err := client.GetImageIDByName(oldImage)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
go buildRuntime(runtime, imageID, req.Rebuild)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RuntimeService) GetNodePackageRunScript(req request.NodePackageReq) ([]response.PackageScripts, error) {
|
||||||
|
fileOp := files.NewFileOp()
|
||||||
|
if !fileOp.Stat(req.CodeDir) {
|
||||||
|
return nil, buserr.New(constant.ErrPathNotFound)
|
||||||
|
}
|
||||||
|
if !fileOp.Stat(path.Join(req.CodeDir, "package.json")) {
|
||||||
|
return nil, buserr.New(constant.ErrPackageJsonNotFound)
|
||||||
|
}
|
||||||
|
content, err := fileOp.GetContent(path.Join(req.CodeDir, "package.json"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var packageMap map[string]interface{}
|
||||||
|
err = json.Unmarshal(content, &packageMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
scripts, ok := packageMap["scripts"]
|
||||||
|
if !ok {
|
||||||
|
return nil, buserr.New(constant.ErrScriptsNotFound)
|
||||||
|
}
|
||||||
|
var packageScripts []response.PackageScripts
|
||||||
|
for k, v := range scripts.(map[string]interface{}) {
|
||||||
|
packageScripts = append(packageScripts, response.PackageScripts{
|
||||||
|
Name: k,
|
||||||
|
Script: v.(string),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return packageScripts, nil
|
||||||
|
}
|
||||||
|
@ -10,22 +10,159 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/subosito/gotenv"
|
"github.com/subosito/gotenv"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func handleNode(create request.RuntimeCreate, runtime *model.Runtime, fileOp files.FileOp, appVersionDir string) (err error) {
|
||||||
|
runtimeDir := path.Join(constant.RuntimeDir, create.Type)
|
||||||
|
if err = fileOp.CopyDir(appVersionDir, runtimeDir); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
versionDir := path.Join(runtimeDir, filepath.Base(appVersionDir))
|
||||||
|
projectDir := path.Join(runtimeDir, create.Name)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = fileOp.DeleteDir(projectDir)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err = fileOp.Rename(versionDir, projectDir); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
composeContent, envContent, _, err := handleParams(create, projectDir)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
runtime.DockerCompose = string(composeContent)
|
||||||
|
runtime.Env = string(envContent)
|
||||||
|
runtime.Status = constant.RuntimeStarting
|
||||||
|
runtime.CodeDir = create.CodeDir
|
||||||
|
|
||||||
|
go startRuntime(runtime)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func handlePHP(create request.RuntimeCreate, runtime *model.Runtime, fileOp files.FileOp, appVersionDir string) (err error) {
|
||||||
|
buildDir := path.Join(appVersionDir, "build")
|
||||||
|
if !fileOp.Stat(buildDir) {
|
||||||
|
return buserr.New(constant.ErrDirNotFound)
|
||||||
|
}
|
||||||
|
runtimeDir := path.Join(constant.RuntimeDir, create.Type)
|
||||||
|
tempDir := filepath.Join(runtimeDir, fmt.Sprintf("%d", time.Now().UnixNano()))
|
||||||
|
if err = fileOp.CopyDir(buildDir, tempDir); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
oldDir := path.Join(tempDir, "build")
|
||||||
|
projectDir := path.Join(runtimeDir, create.Name)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = fileOp.DeleteDir(projectDir)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if oldDir != projectDir {
|
||||||
|
if err = fileOp.Rename(oldDir, projectDir); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = fileOp.DeleteDir(tempDir); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
composeContent, envContent, forms, err := handleParams(create, projectDir)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
runtime.DockerCompose = string(composeContent)
|
||||||
|
runtime.Env = string(envContent)
|
||||||
|
runtime.Params = string(forms)
|
||||||
|
runtime.Status = constant.RuntimeBuildIng
|
||||||
|
|
||||||
|
go buildRuntime(runtime, "", false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func startRuntime(runtime *model.Runtime) {
|
||||||
|
cmd := exec.Command("docker-compose", "-f", runtime.GetComposePath(), "up", "-d")
|
||||||
|
logPath := runtime.GetLogPath()
|
||||||
|
logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("Failed to open log file: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
multiWriterStdout := io.MultiWriter(os.Stdout, logFile)
|
||||||
|
cmd.Stdout = multiWriterStdout
|
||||||
|
var stderrBuf bytes.Buffer
|
||||||
|
multiWriterStderr := io.MultiWriter(&stderrBuf, logFile, os.Stderr)
|
||||||
|
cmd.Stderr = multiWriterStderr
|
||||||
|
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
runtime.Status = constant.RuntimeError
|
||||||
|
runtime.Message = buserr.New(constant.ErrRuntimeStart).Error() + ":" + stderrBuf.String()
|
||||||
|
} else {
|
||||||
|
runtime.Status = constant.RuntimeRunning
|
||||||
|
runtime.Message = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = runtimeRepo.Save(runtime)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runComposeCmdWithLog(operate string, composePath string, logPath string) error {
|
||||||
|
cmd := exec.Command("docker-compose", "-f", composePath, operate)
|
||||||
|
if operate == "up" {
|
||||||
|
cmd = exec.Command("docker-compose", "-f", composePath, operate, "-d")
|
||||||
|
}
|
||||||
|
logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("Failed to open log file: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
multiWriterStdout := io.MultiWriter(os.Stdout, logFile)
|
||||||
|
cmd.Stdout = multiWriterStdout
|
||||||
|
var stderrBuf bytes.Buffer
|
||||||
|
multiWriterStderr := io.MultiWriter(&stderrBuf, logFile, os.Stderr)
|
||||||
|
cmd.Stderr = multiWriterStderr
|
||||||
|
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(buserr.New(constant.ErrRuntimeStart).Error() + ":" + stderrBuf.String())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func reCreateRuntime(runtime *model.Runtime) {
|
||||||
|
var err error
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
runtime.Status = constant.RuntimeError
|
||||||
|
runtime.Message = err.Error()
|
||||||
|
_ = runtimeRepo.Save(runtime)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err = runComposeCmdWithLog("down", runtime.GetComposePath(), runtime.GetLogPath()); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = runComposeCmdWithLog("up", runtime.GetComposePath(), runtime.GetLogPath()); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
runtime.Status = constant.RuntimeRunning
|
||||||
|
_ = runtimeRepo.Save(runtime)
|
||||||
|
}
|
||||||
|
|
||||||
func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) {
|
func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) {
|
||||||
runtimePath := path.Join(constant.RuntimeDir, runtime.Type, runtime.Name)
|
runtimePath := runtime.GetPath()
|
||||||
composePath := path.Join(runtimePath, "docker-compose.yml")
|
composePath := runtime.GetComposePath()
|
||||||
logPath := path.Join(runtimePath, "build.log")
|
logPath := path.Join(runtimePath, "build.log")
|
||||||
|
|
||||||
logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
|
logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Failed to open log file:", err)
|
global.LOG.Errorf("failed to open log file: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -89,35 +226,45 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) {
|
|||||||
_ = runtimeRepo.Save(runtime)
|
_ = runtimeRepo.Save(runtime)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleParams(image, runtimeType, runtimeDir, source string, params map[string]interface{}) (composeContent []byte, envContent []byte, forms []byte, err error) {
|
func handleParams(create request.RuntimeCreate, projectDir string) (composeContent []byte, envContent []byte, forms []byte, err error) {
|
||||||
fileOp := files.NewFileOp()
|
fileOp := files.NewFileOp()
|
||||||
composeContent, err = fileOp.GetContent(path.Join(runtimeDir, "docker-compose.yml"))
|
composeContent, err = fileOp.GetContent(path.Join(projectDir, "docker-compose.yml"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
env, err := gotenv.Read(path.Join(runtimeDir, ".env"))
|
env, err := gotenv.Read(path.Join(projectDir, ".env"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
forms, err = fileOp.GetContent(path.Join(runtimeDir, "config.json"))
|
switch create.Type {
|
||||||
if err != nil {
|
case constant.RuntimePHP:
|
||||||
return
|
create.Params["IMAGE_NAME"] = create.Image
|
||||||
}
|
forms, err = fileOp.GetContent(path.Join(projectDir, "config.json"))
|
||||||
params["IMAGE_NAME"] = image
|
if err != nil {
|
||||||
if runtimeType == constant.RuntimePHP {
|
return
|
||||||
if extends, ok := params["PHP_EXTENSIONS"]; ok {
|
}
|
||||||
|
if extends, ok := create.Params["PHP_EXTENSIONS"]; ok {
|
||||||
if extendsArray, ok := extends.([]interface{}); ok {
|
if extendsArray, ok := extends.([]interface{}); ok {
|
||||||
strArray := make([]string, len(extendsArray))
|
strArray := make([]string, len(extendsArray))
|
||||||
for i, v := range extendsArray {
|
for i, v := range extendsArray {
|
||||||
strArray[i] = strings.ToLower(fmt.Sprintf("%v", v))
|
strArray[i] = strings.ToLower(fmt.Sprintf("%v", v))
|
||||||
}
|
}
|
||||||
params["PHP_EXTENSIONS"] = strings.Join(strArray, ",")
|
create.Params["PHP_EXTENSIONS"] = strings.Join(strArray, ",")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
params["CONTAINER_PACKAGE_URL"] = source
|
create.Params["CONTAINER_PACKAGE_URL"] = create.Source
|
||||||
|
case constant.RuntimeNode:
|
||||||
|
create.Params["CODE_DIR"] = create.CodeDir
|
||||||
|
create.Params["NODE_VERSION"] = create.Version
|
||||||
|
if create.NodeConfig.Install {
|
||||||
|
create.Params["RUN_INSTALL"] = "1"
|
||||||
|
} else {
|
||||||
|
create.Params["RUN_INSTALL"] = "0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newMap := make(map[string]string)
|
newMap := make(map[string]string)
|
||||||
handleMap(params, newMap)
|
handleMap(create.Params, newMap)
|
||||||
for k, v := range newMap {
|
for k, v := range newMap {
|
||||||
env[k] = v
|
env[k] = v
|
||||||
}
|
}
|
||||||
@ -125,7 +272,7 @@ func handleParams(image, runtimeType, runtimeDir, source string, params map[stri
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = gotenv.Write(env, path.Join(runtimeDir, ".env")); err != nil {
|
if err = gotenv.Write(env, path.Join(projectDir, ".env")); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
envContent = []byte(envStr)
|
envContent = []byte(envStr)
|
||||||
|
@ -114,11 +114,14 @@ var (
|
|||||||
|
|
||||||
// runtime
|
// runtime
|
||||||
var (
|
var (
|
||||||
ErrDirNotFound = "ErrDirNotFound"
|
ErrDirNotFound = "ErrDirNotFound"
|
||||||
ErrFileNotExist = "ErrFileNotExist"
|
ErrFileNotExist = "ErrFileNotExist"
|
||||||
ErrImageBuildErr = "ErrImageBuildErr"
|
ErrImageBuildErr = "ErrImageBuildErr"
|
||||||
ErrImageExist = "ErrImageExist"
|
ErrImageExist = "ErrImageExist"
|
||||||
ErrDelWithWebsite = "ErrDelWithWebsite"
|
ErrDelWithWebsite = "ErrDelWithWebsite"
|
||||||
|
ErrRuntimeStart = "ErrRuntimeStart"
|
||||||
|
ErrPackageJsonNotFound = "ErrPackageJsonNotFound"
|
||||||
|
ErrScriptsNotFound = "ErrScriptsNotFound"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -4,11 +4,15 @@ const (
|
|||||||
ResourceLocal = "local"
|
ResourceLocal = "local"
|
||||||
ResourceAppstore = "appstore"
|
ResourceAppstore = "appstore"
|
||||||
|
|
||||||
RuntimeNormal = "normal"
|
RuntimeNormal = "normal"
|
||||||
RuntimeError = "error"
|
RuntimeError = "error"
|
||||||
RuntimeBuildIng = "building"
|
RuntimeBuildIng = "building"
|
||||||
|
RuntimeStarting = "starting"
|
||||||
|
RuntimeRunning = "running"
|
||||||
|
RuntimeReCreating = "recreating"
|
||||||
|
|
||||||
RuntimePHP = "php"
|
RuntimePHP = "php"
|
||||||
|
RuntimeNode = "node"
|
||||||
|
|
||||||
RuntimeProxyUnix = "unix"
|
RuntimeProxyUnix = "unix"
|
||||||
RuntimeProxyTcp = "tcp"
|
RuntimeProxyTcp = "tcp"
|
||||||
|
@ -99,6 +99,9 @@ ErrFileNotExist: "{{ .detail }} file does not exist! Please check source file in
|
|||||||
ErrImageBuildErr: "Image build failed"
|
ErrImageBuildErr: "Image build failed"
|
||||||
ErrImageExist: "Image is already exist!"
|
ErrImageExist: "Image is already exist!"
|
||||||
ErrDelWithWebsite: "The operating environment has been associated with a website and cannot be deleted"
|
ErrDelWithWebsite: "The operating environment has been associated with a website and cannot be deleted"
|
||||||
|
ErrRuntimeStart: "Failed to start"
|
||||||
|
ErrPackageJsonNotFound: "package.json file does not exist"
|
||||||
|
ErrScriptsNotFound: "No scripts configuration item was found in package.json"
|
||||||
|
|
||||||
#setting
|
#setting
|
||||||
ErrBackupInUsed: "The backup account is already being used in a cronjob and cannot be deleted."
|
ErrBackupInUsed: "The backup account is already being used in a cronjob and cannot be deleted."
|
||||||
|
@ -99,6 +99,9 @@ ErrFileNotExist: "{{ .detail }} 文件不存在!請檢查源文件完整性!
|
|||||||
ErrImageBuildErr: "鏡像 build 失敗"
|
ErrImageBuildErr: "鏡像 build 失敗"
|
||||||
ErrImageExist: "鏡像已存在!"
|
ErrImageExist: "鏡像已存在!"
|
||||||
ErrDelWithWebsite: "運行環境已經關聯網站,無法刪除"
|
ErrDelWithWebsite: "運行環境已經關聯網站,無法刪除"
|
||||||
|
ErrRuntimeStart: "啟動失敗"
|
||||||
|
ErrPackageJsonNotFound: "package.json 文件不存在"
|
||||||
|
ErrScriptsNotFound: "沒有在 package.json 中找到 scripts 配置項"
|
||||||
|
|
||||||
#setting
|
#setting
|
||||||
ErrBackupInUsed: "該備份帳號已在計劃任務中使用,無法刪除"
|
ErrBackupInUsed: "該備份帳號已在計劃任務中使用,無法刪除"
|
||||||
|
@ -99,6 +99,9 @@ ErrFileNotExist: "{{ .detail }} 文件不存在!请检查源文件完整性!
|
|||||||
ErrImageBuildErr: "镜像 build 失败"
|
ErrImageBuildErr: "镜像 build 失败"
|
||||||
ErrImageExist: "镜像已存在!"
|
ErrImageExist: "镜像已存在!"
|
||||||
ErrDelWithWebsite: "运行环境已经关联网站,无法删除"
|
ErrDelWithWebsite: "运行环境已经关联网站,无法删除"
|
||||||
|
ErrRuntimeStart: "启动失败"
|
||||||
|
ErrPackageJsonNotFound: "package.json 文件不存在"
|
||||||
|
ErrScriptsNotFound: "没有在 package.json 中找到 scripts 配置项"
|
||||||
|
|
||||||
#setting
|
#setting
|
||||||
ErrBackupInUsed: "该备份账号已在计划任务中使用,无法删除"
|
ErrBackupInUsed: "该备份账号已在计划任务中使用,无法删除"
|
||||||
|
@ -45,6 +45,7 @@ func Init() {
|
|||||||
migrations.DropDatabaseLocal,
|
migrations.DropDatabaseLocal,
|
||||||
|
|
||||||
migrations.AddDefaultNetwork,
|
migrations.AddDefaultNetwork,
|
||||||
|
migrations.UpdateRuntime,
|
||||||
})
|
})
|
||||||
if err := m.Migrate(); err != nil {
|
if err := m.Migrate(); err != nil {
|
||||||
global.LOG.Error(err)
|
global.LOG.Error(err)
|
||||||
|
@ -15,3 +15,13 @@ var AddDefaultNetwork = &gormigrate.Migration{
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var UpdateRuntime = &gormigrate.Migration{
|
||||||
|
ID: "20230920-update-runtime",
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
if err := tx.AutoMigrate(&model.Runtime{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
@ -20,5 +20,6 @@ func (r *RuntimeRouter) InitRuntimeRouter(Router *gin.RouterGroup) {
|
|||||||
groupRouter.POST("/del", baseApi.DeleteRuntime)
|
groupRouter.POST("/del", baseApi.DeleteRuntime)
|
||||||
groupRouter.POST("/update", baseApi.UpdateRuntime)
|
groupRouter.POST("/update", baseApi.UpdateRuntime)
|
||||||
groupRouter.GET("/:id", baseApi.GetRuntime)
|
groupRouter.GET("/:id", baseApi.GetRuntime)
|
||||||
|
groupRouter.POST("/node/package", baseApi.GetNodePackageRunScript)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Code generated by swaggo/swag. DO NOT EDIT.
|
// Package docs GENERATED BY SWAG; DO NOT EDIT
|
||||||
|
// This file was generated by swaggo/swag
|
||||||
package docs
|
package docs
|
||||||
|
|
||||||
import "github.com/swaggo/swag"
|
import "github.com/swaggo/swag"
|
||||||
@ -7830,6 +7830,39 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/runtimes/node/package": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取 Node 项目的 scripts",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Runtime"
|
||||||
|
],
|
||||||
|
"summary": "Get Node package scripts",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.NodePackageReq"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/runtimes/search": {
|
"/runtimes/search": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -12262,8 +12295,26 @@ const docTemplate = `{
|
|||||||
"cpuPercent": {
|
"cpuPercent": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
"cpuTotalUsage": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"memoryCache": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"memoryLimit": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"memoryPercent": {
|
"memoryPercent": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
|
},
|
||||||
|
"memoryUsage": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"percpuUsage": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"systemUsage": {
|
||||||
|
"type": "integer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -16357,6 +16408,14 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"request.NodePackageReq": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"codeDir": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"request.PortUpdate": {
|
"request.PortUpdate": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -16388,9 +16447,18 @@ const docTemplate = `{
|
|||||||
"appDetailId": {
|
"appDetailId": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"clean": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"codeDir": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"image": {
|
"image": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"install": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -16415,6 +16483,9 @@ const docTemplate = `{
|
|||||||
"request.RuntimeDelete": {
|
"request.RuntimeDelete": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"forceDelete": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
}
|
}
|
||||||
@ -16447,12 +16518,21 @@ const docTemplate = `{
|
|||||||
"request.RuntimeUpdate": {
|
"request.RuntimeUpdate": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"clean": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"codeDir": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"image": {
|
"image": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"install": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
@ -7823,6 +7823,39 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/runtimes/node/package": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取 Node 项目的 scripts",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Runtime"
|
||||||
|
],
|
||||||
|
"summary": "Get Node package scripts",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.NodePackageReq"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/runtimes/search": {
|
"/runtimes/search": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -12255,8 +12288,26 @@
|
|||||||
"cpuPercent": {
|
"cpuPercent": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
"cpuTotalUsage": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"memoryCache": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"memoryLimit": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"memoryPercent": {
|
"memoryPercent": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
|
},
|
||||||
|
"memoryUsage": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"percpuUsage": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"systemUsage": {
|
||||||
|
"type": "integer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -16350,6 +16401,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"request.NodePackageReq": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"codeDir": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"request.PortUpdate": {
|
"request.PortUpdate": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -16381,9 +16440,18 @@
|
|||||||
"appDetailId": {
|
"appDetailId": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"clean": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"codeDir": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"image": {
|
"image": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"install": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -16408,6 +16476,9 @@
|
|||||||
"request.RuntimeDelete": {
|
"request.RuntimeDelete": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"forceDelete": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
}
|
}
|
||||||
@ -16440,12 +16511,21 @@
|
|||||||
"request.RuntimeUpdate": {
|
"request.RuntimeUpdate": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"clean": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"codeDir": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"image": {
|
"image": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"install": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
@ -339,8 +339,20 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
cpuPercent:
|
cpuPercent:
|
||||||
type: number
|
type: number
|
||||||
|
cpuTotalUsage:
|
||||||
|
type: integer
|
||||||
|
memoryCache:
|
||||||
|
type: integer
|
||||||
|
memoryLimit:
|
||||||
|
type: integer
|
||||||
memoryPercent:
|
memoryPercent:
|
||||||
type: number
|
type: number
|
||||||
|
memoryUsage:
|
||||||
|
type: integer
|
||||||
|
percpuUsage:
|
||||||
|
type: integer
|
||||||
|
systemUsage:
|
||||||
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
dto.ContainerOperate:
|
dto.ContainerOperate:
|
||||||
properties:
|
properties:
|
||||||
@ -3085,6 +3097,11 @@ definitions:
|
|||||||
required:
|
required:
|
||||||
- scope
|
- scope
|
||||||
type: object
|
type: object
|
||||||
|
request.NodePackageReq:
|
||||||
|
properties:
|
||||||
|
codeDir:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
request.PortUpdate:
|
request.PortUpdate:
|
||||||
properties:
|
properties:
|
||||||
key:
|
key:
|
||||||
@ -3105,8 +3122,14 @@ definitions:
|
|||||||
properties:
|
properties:
|
||||||
appDetailId:
|
appDetailId:
|
||||||
type: integer
|
type: integer
|
||||||
|
clean:
|
||||||
|
type: boolean
|
||||||
|
codeDir:
|
||||||
|
type: string
|
||||||
image:
|
image:
|
||||||
type: string
|
type: string
|
||||||
|
install:
|
||||||
|
type: boolean
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
params:
|
params:
|
||||||
@ -3123,6 +3146,8 @@ definitions:
|
|||||||
type: object
|
type: object
|
||||||
request.RuntimeDelete:
|
request.RuntimeDelete:
|
||||||
properties:
|
properties:
|
||||||
|
forceDelete:
|
||||||
|
type: boolean
|
||||||
id:
|
id:
|
||||||
type: integer
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
@ -3144,10 +3169,16 @@ definitions:
|
|||||||
type: object
|
type: object
|
||||||
request.RuntimeUpdate:
|
request.RuntimeUpdate:
|
||||||
properties:
|
properties:
|
||||||
|
clean:
|
||||||
|
type: boolean
|
||||||
|
codeDir:
|
||||||
|
type: string
|
||||||
id:
|
id:
|
||||||
type: integer
|
type: integer
|
||||||
image:
|
image:
|
||||||
type: string
|
type: string
|
||||||
|
install:
|
||||||
|
type: boolean
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
params:
|
params:
|
||||||
@ -9035,6 +9066,26 @@ paths:
|
|||||||
formatEN: Delete website [name]
|
formatEN: Delete website [name]
|
||||||
formatZH: 删除网站 [name]
|
formatZH: 删除网站 [name]
|
||||||
paramKeys: []
|
paramKeys: []
|
||||||
|
/runtimes/node/package:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 获取 Node 项目的 scripts
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/request.NodePackageReq'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Get Node package scripts
|
||||||
|
tags:
|
||||||
|
- Runtime
|
||||||
/runtimes/search:
|
/runtimes/search:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
@ -3,7 +3,7 @@ import { App } from './app';
|
|||||||
export namespace Runtime {
|
export namespace Runtime {
|
||||||
export interface Runtime extends CommonModel {
|
export interface Runtime extends CommonModel {
|
||||||
name: string;
|
name: string;
|
||||||
appDetailId: number;
|
appDetailID: number;
|
||||||
image: string;
|
image: string;
|
||||||
workDir: string;
|
workDir: string;
|
||||||
dockerCompose: string;
|
dockerCompose: string;
|
||||||
@ -13,46 +13,59 @@ export namespace Runtime {
|
|||||||
resource: string;
|
resource: string;
|
||||||
version: string;
|
version: string;
|
||||||
status: string;
|
status: string;
|
||||||
|
codeDir: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RuntimeReq extends ReqPage {
|
export interface RuntimeReq extends ReqPage {
|
||||||
name?: string;
|
name?: string;
|
||||||
status?: string;
|
status?: string;
|
||||||
|
type?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeReq {
|
||||||
|
codeDir: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeScripts {
|
||||||
|
name: string;
|
||||||
|
script: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RuntimeDTO extends Runtime {
|
export interface RuntimeDTO extends Runtime {
|
||||||
appParams: App.InstallParams[];
|
appParams: App.InstallParams[];
|
||||||
appId: number;
|
appID: number;
|
||||||
source?: string;
|
source?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RuntimeCreate {
|
export interface RuntimeCreate {
|
||||||
id?: number;
|
id?: number;
|
||||||
name: string;
|
name: string;
|
||||||
appDetailId: number;
|
appDetailID: number;
|
||||||
image: string;
|
image: string;
|
||||||
params: object;
|
params: object;
|
||||||
type: string;
|
type: string;
|
||||||
resource: string;
|
resource: string;
|
||||||
appId?: number;
|
appID?: number;
|
||||||
version?: string;
|
version?: string;
|
||||||
rebuild?: boolean;
|
rebuild?: boolean;
|
||||||
source?: string;
|
source?: string;
|
||||||
|
codeDir?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RuntimeUpdate {
|
export interface RuntimeUpdate {
|
||||||
name: string;
|
name: string;
|
||||||
appDetailId: number;
|
appDetailID: number;
|
||||||
image: string;
|
image: string;
|
||||||
params: object;
|
params: object;
|
||||||
type: string;
|
type: string;
|
||||||
resource: string;
|
resource: string;
|
||||||
appId?: number;
|
appID?: number;
|
||||||
version?: string;
|
version?: string;
|
||||||
rebuild?: boolean;
|
rebuild?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RuntimeDelete {
|
export interface RuntimeDelete {
|
||||||
id: number;
|
id: number;
|
||||||
|
forceDelete: boolean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,3 +21,7 @@ export const GetRuntime = (id: number) => {
|
|||||||
export const UpdateRuntime = (req: Runtime.RuntimeUpdate) => {
|
export const UpdateRuntime = (req: Runtime.RuntimeUpdate) => {
|
||||||
return http.post<any>(`/runtimes/update`, req);
|
return http.post<any>(`/runtimes/update`, req);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const GetNodeScripts = (req: Runtime.NodeReq) => {
|
||||||
|
return http.post<Runtime.NodeScripts[]>(`/runtimes/node/package`, req);
|
||||||
|
};
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
popper-class="file-list"
|
popper-class="file-list"
|
||||||
>
|
>
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<el-button :icon="Folder" @click="popoverVisible = true"></el-button>
|
<el-button :icon="Folder" :disabled="disabled" @click="popoverVisible = true"></el-button>
|
||||||
</template>
|
</template>
|
||||||
<div>
|
<div>
|
||||||
<el-button class="close" link @click="closePage">
|
<el-button class="close" link @click="closePage">
|
||||||
@ -116,6 +116,10 @@ const props = defineProps({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const em = defineEmits(['choose']);
|
const em = defineEmits(['choose']);
|
||||||
|
@ -34,7 +34,7 @@ const getType = (status: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadingStatus = ['installing', 'building', 'restarting', 'upgrading', 'rebuilding'];
|
const loadingStatus = ['installing', 'building', 'restarting', 'upgrading', 'rebuilding', 'recreating', 'creating'];
|
||||||
|
|
||||||
const loadingIcon = (status: string): boolean => {
|
const loadingIcon = (status: string): boolean => {
|
||||||
return loadingStatus.indexOf(status) > -1;
|
return loadingStatus.indexOf(status) > -1;
|
||||||
|
@ -228,6 +228,8 @@ const message = {
|
|||||||
accept: 'Accepted',
|
accept: 'Accepted',
|
||||||
used: 'Used',
|
used: 'Used',
|
||||||
unUsed: 'Unused',
|
unUsed: 'Unused',
|
||||||
|
starting: 'Starting',
|
||||||
|
recreating: 'Recreating',
|
||||||
},
|
},
|
||||||
units: {
|
units: {
|
||||||
second: 'Second',
|
second: 'Second',
|
||||||
@ -1691,6 +1693,16 @@ const message = {
|
|||||||
xtomhk: 'XTOM Mirror Station (Hong Kong)',
|
xtomhk: 'XTOM Mirror Station (Hong Kong)',
|
||||||
xtom: 'XTOM Mirror Station (Global)',
|
xtom: 'XTOM Mirror Station (Global)',
|
||||||
phpsourceHelper: 'Choose the appropriate source according to your network environment',
|
phpsourceHelper: 'Choose the appropriate source according to your network environment',
|
||||||
|
appPort: 'App Port',
|
||||||
|
externalPort: 'External Port',
|
||||||
|
packageManager: 'Package Manager',
|
||||||
|
codeDir: 'Code Directory',
|
||||||
|
appPortHelper: 'The port used by the application',
|
||||||
|
externalPortHelper: 'The port exposed to the outside world',
|
||||||
|
runScript: 'Run Script',
|
||||||
|
runScriptHelper: 'The startup command list is parsed from the package.json file in the source directory',
|
||||||
|
open: 'Open',
|
||||||
|
close: 'Close',
|
||||||
},
|
},
|
||||||
process: {
|
process: {
|
||||||
pid: 'Process ID',
|
pid: 'Process ID',
|
||||||
|
@ -226,6 +226,8 @@ const message = {
|
|||||||
accept: '已放行',
|
accept: '已放行',
|
||||||
used: '已使用',
|
used: '已使用',
|
||||||
unUsed: '未使用',
|
unUsed: '未使用',
|
||||||
|
starting: '啟動中',
|
||||||
|
recreating: '重建中',
|
||||||
},
|
},
|
||||||
units: {
|
units: {
|
||||||
second: '秒',
|
second: '秒',
|
||||||
@ -1600,6 +1602,16 @@ const message = {
|
|||||||
xtomhk: 'XTOM 鏡像站(香港)',
|
xtomhk: 'XTOM 鏡像站(香港)',
|
||||||
xtom: 'XTOM 鏡像站(全球)',
|
xtom: 'XTOM 鏡像站(全球)',
|
||||||
phpsourceHelper: '根據你的網絡環境選擇合適的源',
|
phpsourceHelper: '根據你的網絡環境選擇合適的源',
|
||||||
|
appPort: '應用端口',
|
||||||
|
externalPort: '外部映射端口',
|
||||||
|
packageManager: '包管理器',
|
||||||
|
codeDir: '源碼目錄',
|
||||||
|
appPortHelper: '應用端口是指容器內部運行的端口',
|
||||||
|
externalPortHelper: '外部映射端口是指將容器內部端口映射到外部的端口',
|
||||||
|
runScript: '啟動命令',
|
||||||
|
runScriptHelper: '啟動命令是指容器啟動後運行的命令',
|
||||||
|
open: '開啟',
|
||||||
|
close: '關閉',
|
||||||
},
|
},
|
||||||
process: {
|
process: {
|
||||||
pid: '進程ID',
|
pid: '進程ID',
|
||||||
|
@ -226,6 +226,8 @@ const message = {
|
|||||||
accept: '已放行',
|
accept: '已放行',
|
||||||
used: '已使用',
|
used: '已使用',
|
||||||
unUsed: '未使用',
|
unUsed: '未使用',
|
||||||
|
starting: '启动中',
|
||||||
|
recreating: '重建中',
|
||||||
},
|
},
|
||||||
units: {
|
units: {
|
||||||
second: '秒',
|
second: '秒',
|
||||||
@ -1600,6 +1602,16 @@ const message = {
|
|||||||
xtomhk: 'XTOM 镜像站(香港)',
|
xtomhk: 'XTOM 镜像站(香港)',
|
||||||
xtom: 'XTOM 镜像站(全球)',
|
xtom: 'XTOM 镜像站(全球)',
|
||||||
phpsourceHelper: '根据你的网络环境选择合适的源',
|
phpsourceHelper: '根据你的网络环境选择合适的源',
|
||||||
|
appPort: '应用端口',
|
||||||
|
externalPort: '外部映射端口',
|
||||||
|
packageManager: '包管理器',
|
||||||
|
codeDir: '源码目录',
|
||||||
|
appPortHelper: '应用端口是指容器内部的端口',
|
||||||
|
externalPortHelper: '外部映射端口是指容器对外暴露的端口',
|
||||||
|
runScript: '启动命令',
|
||||||
|
runScriptHelper: '启动命令列表是从源码目录下的 package.json 文件中解析而来',
|
||||||
|
open: '放开',
|
||||||
|
close: '关闭',
|
||||||
},
|
},
|
||||||
process: {
|
process: {
|
||||||
pid: '进程ID',
|
pid: '进程ID',
|
||||||
|
@ -40,14 +40,24 @@ const webSiteRouter = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/websites/runtime/php',
|
path: '/websites/runtimes/php',
|
||||||
name: 'Runtime',
|
name: 'PHP',
|
||||||
component: () => import('@/views/website/runtime/index.vue'),
|
component: () => import('@/views/website/runtime/php/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: 'menu.runtime',
|
title: 'menu.runtime',
|
||||||
requiresAuth: false,
|
requiresAuth: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/websites/runtimes/node',
|
||||||
|
name: 'Node',
|
||||||
|
hidden: true,
|
||||||
|
component: () => import('@/views/website/runtime/node/index.vue'),
|
||||||
|
meta: {
|
||||||
|
activeMenu: '/websites/runtimes/php',
|
||||||
|
requiresAuth: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -203,13 +203,18 @@ const search = async (req: App.AppReq) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const openInstall = (app: App.App) => {
|
const openInstall = (app: App.App) => {
|
||||||
if (app.type === 'php') {
|
switch (app.type) {
|
||||||
router.push({ path: '/websites/runtime/php' });
|
case 'php':
|
||||||
} else {
|
router.push({ path: '/websites/runtimes/php' });
|
||||||
const params = {
|
break;
|
||||||
app: app,
|
case 'node':
|
||||||
};
|
router.push({ path: '/websites/runtimes/node' });
|
||||||
installRef.value.acceptParams(params);
|
break;
|
||||||
|
default:
|
||||||
|
const params = {
|
||||||
|
app: app,
|
||||||
|
};
|
||||||
|
installRef.value.acceptParams(params);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
77
frontend/src/views/website/runtime/delete/index.vue
Normal file
77
frontend/src/views/website/runtime/delete/index.vue
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="open"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:title="$t('commons.button.delete') + ' - ' + resourceName"
|
||||||
|
width="30%"
|
||||||
|
:before-close="handleClose"
|
||||||
|
>
|
||||||
|
<div :key="key" :loading="loading">
|
||||||
|
<el-form ref="deleteForm" label-position="left">
|
||||||
|
<el-form-item>
|
||||||
|
<el-checkbox v-model="deleteReq.forceDelete" :label="$t('website.forceDelete')" />
|
||||||
|
<span class="input-help">
|
||||||
|
{{ $t('website.forceDeleteHelper') }}
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="submit()" :loading="loading">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { DeleteRuntime } from '@/api/modules/runtime';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
import { FormInstance } from 'element-plus';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
const key = 1;
|
||||||
|
const open = ref(false);
|
||||||
|
const loading = ref(false);
|
||||||
|
const deleteReq = ref({
|
||||||
|
id: 0,
|
||||||
|
forceDelete: false,
|
||||||
|
});
|
||||||
|
const em = defineEmits(['close']);
|
||||||
|
const deleteForm = ref<FormInstance>();
|
||||||
|
const resourceName = ref('');
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
open.value = false;
|
||||||
|
em('close', false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const acceptParams = async (id: number, name: string) => {
|
||||||
|
deleteReq.value = {
|
||||||
|
id: id,
|
||||||
|
forceDelete: false,
|
||||||
|
};
|
||||||
|
resourceName.value = name;
|
||||||
|
open.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
loading.value = true;
|
||||||
|
DeleteRuntime(deleteReq.value)
|
||||||
|
.then(() => {
|
||||||
|
handleClose();
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
@ -1,161 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<RouterButton
|
<RouterButton :buttons="buttons" />
|
||||||
:buttons="[
|
<LayoutContent>
|
||||||
{
|
<router-view></router-view>
|
||||||
label: 'PHP',
|
|
||||||
path: '/runtimes/php',
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
/>
|
|
||||||
<LayoutContent :title="$t('runtime.runtime')" v-loading="loading">
|
|
||||||
<template #toolbar>
|
|
||||||
<el-button type="primary" @click="openCreate">
|
|
||||||
{{ $t('runtime.create') }}
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
<template #main>
|
|
||||||
<ComplexTable :pagination-config="paginationConfig" :data="items" @search="search()">
|
|
||||||
<el-table-column :label="$t('commons.table.name')" fix prop="name" min-width="120px">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<Tooltip :text="row.name" @click="openDetail(row)" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column :label="$t('runtime.resource')" prop="resource">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<span>{{ $t('runtime.' + toLowerCase(row.resource)) }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column :label="$t('runtime.version')" prop="version"></el-table-column>
|
|
||||||
<el-table-column :label="$t('runtime.image')" prop="image" show-overflow-tooltip></el-table-column>
|
|
||||||
<el-table-column :label="$t('commons.table.status')" prop="status">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-popover
|
|
||||||
v-if="row.status === 'error'"
|
|
||||||
placement="bottom"
|
|
||||||
:width="400"
|
|
||||||
trigger="hover"
|
|
||||||
:content="row.message"
|
|
||||||
>
|
|
||||||
<template #reference>
|
|
||||||
<Status :key="row.status" :status="row.status"></Status>
|
|
||||||
</template>
|
|
||||||
</el-popover>
|
|
||||||
<div v-else>
|
|
||||||
<Status :key="row.status" :status="row.status"></Status>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
prop="createdAt"
|
|
||||||
:label="$t('commons.table.date')"
|
|
||||||
:formatter="dateFormat"
|
|
||||||
show-overflow-tooltip
|
|
||||||
min-width="120"
|
|
||||||
fix
|
|
||||||
/>
|
|
||||||
<fu-table-operations
|
|
||||||
:ellipsis="10"
|
|
||||||
width="120px"
|
|
||||||
:buttons="buttons"
|
|
||||||
:label="$t('commons.table.operate')"
|
|
||||||
fixed="right"
|
|
||||||
fix
|
|
||||||
/>
|
|
||||||
</ComplexTable>
|
|
||||||
</template>
|
|
||||||
</LayoutContent>
|
</LayoutContent>
|
||||||
<CreateRuntime ref="createRef" @close="search" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
|
||||||
import { Runtime } from '@/api/interface/runtime';
|
|
||||||
import { DeleteRuntime, SearchRuntimes } from '@/api/modules/runtime';
|
|
||||||
import { dateFormat, toLowerCase } from '@/utils/util';
|
|
||||||
import CreateRuntime from '@/views/website/runtime/create/index.vue';
|
|
||||||
import Status from '@/components/status/index.vue';
|
|
||||||
import i18n from '@/lang';
|
|
||||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
|
||||||
|
|
||||||
const paginationConfig = reactive({
|
|
||||||
cacheSizeKey: 'runtime-page-size',
|
|
||||||
currentPage: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
total: 0,
|
|
||||||
});
|
|
||||||
let req = reactive<Runtime.RuntimeReq>({
|
|
||||||
name: '',
|
|
||||||
page: 1,
|
|
||||||
pageSize: 40,
|
|
||||||
});
|
|
||||||
let timer: NodeJS.Timer | null = null;
|
|
||||||
|
|
||||||
const buttons = [
|
const buttons = [
|
||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.edit'),
|
label: 'PHP',
|
||||||
click: function (row: Runtime.Runtime) {
|
path: '/websites/runtimes/php',
|
||||||
openDetail(row);
|
|
||||||
},
|
|
||||||
disabled: function (row: Runtime.Runtime) {
|
|
||||||
return row.status === 'building';
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.delete'),
|
label: 'Node.js',
|
||||||
click: function (row: Runtime.Runtime) {
|
path: '/websites/runtimes/node',
|
||||||
openDelete(row);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const loading = ref(false);
|
|
||||||
const items = ref<Runtime.RuntimeDTO[]>([]);
|
|
||||||
const createRef = ref();
|
|
||||||
|
|
||||||
const search = async () => {
|
|
||||||
req.page = paginationConfig.currentPage;
|
|
||||||
req.pageSize = paginationConfig.pageSize;
|
|
||||||
loading.value = true;
|
|
||||||
try {
|
|
||||||
const res = await SearchRuntimes(req);
|
|
||||||
items.value = res.data.items;
|
|
||||||
paginationConfig.total = res.data.total;
|
|
||||||
} catch (error) {
|
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const openCreate = () => {
|
|
||||||
createRef.value.acceptParams({ type: 'php', mode: 'create' });
|
|
||||||
};
|
|
||||||
|
|
||||||
const openDetail = (row: Runtime.Runtime) => {
|
|
||||||
createRef.value.acceptParams({ type: row.type, mode: 'edit', id: row.id });
|
|
||||||
};
|
|
||||||
|
|
||||||
const openDelete = async (row: Runtime.Runtime) => {
|
|
||||||
await useDeleteData(DeleteRuntime, { id: row.id }, 'commons.msg.delete');
|
|
||||||
search();
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
search();
|
|
||||||
timer = setInterval(() => {
|
|
||||||
search();
|
|
||||||
}, 10000 * 3);
|
|
||||||
});
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
clearInterval(Number(timer));
|
|
||||||
timer = null;
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.open-warn {
|
|
||||||
color: $primary-color;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
337
frontend/src/views/website/runtime/node/create/index.vue
Normal file
337
frontend/src/views/website/runtime/node/create/index.vue
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
<template>
|
||||||
|
<el-drawer :close-on-click-modal="false" v-model="open" size="50%">
|
||||||
|
<template #header>
|
||||||
|
<DrawerHeader :header="$t('runtime.' + mode)" :resource="runtime.name" :back="handleClose" />
|
||||||
|
</template>
|
||||||
|
<el-row v-loading="loading">
|
||||||
|
<el-col :span="22" :offset="1">
|
||||||
|
<el-form
|
||||||
|
ref="runtimeForm"
|
||||||
|
label-position="top"
|
||||||
|
:model="runtime"
|
||||||
|
label-width="125px"
|
||||||
|
:rules="rules"
|
||||||
|
:validate-on-rule-change="false"
|
||||||
|
>
|
||||||
|
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||||
|
<el-input :disabled="mode === 'edit'" v-model="runtime.name"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('runtime.app')" prop="appId">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-select
|
||||||
|
v-model="runtime.appId"
|
||||||
|
:disabled="mode === 'edit'"
|
||||||
|
@change="changeApp(runtime.appId)"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="(app, index) in apps"
|
||||||
|
:key="index"
|
||||||
|
:label="app.name"
|
||||||
|
:value="app.id"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-select
|
||||||
|
v-model="runtime.version"
|
||||||
|
:disabled="mode === 'edit'"
|
||||||
|
@change="changeVersion()"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="(version, index) in appVersions"
|
||||||
|
:key="index"
|
||||||
|
:label="version"
|
||||||
|
:value="version"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('runtime.codeDir')" prop="codeDir">
|
||||||
|
<el-input v-model.trim="runtime.codeDir">
|
||||||
|
<template #prepend>
|
||||||
|
<FileList :path="runtime.codeDir" @choose="getPath" :dir="true"></FileList>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('runtime.runScript')" prop="params.EXEC_SCRIPT">
|
||||||
|
<el-select v-model="runtime.params['EXEC_SCRIPT']">
|
||||||
|
<el-option
|
||||||
|
v-for="(script, index) in scripts"
|
||||||
|
:key="index"
|
||||||
|
:label="script.name + ' 【 ' + script.script + ' 】'"
|
||||||
|
:value="script.name"
|
||||||
|
>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="4">{{ script.name }}</el-col>
|
||||||
|
<el-col :span="10">{{ ' 【 ' + script.script + ' 】' }}</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="10">
|
||||||
|
<el-form-item :label="$t('runtime.appPort')" prop="params.NODE_APP_PORT">
|
||||||
|
<el-input v-model.number="runtime.params['NODE_APP_PORT']" />
|
||||||
|
<span class="input-help">{{ $t('runtime.appPortHelper') }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="10">
|
||||||
|
<el-form-item :label="$t('runtime.externalPort')" prop="params.PANEL_APP_PORT_HTTP">
|
||||||
|
<el-input v-model.number="runtime.params['PANEL_APP_PORT_HTTP']" />
|
||||||
|
<span class="input-help">{{ $t('runtime.externalPortHelper') }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="4">
|
||||||
|
<el-form-item :label="$t('app.allowPort')" prop="params.HOST_IP">
|
||||||
|
<el-select v-model="runtime.params['HOST_IP']">
|
||||||
|
<el-option label="放开" value="0.0.0.0"></el-option>
|
||||||
|
<el-option label="不放开" value="127.0.0.1"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-form-item :label="$t('runtime.packageManager')" prop="params.PACKAGE_MANAGER">
|
||||||
|
<el-select v-model="runtime.params['PACKAGE_MANAGER']">
|
||||||
|
<el-option label="npm" value="npm"></el-option>
|
||||||
|
<el-option label="yarn" value="yarn"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('app.containerName')" prop="params.CONTAINER_NAME">
|
||||||
|
<el-input v-model.trim="runtime.params['CONTAINER_NAME']"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<template #footer>
|
||||||
|
<span>
|
||||||
|
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="submit(runtimeForm)" :disabled="loading">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { App } from '@/api/interface/app';
|
||||||
|
import { Runtime } from '@/api/interface/runtime';
|
||||||
|
import { GetApp, GetAppDetail, SearchApp } from '@/api/modules/app';
|
||||||
|
import { CreateRuntime, GetNodeScripts, GetRuntime, UpdateRuntime } from '@/api/modules/runtime';
|
||||||
|
import { Rules } from '@/global/form-rules';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
import { FormInstance } from 'element-plus';
|
||||||
|
import { reactive, ref, watch } from 'vue';
|
||||||
|
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||||
|
|
||||||
|
interface OperateRrops {
|
||||||
|
id?: number;
|
||||||
|
mode: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = ref(false);
|
||||||
|
const apps = ref<App.App[]>([]);
|
||||||
|
const runtimeForm = ref<FormInstance>();
|
||||||
|
const loading = ref(false);
|
||||||
|
const mode = ref('create');
|
||||||
|
const editParams = ref<App.InstallParams[]>();
|
||||||
|
const appVersions = ref<string[]>([]);
|
||||||
|
const appReq = reactive({
|
||||||
|
type: 'node',
|
||||||
|
page: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
const initData = (type: string) => ({
|
||||||
|
name: '',
|
||||||
|
appDetailId: undefined,
|
||||||
|
image: '',
|
||||||
|
params: {
|
||||||
|
PACKAGE_MANAGER: 'npm',
|
||||||
|
HOST_IP: '0.0.0.0',
|
||||||
|
},
|
||||||
|
type: type,
|
||||||
|
resource: 'appstore',
|
||||||
|
rebuild: false,
|
||||||
|
codeDir: '/',
|
||||||
|
});
|
||||||
|
let runtime = reactive<Runtime.RuntimeCreate>(initData('node'));
|
||||||
|
const rules = ref<any>({
|
||||||
|
name: [Rules.appName],
|
||||||
|
appId: [Rules.requiredSelect],
|
||||||
|
codeDir: [Rules.requiredInput],
|
||||||
|
params: {
|
||||||
|
NODE_APP_PORT: [Rules.requiredInput, Rules.port],
|
||||||
|
PANEL_APP_PORT_HTTP: [Rules.requiredInput, Rules.port],
|
||||||
|
PACKAGE_MANAGER: [Rules.requiredSelect],
|
||||||
|
HOST_IP: [Rules.requiredSelect],
|
||||||
|
EXEC_SCRIPT: [Rules.requiredSelect],
|
||||||
|
CONTAINER_NAME: [Rules.requiredInput],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const scripts = ref<Runtime.NodeScripts[]>([]);
|
||||||
|
const em = defineEmits(['close']);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => runtime.params['NODE_APP_PORT'],
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
runtime.params['PANEL_APP_PORT_HTTP'] = newVal;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => runtime.name,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
runtime.params['CONTAINER_NAME'] = newVal;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
open.value = false;
|
||||||
|
em('close', false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPath = (codeDir: string) => {
|
||||||
|
runtime.codeDir = codeDir;
|
||||||
|
getScripts();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getScripts = () => {
|
||||||
|
GetNodeScripts({ codeDir: runtime.codeDir }).then((res) => {
|
||||||
|
scripts.value = res.data;
|
||||||
|
if (scripts.value.length > 0) {
|
||||||
|
runtime.params['EXEC_SCRIPT'] = scripts.value[0].script;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const searchApp = (appId: number) => {
|
||||||
|
SearchApp(appReq).then((res) => {
|
||||||
|
apps.value = res.data.items || [];
|
||||||
|
if (res.data && res.data.items && res.data.items.length > 0) {
|
||||||
|
if (appId == null) {
|
||||||
|
runtime.appId = res.data.items[0].id;
|
||||||
|
getApp(res.data.items[0].key, mode.value);
|
||||||
|
} else {
|
||||||
|
res.data.items.forEach((item) => {
|
||||||
|
if (item.id === appId) {
|
||||||
|
getApp(item.key, mode.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeApp = (appId: number) => {
|
||||||
|
for (const app of apps.value) {
|
||||||
|
if (app.id === appId) {
|
||||||
|
getApp(app.key, mode.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeVersion = () => {
|
||||||
|
loading.value = true;
|
||||||
|
GetAppDetail(runtime.appId, runtime.version, 'runtime')
|
||||||
|
.then((res) => {
|
||||||
|
runtime.appDetailId = res.data.id;
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getApp = (appkey: string, mode: string) => {
|
||||||
|
GetApp(appkey).then((res) => {
|
||||||
|
appVersions.value = res.data.versions || [];
|
||||||
|
if (res.data.versions.length > 0) {
|
||||||
|
runtime.version = res.data.versions[0];
|
||||||
|
if (mode === 'create') {
|
||||||
|
changeVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const submit = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
await formEl.validate((valid) => {
|
||||||
|
if (!valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mode.value == 'create') {
|
||||||
|
loading.value = true;
|
||||||
|
CreateRuntime(runtime)
|
||||||
|
.then(() => {
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
|
||||||
|
handleClose();
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
loading.value = true;
|
||||||
|
UpdateRuntime(runtime)
|
||||||
|
.then(() => {
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
|
||||||
|
handleClose();
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRuntime = async (id: number) => {
|
||||||
|
try {
|
||||||
|
const res = await GetRuntime(id);
|
||||||
|
const data = res.data;
|
||||||
|
Object.assign(runtime, {
|
||||||
|
id: data.id,
|
||||||
|
name: data.name,
|
||||||
|
appDetailId: data.appDetailId,
|
||||||
|
image: data.image,
|
||||||
|
params: {},
|
||||||
|
type: data.type,
|
||||||
|
resource: data.resource,
|
||||||
|
appId: data.appId,
|
||||||
|
version: data.version,
|
||||||
|
rebuild: true,
|
||||||
|
source: data.source,
|
||||||
|
});
|
||||||
|
editParams.value = data.appParams;
|
||||||
|
if (mode.value == 'create') {
|
||||||
|
searchApp(data.appId);
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const acceptParams = async (props: OperateRrops) => {
|
||||||
|
mode.value = props.mode;
|
||||||
|
if (props.mode === 'create') {
|
||||||
|
Object.assign(runtime, initData(props.type));
|
||||||
|
searchApp(null);
|
||||||
|
} else {
|
||||||
|
searchApp(null);
|
||||||
|
getRuntime(props.id);
|
||||||
|
}
|
||||||
|
open.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
164
frontend/src/views/website/runtime/node/index.vue
Normal file
164
frontend/src/views/website/runtime/node/index.vue
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<RouterMenu />
|
||||||
|
<LayoutContent :title="'Node.js'" v-loading="loading">
|
||||||
|
<template #toolbar>
|
||||||
|
<el-button type="primary" @click="openCreate">
|
||||||
|
{{ $t('runtime.create') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<template #main>
|
||||||
|
<ComplexTable :pagination-config="paginationConfig" :data="items" @search="search()">
|
||||||
|
<el-table-column :label="$t('commons.table.name')" fix prop="name" min-width="120px">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<Tooltip :text="row.name" @click="openDetail(row)" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('runtime.codeDir')" prop="codeDir">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button type="primary" link @click="toFolder(row.codeDir)">
|
||||||
|
<el-icon>
|
||||||
|
<FolderOpened />
|
||||||
|
</el-icon>
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('runtime.version')" prop="version"></el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
:label="$t('runtime.externalPort')"
|
||||||
|
prop="params.PANEL_APP_PORT_HTTP"
|
||||||
|
></el-table-column>
|
||||||
|
<el-table-column :label="$t('commons.table.status')" prop="status">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-popover
|
||||||
|
v-if="row.status === 'error'"
|
||||||
|
placement="bottom"
|
||||||
|
:width="400"
|
||||||
|
trigger="hover"
|
||||||
|
:content="row.message"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<Status :key="row.status" :status="row.status"></Status>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
<div v-else>
|
||||||
|
<Status :key="row.status" :status="row.status"></Status>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="createdAt"
|
||||||
|
:label="$t('commons.table.date')"
|
||||||
|
:formatter="dateFormat"
|
||||||
|
show-overflow-tooltip
|
||||||
|
min-width="120"
|
||||||
|
fix
|
||||||
|
/>
|
||||||
|
<fu-table-operations
|
||||||
|
:ellipsis="10"
|
||||||
|
width="120px"
|
||||||
|
:buttons="buttons"
|
||||||
|
:label="$t('commons.table.operate')"
|
||||||
|
fixed="right"
|
||||||
|
fix
|
||||||
|
/>
|
||||||
|
</ComplexTable>
|
||||||
|
</template>
|
||||||
|
</LayoutContent>
|
||||||
|
<OperateNode ref="operateRef" @close="search" />
|
||||||
|
<Delete ref="deleteRef" @close="search()" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
||||||
|
import { Runtime } from '@/api/interface/runtime';
|
||||||
|
import { SearchRuntimes } from '@/api/modules/runtime';
|
||||||
|
import { dateFormat } from '@/utils/util';
|
||||||
|
import OperateNode from '@/views/website/runtime/node/operate/index.vue';
|
||||||
|
import Status from '@/components/status/index.vue';
|
||||||
|
import Delete from '@/views/website/runtime/delete/index.vue';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import RouterMenu from '../index.vue';
|
||||||
|
import router from '@/routers/router';
|
||||||
|
|
||||||
|
const paginationConfig = reactive({
|
||||||
|
cacheSizeKey: 'runtime-page-size',
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
const req = reactive<Runtime.RuntimeReq>({
|
||||||
|
name: '',
|
||||||
|
page: 1,
|
||||||
|
pageSize: 40,
|
||||||
|
type: 'node',
|
||||||
|
});
|
||||||
|
let timer: NodeJS.Timer | null = null;
|
||||||
|
|
||||||
|
const buttons = [
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.edit'),
|
||||||
|
click: function (row: Runtime.Runtime) {
|
||||||
|
openDetail(row);
|
||||||
|
},
|
||||||
|
disabled: function (row: Runtime.Runtime) {
|
||||||
|
return row.status === 'starting' || row.status === 'recreating';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.delete'),
|
||||||
|
click: function (row: Runtime.Runtime) {
|
||||||
|
openDelete(row);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const loading = ref(false);
|
||||||
|
const items = ref<Runtime.RuntimeDTO[]>([]);
|
||||||
|
const operateRef = ref();
|
||||||
|
const deleteRef = ref();
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
req.page = paginationConfig.currentPage;
|
||||||
|
req.pageSize = paginationConfig.pageSize;
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await SearchRuntimes(req);
|
||||||
|
items.value = res.data.items;
|
||||||
|
paginationConfig.total = res.data.total;
|
||||||
|
} catch (error) {
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const openCreate = () => {
|
||||||
|
operateRef.value.acceptParams({ type: 'node', mode: 'create' });
|
||||||
|
};
|
||||||
|
|
||||||
|
const openDetail = (row: Runtime.Runtime) => {
|
||||||
|
operateRef.value.acceptParams({ type: row.type, mode: 'edit', id: row.id });
|
||||||
|
};
|
||||||
|
|
||||||
|
const openDelete = async (row: Runtime.Runtime) => {
|
||||||
|
deleteRef.value.acceptParams(row.id, row.name);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toFolder = (folder: string) => {
|
||||||
|
router.push({ path: '/hosts/files', query: { path: folder } });
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search();
|
||||||
|
timer = setInterval(() => {
|
||||||
|
search();
|
||||||
|
}, 10000 * 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearInterval(Number(timer));
|
||||||
|
timer = null;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
351
frontend/src/views/website/runtime/node/operate/index.vue
Normal file
351
frontend/src/views/website/runtime/node/operate/index.vue
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
<template>
|
||||||
|
<el-drawer :close-on-click-modal="false" v-model="open" size="50%">
|
||||||
|
<template #header>
|
||||||
|
<DrawerHeader
|
||||||
|
:header="$t('runtime.' + mode)"
|
||||||
|
:hideResource="mode == 'create'"
|
||||||
|
:resource="runtime.name"
|
||||||
|
:back="handleClose"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<el-row v-loading="loading">
|
||||||
|
<el-col :span="22" :offset="1">
|
||||||
|
<el-form
|
||||||
|
ref="runtimeForm"
|
||||||
|
label-position="top"
|
||||||
|
:model="runtime"
|
||||||
|
label-width="125px"
|
||||||
|
:rules="rules"
|
||||||
|
:validate-on-rule-change="false"
|
||||||
|
>
|
||||||
|
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||||
|
<el-input :disabled="mode === 'edit'" v-model="runtime.name"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('runtime.app')" prop="appID">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-select
|
||||||
|
v-model="runtime.appID"
|
||||||
|
:disabled="mode === 'edit'"
|
||||||
|
@change="changeApp(runtime.appID)"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="(app, index) in apps"
|
||||||
|
:key="index"
|
||||||
|
:label="app.name"
|
||||||
|
:value="app.id"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-select
|
||||||
|
v-model="runtime.version"
|
||||||
|
:disabled="mode === 'edit'"
|
||||||
|
@change="changeVersion()"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="(version, index) in appVersions"
|
||||||
|
:key="index"
|
||||||
|
:label="version"
|
||||||
|
:value="version"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('runtime.codeDir')" prop="codeDir">
|
||||||
|
<el-input v-model.trim="runtime.codeDir" :disabled="mode === 'edit'">
|
||||||
|
<template #prepend>
|
||||||
|
<FileList
|
||||||
|
:disabled="mode === 'edit'"
|
||||||
|
:path="runtime.codeDir"
|
||||||
|
@choose="getPath"
|
||||||
|
:dir="true"
|
||||||
|
></FileList>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('runtime.runScript')" prop="params.EXEC_SCRIPT">
|
||||||
|
<el-select v-model="runtime.params['EXEC_SCRIPT']">
|
||||||
|
<el-option
|
||||||
|
v-for="(script, index) in scripts"
|
||||||
|
:key="index"
|
||||||
|
:label="script.name + ' 【 ' + script.script + ' 】'"
|
||||||
|
:value="script.name"
|
||||||
|
>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="4">{{ script.name }}</el-col>
|
||||||
|
<el-col :span="10">{{ ' 【 ' + script.script + ' 】' }}</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
<span class="input-help">{{ $t('runtime.runScriptHelper') }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="10">
|
||||||
|
<el-form-item :label="$t('runtime.appPort')" prop="params.NODE_APP_PORT">
|
||||||
|
<el-input v-model.number="runtime.params['NODE_APP_PORT']" />
|
||||||
|
<span class="input-help">{{ $t('runtime.appPortHelper') }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="10">
|
||||||
|
<el-form-item :label="$t('runtime.externalPort')" prop="params.PANEL_APP_PORT_HTTP">
|
||||||
|
<el-input v-model.number="runtime.params['PANEL_APP_PORT_HTTP']" />
|
||||||
|
<span class="input-help">{{ $t('runtime.externalPortHelper') }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="4">
|
||||||
|
<el-form-item :label="$t('app.allowPort')" prop="params.HOST_IP">
|
||||||
|
<el-select v-model="runtime.params['HOST_IP']">
|
||||||
|
<el-option :label="$t('runtime.open')" value="0.0.0.0"></el-option>
|
||||||
|
<el-option :label="$t('runtime.close')" value="127.0.0.1"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-form-item :label="$t('runtime.packageManager')" prop="params.PACKAGE_MANAGER">
|
||||||
|
<el-select v-model="runtime.params['PACKAGE_MANAGER']">
|
||||||
|
<el-option label="npm" value="npm"></el-option>
|
||||||
|
<el-option label="yarn" value="yarn"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('app.containerName')" prop="params.CONTAINER_NAME">
|
||||||
|
<el-input v-model.trim="runtime.params['CONTAINER_NAME']"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<template #footer>
|
||||||
|
<span>
|
||||||
|
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="submit(runtimeForm)" :disabled="loading">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { App } from '@/api/interface/app';
|
||||||
|
import { Runtime } from '@/api/interface/runtime';
|
||||||
|
import { GetApp, GetAppDetail, SearchApp } from '@/api/modules/app';
|
||||||
|
import { CreateRuntime, GetNodeScripts, GetRuntime, UpdateRuntime } from '@/api/modules/runtime';
|
||||||
|
import { Rules } from '@/global/form-rules';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
import { FormInstance } from 'element-plus';
|
||||||
|
import { reactive, ref, watch } from 'vue';
|
||||||
|
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||||
|
|
||||||
|
interface OperateRrops {
|
||||||
|
id?: number;
|
||||||
|
mode: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = ref(false);
|
||||||
|
const apps = ref<App.App[]>([]);
|
||||||
|
const runtimeForm = ref<FormInstance>();
|
||||||
|
const loading = ref(false);
|
||||||
|
const mode = ref('create');
|
||||||
|
const editParams = ref<App.InstallParams[]>();
|
||||||
|
const appVersions = ref<string[]>([]);
|
||||||
|
const appReq = reactive({
|
||||||
|
type: 'node',
|
||||||
|
page: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
const initData = (type: string) => ({
|
||||||
|
name: '',
|
||||||
|
appDetailID: undefined,
|
||||||
|
image: '',
|
||||||
|
params: {
|
||||||
|
PACKAGE_MANAGER: 'npm',
|
||||||
|
HOST_IP: '0.0.0.0',
|
||||||
|
},
|
||||||
|
type: type,
|
||||||
|
resource: 'appstore',
|
||||||
|
rebuild: false,
|
||||||
|
codeDir: '/',
|
||||||
|
});
|
||||||
|
let runtime = reactive<Runtime.RuntimeCreate>(initData('node'));
|
||||||
|
const rules = ref<any>({
|
||||||
|
name: [Rules.appName],
|
||||||
|
appID: [Rules.requiredSelect],
|
||||||
|
codeDir: [Rules.requiredInput],
|
||||||
|
params: {
|
||||||
|
NODE_APP_PORT: [Rules.requiredInput, Rules.port],
|
||||||
|
PANEL_APP_PORT_HTTP: [Rules.requiredInput, Rules.port],
|
||||||
|
PACKAGE_MANAGER: [Rules.requiredSelect],
|
||||||
|
HOST_IP: [Rules.requiredSelect],
|
||||||
|
EXEC_SCRIPT: [Rules.requiredSelect],
|
||||||
|
CONTAINER_NAME: [Rules.requiredInput],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const scripts = ref<Runtime.NodeScripts[]>([]);
|
||||||
|
const em = defineEmits(['close']);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => runtime.params['NODE_APP_PORT'],
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal && mode.value == 'create') {
|
||||||
|
runtime.params['PANEL_APP_PORT_HTTP'] = newVal;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => runtime.name,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
runtime.params['CONTAINER_NAME'] = newVal;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
open.value = false;
|
||||||
|
em('close', false);
|
||||||
|
runtimeForm.value?.resetFields();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPath = (codeDir: string) => {
|
||||||
|
runtime.codeDir = codeDir;
|
||||||
|
getScripts();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getScripts = () => {
|
||||||
|
GetNodeScripts({ codeDir: runtime.codeDir }).then((res) => {
|
||||||
|
scripts.value = res.data;
|
||||||
|
if (mode.value == 'create' && scripts.value.length > 0) {
|
||||||
|
runtime.params['EXEC_SCRIPT'] = scripts.value[0].name;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const searchApp = (appID: number) => {
|
||||||
|
SearchApp(appReq).then((res) => {
|
||||||
|
apps.value = res.data.items || [];
|
||||||
|
if (res.data && res.data.items && res.data.items.length > 0) {
|
||||||
|
if (appID == null) {
|
||||||
|
runtime.appID = res.data.items[0].id;
|
||||||
|
getApp(res.data.items[0].key, mode.value);
|
||||||
|
} else {
|
||||||
|
res.data.items.forEach((item) => {
|
||||||
|
if (item.id === appID) {
|
||||||
|
getApp(item.key, mode.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeApp = (appID: number) => {
|
||||||
|
for (const app of apps.value) {
|
||||||
|
if (app.id === appID) {
|
||||||
|
getApp(app.key, mode.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeVersion = () => {
|
||||||
|
loading.value = true;
|
||||||
|
GetAppDetail(runtime.appID, runtime.version, 'runtime')
|
||||||
|
.then((res) => {
|
||||||
|
runtime.appDetailID = res.data.id;
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getApp = (appkey: string, mode: string) => {
|
||||||
|
GetApp(appkey).then((res) => {
|
||||||
|
appVersions.value = res.data.versions || [];
|
||||||
|
if (res.data.versions.length > 0) {
|
||||||
|
runtime.version = res.data.versions[0];
|
||||||
|
if (mode === 'create') {
|
||||||
|
changeVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const submit = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
await formEl.validate((valid) => {
|
||||||
|
if (!valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mode.value == 'create') {
|
||||||
|
loading.value = true;
|
||||||
|
CreateRuntime(runtime)
|
||||||
|
.then(() => {
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
|
||||||
|
handleClose();
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
loading.value = true;
|
||||||
|
UpdateRuntime(runtime)
|
||||||
|
.then(() => {
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
|
||||||
|
handleClose();
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRuntime = async (id: number) => {
|
||||||
|
try {
|
||||||
|
const res = await GetRuntime(id);
|
||||||
|
const data = res.data;
|
||||||
|
Object.assign(runtime, {
|
||||||
|
id: data.id,
|
||||||
|
name: data.name,
|
||||||
|
appDetailId: data.appDetailID,
|
||||||
|
image: data.image,
|
||||||
|
type: data.type,
|
||||||
|
resource: data.resource,
|
||||||
|
appID: data.appID,
|
||||||
|
version: data.version,
|
||||||
|
rebuild: true,
|
||||||
|
source: data.source,
|
||||||
|
params: data.params,
|
||||||
|
codeDir: data.codeDir,
|
||||||
|
});
|
||||||
|
editParams.value = data.appParams;
|
||||||
|
if (mode.value == 'edit') {
|
||||||
|
searchApp(data.appID);
|
||||||
|
}
|
||||||
|
getScripts();
|
||||||
|
} catch (error) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const acceptParams = async (props: OperateRrops) => {
|
||||||
|
mode.value = props.mode;
|
||||||
|
scripts.value = [];
|
||||||
|
if (props.mode === 'create') {
|
||||||
|
Object.assign(runtime, initData(props.type));
|
||||||
|
searchApp(null);
|
||||||
|
} else {
|
||||||
|
getRuntime(props.id);
|
||||||
|
}
|
||||||
|
open.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
@ -35,9 +35,9 @@
|
|||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="runtime.appId"
|
v-model="runtime.appID"
|
||||||
:disabled="mode === 'edit'"
|
:disabled="mode === 'edit'"
|
||||||
@change="changeApp(runtime.appId)"
|
@change="changeApp(runtime.appID)"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="(app, index) in apps"
|
v-for="(app, index) in apps"
|
||||||
@ -178,7 +178,7 @@ const appReq = reactive({
|
|||||||
});
|
});
|
||||||
const initData = (type: string) => ({
|
const initData = (type: string) => ({
|
||||||
name: '',
|
name: '',
|
||||||
appDetailId: undefined,
|
appDetailID: undefined,
|
||||||
image: '',
|
image: '',
|
||||||
params: {},
|
params: {},
|
||||||
type: type,
|
type: type,
|
||||||
@ -192,7 +192,7 @@ let runtime = reactive<Runtime.RuntimeCreate>(initData('php'));
|
|||||||
const rules = ref<any>({
|
const rules = ref<any>({
|
||||||
name: [Rules.appName],
|
name: [Rules.appName],
|
||||||
resource: [Rules.requiredInput],
|
resource: [Rules.requiredInput],
|
||||||
appId: [Rules.requiredSelect],
|
appID: [Rules.requiredSelect],
|
||||||
version: [Rules.requiredInput, Rules.paramCommon],
|
version: [Rules.requiredInput, Rules.paramCommon],
|
||||||
image: [Rules.requiredInput, Rules.imageName],
|
image: [Rules.requiredInput, Rules.imageName],
|
||||||
source: [Rules.requiredSelect],
|
source: [Rules.requiredSelect],
|
||||||
@ -238,7 +238,7 @@ const handleClose = () => {
|
|||||||
|
|
||||||
const changeResource = (resource: string) => {
|
const changeResource = (resource: string) => {
|
||||||
if (resource === 'local') {
|
if (resource === 'local') {
|
||||||
runtime.appDetailId = undefined;
|
runtime.appDetailID = undefined;
|
||||||
runtime.version = '';
|
runtime.version = '';
|
||||||
runtime.params = {};
|
runtime.params = {};
|
||||||
runtime.image = '';
|
runtime.image = '';
|
||||||
@ -253,7 +253,7 @@ const searchApp = (appId: number) => {
|
|||||||
apps.value = res.data.items || [];
|
apps.value = res.data.items || [];
|
||||||
if (res.data && res.data.items && res.data.items.length > 0) {
|
if (res.data && res.data.items && res.data.items.length > 0) {
|
||||||
if (appId == null) {
|
if (appId == null) {
|
||||||
runtime.appId = res.data.items[0].id;
|
runtime.appID = res.data.items[0].id;
|
||||||
getApp(res.data.items[0].key, mode.value);
|
getApp(res.data.items[0].key, mode.value);
|
||||||
} else {
|
} else {
|
||||||
res.data.items.forEach((item) => {
|
res.data.items.forEach((item) => {
|
||||||
@ -279,9 +279,9 @@ const changeApp = (appId: number) => {
|
|||||||
const changeVersion = () => {
|
const changeVersion = () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
initParam.value = false;
|
initParam.value = false;
|
||||||
GetAppDetail(runtime.appId, runtime.version, 'runtime')
|
GetAppDetail(runtime.appID, runtime.version, 'runtime')
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
runtime.appDetailId = res.data.id;
|
runtime.appDetailID = res.data.id;
|
||||||
runtime.image = res.data.image + ':' + runtime.version;
|
runtime.image = res.data.image + ':' + runtime.version;
|
||||||
appParams.value = res.data.params;
|
appParams.value = res.data.params;
|
||||||
initParam.value = true;
|
initParam.value = true;
|
||||||
@ -342,19 +342,19 @@ const getRuntime = async (id: number) => {
|
|||||||
Object.assign(runtime, {
|
Object.assign(runtime, {
|
||||||
id: data.id,
|
id: data.id,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
appDetailId: data.appDetailId,
|
appDetailID: data.appDetailID,
|
||||||
image: data.image,
|
image: data.image,
|
||||||
params: {},
|
params: {},
|
||||||
type: data.type,
|
type: data.type,
|
||||||
resource: data.resource,
|
resource: data.resource,
|
||||||
appId: data.appId,
|
appID: data.appID,
|
||||||
version: data.version,
|
version: data.version,
|
||||||
rebuild: true,
|
rebuild: true,
|
||||||
source: data.source,
|
source: data.source,
|
||||||
});
|
});
|
||||||
editParams.value = data.appParams;
|
editParams.value = data.appParams;
|
||||||
if (mode.value == 'create') {
|
if (mode.value == 'create') {
|
||||||
searchApp(data.appId);
|
searchApp(data.appID);
|
||||||
} else {
|
} else {
|
||||||
initParam.value = true;
|
initParam.value = true;
|
||||||
}
|
}
|
156
frontend/src/views/website/runtime/php/index.vue
Normal file
156
frontend/src/views/website/runtime/php/index.vue
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<RouterMenu />
|
||||||
|
<LayoutContent :title="'PHP'" v-loading="loading">
|
||||||
|
<template #toolbar>
|
||||||
|
<el-button type="primary" @click="openCreate">
|
||||||
|
{{ $t('runtime.create') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<template #main>
|
||||||
|
<ComplexTable :pagination-config="paginationConfig" :data="items" @search="search()">
|
||||||
|
<el-table-column :label="$t('commons.table.name')" fix prop="name" min-width="120px">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<Tooltip :text="row.name" @click="openDetail(row)" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('runtime.resource')" prop="resource">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span>{{ $t('runtime.' + toLowerCase(row.resource)) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('runtime.version')" prop="version"></el-table-column>
|
||||||
|
<el-table-column :label="$t('runtime.image')" prop="image" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column :label="$t('commons.table.status')" prop="status">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-popover
|
||||||
|
v-if="row.status === 'error'"
|
||||||
|
placement="bottom"
|
||||||
|
:width="400"
|
||||||
|
trigger="hover"
|
||||||
|
:content="row.message"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<Status :key="row.status" :status="row.status"></Status>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
<div v-else>
|
||||||
|
<Status :key="row.status" :status="row.status"></Status>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="createdAt"
|
||||||
|
:label="$t('commons.table.date')"
|
||||||
|
:formatter="dateFormat"
|
||||||
|
show-overflow-tooltip
|
||||||
|
min-width="120"
|
||||||
|
fix
|
||||||
|
/>
|
||||||
|
<fu-table-operations
|
||||||
|
:ellipsis="10"
|
||||||
|
width="120px"
|
||||||
|
:buttons="buttons"
|
||||||
|
:label="$t('commons.table.operate')"
|
||||||
|
fixed="right"
|
||||||
|
fix
|
||||||
|
/>
|
||||||
|
</ComplexTable>
|
||||||
|
</template>
|
||||||
|
</LayoutContent>
|
||||||
|
<CreateRuntime ref="createRef" @close="search" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
||||||
|
import { Runtime } from '@/api/interface/runtime';
|
||||||
|
import { DeleteRuntime, SearchRuntimes } from '@/api/modules/runtime';
|
||||||
|
import { dateFormat, toLowerCase } from '@/utils/util';
|
||||||
|
import CreateRuntime from '@/views/website/runtime/php/create/index.vue';
|
||||||
|
import Status from '@/components/status/index.vue';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||||
|
import RouterMenu from '../index.vue';
|
||||||
|
|
||||||
|
const paginationConfig = reactive({
|
||||||
|
cacheSizeKey: 'runtime-page-size',
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
let req = reactive<Runtime.RuntimeReq>({
|
||||||
|
name: '',
|
||||||
|
page: 1,
|
||||||
|
pageSize: 40,
|
||||||
|
type: 'php',
|
||||||
|
});
|
||||||
|
let timer: NodeJS.Timer | null = null;
|
||||||
|
|
||||||
|
const buttons = [
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.edit'),
|
||||||
|
click: function (row: Runtime.Runtime) {
|
||||||
|
openDetail(row);
|
||||||
|
},
|
||||||
|
disabled: function (row: Runtime.Runtime) {
|
||||||
|
return row.status === 'building';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.delete'),
|
||||||
|
click: function (row: Runtime.Runtime) {
|
||||||
|
openDelete(row);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const loading = ref(false);
|
||||||
|
const items = ref<Runtime.RuntimeDTO[]>([]);
|
||||||
|
const createRef = ref();
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
req.page = paginationConfig.currentPage;
|
||||||
|
req.pageSize = paginationConfig.pageSize;
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await SearchRuntimes(req);
|
||||||
|
items.value = res.data.items;
|
||||||
|
paginationConfig.total = res.data.total;
|
||||||
|
} catch (error) {
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const openCreate = () => {
|
||||||
|
createRef.value.acceptParams({ type: 'php', mode: 'create' });
|
||||||
|
};
|
||||||
|
|
||||||
|
const openDetail = (row: Runtime.Runtime) => {
|
||||||
|
createRef.value.acceptParams({ type: row.type, mode: 'edit', id: row.id });
|
||||||
|
};
|
||||||
|
|
||||||
|
const openDelete = async (row: Runtime.Runtime) => {
|
||||||
|
await useDeleteData(DeleteRuntime, { id: row.id }, 'commons.msg.delete');
|
||||||
|
search();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search();
|
||||||
|
timer = setInterval(() => {
|
||||||
|
search();
|
||||||
|
}, 10000 * 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearInterval(Number(timer));
|
||||||
|
timer = null;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.open-warn {
|
||||||
|
color: $primary-color;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
x
Reference in New Issue
Block a user