From 0c3c47361bfc361baf01a7b5a365252fcb12e166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Mon, 3 Jun 2024 02:07:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20frp=E7=AE=A1=E7=90=86=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/plugins/frp_controller.go | 225 +++++++++++++ .../controllers/plugins/rsync_controller.go | 7 +- app/http/requests/plugins/frp/service.go | 32 ++ .../requests/plugins/frp/update_config.go | 34 ++ docs/docs.go | 309 +++++++++++++++++- docs/swagger.json | 309 +++++++++++++++++- docs/swagger.yaml | 190 ++++++++++- internal/services/plugin.go | 39 +-- pkg/tools/service.go | 18 + routes/plugin.go | 11 + scripts/frp/install.sh | 98 ++++++ scripts/frp/uninstall.sh | 37 +++ scripts/rsync/uninstall.sh | 5 + scripts/supervisor/uninstall.sh | 4 + {internal => types}/plugin_list.go | 55 ++-- 15 files changed, 1301 insertions(+), 72 deletions(-) create mode 100644 app/http/controllers/plugins/frp_controller.go create mode 100644 app/http/requests/plugins/frp/service.go create mode 100644 app/http/requests/plugins/frp/update_config.go create mode 100644 scripts/frp/install.sh create mode 100644 scripts/frp/uninstall.sh rename {internal => types}/plugin_list.go (90%) diff --git a/app/http/controllers/plugins/frp_controller.go b/app/http/controllers/plugins/frp_controller.go new file mode 100644 index 00000000..11ca7957 --- /dev/null +++ b/app/http/controllers/plugins/frp_controller.go @@ -0,0 +1,225 @@ +package plugins + +import ( + "fmt" + "github.com/goravel/framework/contracts/http" + + "github.com/TheTNB/panel/app/http/controllers" + requests "github.com/TheTNB/panel/app/http/requests/plugins/frp" + "github.com/TheTNB/panel/pkg/tools" +) + +type FrpController struct { +} + +func NewFrpController() *FrpController { + return &FrpController{} +} + +// Status +// +// @Summary 服务状态 +// @Description 获取 Frp 服务状态 +// @Tags 插件-Frp +// @Produce json +// @Security BearerToken +// @Success 200 {object} controllers.SuccessResponse +// @Router /plugins/frp/status [get] +func (r *FrpController) Status(ctx http.Context) http.Response { + frps, err := tools.ServiceStatus("frps") + if err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, "获取 frpc 服务运行状态失败") + } + frpc, err := tools.ServiceStatus("frpc") + if err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, "获取 frpc 服务运行状态失败") + } + + return controllers.Success(ctx, http.Json{ + "frps": frps, + "frpc": frpc, + }) +} + +// Enable +// +// @Summary 启用服务 +// @Description 启用 Frp 服务 +// @Tags 插件-Frp +// @Produce json +// @Security BearerToken +// @Param data body requests.Service true "request" +// @Success 200 {object} controllers.SuccessResponse +// @Router /plugins/frp/enable [post] +func (r *FrpController) Enable(ctx http.Context) http.Response { + var serviceRequest requests.Service + sanitize := controllers.Sanitize(ctx, &serviceRequest) + if sanitize != nil { + return sanitize + } + + if err := tools.ServiceEnable(serviceRequest.Service); err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("启用 %s 服务失败", serviceRequest.Service)) + } + + return controllers.Success(ctx, nil) +} + +// Disable +// +// @Summary 禁用服务 +// @Description 禁用 Frp 服务 +// @Tags 插件-Frp +// @Produce json +// @Security BearerToken +// @Param data body requests.Service true "request" +// @Success 200 {object} controllers.SuccessResponse +// @Router /plugins/frp/disable [post] +func (r *FrpController) Disable(ctx http.Context) http.Response { + var serviceRequest requests.Service + sanitize := controllers.Sanitize(ctx, &serviceRequest) + if sanitize != nil { + return sanitize + } + + if err := tools.ServiceDisable(serviceRequest.Service); err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("禁用 %s 服务失败", serviceRequest.Service)) + } + + return controllers.Success(ctx, nil) +} + +// Restart +// +// @Summary 重启服务 +// @Description 重启 Frp 服务 +// @Tags 插件-Frp +// @Produce json +// @Security BearerToken +// @param data body requests.Service true "request" +// @Success 200 {object} controllers.SuccessResponse +// @Router /plugins/frp/restart [post] +func (r *FrpController) Restart(ctx http.Context) http.Response { + var serviceRequest requests.Service + sanitize := controllers.Sanitize(ctx, &serviceRequest) + if sanitize != nil { + return sanitize + } + + if err := tools.ServiceRestart(serviceRequest.Service); err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("重启 %s 服务失败", serviceRequest.Service)) + } + + return controllers.Success(ctx, nil) +} + +// Start +// +// @Summary 启动服务 +// @Description 启动 Frp 服务 +// @Tags 插件-Frp +// @Produce json +// @Security BearerToken +// @Param data body requests.Service true "request" +// @Success 200 {object} controllers.SuccessResponse +// @Router /plugins/frp/start [post] +func (r *FrpController) Start(ctx http.Context) http.Response { + var serviceRequest requests.Service + sanitize := controllers.Sanitize(ctx, &serviceRequest) + if sanitize != nil { + return sanitize + } + + if err := tools.ServiceStart(serviceRequest.Service); err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("启动 %s 服务失败", serviceRequest.Service)) + } + + status, err := tools.ServiceStatus(serviceRequest.Service) + if err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("获取 %s 服务运行状态失败", serviceRequest.Service)) + } + + return controllers.Success(ctx, status) +} + +// Stop +// +// @Summary 停止服务 +// @Description 停止 Frp 服务 +// @Tags 插件-Frp +// @Produce json +// @Security BearerToken +// @Param data body requests.Service true "request" +// @Success 200 {object} controllers.SuccessResponse +// @Router /plugins/frp/stop [post] +func (r *FrpController) Stop(ctx http.Context) http.Response { + var serviceRequest requests.Service + sanitize := controllers.Sanitize(ctx, &serviceRequest) + if sanitize != nil { + return sanitize + } + + if err := tools.ServiceStop(serviceRequest.Service); err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("停止 %s 服务失败", serviceRequest.Service)) + } + + status, err := tools.ServiceStatus(serviceRequest.Service) + if err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("获取 %s 服务运行状态失败", serviceRequest.Service)) + } + + return controllers.Success(ctx, !status) +} + +// GetConfig +// +// @Summary 获取配置 +// @Description 获取 Frp 配置 +// @Tags 插件-Frp +// @Produce json +// @Security BearerToken +// @Param service query string false "服务" +// @Success 200 {object} controllers.SuccessResponse +// @Router /plugins/frp/config [get] +func (r *FrpController) GetConfig(ctx http.Context) http.Response { + var serviceRequest requests.Service + sanitize := controllers.Sanitize(ctx, &serviceRequest) + if sanitize != nil { + return sanitize + } + + config, err := tools.Read(fmt.Sprintf("/www/server/frp/%s.toml", serviceRequest.Service)) + if err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return controllers.Success(ctx, config) +} + +// UpdateConfig +// +// @Summary 更新配置 +// @Description 更新 Frp 配置 +// @Tags 插件-Frp +// @Produce json +// @Security BearerToken +// @Param data body requests.UpdateConfig true "request" +// @Success 200 {object} controllers.SuccessResponse +// @Router /plugins/frp/config [post] +func (r *FrpController) UpdateConfig(ctx http.Context) http.Response { + var updateRequest requests.UpdateConfig + sanitize := controllers.Sanitize(ctx, &updateRequest) + if sanitize != nil { + return sanitize + } + + if err := tools.Write(fmt.Sprintf("/www/server/frp/%s.toml", updateRequest.Service), updateRequest.Config, 0644); err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, err.Error()) + } + + if err := tools.ServiceRestart(updateRequest.Service); err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return controllers.Success(ctx, nil) +} diff --git a/app/http/controllers/plugins/rsync_controller.go b/app/http/controllers/plugins/rsync_controller.go index b031268d..b11bc02c 100644 --- a/app/http/controllers/plugins/rsync_controller.go +++ b/app/http/controllers/plugins/rsync_controller.go @@ -9,20 +9,15 @@ import ( "github.com/TheTNB/panel/app/http/controllers" commonrequests "github.com/TheTNB/panel/app/http/requests/common" requests "github.com/TheTNB/panel/app/http/requests/plugins/rsync" - "github.com/TheTNB/panel/internal" - "github.com/TheTNB/panel/internal/services" "github.com/TheTNB/panel/pkg/tools" "github.com/TheTNB/panel/types" ) type RsyncController struct { - setting internal.Setting } func NewRsyncController() *RsyncController { - return &RsyncController{ - setting: services.NewSettingImpl(), - } + return &RsyncController{} } // Status diff --git a/app/http/requests/plugins/frp/service.go b/app/http/requests/plugins/frp/service.go new file mode 100644 index 00000000..9532cb02 --- /dev/null +++ b/app/http/requests/plugins/frp/service.go @@ -0,0 +1,32 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type Service struct { + Service string `form:"service" json:"service"` +} + +func (r *Service) Authorize(ctx http.Context) error { + return nil +} + +func (r *Service) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "service": "required|string|in:frps,frpc", + } +} + +func (r *Service) Messages(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *Service) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *Service) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/app/http/requests/plugins/frp/update_config.go b/app/http/requests/plugins/frp/update_config.go new file mode 100644 index 00000000..c80256fe --- /dev/null +++ b/app/http/requests/plugins/frp/update_config.go @@ -0,0 +1,34 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type UpdateConfig struct { + Service string `form:"service" json:"service"` + Config string `form:"config" json:"config"` +} + +func (r *UpdateConfig) Authorize(ctx http.Context) error { + return nil +} + +func (r *UpdateConfig) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "service": "required|string|in:frps,frpc", + "config": "required|string", + } +} + +func (r *UpdateConfig) Messages(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *UpdateConfig) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *UpdateConfig) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/docs/docs.go b/docs/docs.go index 49e87fce..ffbc079a 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -3412,6 +3412,278 @@ const docTemplate = `{ } } }, + "/plugins/frp/config": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取 Frp 配置", + "produces": [ + "application/json" + ], + "tags": [ + "插件-Frp" + ], + "summary": "获取配置", + "parameters": [ + { + "type": "string", + "description": "服务", + "name": "service", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + } + } + }, + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "更新 Frp 配置", + "produces": [ + "application/json" + ], + "tags": [ + "插件-Frp" + ], + "summary": "更新配置", + "parameters": [ + { + "description": "request", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_plugins_frp.UpdateConfig" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + } + } + } + }, + "/plugins/frp/disable": { + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "禁用 Frp 服务", + "produces": [ + "application/json" + ], + "tags": [ + "插件-Frp" + ], + "summary": "禁用服务", + "parameters": [ + { + "description": "request", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.Service" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + } + } + } + }, + "/plugins/frp/enable": { + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "启用 Frp 服务", + "produces": [ + "application/json" + ], + "tags": [ + "插件-Frp" + ], + "summary": "启用服务", + "parameters": [ + { + "description": "request", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.Service" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + } + } + } + }, + "/plugins/frp/restart": { + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "重启 Frp 服务", + "produces": [ + "application/json" + ], + "tags": [ + "插件-Frp" + ], + "summary": "重启服务", + "parameters": [ + { + "description": "request", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.Service" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + } + } + } + }, + "/plugins/frp/start": { + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "启动 Frp 服务", + "produces": [ + "application/json" + ], + "tags": [ + "插件-Frp" + ], + "summary": "启动服务", + "parameters": [ + { + "description": "request", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.Service" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + } + } + } + }, + "/plugins/frp/status": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取 Frp 服务状态", + "produces": [ + "application/json" + ], + "tags": [ + "插件-Frp" + ], + "summary": "服务状态", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + } + } + } + }, + "/plugins/frp/stop": { + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "停止 Frp 服务", + "produces": [ + "application/json" + ], + "tags": [ + "插件-Frp" + ], + "summary": "停止服务", + "parameters": [ + { + "description": "request", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.Service" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + } + } + } + }, "/plugins/rsync/config": { "get": { "security": [ @@ -3457,7 +3729,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/requests.UpdateConfig" + "$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_plugins_rsync.UpdateConfig" } } ], @@ -3792,6 +4064,17 @@ const docTemplate = `{ } } }, + "github_com_TheTNB_panel_app_http_requests_plugins_frp.UpdateConfig": { + "type": "object", + "properties": { + "config": { + "type": "string" + }, + "service": { + "type": "string" + } + } + }, "github_com_TheTNB_panel_app_http_requests_plugins_rsync.Update": { "type": "object", "properties": { @@ -3815,6 +4098,14 @@ const docTemplate = `{ } } }, + "github_com_TheTNB_panel_app_http_requests_plugins_rsync.UpdateConfig": { + "type": "object", + "properties": { + "config": { + "type": "string" + } + } + }, "github_com_TheTNB_panel_app_http_requests_setting.Update": { "type": "object", "properties": { @@ -4504,6 +4795,14 @@ const docTemplate = `{ } } }, + "requests.Service": { + "type": "object", + "properties": { + "service": { + "type": "string" + } + } + }, "requests.UnArchive": { "type": "object", "properties": { @@ -4515,14 +4814,6 @@ const docTemplate = `{ } } }, - "requests.UpdateConfig": { - "type": "object", - "properties": { - "config": { - "type": "string" - } - } - }, "requests.UserStore": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index c5167a06..d6b30546 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -3405,6 +3405,278 @@ } } }, + "/plugins/frp/config": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取 Frp 配置", + "produces": [ + "application/json" + ], + "tags": [ + "插件-Frp" + ], + "summary": "获取配置", + "parameters": [ + { + "type": "string", + "description": "服务", + "name": "service", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + } + } + }, + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "更新 Frp 配置", + "produces": [ + "application/json" + ], + "tags": [ + "插件-Frp" + ], + "summary": "更新配置", + "parameters": [ + { + "description": "request", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_plugins_frp.UpdateConfig" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + } + } + } + }, + "/plugins/frp/disable": { + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "禁用 Frp 服务", + "produces": [ + "application/json" + ], + "tags": [ + "插件-Frp" + ], + "summary": "禁用服务", + "parameters": [ + { + "description": "request", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.Service" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + } + } + } + }, + "/plugins/frp/enable": { + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "启用 Frp 服务", + "produces": [ + "application/json" + ], + "tags": [ + "插件-Frp" + ], + "summary": "启用服务", + "parameters": [ + { + "description": "request", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.Service" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + } + } + } + }, + "/plugins/frp/restart": { + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "重启 Frp 服务", + "produces": [ + "application/json" + ], + "tags": [ + "插件-Frp" + ], + "summary": "重启服务", + "parameters": [ + { + "description": "request", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.Service" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + } + } + } + }, + "/plugins/frp/start": { + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "启动 Frp 服务", + "produces": [ + "application/json" + ], + "tags": [ + "插件-Frp" + ], + "summary": "启动服务", + "parameters": [ + { + "description": "request", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.Service" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + } + } + } + }, + "/plugins/frp/status": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取 Frp 服务状态", + "produces": [ + "application/json" + ], + "tags": [ + "插件-Frp" + ], + "summary": "服务状态", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + } + } + } + }, + "/plugins/frp/stop": { + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "停止 Frp 服务", + "produces": [ + "application/json" + ], + "tags": [ + "插件-Frp" + ], + "summary": "停止服务", + "parameters": [ + { + "description": "request", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.Service" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + } + } + } + }, "/plugins/rsync/config": { "get": { "security": [ @@ -3450,7 +3722,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/requests.UpdateConfig" + "$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_plugins_rsync.UpdateConfig" } } ], @@ -3785,6 +4057,17 @@ } } }, + "github_com_TheTNB_panel_app_http_requests_plugins_frp.UpdateConfig": { + "type": "object", + "properties": { + "config": { + "type": "string" + }, + "service": { + "type": "string" + } + } + }, "github_com_TheTNB_panel_app_http_requests_plugins_rsync.Update": { "type": "object", "properties": { @@ -3808,6 +4091,14 @@ } } }, + "github_com_TheTNB_panel_app_http_requests_plugins_rsync.UpdateConfig": { + "type": "object", + "properties": { + "config": { + "type": "string" + } + } + }, "github_com_TheTNB_panel_app_http_requests_setting.Update": { "type": "object", "properties": { @@ -4497,6 +4788,14 @@ } } }, + "requests.Service": { + "type": "object", + "properties": { + "service": { + "type": "string" + } + } + }, "requests.UnArchive": { "type": "object", "properties": { @@ -4508,14 +4807,6 @@ } } }, - "requests.UpdateConfig": { - "type": "object", - "properties": { - "config": { - "type": "string" - } - } - }, "requests.UserStore": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 49c22bc9..d13777f1 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -36,6 +36,13 @@ definitions: id: type: string type: object + github_com_TheTNB_panel_app_http_requests_plugins_frp.UpdateConfig: + properties: + config: + type: string + service: + type: string + type: object github_com_TheTNB_panel_app_http_requests_plugins_rsync.Update: properties: auth_user: @@ -51,6 +58,11 @@ definitions: secret: type: string type: object + github_com_TheTNB_panel_app_http_requests_plugins_rsync.UpdateConfig: + properties: + config: + type: string + type: object github_com_TheTNB_panel_app_http_requests_setting.Update: properties: backup_path: @@ -504,6 +516,11 @@ definitions: path: type: string type: object + requests.Service: + properties: + service: + type: string + type: object requests.UnArchive: properties: file: @@ -511,11 +528,6 @@ definitions: path: type: string type: object - requests.UpdateConfig: - properties: - config: - type: string - type: object requests.UserStore: properties: ca: @@ -2714,6 +2726,172 @@ paths: summary: 更新备注 tags: - 网站管理 + /plugins/frp/config: + get: + description: 获取 Frp 配置 + parameters: + - description: 服务 + in: query + name: service + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + security: + - BearerToken: [] + summary: 获取配置 + tags: + - 插件-Frp + post: + description: 更新 Frp 配置 + parameters: + - description: request + in: body + name: data + required: true + schema: + $ref: '#/definitions/github_com_TheTNB_panel_app_http_requests_plugins_frp.UpdateConfig' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + security: + - BearerToken: [] + summary: 更新配置 + tags: + - 插件-Frp + /plugins/frp/disable: + post: + description: 禁用 Frp 服务 + parameters: + - description: request + in: body + name: data + required: true + schema: + $ref: '#/definitions/requests.Service' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + security: + - BearerToken: [] + summary: 禁用服务 + tags: + - 插件-Frp + /plugins/frp/enable: + post: + description: 启用 Frp 服务 + parameters: + - description: request + in: body + name: data + required: true + schema: + $ref: '#/definitions/requests.Service' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + security: + - BearerToken: [] + summary: 启用服务 + tags: + - 插件-Frp + /plugins/frp/restart: + post: + description: 重启 Frp 服务 + parameters: + - description: request + in: body + name: data + required: true + schema: + $ref: '#/definitions/requests.Service' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + security: + - BearerToken: [] + summary: 重启服务 + tags: + - 插件-Frp + /plugins/frp/start: + post: + description: 启动 Frp 服务 + parameters: + - description: request + in: body + name: data + required: true + schema: + $ref: '#/definitions/requests.Service' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + security: + - BearerToken: [] + summary: 启动服务 + tags: + - 插件-Frp + /plugins/frp/status: + get: + description: 获取 Frp 服务状态 + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + security: + - BearerToken: [] + summary: 服务状态 + tags: + - 插件-Frp + /plugins/frp/stop: + post: + description: 停止 Frp 服务 + parameters: + - description: request + in: body + name: data + required: true + schema: + $ref: '#/definitions/requests.Service' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + security: + - BearerToken: [] + summary: 停止服务 + tags: + - 插件-Frp /plugins/rsync/config: get: description: 获取 Rsync 配置 @@ -2737,7 +2915,7 @@ paths: name: data required: true schema: - $ref: '#/definitions/requests.UpdateConfig' + $ref: '#/definitions/github_com_TheTNB_panel_app_http_requests_plugins_rsync.UpdateConfig' produces: - application/json responses: diff --git a/internal/services/plugin.go b/internal/services/plugin.go index e179afc4..26ce5b81 100644 --- a/internal/services/plugin.go +++ b/internal/services/plugin.go @@ -34,25 +34,26 @@ func (r *PluginImpl) AllInstalled() ([]models.Plugin, error) { // All 获取所有插件 func (r *PluginImpl) All() []types.Plugin { var plugins = []types.Plugin{ - internal.PluginOpenResty, - internal.PluginMySQL57, - internal.PluginMySQL80, - internal.PluginMySQL84, - internal.PluginPostgreSQL15, - internal.PluginPostgreSQL16, - internal.PluginPHP74, - internal.PluginPHP80, - internal.PluginPHP81, - internal.PluginPHP82, - internal.PluginPHP83, - internal.PluginPHPMyAdmin, - internal.PluginPureFTPd, - internal.PluginRedis, - internal.PluginS3fs, - internal.PluginRsync, - internal.PluginSupervisor, - internal.PluginFail2ban, - internal.PluginToolBox, + types.PluginOpenResty, + types.PluginMySQL57, + types.PluginMySQL80, + types.PluginMySQL84, + types.PluginPostgreSQL15, + types.PluginPostgreSQL16, + types.PluginPHP74, + types.PluginPHP80, + types.PluginPHP81, + types.PluginPHP82, + types.PluginPHP83, + types.PluginPHPMyAdmin, + types.PluginPureFTPd, + types.PluginRedis, + types.PluginS3fs, + types.PluginRsync, + types.PluginSupervisor, + types.PluginFail2ban, + types.PluginFrp, + types.PluginToolBox, } return plugins diff --git a/pkg/tools/service.go b/pkg/tools/service.go index 8c14c5ef..e3bb7fd8 100644 --- a/pkg/tools/service.go +++ b/pkg/tools/service.go @@ -8,6 +8,12 @@ func ServiceStatus(name string) (bool, error) { return output == "active", err } +// ServiceIsEnabled 服务是否启用 +func ServiceIsEnabled(name string) (bool, error) { + output, err := Exec(fmt.Sprintf("systemctl is-enabled %s", name)) + return output == "enabled", err +} + // ServiceStart 启动服务 func ServiceStart(name string) error { _, err := Exec(fmt.Sprintf("systemctl start %s", name)) @@ -31,3 +37,15 @@ func ServiceReload(name string) error { _, err := Exec(fmt.Sprintf("systemctl reload %s", name)) return err } + +// ServiceEnable 启用服务 +func ServiceEnable(name string) error { + _, err := Exec(fmt.Sprintf("systemctl enable %s", name)) + return err +} + +// ServiceDisable 禁用服务 +func ServiceDisable(name string) error { + _, err := Exec(fmt.Sprintf("systemctl disable %s", name)) + return err +} diff --git a/routes/plugin.go b/routes/plugin.go index fda5846b..735d92e9 100644 --- a/routes/plugin.go +++ b/routes/plugin.go @@ -353,6 +353,17 @@ func Plugin() { route.Get("config", rsyncController.GetConfig) route.Post("config", rsyncController.UpdateConfig) }) + r.Prefix("frp").Group(func(route route.Router) { + frpController := plugins.NewFrpController() + route.Get("status", frpController.Status) + route.Get("enable", frpController.Enable) + route.Get("disable", frpController.Disable) + route.Post("start", frpController.Start) + route.Post("stop", frpController.Stop) + route.Post("restart", frpController.Restart) + route.Get("config", frpController.GetConfig) + route.Post("config", frpController.UpdateConfig) + }) r.Prefix("toolbox").Group(func(route route.Router) { toolboxController := plugins.NewToolBoxController() route.Get("dns", toolboxController.GetDNS) diff --git a/scripts/frp/install.sh b/scripts/frp/install.sh new file mode 100644 index 00000000..e9d827e2 --- /dev/null +++ b/scripts/frp/install.sh @@ -0,0 +1,98 @@ +#!/bin/bash +export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/www/server/bin:/www/server/sbin:$PATH + +: ' +Copyright (C) 2022 - now HaoZi Technology Co., Ltd. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +' + +HR="+----------------------------------------------------" +ARCH=$(uname -m) +downloadUrl="https://dl.cdn.haozi.net/panel/frp" +frpPath="/www/server/frp" +frpVersion="0.58.0" + +if [ ! -d "${frpPath}" ]; then + mkdir -p ${frpPath} +fi + +# 架构判断 +if [ "${ARCH}" == "x86_64" ]; then + frpFile="frp_${frpVersion}_linux_amd64.7z" +elif [ "${ARCH}" == "aarch64" ]; then + frpFile="frp_${frpVersion}_linux_arm64.7z" +else + echo -e $HR + echo "错误:不支持的架构" + exit 1 +fi + +# 下载frp +wget -T 120 -t 3 -O ${frpPath}/${frpFile} ${downloadUrl}/${frpFile} +wget -T 20 -t 3 -O ${frpPath}/${frpFile}.checksum.txt ${downloadUrl}/${frpFile}.checksum.txt +if ! sha256sum --status -c ${frpPath}/${frpFile}.checksum.txt; then + echo -e $HR + echo "错误:frp checksum 校验失败,文件可能被篡改或不完整,已终止操作" + rm -rf ${frpPath} + exit 1 +fi + +# 解压frp +cd ${frpPath} +7z x ${frpFile} +chmod -R 700 ${frpPath} +rm -f ${frpFile} ${frpFile}.checksum.txt + +# 配置systemd +cat >/etc/systemd/system/frps.service </etc/systemd/system/frpc.service <. +' + +HR="+----------------------------------------------------" +frpPath="/usr/local/frp" + +systemctl stop frps +systemctl stop frpc +systemctl disable frps +systemctl disable frpc + +rm -rf ${frpPath} +rm -f /etc/systemd/system/frps.service +rm -f /etc/systemd/system/frpc.service +systemctl daemon-reload + +panel deletePlugin frp +echo -e $HR +echo "frp 卸载完成" +echo -e $HR \ No newline at end of file diff --git a/scripts/rsync/uninstall.sh b/scripts/rsync/uninstall.sh index 1bb58d56..912f38de 100644 --- a/scripts/rsync/uninstall.sh +++ b/scripts/rsync/uninstall.sh @@ -21,6 +21,11 @@ along with this program. If not, see . HR="+----------------------------------------------------" OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown") +systemctl stop rsync +systemctl disable rsync +rm -f /etc/systemd/system/rsyncd.service +systemctl daemon-reload + if [ "${OS}" == "centos" ]; then dnf remove -y rsync elif [ "${OS}" == "debian" ]; then diff --git a/scripts/supervisor/uninstall.sh b/scripts/supervisor/uninstall.sh index 05196dbd..797dff7e 100644 --- a/scripts/supervisor/uninstall.sh +++ b/scripts/supervisor/uninstall.sh @@ -22,8 +22,12 @@ 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 + systemctl stop supervisord + systemctl disable supervisord dnf remove -y supervisor elif [ "${OS}" == "debian" ]; then + systemctl stop supervisor + systemctl disable supervisor apt-get purge -y supervisor else echo -e $HR diff --git a/internal/plugin_list.go b/types/plugin_list.go similarity index 90% rename from internal/plugin_list.go rename to types/plugin_list.go index b1466564..6e517e60 100644 --- a/internal/plugin_list.go +++ b/types/plugin_list.go @@ -1,9 +1,6 @@ -// Package internal 插件定义文件 -package internal +package types -import "github.com/TheTNB/panel/types" - -var PluginOpenResty = types.Plugin{ +var PluginOpenResty = Plugin{ Name: "OpenResty", Description: "OpenResty® 是一款基于 NGINX 和 LuaJIT 的 Web 平台。", Slug: "openresty", @@ -15,7 +12,7 @@ var PluginOpenResty = types.Plugin{ Update: "bash /www/panel/scripts/openresty/install.sh", } -var PluginMySQL57 = types.Plugin{ +var PluginMySQL57 = Plugin{ Name: "MySQL-5.7", Description: "MySQL 是最流行的关系型数据库管理系统之一,Oracle 旗下产品。(已停止维护,不建议使用!预计 2025 年 12 月移除)", Slug: "mysql57", @@ -27,7 +24,7 @@ var PluginMySQL57 = types.Plugin{ Update: `bash /www/panel/scripts/mysql/update.sh 57`, } -var PluginMySQL80 = types.Plugin{ +var PluginMySQL80 = Plugin{ Name: "MySQL-8.0", Description: "MySQL 是最流行的关系型数据库管理系统之一,Oracle 旗下产品。(建议内存 > 2G 安装)", Slug: "mysql80", @@ -39,7 +36,7 @@ var PluginMySQL80 = types.Plugin{ Update: `bash /www/panel/scripts/mysql/update.sh 80`, } -var PluginMySQL84 = types.Plugin{ +var PluginMySQL84 = Plugin{ Name: "MySQL-8.4", Description: "MySQL 是最流行的关系型数据库管理系统之一,Oracle 旗下产品。(建议内存 > 2G 安装)", Slug: "mysql84", @@ -51,7 +48,7 @@ var PluginMySQL84 = types.Plugin{ Update: `bash /www/panel/scripts/mysql/update.sh 84`, } -var PluginPostgreSQL15 = types.Plugin{ +var PluginPostgreSQL15 = Plugin{ Name: "PostgreSQL-15", Description: "PostgreSQL 是世界上最先进的开源关系数据库,在类似 BSD 与 MIT 许可的 PostgreSQL 许可下发行。", Slug: "postgresql15", @@ -63,7 +60,7 @@ var PluginPostgreSQL15 = types.Plugin{ Update: `bash /www/panel/scripts/postgresql/update.sh 15`, } -var PluginPostgreSQL16 = types.Plugin{ +var PluginPostgreSQL16 = Plugin{ Name: "PostgreSQL-16", Description: "PostgreSQL 是世界上最先进的开源关系数据库,在类似 BSD 与 MIT 许可的 PostgreSQL 许可下发行。", Slug: "postgresql16", @@ -75,7 +72,7 @@ var PluginPostgreSQL16 = types.Plugin{ Update: `bash /www/panel/scripts/postgresql/update.sh 16`, } -var PluginPHP74 = types.Plugin{ +var PluginPHP74 = Plugin{ Name: "PHP-7.4", Description: "PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。(已停止维护,不建议使用!预计 2024 年 12 月移除)", Slug: "php74", @@ -87,7 +84,7 @@ var PluginPHP74 = types.Plugin{ Update: `bash /www/panel/scripts/php/install.sh 74`, } -var PluginPHP80 = types.Plugin{ +var PluginPHP80 = Plugin{ Name: "PHP-8.0", Description: "PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。(已停止维护,不建议使用!预计 2025 年 12 月移除)", Slug: "php80", @@ -99,7 +96,7 @@ var PluginPHP80 = types.Plugin{ Update: `bash /www/panel/scripts/php/install.sh 80`, } -var PluginPHP81 = types.Plugin{ +var PluginPHP81 = Plugin{ Name: "PHP-8.1", Description: "PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。", Slug: "php81", @@ -111,7 +108,7 @@ var PluginPHP81 = types.Plugin{ Update: `bash /www/panel/scripts/php/install.sh 81`, } -var PluginPHP82 = types.Plugin{ +var PluginPHP82 = Plugin{ Name: "PHP-8.2", Description: "PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。", Slug: "php82", @@ -123,7 +120,7 @@ var PluginPHP82 = types.Plugin{ Update: `bash /www/panel/scripts/php/install.sh 82`, } -var PluginPHP83 = types.Plugin{ +var PluginPHP83 = Plugin{ Name: "PHP-8.3", Description: "PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。", Slug: "php83", @@ -135,7 +132,7 @@ var PluginPHP83 = types.Plugin{ Update: `bash /www/panel/scripts/php/install.sh 83`, } -var PluginPHPMyAdmin = types.Plugin{ +var PluginPHPMyAdmin = Plugin{ Name: "phpMyAdmin", Description: "phpMyAdmin 是一个以 PHP 为基础,以 Web-Base 方式架构在网站主机上的 MySQL 数据库管理工具。", Slug: "phpmyadmin", @@ -147,7 +144,7 @@ var PluginPHPMyAdmin = types.Plugin{ Update: `bash /www/panel/scripts/phpmyadmin/uninstall.sh && bash /www/panel/scripts/phpmyadmin/install.sh`, } -var PluginPureFTPd = types.Plugin{ +var PluginPureFTPd = Plugin{ Name: "Pure-FTPd", Description: "Pure-Ftpd 是一个快速、高效、轻便、安全的 FTP 服务器,它以安全和配置简单为设计目标,支持虚拟主机,IPV6,PAM 等功能。", Slug: "pureftpd", @@ -159,7 +156,7 @@ var PluginPureFTPd = types.Plugin{ Update: `bash /www/panel/scripts/pureftpd/update.sh`, } -var PluginRedis = types.Plugin{ +var PluginRedis = Plugin{ Name: "Redis", Description: "Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。", Slug: "redis", @@ -171,7 +168,7 @@ var PluginRedis = types.Plugin{ Update: `bash /www/panel/scripts/redis/update.sh`, } -var PluginS3fs = types.Plugin{ +var PluginS3fs = Plugin{ Name: "S3fs", Description: "S3fs 通过 FUSE 挂载兼容 S3 标准的存储桶,例如 Amazon S3、阿里云 OSS、腾讯云 COS、七牛云 Kodo 等。", Slug: "s3fs", @@ -183,7 +180,7 @@ var PluginS3fs = types.Plugin{ Update: `bash /www/panel/scripts/s3fs/update.sh`, } -var PluginRsync = types.Plugin{ +var PluginRsync = Plugin{ Name: "Rsync", Description: "Rsync 是一款提供快速增量文件传输的开源工具。", Slug: "rsync", @@ -195,7 +192,7 @@ var PluginRsync = types.Plugin{ Update: `bash /www/panel/scripts/rsync/install.sh`, } -var PluginSupervisor = types.Plugin{ +var PluginSupervisor = Plugin{ Name: "Supervisor", Description: "Supervisor 是一个客户端/服务器系统,允许用户监视和控制类 UNIX 操作系统上的多个进程。", Slug: "supervisor", @@ -207,7 +204,7 @@ var PluginSupervisor = types.Plugin{ Update: `bash /www/panel/scripts/supervisor/update.sh`, } -var PluginFail2ban = types.Plugin{ +var PluginFail2ban = Plugin{ Name: "Fail2ban", Description: "Fail2ban 扫描系统日志文件并从中找出多次尝试失败的IP地址,将该IP地址加入防火墙的拒绝访问列表中。", Slug: "fail2ban", @@ -219,7 +216,19 @@ var PluginFail2ban = types.Plugin{ Update: `bash /www/panel/scripts/fail2ban/update.sh`, } -var PluginToolBox = types.Plugin{ +var PluginFrp = Plugin{ + Name: "Frp", + Description: "frp 是一个专注于内网穿透的高性能的反向代理应用。", + Slug: "frp", + Version: "0.58.0", + Requires: []string{}, + Excludes: []string{}, + Install: `bash /www/panel/scripts/frp/install.sh`, + Uninstall: `bash /www/panel/scripts/frp/uninstall.sh`, + Update: `bash /www/panel/scripts/frp/install.sh`, +} + +var PluginToolBox = Plugin{ Name: "系统工具箱", Description: "可视化调整一些常用的配置项,如 DNS、SWAP、时区等", Slug: "toolbox",