mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 22:07:16 +08:00
286 lines
8.1 KiB
Go
286 lines
8.1 KiB
Go
package controllers
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/goravel/framework/contracts/http"
|
|
"github.com/goravel/framework/facades"
|
|
|
|
"panel/app/models"
|
|
"panel/app/services"
|
|
)
|
|
|
|
type PluginController struct {
|
|
plugin services.Plugin
|
|
task services.Task
|
|
}
|
|
|
|
func NewPluginController() *PluginController {
|
|
return &PluginController{
|
|
plugin: services.NewPluginImpl(),
|
|
task: services.NewTaskImpl(),
|
|
}
|
|
}
|
|
|
|
// List 列出所有插件
|
|
func (r *PluginController) List(ctx http.Context) http.Response {
|
|
plugins := r.plugin.All()
|
|
installedPlugins, err := r.plugin.AllInstalled()
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
|
}
|
|
|
|
var lock sync.RWMutex
|
|
installedPluginsMap := make(map[string]models.Plugin)
|
|
|
|
for _, p := range installedPlugins {
|
|
lock.Lock()
|
|
installedPluginsMap[p.Slug] = p
|
|
lock.Unlock()
|
|
}
|
|
|
|
type plugin struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Slug string `json:"slug"`
|
|
Version string `json:"version"`
|
|
Requires []string `json:"requires"`
|
|
Excludes []string `json:"excludes"`
|
|
Installed bool `json:"installed"`
|
|
InstalledVersion string `json:"installed_version"`
|
|
Show bool `json:"show"`
|
|
}
|
|
|
|
var pluginArr []plugin
|
|
for _, item := range plugins {
|
|
installed, installedVersion, show := false, "", false
|
|
if _, ok := installedPluginsMap[item.Slug]; ok {
|
|
installed = true
|
|
installedVersion = installedPluginsMap[item.Slug].Version
|
|
show = installedPluginsMap[item.Slug].Show
|
|
}
|
|
pluginArr = append(pluginArr, plugin{
|
|
Name: item.Name,
|
|
Description: item.Description,
|
|
Slug: item.Slug,
|
|
Version: item.Version,
|
|
Requires: item.Requires,
|
|
Excludes: item.Excludes,
|
|
Installed: installed,
|
|
InstalledVersion: installedVersion,
|
|
Show: show,
|
|
})
|
|
}
|
|
|
|
page := ctx.Request().QueryInt("page", 1)
|
|
limit := ctx.Request().QueryInt("limit", 10)
|
|
startIndex := (page - 1) * limit
|
|
endIndex := page * limit
|
|
if startIndex > len(pluginArr) {
|
|
return Success(ctx, http.Json{
|
|
"total": 0,
|
|
"items": []plugin{},
|
|
})
|
|
}
|
|
if endIndex > len(pluginArr) {
|
|
endIndex = len(pluginArr)
|
|
}
|
|
pagedPlugins := pluginArr[startIndex:endIndex]
|
|
|
|
return Success(ctx, http.Json{
|
|
"total": len(pluginArr),
|
|
"items": pagedPlugins,
|
|
})
|
|
}
|
|
|
|
// Install 安装插件
|
|
func (r *PluginController) Install(ctx http.Context) http.Response {
|
|
slug := ctx.Request().Input("slug")
|
|
plugin := r.plugin.GetBySlug(slug)
|
|
installedPlugin := r.plugin.GetInstalledBySlug(slug)
|
|
installedPlugins, err := r.plugin.AllInstalled()
|
|
if err != nil {
|
|
facades.Log().Error("[面板][PluginController] 获取已安装插件失败")
|
|
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
|
}
|
|
|
|
if installedPlugin.ID != 0 {
|
|
return Error(ctx, http.StatusBadRequest, "插件已安装")
|
|
}
|
|
|
|
var lock sync.RWMutex
|
|
pluginsMap := make(map[string]bool)
|
|
|
|
for _, p := range installedPlugins {
|
|
lock.Lock()
|
|
pluginsMap[p.Slug] = true
|
|
lock.Unlock()
|
|
}
|
|
|
|
for _, require := range plugin.Requires {
|
|
lock.RLock()
|
|
_, requireFound := pluginsMap[require]
|
|
lock.RUnlock()
|
|
if !requireFound {
|
|
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 需要依赖 "+require+" 插件")
|
|
}
|
|
}
|
|
|
|
for _, exclude := range plugin.Excludes {
|
|
lock.RLock()
|
|
_, excludeFound := pluginsMap[exclude]
|
|
lock.RUnlock()
|
|
if excludeFound {
|
|
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 不兼容 "+exclude+" 插件")
|
|
}
|
|
}
|
|
|
|
var task models.Task
|
|
task.Name = "安装插件 " + plugin.Name
|
|
task.Status = models.TaskStatusWaiting
|
|
task.Shell = plugin.Install + " >> /tmp/" + plugin.Slug + ".log 2>&1"
|
|
task.Log = "/tmp/" + plugin.Slug + ".log"
|
|
if err := facades.Orm().Query().Create(&task); err != nil {
|
|
facades.Log().Error("[面板][PluginController] 创建任务失败: " + err.Error())
|
|
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
|
}
|
|
|
|
r.task.Process(task.ID)
|
|
return Success(ctx, "任务已提交")
|
|
}
|
|
|
|
// Uninstall 卸载插件
|
|
func (r *PluginController) Uninstall(ctx http.Context) http.Response {
|
|
slug := ctx.Request().Input("slug")
|
|
plugin := r.plugin.GetBySlug(slug)
|
|
installedPlugin := r.plugin.GetInstalledBySlug(slug)
|
|
installedPlugins, err := r.plugin.AllInstalled()
|
|
if err != nil {
|
|
facades.Log().Error("[面板][PluginController] 获取已安装插件失败")
|
|
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
|
}
|
|
|
|
if installedPlugin.ID == 0 {
|
|
return Error(ctx, http.StatusBadRequest, "插件未安装")
|
|
}
|
|
|
|
var lock sync.RWMutex
|
|
pluginsMap := make(map[string]bool)
|
|
|
|
for _, p := range installedPlugins {
|
|
lock.Lock()
|
|
pluginsMap[p.Slug] = true
|
|
lock.Unlock()
|
|
}
|
|
|
|
for _, require := range plugin.Requires {
|
|
lock.RLock()
|
|
_, requireFound := pluginsMap[require]
|
|
lock.RUnlock()
|
|
if !requireFound {
|
|
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 需要依赖 "+require+" 插件")
|
|
}
|
|
}
|
|
|
|
for _, exclude := range plugin.Excludes {
|
|
lock.RLock()
|
|
_, excludeFound := pluginsMap[exclude]
|
|
lock.RUnlock()
|
|
if excludeFound {
|
|
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 不兼容 "+exclude+" 插件")
|
|
}
|
|
}
|
|
|
|
var task models.Task
|
|
task.Name = "卸载插件 " + plugin.Name
|
|
task.Status = models.TaskStatusWaiting
|
|
task.Shell = plugin.Uninstall + " >> /tmp/" + plugin.Slug + ".log 2>&1"
|
|
task.Log = "/tmp/" + plugin.Slug + ".log"
|
|
if err := facades.Orm().Query().Create(&task); err != nil {
|
|
facades.Log().Error("[面板][PluginController] 创建任务失败: " + err.Error())
|
|
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
|
}
|
|
|
|
r.task.Process(task.ID)
|
|
return Success(ctx, "任务已提交")
|
|
}
|
|
|
|
// Update 更新插件
|
|
func (r *PluginController) Update(ctx http.Context) http.Response {
|
|
slug := ctx.Request().Input("slug")
|
|
plugin := r.plugin.GetBySlug(slug)
|
|
installedPlugin := r.plugin.GetInstalledBySlug(slug)
|
|
installedPlugins, err := r.plugin.AllInstalled()
|
|
if err != nil {
|
|
facades.Log().Error("[面板][PluginController] 获取已安装插件失败")
|
|
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
|
}
|
|
|
|
if installedPlugin.ID == 0 {
|
|
return Error(ctx, http.StatusBadRequest, "插件未安装")
|
|
}
|
|
|
|
var lock sync.RWMutex
|
|
pluginsMap := make(map[string]bool)
|
|
|
|
for _, p := range installedPlugins {
|
|
lock.Lock()
|
|
pluginsMap[p.Slug] = true
|
|
lock.Unlock()
|
|
}
|
|
|
|
for _, require := range plugin.Requires {
|
|
lock.RLock()
|
|
_, requireFound := pluginsMap[require]
|
|
lock.RUnlock()
|
|
if !requireFound {
|
|
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 需要依赖 "+require+" 插件")
|
|
}
|
|
}
|
|
|
|
for _, exclude := range plugin.Excludes {
|
|
lock.RLock()
|
|
_, excludeFound := pluginsMap[exclude]
|
|
lock.RUnlock()
|
|
if excludeFound {
|
|
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 不兼容 "+exclude+" 插件")
|
|
}
|
|
}
|
|
|
|
var task models.Task
|
|
task.Name = "更新插件 " + plugin.Name
|
|
task.Status = models.TaskStatusWaiting
|
|
task.Shell = plugin.Update + " >> /tmp/" + plugin.Slug + ".log 2>&1"
|
|
task.Log = "/tmp/" + plugin.Slug + ".log"
|
|
if err := facades.Orm().Query().Create(&task); err != nil {
|
|
facades.Log().Error("[面板][PluginController] 创建任务失败: " + err.Error())
|
|
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
|
}
|
|
|
|
r.task.Process(task.ID)
|
|
return Success(ctx, "任务已提交")
|
|
}
|
|
|
|
// UpdateShow 更新插件首页显示状态
|
|
func (r *PluginController) UpdateShow(ctx http.Context) http.Response {
|
|
slug := ctx.Request().Input("slug")
|
|
show := ctx.Request().InputBool("show")
|
|
|
|
var plugin models.Plugin
|
|
if err := facades.Orm().Query().Where("slug", slug).First(&plugin); err != nil {
|
|
facades.Log().Error("[面板][PluginController] 查询插件失败: " + err.Error())
|
|
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
|
}
|
|
if plugin.ID == 0 {
|
|
return Error(ctx, http.StatusBadRequest, "插件未安装")
|
|
}
|
|
|
|
plugin.Show = show
|
|
if err := facades.Orm().Query().Save(&plugin); err != nil {
|
|
facades.Log().Error("[面板][PluginController] 更新插件失败: " + err.Error())
|
|
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
|
}
|
|
|
|
return Success(ctx, "操作成功")
|
|
}
|