2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 12:40:25 +08:00

feat: supervisor plugin and optimize code

This commit is contained in:
耗子
2023-07-26 02:14:26 +08:00
parent b4720a1c84
commit 65a46796ea
11 changed files with 1063 additions and 6 deletions

View File

@@ -35,8 +35,7 @@ func (c *Php74Controller) Status(ctx http.Context) {
return
}
out := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
status := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return

View File

@@ -35,8 +35,7 @@ func (c *Php80Controller) Status(ctx http.Context) {
return
}
out := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
status := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return

View File

@@ -0,0 +1,390 @@
package supervisor
import (
"strconv"
"strings"
"github.com/goravel/framework/contracts/http"
"panel/pkg/tools"
"panel/app/http/controllers"
)
type SupervisorController struct {
ServiceName string
}
func NewSupervisorController() *SupervisorController {
var serviceName string
if tools.IsRHEL() {
serviceName = "supervisord"
} else {
serviceName = "supervisor"
}
return &SupervisorController{
ServiceName: serviceName,
}
}
// Status 状态
func (r *SupervisorController) Status(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
status := tools.ExecShell(`systemctl status ` + r.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
if status == "active" {
controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
}
}
// Start 启动
func (r *SupervisorController) Start(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
tools.ExecShell(`systemctl start ` + r.ServiceName)
status := tools.ExecShell(`systemctl status ` + r.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
if status == "active" {
controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
}
}
// Stop 停止
func (r *SupervisorController) Stop(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
tools.ExecShell(`systemctl stop ` + r.ServiceName)
status := tools.ExecShell(`systemctl status ` + r.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
if status != "active" {
controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
}
}
// Restart 重启
func (r *SupervisorController) Restart(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
tools.ExecShell(`systemctl restart ` + r.ServiceName)
status := tools.ExecShell(`systemctl status ` + r.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
if status == "active" {
controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
}
}
// Reload 重载
func (r *SupervisorController) Reload(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
tools.ExecShell(`systemctl reload ` + r.ServiceName)
status := tools.ExecShell(`systemctl status ` + r.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
if status == "active" {
controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
}
}
// Log 日志
func (r *SupervisorController) Log(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
log := tools.ExecShell(`tail -n 200 /var/log/supervisor/supervisord.log`)
controllers.Success(ctx, log)
}
// ClearLog 清空日志
func (r *SupervisorController) ClearLog(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
tools.ExecShell(`echo "" > /var/log/supervisor/supervisord.log`)
controllers.Success(ctx, nil)
}
// Config 获取配置
func (r *SupervisorController) Config(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
var config string
if tools.IsRHEL() {
config = tools.ReadFile(`/etc/supervisord.conf`)
} else {
config = tools.ReadFile(`/etc/supervisor/supervisord.conf`)
}
controllers.Success(ctx, config)
}
// SaveConfig 保存配置
func (r *SupervisorController) SaveConfig(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
config := ctx.Request().Input("config")
if tools.IsRHEL() {
tools.WriteFile(`/etc/supervisord.conf`, config, 0644)
} else {
tools.WriteFile(`/etc/supervisor/supervisord.conf`, config, 0644)
}
tools.ExecShell(`systemctl restart ` + r.ServiceName)
controllers.Success(ctx, nil)
}
// Processes 进程列表
func (r *SupervisorController) Processes(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
page := ctx.Request().QueryInt("page", 1)
limit := ctx.Request().QueryInt("limit", 10)
type process struct {
Name string `json:"name"`
Status string `json:"status"`
Pid string `json:"pid"`
Uptime string `json:"uptime"`
}
out := tools.ExecShell(`supervisorctl status | awk '{print $1}'`)
var processList []process
for _, line := range strings.Split(out, "\n") {
if len(line) == 0 {
continue
}
var p process
p.Name = line
p.Status = tools.ExecShell(`supervisorctl status ` + line + ` | awk '{print $2}'`)
if p.Status == "RUNNING" {
p.Pid = strings.ReplaceAll(tools.ExecShell(`supervisorctl status `+line+` | awk '{print $4}'`), ",", "")
p.Uptime = tools.ExecShell(`supervisorctl status ` + line + ` | awk '{print $6}'`)
} else {
p.Pid = "-"
p.Uptime = "-"
}
processList = append(processList, p)
}
startIndex := (page - 1) * limit
endIndex := page * limit
if startIndex > len(processList) {
controllers.Success(ctx, http.Json{
"total": 0,
"items": []process{},
})
return
}
if endIndex > len(processList) {
endIndex = len(processList)
}
pagedProcessList := processList[startIndex:endIndex]
controllers.Success(ctx, http.Json{
"total": len(processList),
"items": pagedProcessList,
})
}
// StartProcess 启动进程
func (r *SupervisorController) StartProcess(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
process := ctx.Request().Input("process")
tools.ExecShell(`supervisorctl start ` + process)
controllers.Success(ctx, nil)
}
// StopProcess 停止进程
func (r *SupervisorController) StopProcess(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
process := ctx.Request().Input("process")
tools.ExecShell(`supervisorctl stop ` + process)
controllers.Success(ctx, nil)
}
// RestartProcess 重启进程
func (r *SupervisorController) RestartProcess(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
process := ctx.Request().Input("process")
tools.ExecShell(`supervisorctl restart ` + process)
controllers.Success(ctx, nil)
}
// ProcessLog 进程日志
func (r *SupervisorController) ProcessLog(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
process := ctx.Request().Input("process")
var logPath string
if tools.IsRHEL() {
logPath = tools.ExecShell(`cat '/etc/supervisord.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
} else {
logPath = tools.ExecShell(`cat '/etc/supervisor/conf.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
}
log := tools.ExecShell(`tail -n 200 ` + logPath)
controllers.Success(ctx, log)
}
// ClearProcessLog 清空进程日志
func (r *SupervisorController) ClearProcessLog(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
process := ctx.Request().Input("process")
var logPath string
if tools.IsRHEL() {
logPath = tools.ExecShell(`cat '/etc/supervisord.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
} else {
logPath = tools.ExecShell(`cat '/etc/supervisor/conf.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
}
tools.ExecShell(`echo "" > ` + logPath)
controllers.Success(ctx, nil)
}
// ProcessConfig 获取进程配置
func (r *SupervisorController) ProcessConfig(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
process := ctx.Request().Query("process")
var config string
if tools.IsRHEL() {
config = tools.ReadFile(`/etc/supervisord.d/` + process + `.conf`)
} else {
config = tools.ReadFile(`/etc/supervisor/conf.d/` + process + `.conf`)
}
controllers.Success(ctx, config)
}
// SaveProcessConfig 保存进程配置
func (r *SupervisorController) SaveProcessConfig(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
process := ctx.Request().Input("process")
config := ctx.Request().Input("config")
if tools.IsRHEL() {
tools.WriteFile(`/etc/supervisord.d/`+process+`.conf`, config, 0644)
} else {
tools.WriteFile(`/etc/supervisor/conf.d/`+process+`.conf`, config, 0644)
}
tools.ExecShell(`supervisorctl reread`)
tools.ExecShell(`supervisorctl update`)
tools.ExecShell(`supervisorctl start ` + process)
controllers.Success(ctx, nil)
}
// AddProcess 添加进程
func (r *SupervisorController) AddProcess(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
validator, err := ctx.Request().Validate(map[string]string{
"name": "required|alpha_dash",
"user": "required|alpha_dash",
"path": "required",
"command": "required",
"num": "required",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
}
name := ctx.Request().Input("name")
user := ctx.Request().Input("user")
path := ctx.Request().Input("path")
command := ctx.Request().Input("command")
num := ctx.Request().InputInt("num", 1)
config := `[program:` + name + `]
command=` + command + `
process_name=%(program_name)s
directory=` + path + `
autostart=true
autorestart=true
user=` + user + `
numprocs=` + strconv.Itoa(num) + `
redirect_stderr=true
stdout_logfile=/var/log/supervisor/` + name + `.log
stdout_logfile_maxbytes=2MB
`
if tools.IsRHEL() {
tools.WriteFile(`/etc/supervisord.d/`+name+`.conf`, config, 0644)
} else {
tools.WriteFile(`/etc/supervisor/conf.d/`+name+`.conf`, config, 0644)
}
tools.ExecShell(`supervisorctl reread`)
tools.ExecShell(`supervisorctl update`)
tools.ExecShell(`supervisorctl start ` + name)
controllers.Success(ctx, nil)
}
// DeleteProcess 删除进程
func (r *SupervisorController) DeleteProcess(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
}
process := ctx.Request().Input("process")
tools.ExecShell(`supervisorctl stop ` + process)
var logPath string
if tools.IsRHEL() {
logPath = tools.ExecShell(`cat '/etc/supervisord.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
tools.RemoveFile(`/etc/supervisord.d/` + process + `.conf`)
} else {
logPath = tools.ExecShell(`cat '/etc/supervisor/conf.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
tools.RemoveFile(`/etc/supervisor/conf.d/` + process + `.conf`)
}
tools.RemoveFile(logPath)
tools.ExecShell(`supervisorctl reread`)
tools.ExecShell(`supervisorctl update`)
controllers.Success(ctx, nil)
}

View File

@@ -0,0 +1,13 @@
package supervisor
var (
Name = "Supervisor"
Description = "Supervisor 是一个客户端/服务器系统,允许用户监视和控制类 UNIX 操作系统上的多个进程。"
Slug = "supervisor"
Version = "4.2.5"
Requires = []string{}
Excludes = []string{}
Install = `bash /www/panel/scripts/supervisor/install.sh`
Uninstall = `bash /www/panel/scripts/supervisor/uninstall.sh`
Update = `bash /www/panel/scripts/supervisor/install.sh`
)

View File

@@ -12,6 +12,7 @@ import (
"panel/app/plugins/php80"
"panel/app/plugins/phpmyadmin"
"panel/app/plugins/s3fs"
"panel/app/plugins/supervisor"
)
// PanelPlugin 插件元数据结构
@@ -132,6 +133,17 @@ func (r *PluginImpl) All() []PanelPlugin {
Uninstall: s3fs.Uninstall,
Update: s3fs.Update,
})
p = append(p, PanelPlugin{
Name: supervisor.Name,
Description: supervisor.Description,
Slug: supervisor.Slug,
Version: supervisor.Version,
Requires: supervisor.Requires,
Excludes: supervisor.Excludes,
Install: supervisor.Install,
Uninstall: supervisor.Uninstall,
Update: supervisor.Update,
})
return p
}

View File

@@ -205,7 +205,7 @@ Date: 2023-07-21
if (obj.event === 'log') {
// 打开日志弹窗
admin.popup({
title: '日志'
title: '日志 - ' + data.name
,
area: ['80%', '80%']
,
@@ -214,10 +214,12 @@ Date: 2023-07-21
content: '<pre id="cron-log-view" style="overflow: auto; height: 95%;border: 0 none; line-height:23px; padding: 15px; margin: 0; white-space: pre-wrap; background-color: rgb(51,51,51); color:#f1f1f1; border-radius:0;"></pre>'
,
success: function (layero, index) {
index2 = layer.msg('正在获取日志...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: '/api/panel/cron/log?id=' + data.id
, type: 'GET'
, success: function (res) {
layer.close(index2);
if (res.code === 0) {
$('#cron-log-view').html(res.data);
} else {
@@ -236,7 +238,7 @@ Date: 2023-07-21
, success: function (res) {
if (res.code === 0) {
admin.popup({
title: '编辑'
title: '编辑 - ' + data.name
,
area: ['80%', '80%']
,
@@ -258,6 +260,7 @@ Date: 2023-07-21
}
});
$('#cron-edit-' + data.id).click(function () {
index2 = layer.msg('正在保存...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: '/api/panel/cron/update'
, type: 'POST'
@@ -271,6 +274,7 @@ Date: 2023-07-21
if (res.code === 0) {
layer.msg('保存成功', {icon: 1, time: 1000});
table.reload('panel-cron');
layer.close(index2);
layer.close(index);
} else {
layer.msg(res.message, {icon: 2, time: 1000});

View File

@@ -0,0 +1,451 @@
<!--
Name: Supervisor
Author: 耗子
Date: 2023-07-26
-->
<title>Supervisor</title>
<div class="layui-fluid" id="component-tabs">
<div class="layui-row">
<div class="layui-col-md12">
<div class="layui-card">
<div class="layui-card-header">Supervisor 管理</div>
<div class="layui-card-body">
<div class="layui-tab">
<ul class="layui-tab-title">
<li class="layui-this">运行状态</li>
<li>进程列表</li>
<li>配置修改</li>
<li>运行日志</li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
<blockquote id="supervisor-status" class="layui-elem-quote layui-quote-nm">
当前状态:<span
class="layui-badge layui-bg-black">获取中</span></blockquote>
<div class="layui-btn-container" style="padding-top: 30px;">
<button id="supervisor-start" class="layui-btn">启动</button>
<button id="supervisor-stop" class="layui-btn layui-btn-danger">停止</button>
<button id="supervisor-restart" class="layui-btn layui-btn-warm">重启</button>
<button id="supervisor-reload" class="layui-btn layui-btn-normal">重载</button>
</div>
</div>
<div class="layui-tab-item">
<table class="layui-hide" id="supervisor-list" lay-filter="supervisor-list"></table>
<!-- 顶部工具栏 -->
<script type="text/html" id="supervisor-list-bar">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="add_process">添加进程</button>
</div>
</script>
<!-- 右侧管理 -->
<script type="text/html" id="supervisor-list-control">
<a class="layui-btn layui-btn-xs" lay-event="log">日志</a>
<a class="layui-btn layui-bg-blue layui-btn-xs" lay-event="config">配置</a>
<a class="layui-btn layui-border layui-btn-xs" lay-event="start">启动</a>
<a class="layui-btn layui-bg-purple layui-btn-xs" lay-event="restart">重启</a>
<a class="layui-btn layui-bg-orange layui-btn-xs" lay-event="stop">停止</a>
<a class="layui-btn layui-bg-red layui-btn-xs" lay-event="delete">删除</a>
</script>
</div>
<div class="layui-tab-item">
<blockquote class="layui-elem-quote">此处修改的是Supervisor主配置文件如果你不了解各参数的含义请不要随意修改<br>
提示Ctrl+F 搜索关键字Ctrl+S 保存Ctrl+H 查找替换!
</blockquote>
<div id="supervisor-config-editor"
style="height: 600px;"></div>
<div class="layui-btn-container" style="padding-top: 30px;">
<button id="supervisor-config-save" class="layui-btn">保存</button>
</div>
</div>
<div class="layui-tab-item">
<div class="layui-btn-container">
<button id="supervisor-clear-log" class="layui-btn">清空日志</button>
</div>
<pre id="supervisor-log" class="layui-code">
获取中...
</pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
let supervisor_config_editor;
let supervisor_process_config_editor;
layui.use(['jquery', 'admin', 'view', 'form', 'code', 'table'], function () {
let $ = layui.$
, admin = layui.admin
, table = layui.table
, form = layui.form
, view = layui.view
, code = layui.code;
admin.req({
url: "/api/plugins/supervisor/status"
, type: 'get'
, success: function (result) {
if (result.code !== 0) {
return false;
}
if (result.data) {
$('#supervisor-status').html('当前状态:<span class="layui-badge layui-bg-green">运行中</span>');
} else {
$('#supervisor-status').html('当前状态:<span class="layui-badge layui-bg-red">已停止</span>');
}
}
});
table.render({
elem: '#supervisor-list'
, url: '/api/plugins/supervisor/processes'
, toolbar: '#supervisor-list-bar'
, title: 'Supervisor进程列表'
, cols: [[
{field: 'name', title: '名称', fixed: 'left', unresize: true, sort: true}
, {field: 'status', title: '状态', sort: true}
, {field: 'pid', title: 'PID', sort: true}
, {field: 'uptime', title: '运行时间(天)', sort: true}
, {fixed: 'right', title: '操作', toolbar: '#supervisor-list-control', width: 350}
]]
, page: true
, text: {
none: '无进程'
}
, parseData: function (res) {
return {
"code": res.code,
"msg": res.message,
"count": res.data.total,
"data": res.data.items
};
}
});
// 头工具栏事件
table.on('toolbar(supervisor-list)', function (obj) {
if (obj.event === 'add_process') {
admin.popup({
title: '添加进程'
, area: ['600px', '600px']
, id: 'LAY-popup-supervisor-process-add'
, success: function (layer, index) {
view(this.id).render('plugins/supervisor/add_process', {}).done(function () {
form.render(null, 'LAY-popup-supervisor-process-add');
});
}
});
}
});
// 行工具事件
table.on('tool(supervisor-list)', function (obj) {
let data = obj.data;
if (obj.event === 'log') {
admin.popup({
title: '日志 - ' + data.name
,
area: ['80%', '80%']
,
id: 'process-log'
,
content: '<pre id="process-log-view" style="overflow: auto; height: 95%;border: 0 none; line-height:23px; padding: 15px; margin: 0; white-space: pre-wrap; background-color: rgb(51,51,51); color:#f1f1f1; border-radius:0;"></pre>'
,
success: function (layero, index) {
index2 = layer.msg('正在获取日志...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: '/api/plugins/supervisor/processLog?process=' + data.name
, type: 'GET'
, success: function (res) {
layer.close(index2);
if (res.code === 0) {
$('#process-log-view').html(res.data);
} else {
layer.msg(res.message, {icon: 2, time: 1000});
}
}
});
}
});
}
if (obj.event === 'config') {
admin.req({
url: '/api/plugins/supervisor/processConfig?process=' + data.name
, type: 'GET'
, success: function (res) {
if (res.code === 0) {
admin.popup({
title: '编辑 - ' + data.name
,
area: ['80%', '80%']
,
id: 'process-config'
,
content: '<div id="process-config-editor" style="height: 80%;">' + res.data + '</div><br><button id="process-config-' + data.name + '" class="layui-btn">保存</button>'
,
success: function (layero, index) {
supervisor_process_config_editor = ace.edit("process-config-editor", {
mode: "ace/mode/ini",
selectionStyle: "text"
});
$('#process-config-' + data.name).click(function () {
index2 = layer.msg('正在保存配置...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: '/api/plugins/supervisor/processConfig'
, type: 'POST'
, data: {
process: data.name,
config: supervisor_process_config_editor.getValue()
}
, success: function (res) {
if (res.code === 0) {
layer.msg('保存成功', {icon: 1, time: 1000});
table.reload('supervisor-list');
layer.close(index2);
layer.close(index);
} else {
layer.msg(res.message, {icon: 2, time: 1000});
}
}
});
});
}
});
} else {
layer.msg(res.message, {icon: 2, time: 1000});
}
}
});
}
if (obj.event === 'start') {
index = layer.msg('正在启动进程...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/supervisor/startProcess"
, type: 'post'
, data: {
process: data.name
}
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
admin.events.refresh();
layer.alert('进程启动成功!');
}
});
}
if (obj.event === 'restart') {
layer.confirm('确定要重启进程吗?', function (index) {
index = layer.msg('请稍候...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/supervisor/restartProcess"
, type: 'post'
, data: {
process: data.name
}
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
layer.alert('进程重启成功!');
}
});
layer.close(index);
});
}
if (obj.event === 'stop') {
layer.confirm('确定要停止进程吗?', function (index) {
index = layer.msg('请稍候...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/supervisor/stopProcess"
, type: 'post'
, data: {
process: data.name
}
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
layer.alert('进程停止成功!');
}
});
layer.close(index);
});
}
if (obj.event === 'delete') {
layer.confirm('确定要删除进程吗?', function (index) {
index = layer.msg('请稍候...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/supervisor/deleteProcess"
, type: 'post'
, data: {
process: data.name
}
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
obj.del();
layer.alert('进程删除成功!');
}
});
layer.close(index);
});
}
});
admin.req({
url: "/api/plugins/supervisor/log"
, type: 'get'
, success: function (result) {
if (result.code !== 0) {
$('#supervisor-log').text('Supervisor日志获取失败请刷新重试');
code({
elem: '#supervisor-log'
, title: 'supervisor.log'
, encode: true
, about: false
});
return false;
}
$('#supervisor-log').text(result.data);
code({
elem: '#supervisor-log'
, title: 'supervisor.log'
, encode: true
, about: false
});
}
});
admin.req({
url: "/api/plugins/supervisor/config"
, type: 'get'
, success: function (result) {
if (result.code !== 0) {
return false;
}
$('#supervisor-config-editor').text(result.data);
supervisor_config_editor = ace.edit("supervisor-config-editor", {
mode: "ace/mode/ini",
selectionStyle: "text"
});
}
});
$('#supervisor-start').click(function () {
layer.confirm('确定要启动Supervisor吗', {
btn: ['启动', '取消']
}, function () {
index = layer.msg('正在启动Supervisor...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/supervisor/start"
, type: 'post'
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
admin.events.refresh();
layer.alert('Supervisor启动成功');
}
});
});
});
$('#supervisor-stop').click(function () {
layer.confirm('确定要停止Supervisor吗', {
btn: ['停止', '取消']
}, function () {
index = layer.msg('正在停止Supervisor...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/supervisor/stop"
, type: 'post'
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
admin.events.refresh();
layer.alert('Supervisor停止成功');
}
});
});
});
$('#supervisor-restart').click(function () {
layer.confirm('确定要重启Supervisor吗', {
btn: ['重启', '取消']
}, function () {
index = layer.msg('正在重启Supervisor...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/supervisor/restart"
, type: 'post'
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
admin.events.refresh();
layer.alert('Supervisor重启成功');
}
});
});
});
$('#supervisor-reload').click(function () {
index = layer.msg('正在重载Supervisor...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/supervisor/reload"
, type: 'post'
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
admin.events.refresh();
layer.alert('Supervisor重载成功');
}
});
});
$('#supervisor-config-save').click(function () {
index = layer.msg('正在保存配置...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/supervisor/config"
, type: 'post'
, data: {
config: supervisor_config_editor.getValue()
}
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
layer.alert('Supervisor配置保存成功');
}
});
});
$('#supervisor-clear-log').click(function () {
index = layer.msg('正在清空Supervisor日志...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/supervisor/clearLog"
, type: 'post'
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
layer.msg('Supervisor日志已清空');
setTimeout(function () {
admin.events.refresh();
}, 1000);
}
});
});
});
</script>

