mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 09:13:49 +08:00
refactor: plugin
This commit is contained in:
22
.github/workflows/issue-auto-reply.yml
vendored
22
.github/workflows/issue-auto-reply.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: bug or enhancement
|
||||
- name: ✏️ Feature
|
||||
if: github.event.label.name == '✏️ Feature'
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
@@ -24,5 +24,21 @@ jobs:
|
||||
body: |
|
||||
Hi @${{ github.event.issue.user.login }} 👋
|
||||
|
||||
我们认为您的反馈非常有价值!如果有兴趣欢迎提交 PR,请包含相应的测试用例、文档等,并确保 CI 通过,感谢和期待您的贡献!
|
||||
We think your feedback is very valuable! If you are interested, please submit a PR, please include test cases, documentation, etc., and ensure that the CI is passed, thank you and look forward to your contribution!
|
||||
我们认为您的建议非常有价值!欢迎提交 PR,请包含相应的测试用例、文档等,并确保 CI 通过,感谢和期待您的贡献!
|
||||
We think your suggestion is very valuable! Welcome to submit a PR, please include test cases, documentation, etc., and ensure that the CI is passed, thank you and look forward to your contribution!
|
||||
|
||||
"Talk is cheap, Show me the Code." - Linus Torvalds
|
||||
- name: ☢️ Bug
|
||||
if: github.event.label.name == '☢️ Bug'
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'create-comment'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
body: |
|
||||
Hi @${{ github.event.issue.user.login }} 👋
|
||||
|
||||
我们认为您的反馈非常有价值!欢迎提交 PR,请包含相应的测试用例、文档等,并确保 CI 通过,感谢和期待您的贡献!
|
||||
We think your feedback is very valuable! Welcome to submit a PR, please include test cases, documentation, etc., and ensure that the CI is passed, thank you and look forward to your contribution!
|
||||
|
||||
"Talk is cheap, Show me the Code." - Linus Torvalds
|
||||
|
||||
246
app/http/controllers/plugin_controller.go
Normal file
246
app/http/controllers/plugin_controller.go
Normal file
@@ -0,0 +1,246 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/goravel/framework/contracts/queue"
|
||||
"github.com/goravel/framework/facades"
|
||||
"panel/app/jobs"
|
||||
"sync"
|
||||
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
|
||||
"panel/app/models"
|
||||
"panel/app/services"
|
||||
)
|
||||
|
||||
type PluginController struct {
|
||||
plugin services.Plugin
|
||||
}
|
||||
|
||||
func NewPluginController() *PluginController {
|
||||
return &PluginController{
|
||||
plugin: services.NewPluginImpl(),
|
||||
}
|
||||
}
|
||||
|
||||
// List 列出所有插件
|
||||
func (r *PluginController) List(ctx http.Context) {
|
||||
plugins := r.plugin.All()
|
||||
installedPlugins, err := r.plugin.AllInstalled()
|
||||
if err != nil {
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
}
|
||||
|
||||
var lock sync.RWMutex
|
||||
installedPluginsMap := make(map[string]models.Plugin)
|
||||
|
||||
for _, p := range installedPlugins {
|
||||
lock.Lock()
|
||||
installedPluginsMap[p.Slug] = p
|
||||
lock.Unlock()
|
||||
}
|
||||
|
||||
type plugin struct {
|
||||
Name string `json:"name"`
|
||||
Author string `json:"author"`
|
||||
Description string `json:"description"`
|
||||
Slug string `json:"slug"`
|
||||
Version string `json:"version"`
|
||||
Requires []string `json:"requires"`
|
||||
Excludes []string `json:"excludes"`
|
||||
Installed bool `json:"installed"`
|
||||
InstalledVersion string `json:"installed_version"`
|
||||
Show bool `json:"show"`
|
||||
}
|
||||
|
||||
var p []plugin
|
||||
for _, item := range plugins {
|
||||
installed, installedVersion, show := false, "", false
|
||||
if _, ok := installedPluginsMap[item.Slug]; ok {
|
||||
installed = true
|
||||
installedVersion = installedPluginsMap[item.Slug].Version
|
||||
show = installedPluginsMap[item.Slug].Show
|
||||
}
|
||||
p = append(p, plugin{
|
||||
Name: item.Name,
|
||||
Author: item.Author,
|
||||
Description: item.Description,
|
||||
Slug: item.Slug,
|
||||
Version: item.Version,
|
||||
Requires: item.Requires,
|
||||
Excludes: item.Excludes,
|
||||
Installed: installed,
|
||||
InstalledVersion: installedVersion,
|
||||
Show: show,
|
||||
})
|
||||
}
|
||||
|
||||
Success(ctx, p)
|
||||
}
|
||||
|
||||
// Install 安装插件
|
||||
func (r *PluginController) Install(ctx http.Context) {
|
||||
slug := ctx.Request().Input("slug")
|
||||
plugins := r.plugin.All()
|
||||
|
||||
var plugin services.PanelPlugin
|
||||
check := false
|
||||
for _, item := range plugins {
|
||||
if item.Slug == slug {
|
||||
check = true
|
||||
plugin = item
|
||||
break
|
||||
}
|
||||
}
|
||||
if !check {
|
||||
Error(ctx, http.StatusBadRequest, "插件不存在")
|
||||
return
|
||||
}
|
||||
|
||||
var installedPlugin models.Plugin
|
||||
if err := facades.Orm().Query().Where("slug", slug).First(&installedPlugin); err != nil {
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
if installedPlugin.ID != 0 {
|
||||
Error(ctx, http.StatusBadRequest, "插件已安装")
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "安装插件 " + plugin.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = "bash scripts/plugins/" + plugin.Slug + "/install.sh >> /tmp/" + plugin.Slug + ".log 2>&1"
|
||||
task.Log = "/tmp/" + plugin.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Error("[面板][PluginController] 创建任务失败: " + err.Error())
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
processTask(task.ID)
|
||||
Success(ctx, "任务已提交")
|
||||
}
|
||||
|
||||
// Uninstall 卸载插件
|
||||
func (r *PluginController) Uninstall(ctx http.Context) {
|
||||
slug := ctx.Request().Input("slug")
|
||||
plugins := r.plugin.All()
|
||||
|
||||
var plugin services.PanelPlugin
|
||||
check := false
|
||||
for _, item := range plugins {
|
||||
if item.Slug == slug {
|
||||
check = true
|
||||
plugin = item
|
||||
break
|
||||
}
|
||||
}
|
||||
if !check {
|
||||
Error(ctx, http.StatusBadRequest, "插件不存在")
|
||||
return
|
||||
}
|
||||
|
||||
var installedPlugin models.Plugin
|
||||
if err := facades.Orm().Query().Where("slug", slug).First(&installedPlugin); err != nil {
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
if installedPlugin.ID == 0 {
|
||||
Error(ctx, http.StatusBadRequest, "插件未安装")
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "卸载插件 " + plugin.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = "bash scripts/plugins/" + plugin.Slug + "/uninstall.sh >> /tmp/" + plugin.Slug + ".log 2>&1"
|
||||
task.Log = "/tmp/" + plugin.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Error("[面板][PluginController] 创建任务失败: " + err.Error())
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
processTask(task.ID)
|
||||
Success(ctx, "任务已提交")
|
||||
}
|
||||
|
||||
// Update 更新插件
|
||||
func (r *PluginController) Update(ctx http.Context) {
|
||||
slug := ctx.Request().Input("slug")
|
||||
plugins := r.plugin.All()
|
||||
|
||||
var plugin services.PanelPlugin
|
||||
check := false
|
||||
for _, item := range plugins {
|
||||
if item.Slug == slug {
|
||||
check = true
|
||||
plugin = item
|
||||
break
|
||||
}
|
||||
}
|
||||
if !check {
|
||||
Error(ctx, http.StatusBadRequest, "插件不存在")
|
||||
return
|
||||
}
|
||||
|
||||
var installedPlugin models.Plugin
|
||||
if err := facades.Orm().Query().Where("slug", slug).First(&installedPlugin); err != nil {
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
if installedPlugin.ID == 0 {
|
||||
Error(ctx, http.StatusBadRequest, "插件未安装")
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "更新插件 " + plugin.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = "bash scripts/plugins/" + plugin.Slug + "/update.sh >> /tmp/" + plugin.Slug + ".log 2>&1"
|
||||
task.Log = "/tmp/" + plugin.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Error("[面板][PluginController] 创建任务失败: " + err.Error())
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
processTask(task.ID)
|
||||
Success(ctx, "任务已提交")
|
||||
}
|
||||
|
||||
// UpdateShow 更新插件首页显示状态
|
||||
func (r *PluginController) UpdateShow(ctx http.Context) {
|
||||
slug := ctx.Request().Input("slug")
|
||||
show := ctx.Request().InputBool("show")
|
||||
|
||||
var plugin models.Plugin
|
||||
if err := facades.Orm().Query().Where("slug", slug).First(&plugin); err != nil {
|
||||
facades.Log().Error("[面板][PluginController] 查询插件失败: " + err.Error())
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
if plugin.ID == 0 {
|
||||
Error(ctx, http.StatusBadRequest, "插件未安装")
|
||||
return
|
||||
}
|
||||
|
||||
plugin.Show = show
|
||||
if err := facades.Orm().Query().Save(&plugin); err != nil {
|
||||
facades.Log().Error("[面板][PluginController] 更新插件失败: " + err.Error())
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
Success(ctx, "操作成功")
|
||||
}
|
||||
|
||||
// processTask 处理任务
|
||||
func processTask(taskID uint) {
|
||||
go func() {
|
||||
err := facades.Queue().Job(&jobs.ProcessTask{}, []queue.Arg{
|
||||
{Type: "uint", Value: taskID},
|
||||
}).Dispatch()
|
||||
if err != nil {
|
||||
facades.Log().Error("[面板][PluginController] 运行任务失败: " + err.Error())
|
||||
return
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package controllers
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -29,6 +29,8 @@ func NewOpenrestyController() *OpenRestyController {
|
||||
|
||||
// Status 获取运行状态
|
||||
func (r *OpenRestyController) Status(ctx http.Context) {
|
||||
Check(ctx, "openresty")
|
||||
|
||||
cmd := exec.Command("bash", "-c", "systemctl status openresty | grep Active | grep -v grep | awk '{print $2}'")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
@@ -51,6 +53,8 @@ func (r *OpenRestyController) Status(ctx http.Context) {
|
||||
|
||||
// Reload 重载配置
|
||||
func (r *OpenRestyController) Reload(ctx http.Context) {
|
||||
Check(ctx, "openresty")
|
||||
|
||||
cmd := exec.Command("bash", "-c", "systemctl reload openresty")
|
||||
_, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
@@ -82,6 +86,8 @@ func (r *OpenRestyController) Reload(ctx http.Context) {
|
||||
|
||||
// Start 启动OpenResty
|
||||
func (r *OpenRestyController) Start(ctx http.Context) {
|
||||
Check(ctx, "openresty")
|
||||
|
||||
cmd := exec.Command("bash", "-c", "systemctl start openresty")
|
||||
_, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
@@ -113,6 +119,8 @@ func (r *OpenRestyController) Start(ctx http.Context) {
|
||||
|
||||
// Stop 停止OpenResty
|
||||
func (r *OpenRestyController) Stop(ctx http.Context) {
|
||||
Check(ctx, "openresty")
|
||||
|
||||
cmd := exec.Command("bash", "-c", "systemctl stop openresty")
|
||||
_, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
52
app/http/controllers/plugins/plugins.go
Normal file
52
app/http/controllers/plugins/plugins.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/facades"
|
||||
|
||||
"panel/app/services"
|
||||
)
|
||||
|
||||
// Check 检查插件是否可用
|
||||
func Check(ctx http.Context, slug string) {
|
||||
plugin := services.NewPluginImpl().GetBySlug(slug)
|
||||
installedPlugin := services.NewPluginImpl().GetInstalledBySlug(slug)
|
||||
installedPlugins, err := services.NewPluginImpl().AllInstalled()
|
||||
if err != nil {
|
||||
facades.Log().Error("[面板][插件] 获取已安装插件失败")
|
||||
ctx.Request().AbortWithStatusJson(http.StatusInternalServerError, "系统内部错误")
|
||||
}
|
||||
|
||||
if installedPlugin.Version != plugin.Version || installedPlugin.Slug != plugin.Slug {
|
||||
ctx.Request().AbortWithStatusJson(http.StatusForbidden, "插件 "+slug+" 需要更新至 "+plugin.Version+" 版本")
|
||||
}
|
||||
|
||||
var lock sync.RWMutex
|
||||
pluginsMap := make(map[string]bool)
|
||||
|
||||
for _, p := range installedPlugins {
|
||||
lock.Lock()
|
||||
pluginsMap[p.Slug] = true
|
||||
lock.Unlock()
|
||||
}
|
||||
|
||||
for _, require := range plugin.Requires {
|
||||
lock.RLock()
|
||||
_, requireFound := pluginsMap[require]
|
||||
lock.RUnlock()
|
||||
if !requireFound {
|
||||
ctx.Request().AbortWithStatusJson(http.StatusForbidden, "插件 "+slug+" 需要依赖 "+require+" 插件")
|
||||
}
|
||||
}
|
||||
|
||||
for _, exclude := range plugin.Excludes {
|
||||
lock.RLock()
|
||||
_, excludeFound := pluginsMap[exclude]
|
||||
lock.RUnlock()
|
||||
if excludeFound {
|
||||
ctx.Request().AbortWithStatusJson(http.StatusForbidden, "插件 "+slug+" 不兼容 "+exclude+" 插件")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// Jwt 确保通过 JWT 鉴权
|
||||
func Jwt() http.Middleware {
|
||||
return func(ctx http.Context) {
|
||||
token := ctx.Request().Input("access_token", "")
|
||||
token := ctx.Request().Header("access_token", ctx.Request().Input("access_token", ""))
|
||||
if len(token) == 0 {
|
||||
ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{
|
||||
"code": 401,
|
||||
|
||||
84
app/jobs/process_task.go
Normal file
84
app/jobs/process_task.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package jobs
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/goravel/framework/facades"
|
||||
|
||||
"panel/app/models"
|
||||
)
|
||||
|
||||
// ProcessTask 处理面板任务
|
||||
type ProcessTask struct {
|
||||
}
|
||||
|
||||
// Signature The name and signature of the job.
|
||||
func (receiver *ProcessTask) Signature() string {
|
||||
return "process_task"
|
||||
}
|
||||
|
||||
// Handle Execute the job.
|
||||
func (receiver *ProcessTask) Handle(args ...any) error {
|
||||
taskID, ok := args[0].(uint)
|
||||
if !ok {
|
||||
facades.Log().Error("[面板][ProcessTask] 任务ID参数错误")
|
||||
return nil
|
||||
}
|
||||
|
||||
for {
|
||||
if !haveRunningTask() {
|
||||
break
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
if err := facades.Orm().Query().Where("id = ?", taskID).Get(&task); err != nil {
|
||||
facades.Log().Errorf("[面板][ProcessTask] 获取任务%d失败: %s", taskID, err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
task.Status = models.TaskStatusRunning
|
||||
if err := facades.Orm().Query().Save(&task); err != nil {
|
||||
facades.Log().Errorf("[面板][ProcessTask] 更新任务%d失败: %s", taskID, err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
facades.Log().Infof("[面板][ProcessTask] 开始执行任务%d", taskID)
|
||||
cmd := exec.Command("bash", "-c", task.Shell)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
task.Status = models.TaskStatusFailed
|
||||
if err := facades.Orm().Query().Save(&task); err != nil {
|
||||
facades.Log().Errorf("[面板][ProcessTask] 更新任务%d失败: %s", taskID, err.Error())
|
||||
return nil
|
||||
}
|
||||
facades.Log().Errorf("[面板][ProcessTask] 任务%d执行失败: %s", taskID, err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
task.Status = models.TaskStatusSuccess
|
||||
if err := facades.Orm().Query().Save(&task); err != nil {
|
||||
facades.Log().Errorf("[面板][ProcessTask] 更新任务%d失败: %s", taskID, err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
facades.Log().Infof("[面板][ProcessTask] 任务%d执行成功", taskID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// haveRunningTask 是否有任务正在执行
|
||||
func haveRunningTask() bool {
|
||||
var task models.Task
|
||||
if err := facades.Orm().Query().Where("status = ?", models.TaskStatusRunning).Get(&task); err != nil {
|
||||
facades.Log().Error("[面板][ProcessTask] 获取任务失败: " + err.Error())
|
||||
return true
|
||||
}
|
||||
|
||||
if task.ID != 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"github.com/goravel/framework/contracts/foundation"
|
||||
"github.com/goravel/framework/contracts/queue"
|
||||
"github.com/goravel/framework/facades"
|
||||
|
||||
"panel/app/jobs"
|
||||
)
|
||||
|
||||
type QueueServiceProvider struct {
|
||||
@@ -18,5 +20,7 @@ func (receiver *QueueServiceProvider) Boot(app foundation.Application) {
|
||||
}
|
||||
|
||||
func (receiver *QueueServiceProvider) Jobs() []queue.Job {
|
||||
return []queue.Job{}
|
||||
return []queue.Job{
|
||||
&jobs.ProcessTask{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ func (receiver *RouteServiceProvider) Boot(app foundation.Application) {
|
||||
receiver.configureRateLimiting()
|
||||
|
||||
routes.Web()
|
||||
routes.Plugin()
|
||||
}
|
||||
|
||||
func (receiver *RouteServiceProvider) configureRateLimiting() {
|
||||
|
||||
79
app/services/plugin.go
Normal file
79
app/services/plugin.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"github.com/goravel/framework/facades"
|
||||
|
||||
"panel/app/models"
|
||||
"panel/plugins/openresty"
|
||||
)
|
||||
|
||||
// PanelPlugin 插件元数据结构
|
||||
type PanelPlugin struct {
|
||||
Name string
|
||||
Author string
|
||||
Description string
|
||||
Slug string
|
||||
Version string
|
||||
Requires []string
|
||||
Excludes []string
|
||||
}
|
||||
|
||||
type Plugin interface {
|
||||
AllInstalled() ([]models.Plugin, error)
|
||||
All() []PanelPlugin
|
||||
}
|
||||
|
||||
type PluginImpl struct {
|
||||
}
|
||||
|
||||
func NewPluginImpl() *PluginImpl {
|
||||
return &PluginImpl{}
|
||||
}
|
||||
|
||||
// AllInstalled 获取已安装的所有插件
|
||||
func (r *PluginImpl) AllInstalled() ([]models.Plugin, error) {
|
||||
var plugins []models.Plugin
|
||||
if err := facades.Orm().Query().Get(&plugins); err != nil {
|
||||
return plugins, err
|
||||
}
|
||||
|
||||
return plugins, nil
|
||||
}
|
||||
|
||||
// All 获取所有插件
|
||||
func (r *PluginImpl) All() []PanelPlugin {
|
||||
var p []PanelPlugin
|
||||
|
||||
p = append(p, PanelPlugin{
|
||||
Name: openresty.Name,
|
||||
Author: openresty.Author,
|
||||
Description: openresty.Description,
|
||||
Slug: openresty.Slug,
|
||||
Version: openresty.Version,
|
||||
Requires: openresty.Requires,
|
||||
Excludes: openresty.Excludes,
|
||||
})
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// GetBySlug 根据slug获取插件
|
||||
func (r *PluginImpl) GetBySlug(slug string) PanelPlugin {
|
||||
for _, item := range r.All() {
|
||||
if item.Slug == slug {
|
||||
return item
|
||||
}
|
||||
}
|
||||
|
||||
return PanelPlugin{}
|
||||
}
|
||||
|
||||
// GetInstalledBySlug 根据slug获取已安装的插件
|
||||
func (r *PluginImpl) GetInstalledBySlug(slug string) models.Plugin {
|
||||
var plugin models.Plugin
|
||||
if err := facades.Orm().Query().Where("slug", slug).Get(&plugin); err != nil {
|
||||
return plugin
|
||||
}
|
||||
|
||||
return plugin
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package bootstrap
|
||||
|
||||
import "panel/plugins/openresty"
|
||||
|
||||
func Plugins() {
|
||||
openresty.Boot()
|
||||
}
|
||||
@@ -8,7 +8,7 @@ func init() {
|
||||
config := facades.Config()
|
||||
config.Add("queue", map[string]any{
|
||||
// Default Queue Connection Name
|
||||
"default": config.Env("QUEUE_CONNECTION", "sync"),
|
||||
"default": "sync",
|
||||
|
||||
// Queue Connections
|
||||
//
|
||||
@@ -18,11 +18,6 @@ func init() {
|
||||
"sync": map[string]any{
|
||||
"driver": "sync",
|
||||
},
|
||||
"redis": map[string]any{
|
||||
"driver": "redis",
|
||||
"connection": "default",
|
||||
"queue": config.Env("REDIS_QUEUE", "default"),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
3
main.go
3
main.go
@@ -25,9 +25,6 @@ func main() {
|
||||
// 启动框架
|
||||
bootstrap.Boot()
|
||||
|
||||
// 加载插件
|
||||
bootstrap.Plugins()
|
||||
|
||||
// 启动 HTTP 服务
|
||||
go func() {
|
||||
if err := facades.Route().Run(); err != nil {
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
package openresty
|
||||
|
||||
const (
|
||||
var (
|
||||
Name = "OpenResty"
|
||||
Author = "耗子"
|
||||
Description = "OpenResty® 是一款基于 NGINX 和 LuaJIT 的 Web 平台。"
|
||||
Slug = "openresty"
|
||||
Version = "1.21.4.1"
|
||||
Requires = ""
|
||||
Excludes = ""
|
||||
Requires = []string{}
|
||||
Excludes = []string{}
|
||||
)
|
||||
|
||||
func Boot() {
|
||||
Route()
|
||||
}
|
||||
|
||||
@@ -63,8 +63,10 @@ layui.define(['laytpl', 'layer'], function (exports) {
|
||||
delete options.success
|
||||
delete options.error
|
||||
|
||||
options.data = JSON.stringify(options.data)
|
||||
|
||||
return $.ajax($.extend({
|
||||
type: 'get', dataType: 'json', success: function (res) {
|
||||
type: 'get', dataType: 'json', contentType: 'application/json', success: function (res) {
|
||||
var statusCode = response.statusCode
|
||||
|
||||
//只有 response 的 code 一切正常才执行 done
|
||||
|
||||
184
public/panel/views/plugin.html
Normal file
184
public/panel/views/plugin.html
Normal file
@@ -0,0 +1,184 @@
|
||||
<title>插件中心</title>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">
|
||||
按钮点击一次即可,请勿重复点击以免重复操作,任务中心在右上方!
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<table id="panel-plugin" lay-filter="panel-plugin"></table>
|
||||
<!-- 操作按钮模板 -->
|
||||
<script type="text/html" id="panel-plugin-control">
|
||||
{{# if(d.installed && d.installed_version != d.version){ }}
|
||||
<a class="layui-btn layui-btn-xs" lay-event="update">更新</a>
|
||||
{{# } }}
|
||||
{{# if(d.installed && d.installed_version == d.version){ }}
|
||||
<a class="layui-btn layui-btn-xs" lay-event="open">管理</a>
|
||||
{{# } }}
|
||||
{{# if(d.installed && d.installed_version == d.version){ }}
|
||||
<a class="layui-btn layui-btn-warm layui-btn-xs" lay-event="uninstall">卸载</a>
|
||||
{{# } }}
|
||||
{{# if(!d.installed){ }}
|
||||
<a class="layui-btn layui-btn-xs" lay-event="install">安装</a>
|
||||
{{# } }}
|
||||
</script>
|
||||
<!-- 首页显示开关 -->
|
||||
<script type="text/html" id="plugin-show">
|
||||
<input type="checkbox" name="plugin-show-home" lay-skin="switch" lay-text="ON|OFF"
|
||||
lay-filter="plugin-show-home"
|
||||
value="{{ d.show }}" data-plugin-slug="{{ d.slug }}"
|
||||
{{ d.show==
|
||||
1 ? 'checked' : '' }}>
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
layui.use(['admin', 'table', 'jquery'], function () {
|
||||
var $ = layui.$
|
||||
, form = layui.form
|
||||
, table = layui.table
|
||||
, admin = layui.admin
|
||||
|
||||
table.render({
|
||||
elem: '#panel-plugin'
|
||||
, url: '/api/panel/plugin/list'
|
||||
, cols: [[
|
||||
{ field: 'slug', hide: true, title: 'Slug' }
|
||||
, { field: 'name', width: 150, title: '插件名', sort: true }
|
||||
, { field: 'description', title: '描述' }
|
||||
, { field: 'author', width: 100, title: '作者' }
|
||||
, { field: 'installed_version', width: 140, title: '已装版本' }
|
||||
, { field: 'version', width: 140, title: '最新版本' }
|
||||
, { field: 'show', title: '首页显示', width: 90, templet: '#plugin-show', unresize: true }
|
||||
, {
|
||||
field: 'control',
|
||||
width: 180,
|
||||
title: '操作',
|
||||
templet: '#panel-plugin-control',
|
||||
fixed: 'right',
|
||||
align: 'left'
|
||||
}
|
||||
]]
|
||||
, page: false
|
||||
, text: '耗子Linux面板:数据加载出现异常!'
|
||||
, done: function () {
|
||||
//element.render('progress');
|
||||
}
|
||||
})
|
||||
|
||||
// 工具条
|
||||
table.on('tool(panel-plugin)', function (obj) {
|
||||
let data = obj.data
|
||||
if (obj.event === 'open') {
|
||||
location.hash = '/plugin/' + data.slug
|
||||
} else if (obj.event === 'install') {
|
||||
layer.confirm('确定安装该插件吗?', function (index) {
|
||||
layer.close(index)
|
||||
admin.req({
|
||||
url: '/api/panel/plugin/install',
|
||||
type: 'POST',
|
||||
data: {
|
||||
slug: data.slug
|
||||
}
|
||||
, success: function (res) {
|
||||
if (res.code === 0) {
|
||||
table.reload('panel-plugin')
|
||||
layer.msg('安装:' + data.name + ' 成功加入任务队列', {
|
||||
icon: 1,
|
||||
time: 1000
|
||||
})
|
||||
} else {
|
||||
layer.msg(res.msg, { icon: 2, time: 1000 })
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
} else if (obj.event === 'uninstall') {
|
||||
layer.confirm('确定卸载该插件吗?', function (index) {
|
||||
layer.close(index)
|
||||
admin.req({
|
||||
url: '/api/panel/plugin/uninstall',
|
||||
type: 'POST',
|
||||
data: {
|
||||
slug: data.slug
|
||||
}
|
||||
, success: function (res) {
|
||||
if (res.code === 0) {
|
||||
table.reload('panel-plugin')
|
||||
layer.msg('卸载:' + data.name + ' 成功加入任务队列', {
|
||||
icon: 1,
|
||||
time: 1000
|
||||
})
|
||||
} else {
|
||||
layer.msg(res.msg, { icon: 2, time: 1000 })
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
} else if (obj.event === 'update') {
|
||||
layer.confirm('确定升级该插件吗?', function (index) {
|
||||
layer.close(index)
|
||||
admin.req({
|
||||
url: '/api/panel/plugin/update',
|
||||
type: 'POST',
|
||||
data: {
|
||||
slug: data.slug
|
||||
}
|
||||
, success: function (res) {
|
||||
if (res.code === 0) {
|
||||
table.reload('panel-plugin')
|
||||
layer.msg('升级:' + data.name + ' 成功加入任务队列', {
|
||||
icon: 1,
|
||||
time: 1000
|
||||
})
|
||||
} else {
|
||||
layer.msg(res.msg, { icon: 2, time: 1000 })
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
form.on('switch(plugin-show-home)', function (obj) {
|
||||
let $ = layui.$
|
||||
let plugin_slug = $(this).data('plugin-slug')
|
||||
let show = obj.elem.checked ? 1 : 0
|
||||
|
||||
admin.req({
|
||||
url: '/api/panel/plugin/updateShow',
|
||||
type: 'POST',
|
||||
data: {
|
||||
slug: plugin_slug,
|
||||
show: show
|
||||
}
|
||||
, success: function (res) {
|
||||
if (res.code === 0) {
|
||||
layer.msg('设置成功', { icon: 1, time: 1000 })
|
||||
} else {
|
||||
// 还原开关状态
|
||||
obj.elem.checked = !obj.elem.checked
|
||||
form.render('checkbox')
|
||||
layer.msg(res.msg, { icon: 2, time: 1000 })
|
||||
}
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error)
|
||||
}
|
||||
})
|
||||
})
|
||||
/*form.render(null, 'plugin-form');
|
||||
|
||||
//搜索
|
||||
form.on('submit(plugin-search-submit)', function (data) {
|
||||
var field = data.field;
|
||||
|
||||
//执行重载
|
||||
table.reload('plugin-search-submit', {
|
||||
where: field
|
||||
});
|
||||
});*/
|
||||
})
|
||||
</script>
|
||||
@@ -1,16 +1,17 @@
|
||||
package openresty
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/goravel/framework/contracts/route"
|
||||
"github.com/goravel/framework/facades"
|
||||
|
||||
"panel/app/http/controllers/plugins"
|
||||
"panel/app/http/middleware"
|
||||
"panel/plugins/openresty/http/controllers"
|
||||
)
|
||||
|
||||
func Route() {
|
||||
// Plugin 加载插件路由
|
||||
func Plugin() {
|
||||
facades.Route().Prefix("api/plugins/openresty").Middleware(middleware.Jwt()).Group(func(route route.Route) {
|
||||
openRestyController := controllers.NewOpenrestyController()
|
||||
openRestyController := plugins.NewOpenrestyController()
|
||||
route.Get("status", openRestyController.Status)
|
||||
route.Post("reload", openRestyController.Reload)
|
||||
route.Post("start", openRestyController.Start)
|
||||
@@ -33,6 +33,14 @@ func Web() {
|
||||
websiteController := controllers.NewWebsiteController()
|
||||
r.Get("list", websiteController.List)
|
||||
})
|
||||
r.Prefix("plugin").Middleware(middleware.Jwt()).Group(func(r route.Route) {
|
||||
pluginController := controllers.NewPluginController()
|
||||
r.Get("list", pluginController.List)
|
||||
r.Post("install", pluginController.Install)
|
||||
r.Post("uninstall", pluginController.Uninstall)
|
||||
r.Post("update", pluginController.Update)
|
||||
r.Post("updateShow", pluginController.UpdateShow)
|
||||
})
|
||||
})
|
||||
|
||||
facades.Route().Fallback(func(ctx http.Context) {
|
||||
|
||||
353
scripts/plugins/openresty/install.sh
Normal file
353
scripts/plugins/openresty/install.sh
Normal file
@@ -0,0 +1,353 @@
|
||||
#!/bin/bash
|
||||
|
||||
: '
|
||||
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="+----------------------------------------------------"
|
||||
|
||||
ARCH=$(uname -m)
|
||||
cpuCore=$(cat /proc/cpuinfo | grep "processor" | wc -l)
|
||||
OS=$(source /etc/os-release && [[ "$ID" == "debian" ]] && echo "debian" || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]]; } && echo "centos" || echo "unknown")
|
||||
downloadUrl="https://dl.cdn.haozi.net"
|
||||
setupPath="/www"
|
||||
openrestyPath="${setupPath}/server/openresty"
|
||||
openrestyVersion="1.21.4.1"
|
||||
|
||||
# 安装依赖
|
||||
if [ "${OS}" == "centos" ]; then
|
||||
dnf install gcc gcc-c++ make tar unzip gd gd-devel git-core perl oniguruma oniguruma-devel bison yajl yajl-devel curl curl-devel libtermcap-devel ncurses-devel libevent-devel readline-devel libuuid-devel brotli-devel icu libicu libicu-devel openssl openssl-devel -y
|
||||
elif [ "${OS}" == "debian" ]; then
|
||||
apt install gcc g++ make tar unzip libgd3 libgd-dev git perl libonig-dev bison libyajl-dev curl libcurl4-openssl-dev libncurses5-dev libevent-dev libreadline-dev uuid-dev libbrotli-dev icu-devtools libicu-dev openssl libssl-dev -y
|
||||
else
|
||||
echo -e $HR
|
||||
echo "错误:耗子Linux面板不支持该系统"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 准备目录
|
||||
rm -rf ${openrestyPath}
|
||||
mkdir -p ${openrestyPath}
|
||||
cd ${openrestyPath}
|
||||
|
||||
# 下载源码
|
||||
wget -T 120 -O ${openrestyPath}/openresty-${openrestyVersion}.tar.gz ${downloadUrl}/panel/plugins/openresty/openresty-${openrestyVersion}.tar.gz
|
||||
tar -xvf openresty-${openrestyVersion}.tar.gz
|
||||
rm -f openresty-${openrestyVersion}.tar.gz
|
||||
mv openresty-${openrestyVersion} src
|
||||
cd src
|
||||
|
||||
# openssl
|
||||
wget -T 120 -O openssl.tar.gz ${downloadUrl}/panel/plugins/openresty/openssl-1.1.1u.tar.gz
|
||||
tar -zxvf openssl.tar.gz
|
||||
rm -f openssl.tar.gz
|
||||
mv openssl-1.1.1u openssl
|
||||
rm -f openssl.tar.gz
|
||||
|
||||
# pcre
|
||||
wget -T 60 -O pcre-8.45.tar.gz ${downloadUrl}/panel/plugins/openresty/pcre-8.45.tar.gz
|
||||
tar -zxvf pcre-8.45.tar.gz
|
||||
rm -f pcre-8.45.tar.gz
|
||||
mv pcre-8.45 pcre
|
||||
rm -f pcre-8.45.tar.gz
|
||||
|
||||
# ngx_cache_purge
|
||||
wget -T 20 -O ngx_cache_purge.tar.gz ${downloadUrl}/panel/plugins/openresty/ngx_cache_purge-2.3.tar.gz
|
||||
tar -zxvf ngx_cache_purge.tar.gz
|
||||
rm -f ngx_cache_purge.tar.gz
|
||||
mv ngx_cache_purge-2.3 ngx_cache_purge
|
||||
rm -f ngx_cache_purge.tar.gz
|
||||
|
||||
# nginx-sticky-module
|
||||
wget -T 20 -O nginx-sticky-module.zip ${downloadUrl}/panel/plugins/openresty/nginx-sticky-module.zip
|
||||
unzip -o nginx-sticky-module.zip
|
||||
rm -f nginx-sticky-module.zip
|
||||
|
||||
# nginx-dav-ext-module
|
||||
wget -T 20 -O nginx-dav-ext-module-3.0.0.tar.gz ${downloadUrl}/panel/plugins/openresty/nginx-dav-ext-module-3.0.0.tar.gz
|
||||
tar -xvf nginx-dav-ext-module-3.0.0.tar.gz
|
||||
rm -f nginx-dav-ext-module-3.0.0.tar.gz
|
||||
mv nginx-dav-ext-module-3.0.0 nginx-dav-ext-module
|
||||
|
||||
# waf
|
||||
cd ${openrestyPath}
|
||||
git clone -b lts https://ghproxy.com/https://github.com/ADD-SP/ngx_waf.git
|
||||
if [ "$?" != "0" ]; then
|
||||
echo -e $HR
|
||||
echo "错误:OpenResty waf拓展下载失败,请截图错误信息寻求帮助。"
|
||||
rm -rf ${openrestyPath}
|
||||
exit 1
|
||||
fi
|
||||
git clone -b v2.3.0 https://ghproxy.com/https://github.com/troydhanson/uthash.git
|
||||
if [ "$?" != "0" ]; then
|
||||
echo -e $HR
|
||||
echo "错误:OpenResty waf拓展uthash下载失败,请截图错误信息寻求帮助。"
|
||||
rm -rf ${openrestyPath}
|
||||
exit 1
|
||||
fi
|
||||
cd ngx_waf/inc
|
||||
wget -T 60 -O libinjection.zip ${downloadUrl}/panel/plugins/openresty/libinjection-3.10.0.zip
|
||||
unzip -o libinjection.zip
|
||||
mv libinjection-3.10.0 libinjection
|
||||
rm -rf libinjection.zip
|
||||
cd ../
|
||||
make -j${cpuCore}
|
||||
if [ "$?" != "0" ]; then
|
||||
echo -e $HR
|
||||
echo "错误:OpenResty waf拓展初始化失败,请截图错误信息寻求帮助。"
|
||||
rm -rf ${openrestyPath}
|
||||
exit 1
|
||||
fi
|
||||
cd ${openrestyPath}/src
|
||||
|
||||
# brotli
|
||||
wget -T 20 -O ngx_brotli.zip ${downloadUrl}/panel/plugins/openresty/ngx_brotli-1.0.0rc.zip
|
||||
unzip -o ngx_brotli.zip
|
||||
mv ngx_brotli-1.0.0rc ngx_brotli
|
||||
cd ngx_brotli/deps
|
||||
rm -rf brotli
|
||||
wget -T 20 -O brotli.zip ${downloadUrl}/panel/plugins/openresty/brotli-1.0.9.zip
|
||||
unzip -o brotli.zip
|
||||
mv brotli-1.0.9 brotli
|
||||
cd ${openrestyPath}/src
|
||||
|
||||
cd ${openrestyPath}/src
|
||||
export LD_LIBRARY_PATH=/usr/local/lib/:$LD_LIBRARY_PATH
|
||||
export LIB_UTHASH=${openrestyPath}/uthash
|
||||
|
||||
./configure --user=www --group=www --prefix=${openrestyPath} --with-luajit --add-module=${openrestyPath}/src/ngx_cache_purge --add-module=${openrestyPath}/src/nginx-sticky-module --with-openssl=${openrestyPath}/src/openssl --with-pcre=${openrestyPath}/src/pcre --with-http_v2_module --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module --with-http_stub_status_module --with-http_ssl_module --with-http_image_filter_module --with-http_gzip_static_module --with-http_gunzip_module --with-ipv6 --with-http_sub_module --with-http_flv_module --with-http_addition_module --with-http_realip_module --with-http_mp4_module --with-ld-opt="-Wl,-E" --with-cc-opt="-O2 -std=gnu99" --with-cpu-opt="amd64" --with-http_dav_module --add-module=${openrestyPath}/src/nginx-dav-ext-module --add-module=${openrestyPath}/src/ngx_brotli --add-module=${openrestyPath}/ngx_waf
|
||||
make -j${cpuCore}
|
||||
if [ "$?" != "0" ]; then
|
||||
echo -e $HR
|
||||
echo "提示:OpenResty多线程编译失败,尝试单线程编译..."
|
||||
make
|
||||
if [ "$?" != "0" ]; then
|
||||
echo -e $HR
|
||||
echo "错误:OpenResty编译失败,请截图错误信息寻求帮助。"
|
||||
rm -rf ${openrestyPath}
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
make install
|
||||
if [ ! -f "${openrestyPath}/nginx/sbin/nginx" ]; then
|
||||
echo -e $HR
|
||||
echo "错误:OpenResty安装失败,请截图错误信息寻求帮助。"
|
||||
rm -rf ${openrestyPath}
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 设置软链接
|
||||
ln -sf ${openrestyPath}/nginx/html ${openrestyPath}/html
|
||||
ln -sf ${openrestyPath}/nginx/conf ${openrestyPath}/conf
|
||||
ln -sf ${openrestyPath}/nginx/logs ${openrestyPath}/logs
|
||||
ln -sf ${openrestyPath}/nginx/sbin ${openrestyPath}/sbin
|
||||
ln -sf ${openrestyPath}/nginx/sbin/nginx /usr/bin/openresty
|
||||
rm -f ${openrestyPath}/conf/nginx.conf
|
||||
|
||||
# 创建配置目录
|
||||
cd ${openrestyPath}
|
||||
rm -f openresty-${openrestyVersion}.tar.gz
|
||||
rm -rf src
|
||||
mkdir -p /www/wwwroot/default
|
||||
mkdir -p /www/wwwlogs
|
||||
mkdir -p /www/server/vhost/openresty
|
||||
mkdir -p /www/server/vhost/openresty/rewrite
|
||||
mkdir -p /www/server/vhost/openresty/ssl
|
||||
|
||||
# 写入主配置文件
|
||||
cat >${openrestyPath}/conf/nginx.conf <<EOF
|
||||
# 该文件为OpenResty主配置文件,不建议随意修改!
|
||||
user www www;
|
||||
worker_processes auto;
|
||||
error_log /www/wwwlogs/openresty_error.log crit;
|
||||
pid /www/server/openresty/logs/nginx.pid;
|
||||
worker_rlimit_nofile 51200;
|
||||
|
||||
stream {
|
||||
log_format tcp_format '\$time_local|\$remote_addr|\$protocol|\$status|\$bytes_sent|\$bytes_received|\$session_time|\$upstream_addr|\$upstream_bytes_sent|\$upstream_bytes_received|\$upstream_connect_time';
|
||||
|
||||
access_log /www/wwwlogs/tcp-access.log tcp_format;
|
||||
error_log /www/wwwlogs/tcp-error.log;
|
||||
}
|
||||
|
||||
events {
|
||||
use epoll;
|
||||
worker_connections 51200;
|
||||
multi_accept on;
|
||||
}
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
include proxy.conf;
|
||||
default_type application/octet-stream;
|
||||
|
||||
server_names_hash_bucket_size 512;
|
||||
client_header_buffer_size 32k;
|
||||
large_client_header_buffers 4 32k;
|
||||
client_max_body_size 200m;
|
||||
client_body_buffer_size 10M;
|
||||
client_body_in_file_only off;
|
||||
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
|
||||
keepalive_timeout 60;
|
||||
|
||||
tcp_nodelay on;
|
||||
|
||||
fastcgi_connect_timeout 300;
|
||||
fastcgi_send_timeout 300;
|
||||
fastcgi_read_timeout 300;
|
||||
fastcgi_buffer_size 64k;
|
||||
fastcgi_buffers 8 64k;
|
||||
fastcgi_busy_buffers_size 256k;
|
||||
fastcgi_temp_file_write_size 256k;
|
||||
fastcgi_intercept_errors on;
|
||||
|
||||
gzip on;
|
||||
gzip_min_length 1k;
|
||||
gzip_buffers 32 4k;
|
||||
gzip_http_version 1.1;
|
||||
gzip_comp_level 6;
|
||||
gzip_types *;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_disable "MSIE [1-6]\.";
|
||||
brotli on;
|
||||
brotli_comp_level 6;
|
||||
brotli_min_length 10;
|
||||
brotli_window 1m;
|
||||
brotli_types *;
|
||||
brotli_static on;
|
||||
|
||||
limit_conn_zone \$binary_remote_addr zone=perip:10m;
|
||||
limit_conn_zone \$server_name zone=perserver:10m;
|
||||
|
||||
server_tokens off;
|
||||
access_log off;
|
||||
|
||||
# 服务状态页
|
||||
server {
|
||||
listen 80;
|
||||
server_name 127.0.0.1;
|
||||
allow 127.0.0.1;
|
||||
|
||||
location /nginx_status {
|
||||
stub_status on;
|
||||
access_log off;
|
||||
}
|
||||
location ~ ^/phpfpm_status/(?<version>\d+)$ {
|
||||
fastcgi_pass unix:/tmp/php-cgi-$version.sock;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
include /www/server/vhost/openresty/*.conf;
|
||||
}
|
||||
EOF
|
||||
# 写入pathinfo配置文件
|
||||
cat >${openrestyPath}/conf/pathinfo.conf <<EOF
|
||||
set \$real_script_name \$fastcgi_script_name;
|
||||
if (\$fastcgi_script_name ~ "^(.+?\.php)(/.+)$") {
|
||||
set \$real_script_name \$1;
|
||||
set \$path_info \$2;
|
||||
}
|
||||
fastcgi_param SCRIPT_FILENAME \$document_root\$real_script_name;
|
||||
fastcgi_param SCRIPT_NAME \$real_script_name;
|
||||
fastcgi_param PATH_INFO \$path_info;
|
||||
EOF
|
||||
# 写入默认站点页
|
||||
cat >${openrestyPath}/html/index.html <<EOF
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>耗子Linux面板</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>耗子Linux面板</h1>
|
||||
<p>这是耗子Linux面板的OpenResty默认页面!</p>
|
||||
<p>当您看到此页面,说明该域名尚未与站点绑定。</p>
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
|
||||
# 写入站点停止页
|
||||
cat >${openrestyPath}/html/stop.html <<EOF
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>网站已停止 - 耗子Linux面板</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>耗子Linux面板</h1>
|
||||
<p>该网站已被管理员停止访问!</p>
|
||||
<p>当您看到此页面,说明该网站已被管理员停止对外访问,请联系管理员了解详情。</p>
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
|
||||
# 处理文件权限
|
||||
chmod 755 ${openrestyPath}
|
||||
chmod 644 ${openrestyPath}/html
|
||||
chmod -R 755 /www/wwwroot
|
||||
chown -R www:www /www/wwwroot
|
||||
chmod -R 644 /www/server/vhost
|
||||
|
||||
# 写入无php配置文件
|
||||
echo "" >${openrestyPath}/conf/enable-php-00.conf
|
||||
# 写入代理默认配置文件
|
||||
cat >${openrestyPath}/conf/proxy.conf <<EOF
|
||||
proxy_temp_path ${openrestyPath}/proxy_temp_dir;
|
||||
proxy_cache_path ${openrestyPath}/proxy_cache_dir levels=1:2 keys_zone=cache_one:20m inactive=1d max_size=5g;
|
||||
proxy_connect_timeout 60;
|
||||
proxy_read_timeout 60;
|
||||
proxy_send_timeout 60;
|
||||
proxy_buffer_size 32k;
|
||||
proxy_buffers 4 64k;
|
||||
proxy_busy_buffers_size 128k;
|
||||
proxy_temp_file_write_size 128k;
|
||||
proxy_next_upstream error timeout invalid_header http_500 http_503 http_404;
|
||||
proxy_cache cache_one;
|
||||
EOF
|
||||
|
||||
# 建立日志目录
|
||||
mkdir -p /www/wwwlogs/waf
|
||||
chown www:www /www/wwwlogs/waf
|
||||
chmod 755 /www/wwwlogs/waf
|
||||
|
||||
# 写入服务文件
|
||||
cat >/lib/systemd/system/openresty.service <<EOF
|
||||
[Unit]
|
||||
Description=The OpenResty Application Platform
|
||||
After=syslog.target network-online.target remote-fs.target nss-lookup.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
PIDFile=/www/server/openresty/logs/nginx.pid
|
||||
ExecStartPre=/www/server/openresty/sbin/nginx -t -c /www/server/openresty/conf/nginx.conf
|
||||
ExecStart=/www/server/openresty/sbin/nginx -c /www/server/openresty/conf/nginx.conf
|
||||
ExecReload=/www/server/openresty/sbin/nginx -s reload
|
||||
ExecStop=/www/server/openresty/sbin/nginx -s quit
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable openresty.service
|
||||
systemctl start openresty.service
|
||||
19
scripts/plugins/openresty/uninstall.sh
Normal file
19
scripts/plugins/openresty/uninstall.sh
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
: '
|
||||
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="+----------------------------------------------------"
|
||||
Reference in New Issue
Block a user