mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 16:10:59 +08:00
344 lines
9.7 KiB
Go
344 lines
9.7 KiB
Go
package controllers
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/goravel/framework/contracts/http"
|
|
"github.com/goravel/framework/facades"
|
|
|
|
"github.com/TheTNB/panel/app/models"
|
|
"github.com/TheTNB/panel/internal"
|
|
"github.com/TheTNB/panel/internal/services"
|
|
"github.com/TheTNB/panel/pkg/shell"
|
|
"github.com/TheTNB/panel/pkg/systemctl"
|
|
"github.com/TheTNB/panel/pkg/tools"
|
|
"github.com/TheTNB/panel/pkg/types"
|
|
)
|
|
|
|
type MenuItem struct {
|
|
Name string `json:"name"`
|
|
Title string `json:"title"`
|
|
Icon string `json:"icon"`
|
|
Jump string `json:"jump"`
|
|
}
|
|
|
|
type InfoController struct {
|
|
plugin internal.Plugin
|
|
setting internal.Setting
|
|
}
|
|
|
|
func NewInfoController() *InfoController {
|
|
return &InfoController{
|
|
plugin: services.NewPluginImpl(),
|
|
setting: services.NewSettingImpl(),
|
|
}
|
|
}
|
|
|
|
// Panel 获取面板信息
|
|
func (r *InfoController) Panel(ctx http.Context) http.Response {
|
|
return Success(ctx, http.Json{
|
|
"name": r.setting.Get(models.SettingKeyName),
|
|
"language": facades.Config().GetString("app.locale"),
|
|
})
|
|
}
|
|
|
|
// HomePlugins 获取首页插件
|
|
func (r *InfoController) HomePlugins(ctx http.Context) http.Response {
|
|
var plugins []models.Plugin
|
|
err := facades.Orm().Query().Where("show", 1).Find(&plugins)
|
|
if err != nil {
|
|
facades.Log().Request(ctx.Request()).Tags("面板", "基础信息").With(map[string]any{
|
|
"error": err.Error(),
|
|
}).Info("获取首页插件失败")
|
|
return ErrorSystem(ctx)
|
|
}
|
|
|
|
type pluginsData struct {
|
|
models.Plugin
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
var pluginsJson []pluginsData
|
|
for _, plugin := range plugins {
|
|
pluginsJson = append(pluginsJson, pluginsData{
|
|
Plugin: plugin,
|
|
Name: r.plugin.GetBySlug(plugin.Slug).Name,
|
|
})
|
|
}
|
|
|
|
return Success(ctx, pluginsJson)
|
|
}
|
|
|
|
// NowMonitor 获取当前监控信息
|
|
func (r *InfoController) NowMonitor(ctx http.Context) http.Response {
|
|
return Success(ctx, tools.GetMonitoringInfo())
|
|
}
|
|
|
|
// SystemInfo 获取系统信息
|
|
func (r *InfoController) SystemInfo(ctx http.Context) http.Response {
|
|
monitorInfo := tools.GetMonitoringInfo()
|
|
|
|
return Success(ctx, http.Json{
|
|
"os_name": monitorInfo.Host.Platform + " " + monitorInfo.Host.PlatformVersion,
|
|
"uptime": fmt.Sprintf("%.2f", float64(monitorInfo.Host.Uptime)/86400),
|
|
"panel_version": facades.Config().GetString("panel.version"),
|
|
})
|
|
}
|
|
|
|
// CountInfo 获取面板统计信息
|
|
func (r *InfoController) CountInfo(ctx http.Context) http.Response {
|
|
var websiteCount int64
|
|
err := facades.Orm().Query().Model(models.Website{}).Count(&websiteCount)
|
|
if err != nil {
|
|
websiteCount = -1
|
|
}
|
|
|
|
var mysql models.Plugin
|
|
mysqlInstalled := true
|
|
err = facades.Orm().Query().Where("slug like ?", "mysql%").FirstOrFail(&mysql)
|
|
if err != nil {
|
|
mysqlInstalled = false
|
|
}
|
|
var postgresql models.Plugin
|
|
postgresqlInstalled := true
|
|
err = facades.Orm().Query().Where("slug like ?", "postgresql%").FirstOrFail(&postgresql)
|
|
if err != nil {
|
|
postgresqlInstalled = false
|
|
}
|
|
var databaseCount int64
|
|
if mysqlInstalled {
|
|
status, err := systemctl.Status("mysqld")
|
|
if status && err == nil {
|
|
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
|
type database struct {
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/")
|
|
if err != nil {
|
|
facades.Log().Request(ctx.Request()).Tags("面板", "基础信息").With(map[string]any{
|
|
"error": err.Error(),
|
|
}).Info("获取数据库列表失败")
|
|
databaseCount = -1
|
|
} else {
|
|
defer db.Close()
|
|
rows, err := db.Query("SHOW DATABASES")
|
|
if err != nil {
|
|
facades.Log().Request(ctx.Request()).Tags("面板", "基础信息").With(map[string]any{
|
|
"error": err.Error(),
|
|
}).Info("获取数据库列表失败")
|
|
databaseCount = -1
|
|
} else {
|
|
defer rows.Close()
|
|
var databases []database
|
|
for rows.Next() {
|
|
var d database
|
|
err := rows.Scan(&d.Name)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if d.Name == "information_schema" || d.Name == "performance_schema" || d.Name == "mysql" || d.Name == "sys" {
|
|
continue
|
|
}
|
|
|
|
databases = append(databases, d)
|
|
}
|
|
databaseCount = int64(len(databases))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if postgresqlInstalled {
|
|
status, err := systemctl.Status("postgresql")
|
|
if status && err == nil {
|
|
raw, err := shell.Execf(`echo "\l" | su - postgres -c "psql"`)
|
|
if err == nil {
|
|
databases := strings.Split(raw, "\n")
|
|
if len(databases) >= 4 {
|
|
databases = databases[3 : len(databases)-1]
|
|
for _, db := range databases {
|
|
parts := strings.Split(db, "|")
|
|
if len(parts) != 9 || len(strings.TrimSpace(parts[0])) == 0 || strings.TrimSpace(parts[0]) == "template0" || strings.TrimSpace(parts[0]) == "template1" || strings.TrimSpace(parts[0]) == "postgres" {
|
|
continue
|
|
}
|
|
|
|
databaseCount++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var ftpCount int64
|
|
var ftpPlugin = r.plugin.GetInstalledBySlug("pureftpd")
|
|
if ftpPlugin.ID != 0 {
|
|
listRaw, err := shell.Execf("pure-pw list")
|
|
if len(listRaw) != 0 && err == nil {
|
|
listArr := strings.Split(listRaw, "\n")
|
|
ftpCount = int64(len(listArr))
|
|
}
|
|
}
|
|
|
|
var cronCount int64
|
|
err = facades.Orm().Query().Model(models.Cron{}).Count(&cronCount)
|
|
if err != nil {
|
|
cronCount = -1
|
|
}
|
|
|
|
return Success(ctx, http.Json{
|
|
"website": websiteCount,
|
|
"database": databaseCount,
|
|
"ftp": ftpCount,
|
|
"cron": cronCount,
|
|
})
|
|
}
|
|
|
|
// InstalledDbAndPhp 获取已安装的数据库和 PHP 版本
|
|
func (r *InfoController) InstalledDbAndPhp(ctx http.Context) http.Response {
|
|
var php []models.Plugin
|
|
err := facades.Orm().Query().Where("slug like ?", "php%").Find(&php)
|
|
if err != nil {
|
|
return ErrorSystem(ctx)
|
|
}
|
|
|
|
var mysql models.Plugin
|
|
mysqlInstalled := true
|
|
err = facades.Orm().Query().Where("slug like ?", "mysql%").FirstOrFail(&mysql)
|
|
if err != nil {
|
|
mysqlInstalled = false
|
|
}
|
|
|
|
var postgresql models.Plugin
|
|
postgresqlInstalled := true
|
|
err = facades.Orm().Query().Where("slug like ?", "postgresql%").FirstOrFail(&postgresql)
|
|
if err != nil {
|
|
postgresqlInstalled = false
|
|
}
|
|
|
|
type data struct {
|
|
Label string `json:"label"`
|
|
Value string `json:"value"`
|
|
}
|
|
var phpData []data
|
|
var dbData []data
|
|
phpData = append(phpData, data{Value: "0", Label: "不使用"})
|
|
dbData = append(dbData, data{Value: "0", Label: "不使用"})
|
|
for _, p := range php {
|
|
match := regexp.MustCompile(`php(\d+)`).FindStringSubmatch(p.Slug)
|
|
if len(match) == 0 {
|
|
continue
|
|
}
|
|
|
|
phpData = append(phpData, data{Value: strings.ReplaceAll(p.Slug, "php", ""), Label: r.plugin.GetBySlug(p.Slug).Name})
|
|
}
|
|
|
|
if mysqlInstalled {
|
|
dbData = append(dbData, data{Value: "mysql", Label: "MySQL"})
|
|
}
|
|
if postgresqlInstalled {
|
|
dbData = append(dbData, data{Value: "postgresql", Label: "PostgreSQL"})
|
|
}
|
|
|
|
return Success(ctx, http.Json{
|
|
"php": phpData,
|
|
"db": dbData,
|
|
})
|
|
}
|
|
|
|
// CheckUpdate 检查面板更新
|
|
func (r *InfoController) CheckUpdate(ctx http.Context) http.Response {
|
|
version := facades.Config().GetString("panel.version")
|
|
remote, err := tools.GetLatestPanelVersion()
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, "获取最新版本失败")
|
|
}
|
|
|
|
if tools.VersionCompare(version, remote.Version, ">=") {
|
|
return Success(ctx, http.Json{
|
|
"update": false,
|
|
})
|
|
}
|
|
|
|
return Success(ctx, http.Json{
|
|
"update": true,
|
|
})
|
|
}
|
|
|
|
// UpdateInfo 获取更新信息
|
|
func (r *InfoController) UpdateInfo(ctx http.Context) http.Response {
|
|
version := facades.Config().GetString("panel.version")
|
|
current, err := tools.GetLatestPanelVersion()
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, "获取最新版本失败")
|
|
}
|
|
|
|
if tools.VersionCompare(version, current.Version, ">=") {
|
|
return Error(ctx, http.StatusInternalServerError, "当前版本已是最新版本")
|
|
}
|
|
|
|
versions, err := tools.GenerateVersions(version, current.Version)
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, "获取更新信息失败")
|
|
}
|
|
|
|
var versionInfo []tools.PanelInfo
|
|
for _, v := range versions {
|
|
info, err := tools.GetPanelVersion(v)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
versionInfo = append(versionInfo, info)
|
|
}
|
|
|
|
return Success(ctx, versionInfo)
|
|
}
|
|
|
|
// Update 更新面板
|
|
func (r *InfoController) Update(ctx http.Context) http.Response {
|
|
var task models.Task
|
|
if err := facades.Orm().Query().Where("status", models.TaskStatusRunning).OrWhere("status", models.TaskStatusWaiting).FirstOrFail(&task); err == nil {
|
|
return Error(ctx, http.StatusInternalServerError, "当前有任务正在执行,禁止更新")
|
|
}
|
|
if _, err := facades.Orm().Query().Exec("PRAGMA wal_checkpoint(TRUNCATE)"); err != nil {
|
|
types.Status = types.StatusFailed
|
|
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("面板数据库异常,已终止操作:%s", err.Error()))
|
|
}
|
|
|
|
panel, err := tools.GetLatestPanelVersion()
|
|
if err != nil {
|
|
facades.Log().Request(ctx.Request()).Tags("面板", "基础信息").With(map[string]any{
|
|
"error": err.Error(),
|
|
}).Info("获取最新版本失败")
|
|
return Error(ctx, http.StatusInternalServerError, "获取最新版本失败")
|
|
}
|
|
|
|
types.Status = types.StatusUpgrade
|
|
if err = tools.UpdatePanel(panel); err != nil {
|
|
types.Status = types.StatusFailed
|
|
facades.Log().Request(ctx.Request()).Tags("面板", "基础信息").With(map[string]any{
|
|
"error": err.Error(),
|
|
}).Info("更新面板失败")
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
types.Status = types.StatusNormal
|
|
tools.RestartPanel()
|
|
return Success(ctx, nil)
|
|
}
|
|
|
|
// Restart 重启面板
|
|
func (r *InfoController) Restart(ctx http.Context) http.Response {
|
|
var task models.Task
|
|
err := facades.Orm().Query().Where("status", models.TaskStatusRunning).OrWhere("status", models.TaskStatusWaiting).FirstOrFail(&task)
|
|
if err == nil {
|
|
return Error(ctx, http.StatusInternalServerError, "当前有任务正在执行,禁止重启")
|
|
}
|
|
|
|
tools.RestartPanel()
|
|
return Success(ctx, nil)
|
|
}
|