View File

@@ -0,0 +1,94 @@
<!--
Name: Supervisor - 添加进程
Author: 耗子
Date: 2023-07-26
-->
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
<form class="layui-form" action="" lay-filter="add-mount-s3fs-form">
<div class="layui-form-item">
<label class="layui-form-label">名称</label>
<div class="layui-input-block">
<input type="text" name="name" lay-verify="required" placeholder="请输入名称" class="layui-input"
value="">
</div>
<div class="layui-form-mid layui-word-aux">名称禁止使用中文</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">启动命令</label>
<div class="layui-input-block">
<input type="text" name="command" lay-verify="required" placeholder="请输入启动命令" class="layui-input"
value="">
</div>
<div class="layui-form-mid layui-word-aux">启动命令中的文件请填写绝对路径</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">运行目录</label>
<div class="layui-input-block">
<input type="text" name="path" lay-verify="required" placeholder="请输入运行目录" class="layui-input"
value="">
</div>
<div class="layui-form-mid layui-word-aux">运行目录请填写绝对路径</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">启动用户</label>
<div class="layui-input-block">
<input type="text" name="user" lay-verify="required" placeholder="请输入启动用户" class="layui-input"
value="">
</div>
<div class="layui-form-mid layui-word-aux">一般情况下填写www即可</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">进程数量</label>
<div class="layui-input-block">
<input type="number" name="num" lay-verify="required" placeholder="请输入进程数量" class="layui-input"
value="1" min="1" step="1" lay-affix="number">
</div>
<div class="layui-form-mid layui-word-aux">一般情况下填写1即可</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<div class="layui-footer">
<button class="layui-btn" lay-submit="" lay-filter="supervisor-add-process-submit">立即提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</div>
</form>
</script>
<script>
layui.data.sendParams = function (params) {
layui.use(['admin', 'form', 'jquery', 'table'], function () {
var admin = layui.admin
, layer = layui.layer
, form = layui.form
, table = layui.table;
form.render();
form.on('submit(supervisor-add-process-submit)', function (data) {
index = layer.msg('正在提交...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/supervisor/addProcess"
, type: 'post'
, data: data.field
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
table.reload('supervisor-list');
layer.alert('Supervisor进程添加成功', {
icon: 1
, title: '提示'
, btn: ['确定']
, yes: function (index) {
layer.closeAll();
}
});
}
});
return false;
});
});
};
</script>

