mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-12-04 01:39:20 +08:00
feat: 增加进程守护管理 (#1786)
增加 Supervisor 状态读取 初始化 启动 重启 设置 日志 功能 Refs https://github.com/1Panel-dev/1Panel/issues/1754 Refs https://github.com/1Panel-dev/1Panel/issues/1409 Refs https://github.com/1Panel-dev/1Panel/issues/1388 Refs https://github.com/1Panel-dev/1Panel/issues/379 Refs https://github.com/1Panel-dev/1Panel/issues/353 Refs https://github.com/1Panel-dev/1Panel/issues/331
This commit is contained in:
parent
f9c8aa0484
commit
b59ccc52ae
4
Makefile
4
Makefile
@ -29,10 +29,6 @@ build_backend_on_darwin:
|
||||
cd $(SERVER_PATH) \
|
||||
&& GOOS=linux GOARCH=amd64 $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
|
||||
build_backend_on_archlinux:
|
||||
cd $(SERVER_PATH) \
|
||||
&& GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
|
||||
build_all: build_frontend build_backend_on_linux
|
||||
|
||||
build_on_local: clean_assets build_frontend build_backend_on_darwin upx_bin
|
||||
|
@ -51,4 +51,6 @@ var (
|
||||
|
||||
runtimeService = service.NewRuntimeService()
|
||||
processService = service.NewIProcessService()
|
||||
|
||||
hostToolService = service.NewIHostToolService()
|
||||
)
|
||||
|
174
backend/app/api/v1/host_tool.go
Normal file
174
backend/app/api/v1/host_tool.go
Normal file
@ -0,0 +1,174 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags Host tool
|
||||
// @Summary Get tool
|
||||
// @Description 获取主机工具状态
|
||||
// @Accept json
|
||||
// @Param request body request.HostToolReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/tool [post]
|
||||
func (b *BaseApi) GetToolStatus(c *gin.Context) {
|
||||
var req request.HostToolReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
config, err := hostToolService.GetToolStatus(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, config)
|
||||
}
|
||||
|
||||
// @Tags Host tool
|
||||
// @Summary Create Host tool Config
|
||||
// @Description 创建主机工具配置
|
||||
// @Accept json
|
||||
// @Param request body request.HostToolCreate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/tool/create [post]
|
||||
// @x-panel-log {"bodyKeys":["type"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建 [type] 配置","formatEN":"create [type] config"}
|
||||
func (b *BaseApi) InitToolConfig(c *gin.Context) {
|
||||
var req request.HostToolCreate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := hostToolService.CreateToolConfig(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Host tool
|
||||
// @Summary Operate tool
|
||||
// @Description 操作主机工具
|
||||
// @Accept json
|
||||
// @Param request body request.HostToolReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/tool/operate [post]
|
||||
// @x-panel-log {"bodyKeys":["operate","type"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"[operate] [type] ","formatEN":"[operate] [type]"}
|
||||
func (b *BaseApi) OperateTool(c *gin.Context) {
|
||||
var req request.HostToolReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := hostToolService.OperateTool(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Host tool
|
||||
// @Summary Get tool config
|
||||
// @Description 操作主机工具配置文件
|
||||
// @Accept json
|
||||
// @Param request body request.HostToolConfig true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/tool/config [post]
|
||||
// @x-panel-log {"bodyKeys":["operate"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"[operate] 主机工具配置文件 ","formatEN":"[operate] tool config"}
|
||||
func (b *BaseApi) OperateToolConfig(c *gin.Context) {
|
||||
var req request.HostToolConfig
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
config, err := hostToolService.OperateToolConfig(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, config)
|
||||
}
|
||||
|
||||
// @Tags Host tool
|
||||
// @Summary Get tool
|
||||
// @Description 获取主机工具日志
|
||||
// @Accept json
|
||||
// @Param request body request.HostToolLogReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/tool/log [post]
|
||||
func (b *BaseApi) GetToolLog(c *gin.Context) {
|
||||
var req request.HostToolLogReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
logContent, err := hostToolService.GetToolLog(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, logContent)
|
||||
}
|
||||
|
||||
// @Tags Host tool
|
||||
// @Summary Create Supervisor process
|
||||
// @Description 操作守护进程
|
||||
// @Accept json
|
||||
// @Param request body request.SupervisorProcessConfig true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/tool/supervisor/process [post]
|
||||
// @x-panel-log {"bodyKeys":["operate"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"[operate] 守护进程 ","formatEN":"[operate] process"}
|
||||
func (b *BaseApi) OperateProcess(c *gin.Context) {
|
||||
var req request.SupervisorProcessConfig
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := hostToolService.OperateSupervisorProcess(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
34
backend/app/dto/request/host_tool.go
Normal file
34
backend/app/dto/request/host_tool.go
Normal file
@ -0,0 +1,34 @@
|
||||
package request
|
||||
|
||||
type HostToolReq struct {
|
||||
Type string `json:"type" validate:"required,oneof=supervisord"`
|
||||
Operate string `json:"operate" validate:"oneof=status restart start stop"`
|
||||
}
|
||||
|
||||
type HostToolCreate struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
SupervisorConfig
|
||||
}
|
||||
|
||||
type SupervisorConfig struct {
|
||||
ConfigPath string `json:"configPath"`
|
||||
}
|
||||
|
||||
type HostToolLogReq struct {
|
||||
Type string `json:"type" validate:"required,oneof=supervisord"`
|
||||
}
|
||||
|
||||
type HostToolConfig struct {
|
||||
Type string `json:"type" validate:"required,oneof=supervisord"`
|
||||
Operate string `json:"operate" validate:"oneof=get set"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type SupervisorProcessConfig struct {
|
||||
Name string `json:"name"`
|
||||
Operate string `json:"operate"`
|
||||
Command string `json:"command"`
|
||||
User string `json:"user"`
|
||||
Dir string `json:"dir"`
|
||||
Numprocs string `json:"numprocs"`
|
||||
}
|
22
backend/app/dto/response/host_tool.go
Normal file
22
backend/app/dto/response/host_tool.go
Normal file
@ -0,0 +1,22 @@
|
||||
package response
|
||||
|
||||
type HostToolRes struct {
|
||||
Type string `json:"type"`
|
||||
Config interface{} `json:"config"`
|
||||
}
|
||||
|
||||
type Supervisor struct {
|
||||
ConfigPath string `json:"configPath"`
|
||||
IncludeDir string `json:"includeDir"`
|
||||
LogPath string `json:"logPath"`
|
||||
IsExist bool `json:"isExist"`
|
||||
Init bool `json:"init"`
|
||||
Msg string `json:"msg"`
|
||||
Version string `json:"version"`
|
||||
Status string `json:"status"`
|
||||
CtlExist bool `json:"ctlExist"`
|
||||
}
|
||||
|
||||
type HostToolConfig struct {
|
||||
Content string `json:"content"`
|
||||
}
|
259
backend/app/service/host_tool.go
Normal file
259
backend/app/service/host_tool.go
Normal file
@ -0,0 +1,259 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ini_conf"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/systemctl"
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/ini.v1"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type HostToolService struct{}
|
||||
|
||||
type IHostToolService interface {
|
||||
GetToolStatus(req request.HostToolReq) (*response.HostToolRes, error)
|
||||
CreateToolConfig(req request.HostToolCreate) error
|
||||
OperateTool(req request.HostToolReq) error
|
||||
OperateToolConfig(req request.HostToolConfig) (*response.HostToolConfig, error)
|
||||
GetToolLog(req request.HostToolLogReq) (string, error)
|
||||
OperateSupervisorProcess(req request.SupervisorProcessConfig) error
|
||||
}
|
||||
|
||||
func NewIHostToolService() IHostToolService {
|
||||
return &HostToolService{}
|
||||
}
|
||||
|
||||
func (h *HostToolService) GetToolStatus(req request.HostToolReq) (*response.HostToolRes, error) {
|
||||
res := &response.HostToolRes{}
|
||||
res.Type = req.Type
|
||||
switch req.Type {
|
||||
case constant.Supervisord:
|
||||
exist, err := systemctl.IsExist(constant.Supervisord)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
supervisorConfig := &response.Supervisor{}
|
||||
if !exist {
|
||||
supervisorConfig.IsExist = false
|
||||
return res, nil
|
||||
}
|
||||
supervisorConfig.IsExist = true
|
||||
|
||||
versionRes, _ := cmd.Exec("supervisord -v")
|
||||
supervisorConfig.Version = strings.TrimSuffix(versionRes, "\n")
|
||||
_, ctlRrr := exec.LookPath("supervisorctl")
|
||||
supervisorConfig.CtlExist = ctlRrr == nil
|
||||
|
||||
active, err := systemctl.IsActive(constant.Supervisord)
|
||||
if err != nil {
|
||||
supervisorConfig.Status = "unhealthy"
|
||||
supervisorConfig.Msg = err.Error()
|
||||
res.Config = supervisorConfig
|
||||
return res, nil
|
||||
}
|
||||
if active {
|
||||
supervisorConfig.Status = "running"
|
||||
} else {
|
||||
supervisorConfig.Status = "stopped"
|
||||
}
|
||||
|
||||
pathSet, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorConfigPath))
|
||||
if pathSet.ID != 0 || pathSet.Value != "" {
|
||||
supervisorConfig.ConfigPath = pathSet.Value
|
||||
res.Config = supervisorConfig
|
||||
return res, nil
|
||||
}
|
||||
supervisorConfig.Init = true
|
||||
|
||||
servicePath := "/usr/lib/systemd/system/supervisord.service"
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(servicePath) {
|
||||
servicePath = "/lib/systemd/system/supervisord.service"
|
||||
}
|
||||
if fileOp.Stat(servicePath) {
|
||||
startCmd, _ := ini_conf.GetIniValue(servicePath, "Service", "ExecStart")
|
||||
if startCmd != "" {
|
||||
args := strings.Fields(startCmd)
|
||||
cIndex := -1
|
||||
for i, arg := range args {
|
||||
if arg == "-c" {
|
||||
cIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if cIndex != -1 && cIndex+1 < len(args) {
|
||||
supervisorConfig.ConfigPath = args[cIndex+1]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
configPath := "/etc/supervisord.conf"
|
||||
if !fileOp.Stat(configPath) {
|
||||
configPath = "/etc/supervisor/supervisord.conf"
|
||||
if !fileOp.Stat("configPath") {
|
||||
return nil, errors.New("ErrConfigNotFound")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.Config = supervisorConfig
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (h *HostToolService) CreateToolConfig(req request.HostToolCreate) error {
|
||||
switch req.Type {
|
||||
case constant.Supervisord:
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(req.ConfigPath) {
|
||||
return errors.New("ErrConfigNotFound")
|
||||
}
|
||||
cfg, err := ini.Load(req.ConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
service, err := cfg.GetSection("include")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
targetKey, err := service.GetKey("files")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if targetKey != nil {
|
||||
_, err = service.NewKey(";files", targetKey.Value())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
supervisorDir := path.Join(global.CONF.System.BaseDir, "1panel", "tools", "supervisord")
|
||||
includeDir := path.Join(supervisorDir, "supervisor.d")
|
||||
if !fileOp.Stat(includeDir) {
|
||||
if err = fileOp.CreateDir(includeDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
logDir := path.Join(supervisorDir, "log")
|
||||
if !fileOp.Stat(logDir) {
|
||||
if err = fileOp.CreateDir(logDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
includePath := path.Join(includeDir, "*.ini")
|
||||
targetKey.SetValue(includePath)
|
||||
if err = cfg.SaveTo(req.ConfigPath); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = settingRepo.Create(constant.SupervisorConfigPath, req.ConfigPath); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
if err = systemctl.Restart(constant.Supervisord); err != nil {
|
||||
global.LOG.Errorf("[init] restart supervisord failed err %s", err.Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HostToolService) OperateTool(req request.HostToolReq) error {
|
||||
return systemctl.Operate(req.Operate, req.Type)
|
||||
}
|
||||
|
||||
func (h *HostToolService) OperateToolConfig(req request.HostToolConfig) (*response.HostToolConfig, error) {
|
||||
fileOp := files.NewFileOp()
|
||||
res := &response.HostToolConfig{}
|
||||
configPath := ""
|
||||
switch req.Type {
|
||||
case constant.Supervisord:
|
||||
pathSet, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorConfigPath))
|
||||
if pathSet.ID != 0 || pathSet.Value != "" {
|
||||
configPath = pathSet.Value
|
||||
}
|
||||
}
|
||||
configPath = "/etc/supervisord.conf"
|
||||
switch req.Operate {
|
||||
case "get":
|
||||
content, err := fileOp.GetContent(configPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.Content = string(content)
|
||||
case "set":
|
||||
file, err := fileOp.OpenFile(configPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
oldContent, err := fileOp.GetContent(configPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fileInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = fileOp.WriteFile(configPath, strings.NewReader(req.Content), fileInfo.Mode()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = systemctl.Restart(req.Type); err != nil {
|
||||
_ = fileOp.WriteFile(configPath, bytes.NewReader(oldContent), fileInfo.Mode())
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (h *HostToolService) GetToolLog(req request.HostToolLogReq) (string, error) {
|
||||
fileOp := files.NewFileOp()
|
||||
logfilePath := ""
|
||||
switch req.Type {
|
||||
case constant.Supervisord:
|
||||
configPath := "/etc/supervisord.conf"
|
||||
pathSet, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorConfigPath))
|
||||
if pathSet.ID != 0 || pathSet.Value != "" {
|
||||
configPath = pathSet.Value
|
||||
}
|
||||
logfilePath, _ = ini_conf.GetIniValue(configPath, "supervisord", "logfile")
|
||||
}
|
||||
oldContent, err := fileOp.GetContent(logfilePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(oldContent), nil
|
||||
}
|
||||
|
||||
func (h *HostToolService) OperateSupervisorProcess(req request.SupervisorProcessConfig) error {
|
||||
configFile := ini.Empty()
|
||||
supervisordDir := path.Join(global.CONF.System.BaseDir, "1panel", "tools", "supervisord")
|
||||
logDir := path.Join(supervisordDir, "log")
|
||||
includeDir := path.Join(supervisordDir, "supervisor.d")
|
||||
|
||||
section, err := configFile.NewSection(fmt.Sprintf("program:%s", req.Name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = section.NewKey("command", req.Command)
|
||||
_, _ = section.NewKey("directory", req.Dir)
|
||||
_, _ = section.NewKey("autorestart", "true")
|
||||
_, _ = section.NewKey("startsecs", "3")
|
||||
_, _ = section.NewKey("stdout_logfile", path.Join(logDir, fmt.Sprintf("%s.out.log", req.Name)))
|
||||
_, _ = section.NewKey("stderr_logfile", path.Join(logDir, fmt.Sprintf("%s.err.log", req.Name)))
|
||||
_, _ = section.NewKey("stdout_logfile_maxbytes", "2MB")
|
||||
_, _ = section.NewKey("stderr_logfile_maxbytes", "2MB")
|
||||
_, _ = section.NewKey("user", req.User)
|
||||
_, _ = section.NewKey("priority", "999")
|
||||
_, _ = section.NewKey("numprocs", req.Numprocs)
|
||||
_, _ = section.NewKey("process_name", "%(program_name)s_%(process_num)02d")
|
||||
|
||||
return configFile.SaveTo(path.Join(includeDir, fmt.Sprintf("%s.ini", req.Name)))
|
||||
}
|
6
backend/constant/host_tool.go
Normal file
6
backend/constant/host_tool.go
Normal file
@ -0,0 +1,6 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
Supervisord = "supervisord"
|
||||
SupervisorConfigPath = "SupervisorConfigPath"
|
||||
)
|
@ -97,5 +97,7 @@ ErrDelWithWebsite: "The operating environment has been associated with a website
|
||||
|
||||
#setting
|
||||
ErrBackupInUsed: "The backup account is already being used in a cronjob and cannot be deleted."
|
||||
|
||||
ErrOSSConn: "Unable to successfully request the latest version. Please check if the server can connect to the external network environment."
|
||||
|
||||
#tool
|
||||
ErrConfigNotFound: "Configuration file does not exist"
|
@ -97,5 +97,7 @@ ErrDelWithWebsite: "運行環境已經關聯網站,無法刪除"
|
||||
|
||||
#setting
|
||||
ErrBackupInUsed: "該備份帳號已在計劃任務中使用,無法刪除"
|
||||
|
||||
ErrOSSConn: "無法成功請求最新版本,請檢查伺服器是否能夠連接到外部網絡環境。"
|
||||
|
||||
#tool
|
||||
ErrConfigNotFound: "配置文件不存在"
|
||||
|
@ -97,5 +97,7 @@ ErrDelWithWebsite: "运行环境已经关联网站,无法删除"
|
||||
|
||||
#setting
|
||||
ErrBackupInUsed: "该备份账号已在计划任务中使用,无法删除"
|
||||
|
||||
ErrOSSConn: "无法成功请求最新版本,请检查服务器是否能够连接到外部网络环境。"
|
||||
|
||||
#tool
|
||||
ErrConfigNotFound: "配置文件不存在"
|
||||
|
@ -47,5 +47,12 @@ func (s *HostRouter) InitHostRouter(Router *gin.RouterGroup) {
|
||||
hostRouter.POST("/command/del", baseApi.DeleteCommand)
|
||||
hostRouter.POST("/command/search", baseApi.SearchCommand)
|
||||
hostRouter.POST("/command/update", baseApi.UpdateCommand)
|
||||
|
||||
hostRouter.POST("/tool", baseApi.GetToolStatus)
|
||||
hostRouter.POST("/tool/init", baseApi.InitToolConfig)
|
||||
hostRouter.POST("/tool/operate", baseApi.OperateTool)
|
||||
hostRouter.POST("/tool/config", baseApi.OperateToolConfig)
|
||||
hostRouter.POST("/tool/log", baseApi.GetToolLog)
|
||||
hostRouter.POST("/tool/supervisor/process", baseApi.OperateProcess)
|
||||
}
|
||||
}
|
||||
|
36
backend/utils/ini_conf/ini.go
Normal file
36
backend/utils/ini_conf/ini.go
Normal file
@ -0,0 +1,36 @@
|
||||
package ini_conf
|
||||
|
||||
import "gopkg.in/ini.v1"
|
||||
|
||||
func GetIniValue(filePath, Group, Key string) (string, error) {
|
||||
cfg, err := ini.Load(filePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
service, err := cfg.GetSection(Group)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
startKey, err := service.GetKey(Key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return startKey.Value(), nil
|
||||
}
|
||||
|
||||
func SetIniValue(filePath, Group, Key, value string) error {
|
||||
cfg, err := ini.Load(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
service, err := cfg.GetSection(Group)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
targetKey := service.Key(Key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
targetKey.SetValue(value)
|
||||
return cfg.SaveTo(filePath)
|
||||
}
|
62
backend/utils/systemctl/systemctl.go
Normal file
62
backend/utils/systemctl/systemctl.go
Normal file
@ -0,0 +1,62 @@
|
||||
package systemctl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func RunSystemCtl(args ...string) (string, error) {
|
||||
cmd := exec.Command("systemctl", args...)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return string(output), fmt.Errorf("failed to run command: %w", err)
|
||||
}
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
func IsActive(serviceName string) (bool, error) {
|
||||
out, err := RunSystemCtl("is-active", serviceName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return out == "active\n", nil
|
||||
}
|
||||
|
||||
func IsExist(serviceName string) (bool, error) {
|
||||
out, err := RunSystemCtl("list-unit-files")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return strings.Contains(out, serviceName+".service"), nil
|
||||
}
|
||||
|
||||
func handlerErr(out string, err error) error {
|
||||
if err != nil {
|
||||
if out != "" {
|
||||
return errors.New(out)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Restart(serviceName string) error {
|
||||
out, err := RunSystemCtl("restart", serviceName)
|
||||
return handlerErr(out, err)
|
||||
}
|
||||
func Start(serviceName string) error {
|
||||
out, err := RunSystemCtl("start", serviceName)
|
||||
return handlerErr(out, err)
|
||||
}
|
||||
|
||||
func Stop(serviceName string) error {
|
||||
out, err := RunSystemCtl("stop", serviceName)
|
||||
return handlerErr(out, err)
|
||||
}
|
||||
|
||||
func Operate(operate, serviceName string) error {
|
||||
out, err := RunSystemCtl(operate, serviceName)
|
||||
return handlerErr(out, err)
|
||||
}
|
@ -3910,6 +3910,21 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/databases/load/:from": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "从服务器获取",
|
||||
"tags": [
|
||||
"Database Mysql"
|
||||
],
|
||||
"summary": "Load mysql database from remote",
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/databases/options": {
|
||||
"get": {
|
||||
"security": [
|
||||
@ -4274,6 +4289,28 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/databases/remote/:name": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取远程数据库",
|
||||
"tags": [
|
||||
"Database"
|
||||
],
|
||||
"summary": "Get remote databases",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.RemoteDBInfo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/databases/remote/del": {
|
||||
"post": {
|
||||
"security": [
|
||||
@ -4332,7 +4369,7 @@ const docTemplate = `{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取快速命令列表",
|
||||
"description": "获取远程数据库列表",
|
||||
"tags": [
|
||||
"Database"
|
||||
],
|
||||
@ -5923,6 +5960,241 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/host/tool": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取主机工具状态",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Host tool"
|
||||
],
|
||||
"summary": "Get tool",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.HostToolReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/host/tool/config": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "操作主机工具配置文件",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Host tool"
|
||||
],
|
||||
"summary": "Get tool config",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.HostToolConfig"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [],
|
||||
"bodyKeys": [
|
||||
"operate"
|
||||
],
|
||||
"formatEN": "[operate] tool config",
|
||||
"formatZH": "[operate] 主机工具配置文件 ",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/host/tool/create": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "创建主机工具配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Host tool"
|
||||
],
|
||||
"summary": "Create Host tool Config",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.HostToolCreate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [],
|
||||
"bodyKeys": [
|
||||
"type"
|
||||
],
|
||||
"formatEN": "create [type] config",
|
||||
"formatZH": "创建 [type] 配置",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/host/tool/log": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取主机工具日志",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Host tool"
|
||||
],
|
||||
"summary": "Get tool",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.HostToolLogReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/host/tool/operate": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "操作主机工具",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Host tool"
|
||||
],
|
||||
"summary": "Operate tool",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.HostToolReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [],
|
||||
"bodyKeys": [
|
||||
"operate",
|
||||
"type"
|
||||
],
|
||||
"formatEN": "[operate] [type]",
|
||||
"formatZH": "[operate] [type] ",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/host/tool/supervisor/process": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "操作守护进程",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Host tool"
|
||||
],
|
||||
"summary": "Create Supervisor process",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.SupervisorProcessConfig"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [],
|
||||
"bodyKeys": [
|
||||
"operate"
|
||||
],
|
||||
"formatEN": "[operate] process",
|
||||
"formatZH": "[operate] 守护进程 ",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/hosts": {
|
||||
"post": {
|
||||
"security": [
|
||||
@ -13317,6 +13589,41 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.RemoteDBInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {
|
||||
"type": "string"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"from": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"type": "integer"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.RemoteDBOption": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -14447,6 +14754,9 @@ const docTemplate = `{
|
||||
"operate"
|
||||
],
|
||||
"properties": {
|
||||
"backup": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"backupId": {
|
||||
"type": "integer"
|
||||
},
|
||||
@ -14856,6 +15166,81 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.HostToolConfig": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"operate": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"get",
|
||||
"set"
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"supervisord"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.HostToolCreate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"configPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.HostToolLogReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"supervisord"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.HostToolReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"operate": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"status",
|
||||
"restart",
|
||||
"start",
|
||||
"stop"
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"supervisord"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.NewAppInstall": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -15288,6 +15673,29 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.SupervisorProcessConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"command": {
|
||||
"type": "string"
|
||||
},
|
||||
"dir": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"numprocs": {
|
||||
"type": "string"
|
||||
},
|
||||
"operate": {
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteAcmeAccountCreate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
@ -3903,6 +3903,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/databases/load/:from": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "从服务器获取",
|
||||
"tags": [
|
||||
"Database Mysql"
|
||||
],
|
||||
"summary": "Load mysql database from remote",
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/databases/options": {
|
||||
"get": {
|
||||
"security": [
|
||||
@ -4267,6 +4282,28 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/databases/remote/:name": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取远程数据库",
|
||||
"tags": [
|
||||
"Database"
|
||||
],
|
||||
"summary": "Get remote databases",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.RemoteDBInfo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/databases/remote/del": {
|
||||
"post": {
|
||||
"security": [
|
||||
@ -4325,7 +4362,7 @@
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取快速命令列表",
|
||||
"description": "获取远程数据库列表",
|
||||
"tags": [
|
||||
"Database"
|
||||
],
|
||||
@ -5916,6 +5953,241 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/host/tool": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取主机工具状态",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Host tool"
|
||||
],
|
||||
"summary": "Get tool",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.HostToolReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/host/tool/config": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "操作主机工具配置文件",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Host tool"
|
||||
],
|
||||
"summary": "Get tool config",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.HostToolConfig"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [],
|
||||
"bodyKeys": [
|
||||
"operate"
|
||||
],
|
||||
"formatEN": "[operate] tool config",
|
||||
"formatZH": "[operate] 主机工具配置文件 ",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/host/tool/create": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "创建主机工具配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Host tool"
|
||||
],
|
||||
"summary": "Create Host tool Config",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.HostToolCreate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [],
|
||||
"bodyKeys": [
|
||||
"type"
|
||||
],
|
||||
"formatEN": "create [type] config",
|
||||
"formatZH": "创建 [type] 配置",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/host/tool/log": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取主机工具日志",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Host tool"
|
||||
],
|
||||
"summary": "Get tool",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.HostToolLogReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/host/tool/operate": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "操作主机工具",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Host tool"
|
||||
],
|
||||
"summary": "Operate tool",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.HostToolReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [],
|
||||
"bodyKeys": [
|
||||
"operate",
|
||||
"type"
|
||||
],
|
||||
"formatEN": "[operate] [type]",
|
||||
"formatZH": "[operate] [type] ",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/host/tool/supervisor/process": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "操作守护进程",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Host tool"
|
||||
],
|
||||
"summary": "Create Supervisor process",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.SupervisorProcessConfig"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [],
|
||||
"bodyKeys": [
|
||||
"operate"
|
||||
],
|
||||
"formatEN": "[operate] process",
|
||||
"formatZH": "[operate] 守护进程 ",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/hosts": {
|
||||
"post": {
|
||||
"security": [
|
||||
@ -13310,6 +13582,41 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.RemoteDBInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {
|
||||
"type": "string"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"from": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"type": "integer"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.RemoteDBOption": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -14440,6 +14747,9 @@
|
||||
"operate"
|
||||
],
|
||||
"properties": {
|
||||
"backup": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"backupId": {
|
||||
"type": "integer"
|
||||
},
|
||||
@ -14849,6 +15159,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.HostToolConfig": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"operate": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"get",
|
||||
"set"
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"supervisord"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.HostToolCreate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"configPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.HostToolLogReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"supervisord"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.HostToolReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"operate": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"status",
|
||||
"restart",
|
||||
"start",
|
||||
"stop"
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"supervisord"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.NewAppInstall": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -15281,6 +15666,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.SupervisorProcessConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"command": {
|
||||
"type": "string"
|
||||
},
|
||||
"dir": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"numprocs": {
|
||||
"type": "string"
|
||||
},
|
||||
"operate": {
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteAcmeAccountCreate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
@ -1574,6 +1574,29 @@ definitions:
|
||||
- username
|
||||
- version
|
||||
type: object
|
||||
dto.RemoteDBInfo:
|
||||
properties:
|
||||
address:
|
||||
type: string
|
||||
createdAt:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
from:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
port:
|
||||
type: integer
|
||||
username:
|
||||
type: string
|
||||
version:
|
||||
type: string
|
||||
type: object
|
||||
dto.RemoteDBOption:
|
||||
properties:
|
||||
address:
|
||||
@ -2320,6 +2343,8 @@ definitions:
|
||||
type: object
|
||||
request.AppInstalledOperate:
|
||||
properties:
|
||||
backup:
|
||||
type: boolean
|
||||
backupId:
|
||||
type: integer
|
||||
deleteBackup:
|
||||
@ -2597,6 +2622,56 @@ definitions:
|
||||
- path
|
||||
- url
|
||||
type: object
|
||||
request.HostToolConfig:
|
||||
properties:
|
||||
content:
|
||||
type: string
|
||||
operate:
|
||||
enum:
|
||||
- get
|
||||
- set
|
||||
type: string
|
||||
type:
|
||||
enum:
|
||||
- supervisord
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
type: object
|
||||
request.HostToolCreate:
|
||||
properties:
|
||||
configPath:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
type: object
|
||||
request.HostToolLogReq:
|
||||
properties:
|
||||
type:
|
||||
enum:
|
||||
- supervisord
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
type: object
|
||||
request.HostToolReq:
|
||||
properties:
|
||||
operate:
|
||||
enum:
|
||||
- status
|
||||
- restart
|
||||
- start
|
||||
- stop
|
||||
type: string
|
||||
type:
|
||||
enum:
|
||||
- supervisord
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
type: object
|
||||
request.NewAppInstall:
|
||||
properties:
|
||||
advanced:
|
||||
@ -2888,6 +2963,21 @@ definitions:
|
||||
- pageSize
|
||||
- path
|
||||
type: object
|
||||
request.SupervisorProcessConfig:
|
||||
properties:
|
||||
command:
|
||||
type: string
|
||||
dir:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
numprocs:
|
||||
type: string
|
||||
operate:
|
||||
type: string
|
||||
user:
|
||||
type: string
|
||||
type: object
|
||||
request.WebsiteAcmeAccountCreate:
|
||||
properties:
|
||||
email:
|
||||
@ -6215,6 +6305,15 @@ paths:
|
||||
formatEN: The description of the mysql database [name] is modified => [description]
|
||||
formatZH: mysql 数据库 [name] 描述信息修改 [description]
|
||||
paramKeys: []
|
||||
/databases/load/:from:
|
||||
get:
|
||||
description: 从服务器获取
|
||||
responses: {}
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Load mysql database from remote
|
||||
tags:
|
||||
- Database Mysql
|
||||
/databases/options:
|
||||
get:
|
||||
consumes:
|
||||
@ -6444,6 +6543,19 @@ paths:
|
||||
formatEN: create remote database [name][type]
|
||||
formatZH: 创建远程数据库 [name][type]
|
||||
paramKeys: []
|
||||
/databases/remote/:name:
|
||||
get:
|
||||
description: 获取远程数据库
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/dto.RemoteDBInfo'
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Get remote databases
|
||||
tags:
|
||||
- Database
|
||||
/databases/remote/del:
|
||||
post:
|
||||
consumes:
|
||||
@ -6479,7 +6591,7 @@ paths:
|
||||
paramKeys: []
|
||||
/databases/remote/list/:type:
|
||||
get:
|
||||
description: 获取快速命令列表
|
||||
description: 获取远程数据库列表
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
@ -7495,6 +7607,155 @@ paths:
|
||||
formatEN: update SSH setting [key] => [value]
|
||||
formatZH: 修改 SSH 配置 [key] => [value]
|
||||
paramKeys: []
|
||||
/host/tool:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 获取主机工具状态
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.HostToolReq'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Get tool
|
||||
tags:
|
||||
- Host tool
|
||||
/host/tool/config:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 操作主机工具配置文件
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.HostToolConfig'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Get tool config
|
||||
tags:
|
||||
- Host tool
|
||||
x-panel-log:
|
||||
BeforeFuntions: []
|
||||
bodyKeys:
|
||||
- operate
|
||||
formatEN: '[operate] tool config'
|
||||
formatZH: '[operate] 主机工具配置文件 '
|
||||
paramKeys: []
|
||||
/host/tool/create:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 创建主机工具配置
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.HostToolCreate'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Create Host tool Config
|
||||
tags:
|
||||
- Host tool
|
||||
x-panel-log:
|
||||
BeforeFuntions: []
|
||||
bodyKeys:
|
||||
- type
|
||||
formatEN: create [type] config
|
||||
formatZH: 创建 [type] 配置
|
||||
paramKeys: []
|
||||
/host/tool/log:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 获取主机工具日志
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.HostToolLogReq'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Get tool
|
||||
tags:
|
||||
- Host tool
|
||||
/host/tool/operate:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 操作主机工具
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.HostToolReq'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Operate tool
|
||||
tags:
|
||||
- Host tool
|
||||
x-panel-log:
|
||||
BeforeFuntions: []
|
||||
bodyKeys:
|
||||
- operate
|
||||
- type
|
||||
formatEN: '[operate] [type]'
|
||||
formatZH: '[operate] [type] '
|
||||
paramKeys: []
|
||||
/host/tool/supervisor/process:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 操作守护进程
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.SupervisorProcessConfig'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Create Supervisor process
|
||||
tags:
|
||||
- Host tool
|
||||
x-panel-log:
|
||||
BeforeFuntions: []
|
||||
bodyKeys:
|
||||
- operate
|
||||
formatEN: '[operate] process'
|
||||
formatZH: '[operate] 守护进程 '
|
||||
paramKeys: []
|
||||
/hosts:
|
||||
post:
|
||||
consumes:
|
||||
|
43
frontend/src/api/interface/host-tool.ts
Normal file
43
frontend/src/api/interface/host-tool.ts
Normal file
@ -0,0 +1,43 @@
|
||||
export namespace HostTool {
|
||||
export interface HostTool {
|
||||
type: string;
|
||||
config: {};
|
||||
}
|
||||
|
||||
export interface Supersivor extends HostTool {
|
||||
configPath: string;
|
||||
includeDir: string;
|
||||
logPath: string;
|
||||
isExist: boolean;
|
||||
init: boolean;
|
||||
msg: string;
|
||||
version: string;
|
||||
status: string;
|
||||
ctlExist: boolean;
|
||||
}
|
||||
|
||||
export interface SupersivorConfig {
|
||||
type: string;
|
||||
operate: string;
|
||||
content?: string;
|
||||
}
|
||||
|
||||
export interface SupersivorConfigRes {
|
||||
type: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface SupersivorInit {
|
||||
type: string;
|
||||
configPath: string;
|
||||
}
|
||||
|
||||
export interface SupersivorProcess {
|
||||
operate: string;
|
||||
name: string;
|
||||
command: string;
|
||||
user: string;
|
||||
dir: string;
|
||||
numprocs: string;
|
||||
}
|
||||
}
|
26
frontend/src/api/modules/host-tool.ts
Normal file
26
frontend/src/api/modules/host-tool.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import http from '@/api';
|
||||
import { HostTool } from '../interface/host-tool';
|
||||
|
||||
export const GetSupervisorStatus = () => {
|
||||
return http.post<HostTool.HostTool>(`/hosts/tool`, { type: 'supervisord', operate: 'status' });
|
||||
};
|
||||
|
||||
export const OperateSupervisor = (operate: string) => {
|
||||
return http.post<any>(`/hosts/tool/operate`, { type: 'supervisord', operate: operate });
|
||||
};
|
||||
|
||||
export const OperateSupervisorConfig = (req: HostTool.SupersivorConfig) => {
|
||||
return http.post<HostTool.SupersivorConfigRes>(`/hosts/tool/config`, req);
|
||||
};
|
||||
|
||||
export const GetSupervisorLog = () => {
|
||||
return http.post<any>(`/hosts/tool/log`, { type: 'supervisord' });
|
||||
};
|
||||
|
||||
export const InitSupervisor = (req: HostTool.SupersivorInit) => {
|
||||
return http.post<any>(`/hosts/tool/init`, req);
|
||||
};
|
||||
|
||||
export const OperateSupervisorProcess = (req: HostTool.SupersivorProcess) => {
|
||||
return http.post<any>(`/hosts/tool/supervisor/process`, req);
|
||||
};
|
@ -254,6 +254,7 @@ const message = {
|
||||
processManage: 'Process',
|
||||
process: 'Process',
|
||||
network: 'Network',
|
||||
supervisor: 'Supervisor',
|
||||
},
|
||||
home: {
|
||||
overview: 'Overview',
|
||||
@ -1651,6 +1652,22 @@ const message = {
|
||||
stopProcessWarn: 'Are you sure you want to end this process (PID:{0})? ',
|
||||
processName: 'ProcessName',
|
||||
},
|
||||
tool: {
|
||||
supervisor: {
|
||||
notSupport: 'Supervisor is not detected, please refer to the official document for installation',
|
||||
list: 'Daemon process',
|
||||
config: 'Supervisor configuration',
|
||||
primaryConfig: 'Main configuration file location',
|
||||
notSupportCrl: 'The supervisorctl is not detected, please refer to the official document for installation',
|
||||
user: 'start user',
|
||||
command: 'start command',
|
||||
dir: 'run directory',
|
||||
numprocs: 'Number of processes',
|
||||
initWarn:
|
||||
'Because it is not compatible with the original configuration, initializing Supervisor will modify the files parameter of the configuration file, causing all existing processes to stop, please confirm the risk in advance. The modified process configuration folder is in <1Panel installation directory>/1panel/tools/supervisord/supervisor.d',
|
||||
operatorHelper: 'Operation {0} will be performed on Supervisor, continue? ',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
|
@ -251,6 +251,7 @@ const message = {
|
||||
processManage: '進程管理',
|
||||
process: '進程',
|
||||
network: '網絡',
|
||||
supervisor: '進程守護',
|
||||
},
|
||||
home: {
|
||||
overview: '概覽',
|
||||
@ -1568,6 +1569,22 @@ const message = {
|
||||
stopProcessWarn: '是否確定結束此進程 (PID:{0})?',
|
||||
processName: '進程名稱',
|
||||
},
|
||||
tool: {
|
||||
supervisor: {
|
||||
notSupport: '未檢測到 Supervisor,請參考官方文檔進行安裝',
|
||||
list: '守護進程',
|
||||
config: 'Supervisor 配置',
|
||||
primaryConfig: '主配置文件位置',
|
||||
notSupportCrl: '未檢測到 supervisorctl,請參考官方文檔進行安裝',
|
||||
user: '啟動用戶',
|
||||
command: '啟動命令',
|
||||
dir: '運行目錄',
|
||||
numprocs: '進程數量',
|
||||
initWarn:
|
||||
'由於無法兼容原有配置,初始化 Supervisor 會修改配置文件的 files 參數,導致已有的進程全部停止,請提前確認風險。修改後的進程配置文件夾在 <1Panel安裝目錄>/1panel/tools/supervisord/supervisor.d 中',
|
||||
operatorHelper: '將對 Supervisor 進行 {0} 操作,是否繼續? ',
|
||||
},
|
||||
},
|
||||
};
|
||||
export default {
|
||||
...fit2cloudTwLocale,
|
||||
|
@ -251,6 +251,7 @@ const message = {
|
||||
processManage: '进程管理',
|
||||
process: '进程',
|
||||
network: '网络',
|
||||
supervisor: '进程守护',
|
||||
},
|
||||
home: {
|
||||
overview: '概览',
|
||||
@ -1570,6 +1571,22 @@ const message = {
|
||||
stopProcessWarn: '是否确定结束此进程 (PID:{0})?',
|
||||
processName: '进程名称',
|
||||
},
|
||||
tool: {
|
||||
supervisor: {
|
||||
notSupport: '未检测到 Supervisor,请参考官方文档进行安装',
|
||||
list: '守护进程',
|
||||
config: 'Supervisor 配置',
|
||||
primaryConfig: '主配置文件位置',
|
||||
notSupportCrl: '未检测到 supervisorctl,请参考官方文档进行安装',
|
||||
user: '启动用户',
|
||||
command: '启动命令',
|
||||
dir: '运行目录',
|
||||
numprocs: '进程数量',
|
||||
initWarn:
|
||||
'由于无法兼容原有配置,初始化 Supervisor 会修改配置文件的 files 参数,导致已有的进程全部停止,请提前确认风险。修改后的进程配置文件夹在 <1Panel安装目录>/1panel/tools/supervisord/supervisor.d 中',
|
||||
operatorHelper: '将对 Supervisor 进行 {0} 操作,是否继续?',
|
||||
},
|
||||
},
|
||||
};
|
||||
export default {
|
||||
...fit2cloudZhLocale,
|
||||
|
@ -90,6 +90,17 @@ const hostRouter = {
|
||||
requiresAuth: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/hosts/tool/supersivor',
|
||||
name: 'Supervisor',
|
||||
component: () => import('@/views/host/tool/supervisor/index.vue'),
|
||||
meta: {
|
||||
title: 'menu.supervisor',
|
||||
activeMenu: '/hosts/tool/supersivor',
|
||||
keepAlive: true,
|
||||
requiresAuth: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/hosts/ssh/ssh',
|
||||
name: 'SSH',
|
||||
|
20
frontend/src/views/host/tool/index.vue
Normal file
20
frontend/src/views/host/tool/index.vue
Normal file
@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<div>
|
||||
<RouterButton :buttons="buttons" />
|
||||
<LayoutContent>
|
||||
<router-view></router-view>
|
||||
</LayoutContent>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import i18n from '@/lang';
|
||||
import RouterButton from '@/components/router-button/index.vue';
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('menu.supervisor'),
|
||||
path: '/hosts/tool/supervisor',
|
||||
},
|
||||
];
|
||||
</script>
|
28
frontend/src/views/host/tool/supervisor/config/index.vue
Normal file
28
frontend/src/views/host/tool/supervisor/config/index.vue
Normal file
@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<LayoutContent :title="$t('tool.supervisor.config')" :reload="true">
|
||||
<template #buttons>
|
||||
<el-button type="primary" :plain="activeName !== '1'" @click="changeTab('1')">
|
||||
{{ $t('nginx.configResource') }}
|
||||
</el-button>
|
||||
<el-button type="primary" :plain="activeName !== '2'" @click="changeTab('2')">
|
||||
{{ $t('website.log') }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #main>
|
||||
<Source v-if="activeName === '1'"></Source>
|
||||
<Log v-if="activeName === '2'"></Log>
|
||||
</template>
|
||||
</LayoutContent>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import Source from './source/index.vue';
|
||||
import Log from './log/index.vue';
|
||||
|
||||
const activeName = ref('1');
|
||||
|
||||
const changeTab = (index: string) => {
|
||||
activeName.value = index;
|
||||
};
|
||||
</script>
|
40
frontend/src/views/host/tool/supervisor/config/log/index.vue
Normal file
40
frontend/src/views/host/tool/supervisor/config/log/index.vue
Normal file
@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div v-loading="loading">
|
||||
<codemirror
|
||||
:autofocus="true"
|
||||
:placeholder="$t('commons.msg.noneData')"
|
||||
:indent-with-tab="true"
|
||||
:tabSize="4"
|
||||
style="height: calc(100vh - 375px)"
|
||||
:lineWrapping="true"
|
||||
:matchBrackets="true"
|
||||
theme="cobalt"
|
||||
:styleActiveLine="true"
|
||||
:extensions="extensions"
|
||||
v-model="content"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { Codemirror } from 'vue-codemirror';
|
||||
import { GetSupervisorLog } from '@/api/modules/host-tool';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
|
||||
const extensions = [javascript(), oneDark];
|
||||
|
||||
let content = ref('');
|
||||
let loading = ref(false);
|
||||
|
||||
const getConfig = async () => {
|
||||
const res = await GetSupervisorLog();
|
||||
content.value = res.data;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getConfig();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div v-loading="loading">
|
||||
<codemirror
|
||||
:autofocus="true"
|
||||
:placeholder="$t('commons.msg.noneData')"
|
||||
:indent-with-tab="true"
|
||||
:tabSize="4"
|
||||
style="height: calc(100vh - 375px)"
|
||||
:lineWrapping="true"
|
||||
:matchBrackets="true"
|
||||
theme="cobalt"
|
||||
:styleActiveLine="true"
|
||||
:extensions="extensions"
|
||||
:mode="'text/x-ini'"
|
||||
v-model="content"
|
||||
/>
|
||||
<div style="margin-top: 10px">
|
||||
<el-button type="primary" @click="submit()" :disabled="loading">
|
||||
{{ $t('commons.button.save') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { Codemirror } from 'vue-codemirror';
|
||||
import { StreamLanguage } from '@codemirror/language';
|
||||
import { properties } from '@codemirror/legacy-modes/mode/properties';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { OperateSupervisorConfig } from '@/api/modules/host-tool';
|
||||
|
||||
const extensions = [StreamLanguage.define(properties), oneDark];
|
||||
|
||||
let data = ref();
|
||||
let content = ref('');
|
||||
let loading = ref(false);
|
||||
|
||||
const submit = () => {
|
||||
loading.value = true;
|
||||
OperateSupervisorConfig({ type: 'supervisord', operate: 'set', content: content.value })
|
||||
.then(() => {
|
||||
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
|
||||
getConfig();
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const getConfig = async () => {
|
||||
const res = await OperateSupervisorConfig({ type: 'supervisord', operate: 'get' });
|
||||
data.value = res.data;
|
||||
content.value = data.value.content;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getConfig();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
108
frontend/src/views/host/tool/supervisor/create/index.vue
Normal file
108
frontend/src/views/host/tool/supervisor/create/index.vue
Normal file
@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<el-drawer :close-on-click-modal="false" v-model="open" size="30%">
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('commons.button.create')" :back="handleClose" />
|
||||
</template>
|
||||
<el-row v-loading="loading">
|
||||
<el-col :span="22" :offset="1">
|
||||
<el-form ref="processForm" label-position="top" :model="process" label-width="100px" :rules="rules">
|
||||
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||
<el-input v-model.trim="process.name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('tool.supervisor.user')" prop="user">
|
||||
<el-input v-model.trim="process.user"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('tool.supervisor.dir')" prop="dir">
|
||||
<el-input v-model.trim="process.dir">
|
||||
<template #prepend><FileList @choose="getPath" :dir="true"></FileList></template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('tool.supervisor.command')" prop="command">
|
||||
<el-input v-model.trim="process.command"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('tool.supervisor.numprocs')" prop="numprocs">
|
||||
<el-input v-model.trim="process.numprocs"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submit(processForm)" :disabled="loading">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { OperateSupervisorProcess } from '@/api/modules/host-tool';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import FileList from '@/components/file-list/index.vue';
|
||||
import i18n from '@/lang';
|
||||
import { FormInstance } from 'element-plus';
|
||||
import { ref } from 'vue';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
|
||||
const open = ref(false);
|
||||
const loading = ref(false);
|
||||
const processForm = ref<FormInstance>();
|
||||
const rules = ref({
|
||||
name: [Rules.requiredInput],
|
||||
dir: [Rules.requiredInput],
|
||||
command: [Rules.requiredInput],
|
||||
user: [Rules.requiredInput],
|
||||
numprocs: [Rules.requiredInput],
|
||||
});
|
||||
const process = ref({
|
||||
operate: 'create',
|
||||
name: '',
|
||||
command: '',
|
||||
user: '',
|
||||
dir: '',
|
||||
numprocs: '1',
|
||||
});
|
||||
|
||||
const em = defineEmits(['close']);
|
||||
const handleClose = () => {
|
||||
open.value = false;
|
||||
resetForm();
|
||||
em('close', open);
|
||||
};
|
||||
|
||||
const getPath = (path: string) => {
|
||||
process.value.dir = path;
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
processForm.value?.resetFields();
|
||||
};
|
||||
|
||||
const acceptParams = () => {
|
||||
open.value = true;
|
||||
};
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid) => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
OperateSupervisorProcess(process.value)
|
||||
.then(() => {
|
||||
open.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
48
frontend/src/views/host/tool/supervisor/index.vue
Normal file
48
frontend/src/views/host/tool/supervisor/index.vue
Normal file
@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div>
|
||||
<ToolRouter />
|
||||
<LayoutContent :title="$t('tool.supervisor.list')" v-loading="loading">
|
||||
<template #app>
|
||||
<SuperVisorStatus @setting="setting" v-model:loading="loading" @is-exist="isExist" />
|
||||
</template>
|
||||
<template v-if="isExistSuperVisor && !setSuperVisor" #toolbar>
|
||||
<el-button type="primary" @click="openCreate">
|
||||
{{ $t('commons.button.create') + $t('tool.supervisor.list') }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #main v-if="isExistSuperVisor && !setSuperVisor">
|
||||
<ComplexTable></ComplexTable>
|
||||
</template>
|
||||
<ConfigSuperVisor v-if="setSuperVisor" />
|
||||
</LayoutContent>
|
||||
<Create ref="createRef"></Create>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ToolRouter from '@/views/host/tool/index.vue';
|
||||
import SuperVisorStatus from './status/index.vue';
|
||||
import { ref } from '@vue/runtime-core';
|
||||
import ConfigSuperVisor from './config/index.vue';
|
||||
import { onMounted } from 'vue';
|
||||
import Create from './create/index.vue';
|
||||
|
||||
const loading = ref(false);
|
||||
const setSuperVisor = ref(false);
|
||||
const isExistSuperVisor = ref(false);
|
||||
const createRef = ref();
|
||||
|
||||
const setting = () => {
|
||||
setSuperVisor.value = true;
|
||||
};
|
||||
|
||||
const isExist = (isExist: boolean) => {
|
||||
isExistSuperVisor.value = isExist;
|
||||
};
|
||||
|
||||
const openCreate = () => {
|
||||
createRef.value.acceptParams();
|
||||
};
|
||||
|
||||
onMounted(() => {});
|
||||
</script>
|
140
frontend/src/views/host/tool/supervisor/status/index.vue
Normal file
140
frontend/src/views/host/tool/supervisor/status/index.vue
Normal file
@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="app-status tool-status" v-if="data.isExist">
|
||||
<el-card>
|
||||
<div>
|
||||
<el-tag effect="dark" type="success">{{ 'Supervisor' }}</el-tag>
|
||||
<Status class="status-content" :key="data.status" :status="data.status"></Status>
|
||||
<el-tag class="status-content">{{ $t('app.version') }}:{{ data.version }}</el-tag>
|
||||
<span class="buttons">
|
||||
<el-button type="primary" v-if="data.status != 'running'" link @click="onOperate('start')">
|
||||
{{ $t('app.start') }}
|
||||
</el-button>
|
||||
<el-button type="primary" v-if="data.status == 'running'" link @click="onOperate('stop')">
|
||||
{{ $t('app.stop') }}
|
||||
</el-button>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button type="primary" link @click="onOperate('restart')">
|
||||
{{ $t('app.restart') }}
|
||||
</el-button>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
:disabled="data.status !== 'running' || !data.ctlExist"
|
||||
@click="setting"
|
||||
>
|
||||
{{ $t('commons.button.set') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<LayoutContent :title="$t('tool.supervisor.list')" :divider="true" v-if="!data.isExist || !data.ctlExist">
|
||||
<template #main>
|
||||
<div class="app-warn">
|
||||
<div>
|
||||
<span v-if="!data.isExist">{{ $t('tool.supervisor.notSupport') }}</span>
|
||||
<span v-if="!data.ctlExist">{{ $t('tool.supervisor.notSupportCrl') }}</span>
|
||||
<span @click="toDoc()">
|
||||
<el-icon><Position /></el-icon>
|
||||
{{ $t('firewall.quickJump') }}
|
||||
</span>
|
||||
<div>
|
||||
<img src="@/assets/images/no_app.svg" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</LayoutContent>
|
||||
<InitPage ref="initRef" @close="getStatus"></InitPage>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { GetSupervisorStatus, OperateSupervisor } from '@/api/modules/host-tool';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import Status from '@/components/status/index.vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { HostTool } from '@/api/interface/host-tool';
|
||||
import InitPage from './init/index.vue';
|
||||
|
||||
let operateReq = reactive({
|
||||
installId: 0,
|
||||
operate: '',
|
||||
});
|
||||
const initRef = ref();
|
||||
const data = ref({
|
||||
isExist: false,
|
||||
version: '',
|
||||
status: 'running',
|
||||
init: false,
|
||||
configPath: '',
|
||||
ctlExist: false,
|
||||
});
|
||||
|
||||
const em = defineEmits(['setting', 'isExist', 'before', 'update:loading', 'update:maskShow']);
|
||||
|
||||
const setting = () => {
|
||||
em('setting', false);
|
||||
};
|
||||
|
||||
const toDoc = async () => {
|
||||
window.open('https://1panel.cn/docs/user_manual/hosts/firewall/', '_blank');
|
||||
};
|
||||
|
||||
const onOperate = async (operation: string) => {
|
||||
operateReq.operate = operation;
|
||||
ElMessageBox.confirm(
|
||||
i18n.global.t('tool.supervisor.operatorHelper', [i18n.global.t('app.' + operation)]),
|
||||
i18n.global.t('app.' + operation),
|
||||
{
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
type: 'info',
|
||||
},
|
||||
)
|
||||
.then(() => {
|
||||
em('update:loading', true);
|
||||
em('before');
|
||||
OperateSupervisor(operation)
|
||||
.then(() => {
|
||||
getStatus();
|
||||
em('update:loading', false);
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
})
|
||||
.catch(() => {
|
||||
em('update:loading', false);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
const getStatus = async () => {
|
||||
try {
|
||||
em('update:loading', true);
|
||||
const res = await GetSupervisorStatus();
|
||||
data.value = res.data.config as HostTool.Supersivor;
|
||||
if (!data.value.isExist || !data.value.ctlExist) {
|
||||
em('isExist', false);
|
||||
} else {
|
||||
em('isExist', true);
|
||||
}
|
||||
if (data.value.init) {
|
||||
initRef.value.acceptParams(data.value.configPath);
|
||||
}
|
||||
} catch (error) {}
|
||||
em('update:loading', false);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getStatus();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tool-status {
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<el-drawer :close-on-click-modal="false" v-model="open" size="30%" :show-close="false">
|
||||
<template #header>
|
||||
<span>{{ $t('commons.button.init') }}</span>
|
||||
</template>
|
||||
<el-row v-loading="loading">
|
||||
<el-col :span="22" :offset="1">
|
||||
<el-form ref="initForm" label-position="top" :model="initModel" label-width="100px" :rules="rules">
|
||||
<el-form-item :label="$t('tool.supervisor.primaryConfig')" prop="primaryConfig">
|
||||
<el-input v-model.trim="initModel.primaryConfig"></el-input>
|
||||
</el-form-item>
|
||||
<el-alert
|
||||
:title="$t('tool.supervisor.initWarn')"
|
||||
class="common-prompt"
|
||||
:closable="false"
|
||||
type="error"
|
||||
/>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button type="primary" @click="submit(initForm)" :disabled="loading">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { InitSupervisor } from '@/api/modules/host-tool';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
import { FormInstance } from 'element-plus';
|
||||
import { ref } from 'vue';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
|
||||
const open = ref(false);
|
||||
const loading = ref(false);
|
||||
const initForm = ref<FormInstance>();
|
||||
const rules = ref({
|
||||
primaryConfig: [Rules.requiredInput],
|
||||
});
|
||||
const initModel = ref({
|
||||
primaryConfig: '',
|
||||
});
|
||||
|
||||
const em = defineEmits(['close']);
|
||||
|
||||
const acceptParams = (primaryConfig: string) => {
|
||||
initModel.value.primaryConfig = primaryConfig;
|
||||
open.value = true;
|
||||
};
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid) => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
InitSupervisor({ type: 'supervisord', configPath: initModel.value.primaryConfig })
|
||||
.then(() => {
|
||||
open.value = false;
|
||||
em('close', true);
|
||||
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user