mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 12:40:25 +08:00
feat: 更新说明
This commit is contained in:
@@ -20,6 +20,10 @@
|
||||
|
||||
交流QQ群:[12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | 论坛:[tom.moe](https://tom.moe) | 赞助:[爱发电](https://afdian.com/a/TheTNB)
|
||||
|
||||
## 项目现状
|
||||
|
||||
**目前我在着手使用新的「自研」框架重构本项目,由于更改非常大需要一定时间,预期 8 月底会带来新的更新。**
|
||||
|
||||
## 优势
|
||||
|
||||
1. **极低占用:** 在 Debian 12 下部署面板 + LNMP 环境,内存占用不到 500 MB,遥遥领先于使用容器化的其他面板。
|
||||
|
||||
@@ -1,187 +0,0 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/v2/pkg/h"
|
||||
"github.com/TheTNB/panel/v2/pkg/io"
|
||||
"github.com/TheTNB/panel/v2/pkg/shell"
|
||||
"github.com/TheTNB/panel/v2/pkg/str"
|
||||
"github.com/TheTNB/panel/v2/pkg/systemctl"
|
||||
"github.com/TheTNB/panel/v2/pkg/types"
|
||||
)
|
||||
|
||||
type OpenRestyController struct {
|
||||
// Dependent services
|
||||
}
|
||||
|
||||
func NewOpenrestyController() *OpenRestyController {
|
||||
return &OpenRestyController{}
|
||||
}
|
||||
|
||||
// GetConfig
|
||||
//
|
||||
// @Summary 获取配置
|
||||
// @Tags 插件-OpenResty
|
||||
// @Produce json
|
||||
// @Security BearerToken
|
||||
// @Success 200 {object} controllers.SuccessResponse
|
||||
// @Router /plugins/openresty/config [get]
|
||||
func (r *OpenRestyController) GetConfig(ctx http.Context) http.Response {
|
||||
config, err := io.Read("/www/server/openresty/conf/nginx.conf")
|
||||
if err != nil {
|
||||
return h.Error(ctx, http.StatusInternalServerError, "获取配置失败")
|
||||
}
|
||||
|
||||
return h.Success(ctx, config)
|
||||
}
|
||||
|
||||
// SaveConfig
|
||||
//
|
||||
// @Summary 保存配置
|
||||
// @Tags 插件-OpenResty
|
||||
// @Produce json
|
||||
// @Security BearerToken
|
||||
// @Param config body string true "配置"
|
||||
// @Success 200 {object} controllers.SuccessResponse
|
||||
// @Router /plugins/openresty/config [post]
|
||||
func (r *OpenRestyController) SaveConfig(ctx http.Context) http.Response {
|
||||
config := ctx.Request().Input("config")
|
||||
if len(config) == 0 {
|
||||
return h.Error(ctx, http.StatusInternalServerError, "配置不能为空")
|
||||
}
|
||||
|
||||
if err := io.Write("/www/server/openresty/conf/nginx.conf", config, 0644); err != nil {
|
||||
return h.Error(ctx, http.StatusInternalServerError, "保存配置失败")
|
||||
}
|
||||
|
||||
if err := systemctl.Reload("openresty"); err != nil {
|
||||
_, err = shell.Execf("openresty -t")
|
||||
return h.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("重载服务失败: %v", err))
|
||||
}
|
||||
|
||||
return h.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// ErrorLog
|
||||
//
|
||||
// @Summary 获取错误日志
|
||||
// @Tags 插件-OpenResty
|
||||
// @Produce json
|
||||
// @Security BearerToken
|
||||
// @Success 200 {object} controllers.SuccessResponse
|
||||
// @Router /plugins/openresty/errorLog [get]
|
||||
func (r *OpenRestyController) ErrorLog(ctx http.Context) http.Response {
|
||||
if !io.Exists("/www/wwwlogs/nginx_error.log") {
|
||||
return h.Success(ctx, "")
|
||||
}
|
||||
|
||||
out, err := shell.Execf("tail -n 100 /www/wwwlogs/openresty_error.log")
|
||||
if err != nil {
|
||||
return h.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
return h.Success(ctx, out)
|
||||
}
|
||||
|
||||
// ClearErrorLog
|
||||
//
|
||||
// @Summary 清空错误日志
|
||||
// @Tags 插件-OpenResty
|
||||
// @Produce json
|
||||
// @Security BearerToken
|
||||
// @Success 200 {object} controllers.SuccessResponse
|
||||
// @Router /plugins/openresty/clearErrorLog [post]
|
||||
func (r *OpenRestyController) ClearErrorLog(ctx http.Context) http.Response {
|
||||
if out, err := shell.Execf("echo '' > /www/wwwlogs/openresty_error.log"); err != nil {
|
||||
return h.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
return h.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// Load
|
||||
//
|
||||
// @Summary 获取负载状态
|
||||
// @Tags 插件-OpenResty
|
||||
// @Produce json
|
||||
// @Security BearerToken
|
||||
// @Success 200 {object} controllers.SuccessResponse
|
||||
// @Router /plugins/openresty/load [get]
|
||||
func (r *OpenRestyController) Load(ctx http.Context) http.Response {
|
||||
client := resty.New().SetTimeout(10 * time.Second)
|
||||
resp, err := client.R().Get("http://127.0.0.1/nginx_status")
|
||||
if err != nil || !resp.IsSuccess() {
|
||||
return h.Success(ctx, []types.NV{})
|
||||
}
|
||||
|
||||
raw := resp.String()
|
||||
var data []types.NV
|
||||
|
||||
workers, err := shell.Execf("ps aux | grep nginx | grep 'worker process' | wc -l")
|
||||
if err != nil {
|
||||
return h.Error(ctx, http.StatusInternalServerError, "获取负载失败")
|
||||
}
|
||||
data = append(data, types.NV{
|
||||
Name: "工作进程",
|
||||
Value: workers,
|
||||
})
|
||||
|
||||
out, err := shell.Execf("ps aux | grep nginx | grep 'worker process' | awk '{memsum+=$6};END {print memsum}'")
|
||||
if err != nil {
|
||||
return h.Error(ctx, http.StatusInternalServerError, "获取负载失败")
|
||||
}
|
||||
mem := str.FormatBytes(cast.ToFloat64(out))
|
||||
data = append(data, types.NV{
|
||||
Name: "内存占用",
|
||||
Value: mem,
|
||||
})
|
||||
|
||||
match := regexp.MustCompile(`Active connections:\s+(\d+)`).FindStringSubmatch(raw)
|
||||
if len(match) == 2 {
|
||||
data = append(data, types.NV{
|
||||
Name: "活跃连接数",
|
||||
Value: match[1],
|
||||
})
|
||||
}
|
||||
|
||||
match = regexp.MustCompile(`server accepts handled requests\s+(\d+)\s+(\d+)\s+(\d+)`).FindStringSubmatch(raw)
|
||||
if len(match) == 4 {
|
||||
data = append(data, types.NV{
|
||||
Name: "总连接次数",
|
||||
Value: match[1],
|
||||
})
|
||||
data = append(data, types.NV{
|
||||
Name: "总握手次数",
|
||||
Value: match[2],
|
||||
})
|
||||
data = append(data, types.NV{
|
||||
Name: "总请求次数",
|
||||
Value: match[3],
|
||||
})
|
||||
}
|
||||
|
||||
match = regexp.MustCompile(`Reading:\s+(\d+)\s+Writing:\s+(\d+)\s+Waiting:\s+(\d+)`).FindStringSubmatch(raw)
|
||||
if len(match) == 4 {
|
||||
data = append(data, types.NV{
|
||||
Name: "请求数",
|
||||
Value: match[1],
|
||||
})
|
||||
data = append(data, types.NV{
|
||||
Name: "响应数",
|
||||
Value: match[2],
|
||||
})
|
||||
data = append(data, types.NV{
|
||||
Name: "驻留进程",
|
||||
Value: match[3],
|
||||
})
|
||||
}
|
||||
|
||||
return h.Success(ctx, data)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package plugins
|
||||
package openresty
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
@@ -19,18 +19,18 @@ import (
|
||||
"github.com/TheTNB/panel/v2/pkg/types"
|
||||
)
|
||||
|
||||
type Fail2banController struct {
|
||||
type Controller struct {
|
||||
website internal.Website
|
||||
}
|
||||
|
||||
func NewFail2banController() *Fail2banController {
|
||||
return &Fail2banController{
|
||||
func NewController() *Controller {
|
||||
return &Controller{
|
||||
website: services.NewWebsiteImpl(),
|
||||
}
|
||||
}
|
||||
|
||||
// List 所有 Fail2ban 规则
|
||||
func (r *Fail2banController) List(ctx http.Context) http.Response {
|
||||
func (r *Controller) List(ctx http.Context) http.Response {
|
||||
raw, err := io.Read("/etc/fail2ban/jail.local")
|
||||
if err != nil {
|
||||
return h.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
@@ -77,7 +77,7 @@ func (r *Fail2banController) List(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// Add 添加 Fail2ban 规则
|
||||
func (r *Fail2banController) Add(ctx http.Context) http.Response {
|
||||
func (r *Controller) Add(ctx http.Context) http.Response {
|
||||
if sanitize := h.Sanitize(ctx, map[string]string{
|
||||
"name": "required",
|
||||
"type": "required|in:website,service",
|
||||
@@ -217,7 +217,7 @@ logpath = ` + logPath + `
|
||||
}
|
||||
|
||||
// Delete 删除规则
|
||||
func (r *Fail2banController) Delete(ctx http.Context) http.Response {
|
||||
func (r *Controller) Delete(ctx http.Context) http.Response {
|
||||
jailName := ctx.Request().Input("name")
|
||||
raw, err := io.Read("/etc/fail2ban/jail.local")
|
||||
if err != nil {
|
||||
@@ -242,7 +242,7 @@ func (r *Fail2banController) Delete(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// BanList 获取封禁列表
|
||||
func (r *Fail2banController) BanList(ctx http.Context) http.Response {
|
||||
func (r *Controller) BanList(ctx http.Context) http.Response {
|
||||
name := ctx.Request().Input("name")
|
||||
if len(name) == 0 {
|
||||
return h.Error(ctx, http.StatusUnprocessableEntity, "缺少参数")
|
||||
@@ -283,7 +283,7 @@ func (r *Fail2banController) BanList(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// Unban 解封
|
||||
func (r *Fail2banController) Unban(ctx http.Context) http.Response {
|
||||
func (r *Controller) Unban(ctx http.Context) http.Response {
|
||||
name := ctx.Request().Input("name")
|
||||
ip := ctx.Request().Input("ip")
|
||||
if len(name) == 0 || len(ip) == 0 {
|
||||
@@ -298,7 +298,7 @@ func (r *Fail2banController) Unban(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// SetWhiteList 设置白名单
|
||||
func (r *Fail2banController) SetWhiteList(ctx http.Context) http.Response {
|
||||
func (r *Controller) SetWhiteList(ctx http.Context) http.Response {
|
||||
ip := ctx.Request().Input("ip")
|
||||
if len(ip) == 0 {
|
||||
return h.Error(ctx, http.StatusUnprocessableEntity, "缺少参数")
|
||||
@@ -327,7 +327,7 @@ func (r *Fail2banController) SetWhiteList(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// GetWhiteList 获取白名单
|
||||
func (r *Fail2banController) GetWhiteList(ctx http.Context) http.Response {
|
||||
func (r *Controller) GetWhiteList(ctx http.Context) http.Response {
|
||||
raw, err := io.Read("/etc/fail2ban/jail.local")
|
||||
if err != nil {
|
||||
return h.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
39
app/plugins/fail2ban/main.go
Normal file
39
app/plugins/fail2ban/main.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package openresty
|
||||
|
||||
import (
|
||||
"github.com/goravel/framework/contracts/foundation"
|
||||
"github.com/goravel/framework/contracts/route"
|
||||
|
||||
"github.com/TheTNB/panel/v2/app/http/middleware"
|
||||
"github.com/TheTNB/panel/v2/app/plugins/loader"
|
||||
"github.com/TheTNB/panel/v2/pkg/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
loader.Register(&types.Plugin{
|
||||
Name: "Fail2ban",
|
||||
Description: "Fail2ban 扫描系统日志文件并从中找出多次尝试失败的IP地址,将该IP地址加入防火墙的拒绝访问列表中",
|
||||
Slug: "fail2ban",
|
||||
Version: "1.0.2",
|
||||
Requires: []string{},
|
||||
Excludes: []string{},
|
||||
Install: `bash /www/panel/scripts/fail2ban/install.sh`,
|
||||
Uninstall: `bash /www/panel/scripts/fail2ban/uninstall.sh`,
|
||||
Update: `bash /www/panel/scripts/fail2ban/update.sh`,
|
||||
Boot: func(app foundation.Application) {
|
||||
RouteFacade := app.MakeRoute()
|
||||
RouteFacade.Prefix("api/plugins/fail2ban").Middleware(middleware.Session(), middleware.MustInstall()).Group(func(r route.Router) {
|
||||
r.Prefix("openresty").Group(func(route route.Router) {
|
||||
controller := NewController()
|
||||
route.Get("jails", controller.List)
|
||||
route.Post("jails", controller.Add)
|
||||
route.Delete("jails", controller.Delete)
|
||||
route.Get("jails/{name}", controller.BanList)
|
||||
route.Post("unban", controller.Unban)
|
||||
route.Post("whiteList", controller.SetWhiteList)
|
||||
route.Get("whiteList", controller.GetWhiteList)
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -11,7 +11,7 @@ func All() []*types.Plugin {
|
||||
return data
|
||||
}
|
||||
|
||||
// New 新注册插件
|
||||
func New(plugin *types.Plugin) {
|
||||
// Register 注册插件
|
||||
func Register(plugin *types.Plugin) {
|
||||
data = append(data, plugin)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
loader.New(&types.Plugin{
|
||||
loader.Register(&types.Plugin{
|
||||
Name: "OpenResty",
|
||||
Description: "OpenResty® 是一款基于 NGINX 和 LuaJIT 的 Web 平台",
|
||||
Slug: "openresty",
|
||||
|
||||
Reference in New Issue
Block a user