View File

@@ -3,6 +3,7 @@ package routes
import (
"github.com/goravel/framework/contracts/route"
"github.com/goravel/framework/facades"
"panel/app/http/controllers/plugins/supervisor"
"panel/app/http/controllers/plugins/mysql57"
"panel/app/http/controllers/plugins/mysql80"
@@ -136,4 +137,27 @@ func Plugin() {
route.Post("add", s3fsController.Add)
route.Post("delete", s3fsController.Delete)
})
facades.Route().Prefix("api/plugins/supervisor").Middleware(middleware.Jwt()).Group(func(route route.Route) {
supervisorController := supervisor.NewSupervisorController()
route.Get("status", supervisorController.Status)
route.Post("start", supervisorController.Start)
route.Post("stop", supervisorController.Stop)
route.Post("restart", supervisorController.Restart)
route.Post("reload", supervisorController.Reload)
route.Get("log", supervisorController.Log)
route.Post("clearLog", supervisorController.ClearLog)
route.Get("config", supervisorController.Config)
route.Post("config", supervisorController.SaveConfig)
route.Get("processes", supervisorController.Processes)
route.Post("startProcess", supervisorController.StartProcess)
route.Post("stopProcess", supervisorController.StopProcess)
route.Post("restartProcess", supervisorController.RestartProcess)
route.Get("processLog", supervisorController.ProcessLog)
route.Post("clearProcessLog", supervisorController.ClearProcessLog)
route.Get("processConfig", supervisorController.ProcessConfig)
route.Post("processConfig", supervisorController.SaveProcessConfig)
route.Post("deleteProcess", supervisorController.DeleteProcess)
route.Post("addProcess", supervisorController.AddProcess)
})
}

View File

@@ -0,0 +1,38 @@
#!/bin/bash
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
: '
Copyright 2022 HaoZi Technology Co., Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
'
HR="+----------------------------------------------------"
OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown")
if [ "${OS}" == "centos" ]; then
dnf install -y supervisor
sed -i 's#files = supervisord.d/\*.ini#files = supervisord.d/*.conf#g' /etc/supervisord.conf
systemctl enable supervisord
systemctl start supervisord
elif [ "${OS}" == "debian" ]; then
apt install -y supervisor
systemctl enable supervisor
systemctl start supervisor
else
echo -e $HR
echo "错误:不支持的操作系统"
exit 1
fi
panel writePlugin supervisor 4.2.5

View File

@@ -0,0 +1,33 @@
#!/bin/bash
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
: '
Copyright 2022 HaoZi Technology Co., Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
'
HR="+----------------------------------------------------"
OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown")
if [ "${OS}" == "centos" ]; then
dnf remove -y supervisor
elif [ "${OS}" == "debian" ]; then
apt remove -y supervisor
else
echo -e $HR
echo "错误:不支持的操作系统"
exit 1
fi
panel deletePlugin supervisor