mirror of
https://github.com/acepanel/panel.git
synced 2026-02-06 10:07:15 +08:00
refactor: 重构插件结构
This commit is contained in:
@@ -1,273 +0,0 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/facades"
|
||||
"github.com/imroc/req/v3"
|
||||
|
||||
"panel/app/http/controllers"
|
||||
"panel/app/models"
|
||||
"panel/internal"
|
||||
"panel/internal/services"
|
||||
"panel/pkg/tools"
|
||||
)
|
||||
|
||||
type Php74Controller struct {
|
||||
setting internal.Setting
|
||||
task internal.Task
|
||||
version string
|
||||
}
|
||||
|
||||
func NewPhp74Controller() *Php74Controller {
|
||||
return &Php74Controller{
|
||||
setting: services.NewSettingImpl(),
|
||||
task: services.NewTaskImpl(),
|
||||
version: "74",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Php74Controller) Status(ctx http.Context) http.Response {
|
||||
status, err := tools.ServiceStatus("php-fpm-" + r.version)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, status)
|
||||
}
|
||||
|
||||
func (r *Php74Controller) Reload(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceReload("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重载PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php74Controller) Start(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceStart("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "启动PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php74Controller) Stop(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceStop("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "停止PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php74Controller) Restart(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceRestart("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重启PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php74Controller) GetConfig(ctx http.Context) http.Response {
|
||||
config, err := tools.Read("/www/server/php/" + r.version + "/etc/php.ini")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"配置失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, config)
|
||||
}
|
||||
|
||||
func (r *Php74Controller) SaveConfig(ctx http.Context) http.Response {
|
||||
config := ctx.Request().Input("config")
|
||||
if err := tools.Write("/www/server/php/"+r.version+"/etc/php.ini", config, 0644); err != nil {
|
||||
return nil
|
||||
}
|
||||
return r.Reload(ctx)
|
||||
}
|
||||
|
||||
func (r *Php74Controller) Load(ctx http.Context) http.Response {
|
||||
client := req.C().SetTimeout(10 * time.Second)
|
||||
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + r.version)
|
||||
if err != nil || !resp.IsSuccessState() {
|
||||
facades.Log().Info("获取PHP-" + r.version + "运行状态失败")
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+r.version+"] 获取运行状态失败")
|
||||
}
|
||||
|
||||
raw := resp.String()
|
||||
dataKeys := []string{"应用池", "工作模式", "启动时间", "接受连接", "监听队列", "最大监听队列", "监听队列长度", "空闲进程数量", "活动进程数量", "总进程数量", "最大活跃进程数量", "达到进程上限次数", "慢请求"}
|
||||
regexKeys := []string{"pool", "process manager", "start time", "accepted conn", "listen queue", "max listen queue", "listen queue len", "idle processes", "active processes", "total processes", "max active processes", "max children reached", "slow requests"}
|
||||
|
||||
type Data struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
data := make([]Data, len(dataKeys))
|
||||
for i := range dataKeys {
|
||||
data[i].Name = dataKeys[i]
|
||||
|
||||
r := regexp.MustCompile(fmt.Sprintf("%s:\\s+(.*)", regexKeys[i]))
|
||||
match := r.FindStringSubmatch(raw)
|
||||
|
||||
if len(match) > 1 {
|
||||
data[i].Value = strings.TrimSpace(match[1])
|
||||
}
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, data)
|
||||
}
|
||||
|
||||
func (r *Php74Controller) ErrorLog(ctx http.Context) http.Response {
|
||||
log, _ := tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/php-fpm.log")
|
||||
return controllers.Success(ctx, log)
|
||||
}
|
||||
|
||||
func (r *Php74Controller) SlowLog(ctx http.Context) http.Response {
|
||||
log, _ := tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/slow.log")
|
||||
return controllers.Success(ctx, log)
|
||||
}
|
||||
|
||||
func (r *Php74Controller) ClearErrorLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/php-fpm.log"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php74Controller) ClearSlowLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/slow.log"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php74Controller) GetExtensionList(ctx http.Context) http.Response {
|
||||
extensions := r.GetExtensions()
|
||||
return controllers.Success(ctx, extensions)
|
||||
}
|
||||
|
||||
func (r *Php74Controller) InstallExtension(ctx http.Context) http.Response {
|
||||
slug := ctx.Request().Input("slug")
|
||||
if len(slug) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
|
||||
}
|
||||
|
||||
extensions := r.GetExtensions()
|
||||
for _, item := range extensions {
|
||||
if item.Slug == slug {
|
||||
if item.Installed {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展已安装")
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "安装PHP-" + r.version + "扩展-" + item.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + r.version + ` >> '/tmp/` + item.Slug + `.log' 2>&1`
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Info("[PHP-" + r.version + "] 创建安装拓展任务失败:" + err.Error())
|
||||
return controllers.ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
r.task.Process(task.ID)
|
||||
|
||||
return controllers.Success(ctx, true)
|
||||
}
|
||||
}
|
||||
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在")
|
||||
}
|
||||
|
||||
func (r *Php74Controller) UninstallExtension(ctx http.Context) http.Response {
|
||||
slug := ctx.Request().Input("slug")
|
||||
if len(slug) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
|
||||
}
|
||||
|
||||
extensions := r.GetExtensions()
|
||||
for _, item := range extensions {
|
||||
if item.Slug == slug {
|
||||
if !item.Installed {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展未安装")
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "卸载PHP-" + r.version + "扩展-" + item.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + r.version + ` >> '/tmp/` + item.Slug + `.log' 2>&1`
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Info("[PHP-" + r.version + "] 创建卸载拓展任务失败:" + err.Error())
|
||||
return controllers.ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
r.task.Process(task.ID)
|
||||
|
||||
return controllers.Success(ctx, true)
|
||||
}
|
||||
}
|
||||
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在")
|
||||
}
|
||||
|
||||
func (r *Php74Controller) GetExtensions() []PHPExtension {
|
||||
var extensions []PHPExtension
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "OPcache",
|
||||
Slug: "Zend OPcache",
|
||||
Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "PhpRedis",
|
||||
Slug: "redis",
|
||||
Description: "PhpRedis 是一个用 C 语言编写的 PHP 模块,用来连接并操作 Redis 数据库上的数据。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "ImageMagick",
|
||||
Slug: "imagick",
|
||||
Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "Exif",
|
||||
Slug: "exif",
|
||||
Description: "通过 exif 扩展,你可以操作图像元数据。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "pdo_pgsql",
|
||||
Slug: "pdo_pgsql",
|
||||
Description: "(需先安装PostgreSQL)pdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象(PDO)接口以启用从 PHP 到 PostgreSQL 数据库的访问。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "ionCube",
|
||||
Slug: "ionCube Loader",
|
||||
Description: "ionCube 是一个专业级的 PHP 加密解密工具。",
|
||||
Installed: false,
|
||||
})
|
||||
|
||||
raw, err := tools.Exec("/www/server/php/" + r.version + "/bin/php -m")
|
||||
if err != nil {
|
||||
return extensions
|
||||
}
|
||||
|
||||
rawExtensionList := strings.Split(raw, "\n")
|
||||
for _, item := range rawExtensionList {
|
||||
if !strings.Contains(item, "[") && item != "" {
|
||||
for i := range extensions {
|
||||
if extensions[i].Slug == item {
|
||||
extensions[i].Installed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extensions
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/facades"
|
||||
"github.com/imroc/req/v3"
|
||||
|
||||
"panel/app/http/controllers"
|
||||
"panel/app/models"
|
||||
"panel/internal"
|
||||
"panel/internal/services"
|
||||
"panel/pkg/tools"
|
||||
)
|
||||
|
||||
type Php80Controller struct {
|
||||
setting internal.Setting
|
||||
task internal.Task
|
||||
version string
|
||||
}
|
||||
|
||||
func NewPhp80Controller() *Php80Controller {
|
||||
return &Php80Controller{
|
||||
setting: services.NewSettingImpl(),
|
||||
task: services.NewTaskImpl(),
|
||||
version: "80",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Php80Controller) Status(ctx http.Context) http.Response {
|
||||
status, err := tools.ServiceStatus("php-fpm-" + r.version)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, status)
|
||||
}
|
||||
|
||||
func (r *Php80Controller) Reload(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceReload("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重载PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php80Controller) Start(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceStart("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "启动PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php80Controller) Stop(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceStop("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "停止PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php80Controller) Restart(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceRestart("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重启PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php80Controller) GetConfig(ctx http.Context) http.Response {
|
||||
config, err := tools.Read("/www/server/php/" + r.version + "/etc/php.ini")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"配置失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, config)
|
||||
}
|
||||
|
||||
func (r *Php80Controller) SaveConfig(ctx http.Context) http.Response {
|
||||
config := ctx.Request().Input("config")
|
||||
if err := tools.Write("/www/server/php/"+r.version+"/etc/php.ini", config, 0644); err != nil {
|
||||
return nil
|
||||
}
|
||||
return r.Reload(ctx)
|
||||
}
|
||||
|
||||
func (r *Php80Controller) Load(ctx http.Context) http.Response {
|
||||
client := req.C().SetTimeout(10 * time.Second)
|
||||
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + r.version)
|
||||
if err != nil || !resp.IsSuccessState() {
|
||||
facades.Log().Info("获取PHP-" + r.version + "运行状态失败")
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+r.version+"] 获取运行状态失败")
|
||||
}
|
||||
|
||||
raw := resp.String()
|
||||
dataKeys := []string{"应用池", "工作模式", "启动时间", "接受连接", "监听队列", "最大监听队列", "监听队列长度", "空闲进程数量", "活动进程数量", "总进程数量", "最大活跃进程数量", "达到进程上限次数", "慢请求"}
|
||||
regexKeys := []string{"pool", "process manager", "start time", "accepted conn", "listen queue", "max listen queue", "listen queue len", "idle processes", "active processes", "total processes", "max active processes", "max children reached", "slow requests"}
|
||||
|
||||
type Data struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
data := make([]Data, len(dataKeys))
|
||||
for i := range dataKeys {
|
||||
data[i].Name = dataKeys[i]
|
||||
|
||||
r := regexp.MustCompile(fmt.Sprintf("%s:\\s+(.*)", regexKeys[i]))
|
||||
match := r.FindStringSubmatch(raw)
|
||||
|
||||
if len(match) > 1 {
|
||||
data[i].Value = strings.TrimSpace(match[1])
|
||||
}
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, data)
|
||||
}
|
||||
|
||||
func (r *Php80Controller) ErrorLog(ctx http.Context) http.Response {
|
||||
log, _ := tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/php-fpm.log")
|
||||
return controllers.Success(ctx, log)
|
||||
}
|
||||
|
||||
func (r *Php80Controller) SlowLog(ctx http.Context) http.Response {
|
||||
log, _ := tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/slow.log")
|
||||
return controllers.Success(ctx, log)
|
||||
}
|
||||
|
||||
func (r *Php80Controller) ClearErrorLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/php-fpm.log"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php80Controller) ClearSlowLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/slow.log"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php80Controller) GetExtensionList(ctx http.Context) http.Response {
|
||||
extensions := r.GetExtensions()
|
||||
return controllers.Success(ctx, extensions)
|
||||
}
|
||||
|
||||
func (r *Php80Controller) InstallExtension(ctx http.Context) http.Response {
|
||||
slug := ctx.Request().Input("slug")
|
||||
if len(slug) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
|
||||
}
|
||||
|
||||
extensions := r.GetExtensions()
|
||||
for _, item := range extensions {
|
||||
if item.Slug == slug {
|
||||
if item.Installed {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展已安装")
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "安装PHP-" + r.version + "扩展-" + item.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + r.version + ` >> '/tmp/` + item.Slug + `.log' 2>&1`
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Info("[PHP-" + r.version + "] 创建安装拓展任务失败:" + err.Error())
|
||||
return controllers.ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
r.task.Process(task.ID)
|
||||
|
||||
return controllers.Success(ctx, true)
|
||||
}
|
||||
}
|
||||
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在")
|
||||
}
|
||||
|
||||
func (r *Php80Controller) UninstallExtension(ctx http.Context) http.Response {
|
||||
slug := ctx.Request().Input("slug")
|
||||
if len(slug) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
|
||||
}
|
||||
|
||||
extensions := r.GetExtensions()
|
||||
for _, item := range extensions {
|
||||
if item.Slug == slug {
|
||||
if !item.Installed {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展未安装")
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "卸载PHP-" + r.version + "扩展-" + item.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + r.version + ` >> '/tmp/` + item.Slug + `.log' 2>&1`
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Info("[PHP-" + r.version + "] 创建卸载拓展任务失败:" + err.Error())
|
||||
return controllers.ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
r.task.Process(task.ID)
|
||||
|
||||
return controllers.Success(ctx, true)
|
||||
}
|
||||
}
|
||||
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在")
|
||||
}
|
||||
|
||||
func (r *Php80Controller) GetExtensions() []PHPExtension {
|
||||
var extensions []PHPExtension
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "OPcache",
|
||||
Slug: "Zend OPcache",
|
||||
Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "PhpRedis",
|
||||
Slug: "redis",
|
||||
Description: "PhpRedis 是一个用 C 语言编写的 PHP 模块,用来连接并操作 Redis 数据库上的数据。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "ImageMagick",
|
||||
Slug: "imagick",
|
||||
Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "Exif",
|
||||
Slug: "exif",
|
||||
Description: "通过 exif 扩展,你可以操作图像元数据。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "pdo_pgsql",
|
||||
Slug: "pdo_pgsql",
|
||||
Description: "(需先安装PostgreSQL)pdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象(PDO)接口以启用从 PHP 到 PostgreSQL 数据库的访问。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "ionCube",
|
||||
Slug: "ionCube Loader",
|
||||
Description: "ionCube 是一个专业级的 PHP 加密解密工具。",
|
||||
Installed: false,
|
||||
})
|
||||
|
||||
raw, err := tools.Exec("/www/server/php/" + r.version + "/bin/php -m")
|
||||
if err != nil {
|
||||
return extensions
|
||||
}
|
||||
|
||||
rawExtensionList := strings.Split(raw, "\n")
|
||||
for _, item := range rawExtensionList {
|
||||
if !strings.Contains(item, "[") && item != "" {
|
||||
for i := range extensions {
|
||||
if extensions[i].Slug == item {
|
||||
extensions[i].Installed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extensions
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/facades"
|
||||
"github.com/imroc/req/v3"
|
||||
|
||||
"panel/app/http/controllers"
|
||||
"panel/app/models"
|
||||
"panel/internal"
|
||||
"panel/internal/services"
|
||||
"panel/pkg/tools"
|
||||
)
|
||||
|
||||
type Php81Controller struct {
|
||||
setting internal.Setting
|
||||
task internal.Task
|
||||
version string
|
||||
}
|
||||
|
||||
func NewPhp81Controller() *Php81Controller {
|
||||
return &Php81Controller{
|
||||
setting: services.NewSettingImpl(),
|
||||
task: services.NewTaskImpl(),
|
||||
version: "81",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Php81Controller) Status(ctx http.Context) http.Response {
|
||||
status, err := tools.ServiceStatus("php-fpm-" + r.version)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, status)
|
||||
}
|
||||
|
||||
func (r *Php81Controller) Reload(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceReload("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重载PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php81Controller) Start(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceStart("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "启动PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php81Controller) Stop(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceStop("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "停止PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php81Controller) Restart(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceRestart("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重启PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php81Controller) GetConfig(ctx http.Context) http.Response {
|
||||
config, err := tools.Read("/www/server/php/" + r.version + "/etc/php.ini")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"配置失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, config)
|
||||
}
|
||||
|
||||
func (r *Php81Controller) SaveConfig(ctx http.Context) http.Response {
|
||||
config := ctx.Request().Input("config")
|
||||
if err := tools.Write("/www/server/php/"+r.version+"/etc/php.ini", config, 0644); err != nil {
|
||||
return nil
|
||||
}
|
||||
return r.Reload(ctx)
|
||||
}
|
||||
|
||||
func (r *Php81Controller) Load(ctx http.Context) http.Response {
|
||||
client := req.C().SetTimeout(10 * time.Second)
|
||||
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + r.version)
|
||||
if err != nil || !resp.IsSuccessState() {
|
||||
facades.Log().Info("获取PHP-" + r.version + "运行状态失败")
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+r.version+"] 获取运行状态失败")
|
||||
}
|
||||
|
||||
raw := resp.String()
|
||||
dataKeys := []string{"应用池", "工作模式", "启动时间", "接受连接", "监听队列", "最大监听队列", "监听队列长度", "空闲进程数量", "活动进程数量", "总进程数量", "最大活跃进程数量", "达到进程上限次数", "慢请求"}
|
||||
regexKeys := []string{"pool", "process manager", "start time", "accepted conn", "listen queue", "max listen queue", "listen queue len", "idle processes", "active processes", "total processes", "max active processes", "max children reached", "slow requests"}
|
||||
|
||||
type Data struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
data := make([]Data, len(dataKeys))
|
||||
for i := range dataKeys {
|
||||
data[i].Name = dataKeys[i]
|
||||
|
||||
r := regexp.MustCompile(fmt.Sprintf("%s:\\s+(.*)", regexKeys[i]))
|
||||
match := r.FindStringSubmatch(raw)
|
||||
|
||||
if len(match) > 1 {
|
||||
data[i].Value = strings.TrimSpace(match[1])
|
||||
}
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, data)
|
||||
}
|
||||
|
||||
func (r *Php81Controller) ErrorLog(ctx http.Context) http.Response {
|
||||
log, _ := tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/php-fpm.log")
|
||||
return controllers.Success(ctx, log)
|
||||
}
|
||||
|
||||
func (r *Php81Controller) SlowLog(ctx http.Context) http.Response {
|
||||
log, _ := tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/slow.log")
|
||||
return controllers.Success(ctx, log)
|
||||
}
|
||||
|
||||
func (r *Php81Controller) ClearErrorLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/php-fpm.log"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php81Controller) ClearSlowLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/slow.log"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php81Controller) GetExtensionList(ctx http.Context) http.Response {
|
||||
extensions := r.GetExtensions()
|
||||
return controllers.Success(ctx, extensions)
|
||||
}
|
||||
|
||||
func (r *Php81Controller) InstallExtension(ctx http.Context) http.Response {
|
||||
slug := ctx.Request().Input("slug")
|
||||
if len(slug) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
|
||||
}
|
||||
|
||||
extensions := r.GetExtensions()
|
||||
for _, item := range extensions {
|
||||
if item.Slug == slug {
|
||||
if item.Installed {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展已安装")
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "安装PHP-" + r.version + "扩展-" + item.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + r.version + ` >> '/tmp/` + item.Slug + `.log' 2>&1`
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Info("[PHP-" + r.version + "] 创建安装拓展任务失败:" + err.Error())
|
||||
return controllers.ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
r.task.Process(task.ID)
|
||||
|
||||
return controllers.Success(ctx, true)
|
||||
}
|
||||
}
|
||||
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在")
|
||||
}
|
||||
|
||||
func (r *Php81Controller) UninstallExtension(ctx http.Context) http.Response {
|
||||
slug := ctx.Request().Input("slug")
|
||||
if len(slug) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
|
||||
}
|
||||
|
||||
extensions := r.GetExtensions()
|
||||
for _, item := range extensions {
|
||||
if item.Slug == slug {
|
||||
if !item.Installed {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展未安装")
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "卸载PHP-" + r.version + "扩展-" + item.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + r.version + ` >> '/tmp/` + item.Slug + `.log' 2>&1`
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Info("[PHP-" + r.version + "] 创建卸载拓展任务失败:" + err.Error())
|
||||
return controllers.ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
r.task.Process(task.ID)
|
||||
|
||||
return controllers.Success(ctx, true)
|
||||
}
|
||||
}
|
||||
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在")
|
||||
}
|
||||
|
||||
func (r *Php81Controller) GetExtensions() []PHPExtension {
|
||||
var extensions []PHPExtension
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "OPcache",
|
||||
Slug: "Zend OPcache",
|
||||
Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "PhpRedis",
|
||||
Slug: "redis",
|
||||
Description: "PhpRedis 是一个用 C 语言编写的 PHP 模块,用来连接并操作 Redis 数据库上的数据。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "ImageMagick",
|
||||
Slug: "imagick",
|
||||
Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "Exif",
|
||||
Slug: "exif",
|
||||
Description: "通过 exif 扩展,你可以操作图像元数据。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "pdo_pgsql",
|
||||
Slug: "pdo_pgsql",
|
||||
Description: "(需先安装PostgreSQL)pdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象(PDO)接口以启用从 PHP 到 PostgreSQL 数据库的访问。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "ionCube",
|
||||
Slug: "ionCube Loader",
|
||||
Description: "ionCube 是一个专业级的 PHP 加密解密工具。",
|
||||
Installed: false,
|
||||
})
|
||||
|
||||
raw, err := tools.Exec("/www/server/php/" + r.version + "/bin/php -m")
|
||||
if err != nil {
|
||||
return extensions
|
||||
}
|
||||
|
||||
rawExtensionList := strings.Split(raw, "\n")
|
||||
for _, item := range rawExtensionList {
|
||||
if !strings.Contains(item, "[") && item != "" {
|
||||
for i := range extensions {
|
||||
if extensions[i].Slug == item {
|
||||
extensions[i].Installed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extensions
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/facades"
|
||||
"github.com/imroc/req/v3"
|
||||
|
||||
"panel/app/http/controllers"
|
||||
"panel/app/models"
|
||||
"panel/internal"
|
||||
"panel/internal/services"
|
||||
"panel/pkg/tools"
|
||||
)
|
||||
|
||||
type Php82Controller struct {
|
||||
setting internal.Setting
|
||||
task internal.Task
|
||||
version string
|
||||
}
|
||||
|
||||
func NewPhp82Controller() *Php82Controller {
|
||||
return &Php82Controller{
|
||||
setting: services.NewSettingImpl(),
|
||||
task: services.NewTaskImpl(),
|
||||
version: "82",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Php82Controller) Status(ctx http.Context) http.Response {
|
||||
status, err := tools.ServiceStatus("php-fpm-" + r.version)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, status)
|
||||
}
|
||||
|
||||
func (r *Php82Controller) Reload(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceReload("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重载PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php82Controller) Start(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceStart("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "启动PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php82Controller) Stop(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceStop("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "停止PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php82Controller) Restart(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceRestart("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重启PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php82Controller) GetConfig(ctx http.Context) http.Response {
|
||||
config, err := tools.Read("/www/server/php/" + r.version + "/etc/php.ini")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"配置失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, config)
|
||||
}
|
||||
|
||||
func (r *Php82Controller) SaveConfig(ctx http.Context) http.Response {
|
||||
config := ctx.Request().Input("config")
|
||||
if err := tools.Write("/www/server/php/"+r.version+"/etc/php.ini", config, 0644); err != nil {
|
||||
return nil
|
||||
}
|
||||
return r.Reload(ctx)
|
||||
}
|
||||
|
||||
func (r *Php82Controller) Load(ctx http.Context) http.Response {
|
||||
client := req.C().SetTimeout(10 * time.Second)
|
||||
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + r.version)
|
||||
if err != nil || !resp.IsSuccessState() {
|
||||
facades.Log().Info("获取PHP-" + r.version + "运行状态失败")
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+r.version+"] 获取运行状态失败")
|
||||
}
|
||||
|
||||
raw := resp.String()
|
||||
dataKeys := []string{"应用池", "工作模式", "启动时间", "接受连接", "监听队列", "最大监听队列", "监听队列长度", "空闲进程数量", "活动进程数量", "总进程数量", "最大活跃进程数量", "达到进程上限次数", "慢请求"}
|
||||
regexKeys := []string{"pool", "process manager", "start time", "accepted conn", "listen queue", "max listen queue", "listen queue len", "idle processes", "active processes", "total processes", "max active processes", "max children reached", "slow requests"}
|
||||
|
||||
type Data struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
data := make([]Data, len(dataKeys))
|
||||
for i := range dataKeys {
|
||||
data[i].Name = dataKeys[i]
|
||||
|
||||
r := regexp.MustCompile(fmt.Sprintf("%s:\\s+(.*)", regexKeys[i]))
|
||||
match := r.FindStringSubmatch(raw)
|
||||
|
||||
if len(match) > 1 {
|
||||
data[i].Value = strings.TrimSpace(match[1])
|
||||
}
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, data)
|
||||
}
|
||||
|
||||
func (r *Php82Controller) ErrorLog(ctx http.Context) http.Response {
|
||||
log, _ := tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/php-fpm.log")
|
||||
return controllers.Success(ctx, log)
|
||||
}
|
||||
|
||||
func (r *Php82Controller) SlowLog(ctx http.Context) http.Response {
|
||||
log, _ := tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/slow.log")
|
||||
return controllers.Success(ctx, log)
|
||||
}
|
||||
|
||||
func (r *Php82Controller) ClearErrorLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/php-fpm.log"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php82Controller) ClearSlowLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/slow.log"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php82Controller) GetExtensionList(ctx http.Context) http.Response {
|
||||
extensions := r.GetExtensions()
|
||||
return controllers.Success(ctx, extensions)
|
||||
}
|
||||
|
||||
func (r *Php82Controller) InstallExtension(ctx http.Context) http.Response {
|
||||
slug := ctx.Request().Input("slug")
|
||||
if len(slug) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
|
||||
}
|
||||
|
||||
extensions := r.GetExtensions()
|
||||
for _, item := range extensions {
|
||||
if item.Slug == slug {
|
||||
if item.Installed {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展已安装")
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "安装PHP-" + r.version + "扩展-" + item.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + r.version + ` >> '/tmp/` + item.Slug + `.log' 2>&1`
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Info("[PHP-" + r.version + "] 创建安装拓展任务失败:" + err.Error())
|
||||
return controllers.ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
r.task.Process(task.ID)
|
||||
|
||||
return controllers.Success(ctx, true)
|
||||
}
|
||||
}
|
||||
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在")
|
||||
}
|
||||
|
||||
func (r *Php82Controller) UninstallExtension(ctx http.Context) http.Response {
|
||||
slug := ctx.Request().Input("slug")
|
||||
if len(slug) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
|
||||
}
|
||||
|
||||
extensions := r.GetExtensions()
|
||||
for _, item := range extensions {
|
||||
if item.Slug == slug {
|
||||
if !item.Installed {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展未安装")
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "卸载PHP-" + r.version + "扩展-" + item.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + r.version + ` >> '/tmp/` + item.Slug + `.log' 2>&1`
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Info("[PHP-" + r.version + "] 创建卸载拓展任务失败:" + err.Error())
|
||||
return controllers.ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
r.task.Process(task.ID)
|
||||
|
||||
return controllers.Success(ctx, true)
|
||||
}
|
||||
}
|
||||
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在")
|
||||
}
|
||||
|
||||
func (r *Php82Controller) GetExtensions() []PHPExtension {
|
||||
var extensions []PHPExtension
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "OPcache",
|
||||
Slug: "Zend OPcache",
|
||||
Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "PhpRedis",
|
||||
Slug: "redis",
|
||||
Description: "PhpRedis 是一个用 C 语言编写的 PHP 模块,用来连接并操作 Redis 数据库上的数据。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "ImageMagick",
|
||||
Slug: "imagick",
|
||||
Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "Exif",
|
||||
Slug: "exif",
|
||||
Description: "通过 exif 扩展,你可以操作图像元数据。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "pdo_pgsql",
|
||||
Slug: "pdo_pgsql",
|
||||
Description: "(需先安装PostgreSQL)pdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象(PDO)接口以启用从 PHP 到 PostgreSQL 数据库的访问。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "ionCube",
|
||||
Slug: "ionCube Loader",
|
||||
Description: "ionCube 是一个专业级的 PHP 加密解密工具。",
|
||||
Installed: false,
|
||||
})
|
||||
|
||||
raw, err := tools.Exec("/www/server/php/" + r.version + "/bin/php -m")
|
||||
if err != nil {
|
||||
return extensions
|
||||
}
|
||||
|
||||
rawExtensionList := strings.Split(raw, "\n")
|
||||
for _, item := range rawExtensionList {
|
||||
if !strings.Contains(item, "[") && item != "" {
|
||||
for i := range extensions {
|
||||
if extensions[i].Slug == item {
|
||||
extensions[i].Installed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extensions
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/facades"
|
||||
"github.com/imroc/req/v3"
|
||||
|
||||
"panel/app/http/controllers"
|
||||
"panel/app/models"
|
||||
"panel/internal"
|
||||
"panel/internal/services"
|
||||
"panel/pkg/tools"
|
||||
)
|
||||
|
||||
type Php83Controller struct {
|
||||
setting internal.Setting
|
||||
task internal.Task
|
||||
version string
|
||||
}
|
||||
|
||||
func NewPhp83Controller() *Php83Controller {
|
||||
return &Php83Controller{
|
||||
setting: services.NewSettingImpl(),
|
||||
task: services.NewTaskImpl(),
|
||||
version: "83",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Php83Controller) Status(ctx http.Context) http.Response {
|
||||
status, err := tools.ServiceStatus("php-fpm-" + r.version)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, status)
|
||||
}
|
||||
|
||||
func (r *Php83Controller) Reload(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceReload("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重载PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php83Controller) Start(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceStart("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "启动PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php83Controller) Stop(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceStop("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "停止PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php83Controller) Restart(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceRestart("php-fpm-" + r.version); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重启PHP-"+r.version+"失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php83Controller) GetConfig(ctx http.Context) http.Response {
|
||||
config, err := tools.Read("/www/server/php/" + r.version + "/etc/php.ini")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"配置失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, config)
|
||||
}
|
||||
|
||||
func (r *Php83Controller) SaveConfig(ctx http.Context) http.Response {
|
||||
config := ctx.Request().Input("config")
|
||||
if err := tools.Write("/www/server/php/"+r.version+"/etc/php.ini", config, 0644); err != nil {
|
||||
return nil
|
||||
}
|
||||
return r.Reload(ctx)
|
||||
}
|
||||
|
||||
func (r *Php83Controller) Load(ctx http.Context) http.Response {
|
||||
client := req.C().SetTimeout(10 * time.Second)
|
||||
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + r.version)
|
||||
if err != nil || !resp.IsSuccessState() {
|
||||
facades.Log().Info("获取PHP-" + r.version + "运行状态失败")
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+r.version+"] 获取运行状态失败")
|
||||
}
|
||||
|
||||
raw := resp.String()
|
||||
dataKeys := []string{"应用池", "工作模式", "启动时间", "接受连接", "监听队列", "最大监听队列", "监听队列长度", "空闲进程数量", "活动进程数量", "总进程数量", "最大活跃进程数量", "达到进程上限次数", "慢请求"}
|
||||
regexKeys := []string{"pool", "process manager", "start time", "accepted conn", "listen queue", "max listen queue", "listen queue len", "idle processes", "active processes", "total processes", "max active processes", "max children reached", "slow requests"}
|
||||
|
||||
type Data struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
data := make([]Data, len(dataKeys))
|
||||
for i := range dataKeys {
|
||||
data[i].Name = dataKeys[i]
|
||||
|
||||
r := regexp.MustCompile(fmt.Sprintf("%s:\\s+(.*)", regexKeys[i]))
|
||||
match := r.FindStringSubmatch(raw)
|
||||
|
||||
if len(match) > 1 {
|
||||
data[i].Value = strings.TrimSpace(match[1])
|
||||
}
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, data)
|
||||
}
|
||||
|
||||
func (r *Php83Controller) ErrorLog(ctx http.Context) http.Response {
|
||||
log, _ := tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/php-fpm.log")
|
||||
return controllers.Success(ctx, log)
|
||||
}
|
||||
|
||||
func (r *Php83Controller) SlowLog(ctx http.Context) http.Response {
|
||||
log, _ := tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/slow.log")
|
||||
return controllers.Success(ctx, log)
|
||||
}
|
||||
|
||||
func (r *Php83Controller) ClearErrorLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/php-fpm.log"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php83Controller) ClearSlowLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/slow.log"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *Php83Controller) GetExtensionList(ctx http.Context) http.Response {
|
||||
extensions := r.GetExtensions()
|
||||
return controllers.Success(ctx, extensions)
|
||||
}
|
||||
|
||||
func (r *Php83Controller) InstallExtension(ctx http.Context) http.Response {
|
||||
slug := ctx.Request().Input("slug")
|
||||
if len(slug) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
|
||||
}
|
||||
|
||||
extensions := r.GetExtensions()
|
||||
for _, item := range extensions {
|
||||
if item.Slug == slug {
|
||||
if item.Installed {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展已安装")
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "安装PHP-" + r.version + "扩展-" + item.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + r.version + ` >> '/tmp/` + item.Slug + `.log' 2>&1`
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Info("[PHP-" + r.version + "] 创建安装拓展任务失败:" + err.Error())
|
||||
return controllers.ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
r.task.Process(task.ID)
|
||||
|
||||
return controllers.Success(ctx, true)
|
||||
}
|
||||
}
|
||||
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在")
|
||||
}
|
||||
|
||||
func (r *Php83Controller) UninstallExtension(ctx http.Context) http.Response {
|
||||
slug := ctx.Request().Input("slug")
|
||||
if len(slug) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
|
||||
}
|
||||
|
||||
extensions := r.GetExtensions()
|
||||
for _, item := range extensions {
|
||||
if item.Slug == slug {
|
||||
if !item.Installed {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展未安装")
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "卸载PHP-" + r.version + "扩展-" + item.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + r.version + ` >> '/tmp/` + item.Slug + `.log' 2>&1`
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Info("[PHP-" + r.version + "] 创建卸载拓展任务失败:" + err.Error())
|
||||
return controllers.ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
r.task.Process(task.ID)
|
||||
|
||||
return controllers.Success(ctx, true)
|
||||
}
|
||||
}
|
||||
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在")
|
||||
}
|
||||
|
||||
func (r *Php83Controller) GetExtensions() []PHPExtension {
|
||||
var extensions []PHPExtension
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "OPcache",
|
||||
Slug: "Zend OPcache",
|
||||
Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "PhpRedis",
|
||||
Slug: "redis",
|
||||
Description: "PhpRedis 是一个用 C 语言编写的 PHP 模块,用来连接并操作 Redis 数据库上的数据。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "ImageMagick",
|
||||
Slug: "imagick",
|
||||
Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "Exif",
|
||||
Slug: "exif",
|
||||
Description: "通过 exif 扩展,你可以操作图像元数据。",
|
||||
Installed: false,
|
||||
})
|
||||
extensions = append(extensions, PHPExtension{
|
||||
Name: "pdo_pgsql",
|
||||
Slug: "pdo_pgsql",
|
||||
Description: "(需先安装PostgreSQL)pdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象(PDO)接口以启用从 PHP 到 PostgreSQL 数据库的访问。",
|
||||
Installed: false,
|
||||
})
|
||||
|
||||
raw, err := tools.Exec("/www/server/php/" + r.version + "/bin/php -m")
|
||||
if err != nil {
|
||||
return extensions
|
||||
}
|
||||
|
||||
rawExtensionList := strings.Split(raw, "\n")
|
||||
for _, item := range rawExtensionList {
|
||||
if !strings.Contains(item, "[") && item != "" {
|
||||
for i := range extensions {
|
||||
if extensions[i].Slug == item {
|
||||
extensions[i].Installed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extensions
|
||||
}
|
||||
149
app/http/controllers/plugins/php_controller.go
Normal file
149
app/http/controllers/plugins/php_controller.go
Normal file
@@ -0,0 +1,149 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"panel/app/http/controllers"
|
||||
"panel/internal"
|
||||
"panel/internal/services"
|
||||
)
|
||||
|
||||
type PHPController struct {
|
||||
service internal.PHP
|
||||
}
|
||||
|
||||
func NewPHPController(version uint) *PHPController {
|
||||
return &PHPController{
|
||||
service: services.NewPHPImpl(version),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *PHPController) Status(ctx http.Context) http.Response {
|
||||
status, err := r.service.Status()
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, status)
|
||||
}
|
||||
|
||||
func (r *PHPController) Reload(ctx http.Context) http.Response {
|
||||
if err := r.service.Reload(); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *PHPController) Start(ctx http.Context) http.Response {
|
||||
if err := r.service.Start(); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *PHPController) Stop(ctx http.Context) http.Response {
|
||||
if err := r.service.Stop(); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *PHPController) Restart(ctx http.Context) http.Response {
|
||||
if err := r.service.Restart(); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *PHPController) GetConfig(ctx http.Context) http.Response {
|
||||
config, err := r.service.GetConfig()
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, config)
|
||||
}
|
||||
|
||||
func (r *PHPController) SaveConfig(ctx http.Context) http.Response {
|
||||
config := ctx.Request().Input("config")
|
||||
if err := r.service.SaveConfig(config); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *PHPController) Load(ctx http.Context) http.Response {
|
||||
load, err := r.service.Load()
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, load)
|
||||
}
|
||||
|
||||
func (r *PHPController) ErrorLog(ctx http.Context) http.Response {
|
||||
log, _ := r.service.GetErrorLog()
|
||||
return controllers.Success(ctx, log)
|
||||
}
|
||||
|
||||
func (r *PHPController) SlowLog(ctx http.Context) http.Response {
|
||||
log, _ := r.service.GetSlowLog()
|
||||
return controllers.Success(ctx, log)
|
||||
}
|
||||
|
||||
func (r *PHPController) ClearErrorLog(ctx http.Context) http.Response {
|
||||
err := r.service.ClearErrorLog()
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *PHPController) ClearSlowLog(ctx http.Context) http.Response {
|
||||
err := r.service.ClearSlowLog()
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *PHPController) GetExtensionList(ctx http.Context) http.Response {
|
||||
extensions, err := r.service.GetExtensions()
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, extensions)
|
||||
}
|
||||
|
||||
func (r *PHPController) InstallExtension(ctx http.Context) http.Response {
|
||||
slug := ctx.Request().Input("slug")
|
||||
if len(slug) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
|
||||
}
|
||||
|
||||
if err := r.service.InstallExtension(slug); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (r *PHPController) UninstallExtension(ctx http.Context) http.Response {
|
||||
slug := ctx.Request().Input("slug")
|
||||
if len(slug) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
|
||||
}
|
||||
|
||||
if err := r.service.UninstallExtension(slug); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
Reference in New Issue
Block a user