mirror of
https://github.com/tnb-labs/panel.git
synced 2026-08-31 01:12:17 +08:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e7af2914e | |||
| 7ec378829b | |||
| f2e8ff86c6 | |||
| eed03d644f | |||
| d9be877710 | |||
| 10439d7d97 | |||
| 55eaa26970 | |||
| 0d769d3400 | |||
| 7ff084e5a8 | |||
| 89e133c626 | |||
| c876f4c484 | |||
| 85c38e5bc5 | |||
| 6bd07251fc | |||
| 11d6b40dce | |||
| aa9b55d1eb | |||
| 2e97812776 | |||
| e0a7ada6d5 |
+1
-1
@@ -1,4 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
open_collective: tnb
|
||||
custom: ['https://afdian.net/a/TheTNB']
|
||||
custom: [ 'https://afdian.net/a/TheTNB' ]
|
||||
|
||||
@@ -2,8 +2,6 @@ package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
|
||||
"github.com/goravel/framework/contracts/console"
|
||||
"github.com/goravel/framework/contracts/console/command"
|
||||
@@ -12,7 +10,8 @@ import (
|
||||
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/internal/services"
|
||||
"github.com/TheTNB/panel/types"
|
||||
panelcert "github.com/TheTNB/panel/pkg/cert"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
// CertRenew 证书续签
|
||||
@@ -54,18 +53,15 @@ func (receiver *CertRenew) Handle(console.Context) error {
|
||||
continue
|
||||
}
|
||||
|
||||
block, _ := pem.Decode([]byte(cert.Cert))
|
||||
if block != nil {
|
||||
data, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
decode, err := panelcert.ParseCert(cert.Cert)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// 结束时间大于 7 天的证书不续签
|
||||
endTime := carbon.FromStdTime(data.NotAfter)
|
||||
if endTime.Gt(carbon.Now().AddDays(7)) {
|
||||
continue
|
||||
}
|
||||
// 结束时间大于 7 天的证书不续签
|
||||
endTime := carbon.FromStdTime(decode.NotAfter)
|
||||
if endTime.Gt(carbon.Now().AddDays(7)) {
|
||||
continue
|
||||
}
|
||||
|
||||
certService := services.NewCertImpl()
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/internal/services"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
// Monitoring 系统监控
|
||||
@@ -60,14 +60,7 @@ func (receiver *Monitoring) Handle(console.Context) error {
|
||||
|
||||
// 去除部分数据以减少数据库存储
|
||||
info.Disk = nil
|
||||
for _, cpu := range info.Cpus {
|
||||
cpu.VendorID = ""
|
||||
cpu.Family = ""
|
||||
cpu.Model = ""
|
||||
cpu.PhysicalID = ""
|
||||
cpu.ModelName = ""
|
||||
cpu.Flags = nil
|
||||
}
|
||||
info.Cpus = nil
|
||||
|
||||
if types.Status != types.StatusNormal {
|
||||
return nil
|
||||
|
||||
@@ -17,8 +17,12 @@ import (
|
||||
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/internal/services"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
// Panel 面板命令行
|
||||
@@ -70,7 +74,7 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
hash, err := facades.Hash().Make(tools.RandomString(32))
|
||||
hash, err := facades.Hash().Make(str.RandomString(32))
|
||||
if err != nil {
|
||||
color.Red().Printfln(translate.Get("commands.panel.init.fail"))
|
||||
return nil
|
||||
@@ -104,7 +108,7 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
}
|
||||
|
||||
// 停止面板服务,因为在shell中运行的和systemd的不同
|
||||
_ = tools.ServiceStop("panel")
|
||||
_ = systemctl.Stop("panel")
|
||||
|
||||
types.Status = types.StatusUpgrade
|
||||
if err = tools.UpdatePanel(panel); err != nil {
|
||||
@@ -125,16 +129,16 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
password := tools.RandomString(16)
|
||||
password := str.RandomString(16)
|
||||
hash, err := facades.Hash().Make(password)
|
||||
if err != nil {
|
||||
color.Red().Printfln(translate.Get("commands.panel.getInfo.passwordGenerationFail"))
|
||||
return nil
|
||||
}
|
||||
user.Username = tools.RandomString(8)
|
||||
user.Username = str.RandomString(8)
|
||||
user.Password = hash
|
||||
if user.Email == "" {
|
||||
user.Email = tools.RandomString(8) + "@example.com"
|
||||
user.Email = str.RandomString(8) + "@example.com"
|
||||
}
|
||||
|
||||
err = facades.Orm().Query().Save(&user)
|
||||
@@ -143,7 +147,7 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
port, err := tools.Exec(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
|
||||
port, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
|
||||
if err != nil {
|
||||
color.Red().Printfln(translate.Get("commands.panel.portFail"))
|
||||
return nil
|
||||
@@ -164,7 +168,7 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
color.Green().Printfln(translate.Get("commands.panel.getInfo.address") + ": " + protocol + "://" + ip + ":" + port + facades.Config().GetString("http.entrance"))
|
||||
|
||||
case "getPort":
|
||||
port, err := tools.Exec(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
|
||||
port, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
|
||||
if err != nil {
|
||||
color.Red().Printfln(translate.Get("commands.panel.portFail"))
|
||||
return nil
|
||||
@@ -176,12 +180,12 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
color.Green().Printfln(translate.Get("commands.panel.entrance") + ": " + facades.Config().GetString("http.entrance"))
|
||||
|
||||
case "deleteEntrance":
|
||||
oldEntrance, err := tools.Exec(`cat /www/panel/panel.conf | grep APP_ENTRANCE | awk -F '=' '{print $2}' | tr -d '\n'`)
|
||||
oldEntrance, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_ENTRANCE | awk -F '=' '{print $2}' | tr -d '\n'`)
|
||||
if err != nil {
|
||||
color.Red().Printfln(translate.Get("commands.panel.deleteEntrance.fail"))
|
||||
return nil
|
||||
}
|
||||
if _, err = tools.Exec("sed -i 's!APP_ENTRANCE=" + oldEntrance + "!APP_ENTRANCE=/!g' /www/panel/panel.conf"); err != nil {
|
||||
if _, err = shell.Execf("sed -i 's!APP_ENTRANCE=" + oldEntrance + "!APP_ENTRANCE=/!g' /www/panel/panel.conf"); err != nil {
|
||||
color.Red().Printfln(translate.Get("commands.panel.deleteEntrance.fail"))
|
||||
return nil
|
||||
}
|
||||
@@ -270,8 +274,8 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
color.Green().Printfln("★ " + translate.Get("commands.panel.backup.start") + " [" + carbon.Now().ToDateTimeString() + "]")
|
||||
color.Green().Printfln(hr)
|
||||
|
||||
if !tools.Exists(path) {
|
||||
if err := tools.Mkdir(path, 0644); err != nil {
|
||||
if !io.Exists(path) {
|
||||
if err := io.Mkdir(path, 0644); err != nil {
|
||||
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.backupDirFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
@@ -288,7 +292,7 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
}
|
||||
|
||||
backupFile := path + "/" + website.Name + "_" + carbon.Now().ToShortDateTimeString() + ".zip"
|
||||
if _, err := tools.Exec(`cd '` + website.Path + `' && zip -r '` + backupFile + `' .`); err != nil {
|
||||
if _, err := shell.Execf(`cd '` + website.Path + `' && zip -r '` + backupFile + `' .`); err != nil {
|
||||
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.backupFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
@@ -307,23 +311,23 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
|
||||
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.targetMysql") + ": " + name)
|
||||
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.startExport"))
|
||||
if _, err = tools.Exec(`mysqldump -uroot ` + name + ` > /tmp/` + backupFile + ` 2>&1`); err != nil {
|
||||
if _, err = shell.Execf(`mysqldump -uroot ` + name + ` > /tmp/` + backupFile + ` 2>&1`); err != nil {
|
||||
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.exportFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.exportSuccess"))
|
||||
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.startCompress"))
|
||||
if _, err = tools.Exec("cd /tmp && zip -r " + backupFile + ".zip " + backupFile); err != nil {
|
||||
if _, err = shell.Execf("cd /tmp && zip -r " + backupFile + ".zip " + backupFile); err != nil {
|
||||
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.compressFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
if err := tools.Remove("/tmp/" + backupFile); err != nil {
|
||||
if err := io.Remove("/tmp/" + backupFile); err != nil {
|
||||
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.deleteFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.compressSuccess"))
|
||||
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.startMove"))
|
||||
if err := tools.Mv("/tmp/"+backupFile+".zip", path+"/"+backupFile+".zip"); err != nil {
|
||||
if err := io.Mv("/tmp/"+backupFile+".zip", path+"/"+backupFile+".zip"); err != nil {
|
||||
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.moveFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
@@ -333,7 +337,7 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
|
||||
case "postgresql":
|
||||
backupFile := name + "_" + carbon.Now().ToShortDateTimeString() + ".sql"
|
||||
check, err := tools.Exec(`su - postgres -c "psql -l" 2>&1`)
|
||||
check, err := shell.Execf(`su - postgres -c "psql -l" 2>&1`)
|
||||
if err != nil {
|
||||
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.databaseGetFail") + ": " + err.Error())
|
||||
color.Green().Printfln(hr)
|
||||
@@ -347,23 +351,23 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
|
||||
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.targetPostgres") + ": " + name)
|
||||
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.startExport"))
|
||||
if _, err = tools.Exec(`su - postgres -c "pg_dump '` + name + `'" > /tmp/` + backupFile + ` 2>&1`); err != nil {
|
||||
if _, err = shell.Execf(`su - postgres -c "pg_dump '` + name + `'" > /tmp/` + backupFile + ` 2>&1`); err != nil {
|
||||
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.exportFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.exportSuccess"))
|
||||
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.startCompress"))
|
||||
if _, err = tools.Exec("cd /tmp && zip -r " + backupFile + ".zip " + backupFile); err != nil {
|
||||
if _, err = shell.Execf("cd /tmp && zip -r " + backupFile + ".zip " + backupFile); err != nil {
|
||||
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.compressFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
if err := tools.Remove("/tmp/" + backupFile); err != nil {
|
||||
if err := io.Remove("/tmp/" + backupFile); err != nil {
|
||||
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.deleteFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.compressSuccess"))
|
||||
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.startMove"))
|
||||
if err := tools.Mv("/tmp/"+backupFile+".zip", path+"/"+backupFile+".zip"); err != nil {
|
||||
if err := io.Mv("/tmp/"+backupFile+".zip", path+"/"+backupFile+".zip"); err != nil {
|
||||
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.moveFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
@@ -393,7 +397,7 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
for i := cast.ToInt(save); i < len(filteredFiles); i++ {
|
||||
fileToDelete := filepath.Join(path, filteredFiles[i].Name())
|
||||
color.Yellow().Printfln("|-" + translate.Get("commands.panel.backup.cleanBackup") + ": " + fileToDelete)
|
||||
if err := tools.Remove(fileToDelete); err != nil {
|
||||
if err := io.Remove(fileToDelete); err != nil {
|
||||
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.cleanupFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
@@ -425,18 +429,18 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
}
|
||||
|
||||
logPath := "/www/wwwlogs/" + website.Name + ".log"
|
||||
if !tools.Exists(logPath) {
|
||||
if !io.Exists(logPath) {
|
||||
color.Red().Printfln("|-" + translate.Get("commands.panel.cutoff.logNotExist"))
|
||||
color.Green().Printfln(hr)
|
||||
return nil
|
||||
}
|
||||
|
||||
backupPath := "/www/wwwlogs/" + website.Name + "_" + carbon.Now().ToShortDateTimeString() + ".log.zip"
|
||||
if _, err := tools.Exec(`cd /www/wwwlogs && zip -r ` + backupPath + ` ` + website.Name + ".log"); err != nil {
|
||||
if _, err := shell.Execf(`cd /www/wwwlogs && zip -r ` + backupPath + ` ` + website.Name + ".log"); err != nil {
|
||||
color.Red().Printfln("|-" + translate.Get("commands.panel.cutoff.backupFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
if _, err := tools.Exec(`echo "" > ` + logPath); err != nil {
|
||||
if _, err := shell.Execf(`echo "" > ` + logPath); err != nil {
|
||||
color.Red().Printfln("|-" + translate.Get("commands.panel.cutoff.clearFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
@@ -464,7 +468,7 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
for i := cast.ToInt(save); i < len(filteredFiles); i++ {
|
||||
fileToDelete := filepath.Join("/www/wwwlogs", filteredFiles[i].Name())
|
||||
color.Yellow().Printfln("|-" + translate.Get("commands.panel.cutoff.clearLog") + ": " + fileToDelete)
|
||||
if err := tools.Remove(fileToDelete); err != nil {
|
||||
if err := io.Remove(fileToDelete); err != nil {
|
||||
color.Red().Printfln("|-" + translate.Get("commands.panel.cutoff.cleanupFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,8 +8,9 @@ import (
|
||||
"github.com/goravel/framework/facades"
|
||||
"github.com/goravel/framework/support/carbon"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
// PanelTask 面板每日任务
|
||||
@@ -48,7 +49,7 @@ func (receiver *PanelTask) Handle(console.Context) error {
|
||||
}
|
||||
|
||||
// 备份面板
|
||||
if err := tools.Archive([]string{"/www/panel"}, "/www/backup/panel/panel-"+carbon.Now().ToShortDateTimeString()+".zip"); err != nil {
|
||||
if err := io.Archive([]string{"/www/panel"}, "/www/backup/panel/panel-"+carbon.Now().ToShortDateTimeString()+".zip"); err != nil {
|
||||
types.Status = types.StatusFailed
|
||||
facades.Log().Tags("面板", "每日任务").
|
||||
With(map[string]any{
|
||||
@@ -58,7 +59,7 @@ func (receiver *PanelTask) Handle(console.Context) error {
|
||||
}
|
||||
|
||||
// 清理 7 天前的备份
|
||||
if _, err := tools.Exec(`find /www/backup/panel -mtime +7 -name "*.zip" -exec rm -rf {} \;`); err != nil {
|
||||
if _, err := shell.Execf(`find /www/backup/panel -mtime +7 -name "*.zip" -exec rm -rf {} \;`); err != nil {
|
||||
types.Status = types.StatusFailed
|
||||
facades.Log().Tags("面板", "每日任务").
|
||||
With(map[string]any{
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"github.com/TheTNB/panel/internal"
|
||||
"github.com/TheTNB/panel/internal/services"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
)
|
||||
|
||||
type AssetController struct {
|
||||
@@ -43,7 +43,7 @@ func (r *AssetController) Index(ctx http.Context) http.Response {
|
||||
path = "/index.html"
|
||||
}
|
||||
|
||||
if !tools.Exists("public" + path) {
|
||||
if !io.Exists("public" + path) {
|
||||
return Error(ctx, http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/go-connections/nat"
|
||||
@@ -14,6 +13,7 @@ import (
|
||||
|
||||
requests "github.com/TheTNB/panel/app/http/requests/container"
|
||||
"github.com/TheTNB/panel/internal/services"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
)
|
||||
|
||||
type ContainerController struct {
|
||||
@@ -725,7 +725,7 @@ func (r *ContainerController) ImageList(ctx http.Context) http.Response {
|
||||
"id": item.ID,
|
||||
"created": carbon.FromTimestamp(item.Created).ToDateTimeString(),
|
||||
"containers": item.Containers,
|
||||
"size": tools.FormatBytes(float64(item.Size)),
|
||||
"size": str.FormatBytes(float64(item.Size)),
|
||||
"labels": item.Labels,
|
||||
"repo_tags": item.RepoTags,
|
||||
"repo_digests": item.RepoDigests,
|
||||
@@ -874,7 +874,7 @@ func (r *ContainerController) VolumeList(ctx http.Context) http.Response {
|
||||
if item.UsageData != nil {
|
||||
usage = map[string]any{
|
||||
"ref_count": item.UsageData.RefCount,
|
||||
"size": tools.FormatBytes(float64(item.UsageData.Size)),
|
||||
"size": str.FormatBytes(float64(item.UsageData.Size)),
|
||||
}
|
||||
}
|
||||
items = append(items, map[string]any{
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
commonrequests "github.com/TheTNB/panel/app/http/requests/common"
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/facades"
|
||||
|
||||
commonrequests "github.com/TheTNB/panel/app/http/requests/common"
|
||||
)
|
||||
|
||||
// SuccessResponse 通用成功响应
|
||||
|
||||
@@ -12,7 +12,9 @@ import (
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/internal"
|
||||
"github.com/TheTNB/panel/internal/services"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
)
|
||||
|
||||
type CronController struct {
|
||||
@@ -65,7 +67,7 @@ func (r *CronController) Add(ctx http.Context) http.Response {
|
||||
return Error(ctx, http.StatusUnprocessableEntity, "时间格式错误")
|
||||
}
|
||||
|
||||
shell := ctx.Request().Input("script")
|
||||
script := ctx.Request().Input("script")
|
||||
cronType := ctx.Request().Input("type")
|
||||
if cronType == "backup" {
|
||||
backupType := ctx.Request().Input("backup_type")
|
||||
@@ -78,7 +80,7 @@ func (r *CronController) Add(ctx http.Context) http.Response {
|
||||
backupPath = r.setting.Get(models.SettingKeyBackupPath) + "/" + backupType
|
||||
}
|
||||
backupSave := ctx.Request().InputInt("save", 10)
|
||||
shell = `#!/bin/bash
|
||||
script = `#!/bin/bash
|
||||
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
|
||||
|
||||
# 耗子面板 - 数据备份脚本
|
||||
@@ -95,7 +97,7 @@ panel backup ${type} ${name} ${path} ${save} 2>&1
|
||||
if cronType == "cutoff" {
|
||||
website := ctx.Request().Input("website")
|
||||
save := ctx.Request().InputInt("save", 180)
|
||||
shell = `#!/bin/bash
|
||||
script = `#!/bin/bash
|
||||
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
|
||||
|
||||
# 耗子面板 - 日志切割脚本
|
||||
@@ -110,17 +112,17 @@ panel cutoff ${name} ${save} 2>&1
|
||||
|
||||
shellDir := "/www/server/cron/"
|
||||
shellLogDir := "/www/server/cron/logs/"
|
||||
if !tools.Exists(shellDir) {
|
||||
if !io.Exists(shellDir) {
|
||||
return Error(ctx, http.StatusInternalServerError, "计划任务目录不存在")
|
||||
}
|
||||
if !tools.Exists(shellLogDir) {
|
||||
if !io.Exists(shellLogDir) {
|
||||
return Error(ctx, http.StatusInternalServerError, "计划任务日志目录不存在")
|
||||
}
|
||||
shellFile := strconv.Itoa(int(carbon.Now().Timestamp())) + tools.RandomString(16)
|
||||
if err := tools.Write(shellDir+shellFile+".sh", shell, 0700); err != nil {
|
||||
shellFile := strconv.Itoa(int(carbon.Now().Timestamp())) + str.RandomString(16)
|
||||
if err := io.Write(shellDir+shellFile+".sh", script, 0700); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if out, err := tools.Exec("dos2unix " + shellDir + shellFile + ".sh"); err != nil {
|
||||
if out, err := shell.Execf("dos2unix " + shellDir + shellFile + ".sh"); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -156,12 +158,12 @@ func (r *CronController) Script(ctx http.Context) http.Response {
|
||||
return Error(ctx, http.StatusUnprocessableEntity, "计划任务不存在")
|
||||
}
|
||||
|
||||
shell, err := tools.Read(cron.Shell)
|
||||
script, err := io.Read(cron.Shell)
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return Success(ctx, shell)
|
||||
return Success(ctx, script)
|
||||
}
|
||||
|
||||
// Update 更新计划任务
|
||||
@@ -197,10 +199,10 @@ func (r *CronController) Update(ctx http.Context) http.Response {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
if err := tools.Write(cron.Shell, ctx.Request().Input("script"), 0644); err != nil {
|
||||
if err := io.Write(cron.Shell, ctx.Request().Input("script"), 0644); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if out, err := tools.Exec("dos2unix " + cron.Shell); err != nil {
|
||||
if out, err := shell.Execf("dos2unix " + cron.Shell); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -226,7 +228,7 @@ func (r *CronController) Delete(ctx http.Context) http.Response {
|
||||
if err := r.cron.DeleteFromSystem(cron); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if err := tools.Remove(cron.Shell); err != nil {
|
||||
if err := io.Remove(cron.Shell); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -280,11 +282,11 @@ func (r *CronController) Log(ctx http.Context) http.Response {
|
||||
return Error(ctx, http.StatusUnprocessableEntity, "计划任务不存在")
|
||||
}
|
||||
|
||||
if !tools.Exists(cron.Log) {
|
||||
if !io.Exists(cron.Log) {
|
||||
return Error(ctx, http.StatusUnprocessableEntity, "日志文件不存在")
|
||||
}
|
||||
|
||||
log, err := tools.Exec("tail -n 1000 " + cron.Log)
|
||||
log, err := shell.Execf("tail -n 1000 " + cron.Log)
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
stdio "io"
|
||||
stdos "os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
@@ -12,7 +12,10 @@ import (
|
||||
"github.com/goravel/framework/support/carbon"
|
||||
|
||||
requests "github.com/TheTNB/panel/app/http/requests/file"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/os"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
)
|
||||
|
||||
type FileController struct {
|
||||
@@ -42,11 +45,11 @@ func (r *FileController) Create(ctx http.Context) http.Response {
|
||||
|
||||
isDir := ctx.Request().InputBool("dir")
|
||||
if !isDir {
|
||||
if out, err := tools.Exec("touch " + request.Path); err != nil {
|
||||
if out, err := shell.Execf("touch " + request.Path); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
} else {
|
||||
if err := tools.Mkdir(request.Path, 0755); err != nil {
|
||||
if err := io.Mkdir(request.Path, 0755); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
}
|
||||
@@ -73,7 +76,7 @@ func (r *FileController) Content(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
fileInfo, err := tools.FileInfo(request.Path)
|
||||
fileInfo, err := io.FileInfo(request.Path)
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -84,7 +87,7 @@ func (r *FileController) Content(ctx http.Context) http.Response {
|
||||
return Error(ctx, http.StatusInternalServerError, "文件大小超过 10 M,不支持在线编辑")
|
||||
}
|
||||
|
||||
content, err := tools.Read(request.Path)
|
||||
content, err := io.Read(request.Path)
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -110,11 +113,11 @@ func (r *FileController) Save(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
fileInfo, err := tools.FileInfo(request.Path)
|
||||
fileInfo, err := io.FileInfo(request.Path)
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if err = tools.Write(request.Path, request.Content, fileInfo.Mode()); err != nil {
|
||||
if err = io.Write(request.Path, request.Content, fileInfo.Mode()); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -140,7 +143,7 @@ func (r *FileController) Delete(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
if err := tools.Remove(request.Path); err != nil {
|
||||
if err := io.Remove(request.Path); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -172,16 +175,16 @@ func (r *FileController) Upload(ctx http.Context) http.Response {
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
if tools.Exists(request.Path) && !ctx.Request().InputBool("force") {
|
||||
if io.Exists(request.Path) && !ctx.Request().InputBool("force") {
|
||||
return Error(ctx, http.StatusForbidden, "目标路径已存在,是否覆盖?")
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(src)
|
||||
data, err := stdio.ReadAll(src)
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
if err = tools.Write(request.Path, string(data), 0755); err != nil {
|
||||
if err = io.Write(request.Path, string(data), 0755); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -207,11 +210,11 @@ func (r *FileController) Move(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
if tools.Exists(request.Target) && !ctx.Request().InputBool("force") {
|
||||
if io.Exists(request.Target) && !ctx.Request().InputBool("force") {
|
||||
return Error(ctx, http.StatusForbidden, "目标路径"+request.Target+"已存在")
|
||||
}
|
||||
|
||||
if err := tools.Mv(request.Source, request.Target); err != nil {
|
||||
if err := io.Mv(request.Source, request.Target); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -237,11 +240,11 @@ func (r *FileController) Copy(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
if tools.Exists(request.Target) && !ctx.Request().InputBool("force") {
|
||||
if io.Exists(request.Target) && !ctx.Request().InputBool("force") {
|
||||
return Error(ctx, http.StatusForbidden, "目标路径"+request.Target+"已存在")
|
||||
}
|
||||
|
||||
if err := tools.Cp(request.Source, request.Target); err != nil {
|
||||
if err := io.Cp(request.Source, request.Target); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -267,7 +270,7 @@ func (r *FileController) Download(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
info, err := tools.FileInfo(request.Path)
|
||||
info, err := io.FileInfo(request.Path)
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -318,14 +321,14 @@ func (r *FileController) Info(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
fileInfo, err := tools.FileInfo(request.Path)
|
||||
fileInfo, err := io.FileInfo(request.Path)
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return Success(ctx, http.Json{
|
||||
"name": fileInfo.Name(),
|
||||
"size": tools.FormatBytes(float64(fileInfo.Size())),
|
||||
"size": str.FormatBytes(float64(fileInfo.Size())),
|
||||
"mode_str": fileInfo.Mode().String(),
|
||||
"mode": fmt.Sprintf("%04o", fileInfo.Mode().Perm()),
|
||||
"dir": fileInfo.IsDir(),
|
||||
@@ -351,10 +354,10 @@ func (r *FileController) Permission(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
if err := tools.Chmod(request.Path, os.FileMode(request.Mode)); err != nil {
|
||||
if err := io.Chmod(request.Path, stdos.FileMode(request.Mode)); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if err := tools.Chown(request.Path, request.Owner, request.Group); err != nil {
|
||||
if err := io.Chown(request.Path, request.Owner, request.Group); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -379,7 +382,7 @@ func (r *FileController) Archive(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
if err := tools.Archive(request.Paths, request.File); err != nil {
|
||||
if err := io.Archive(request.Paths, request.File); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -405,7 +408,7 @@ func (r *FileController) UnArchive(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
if err := tools.UnArchive(request.File, request.Path); err != nil {
|
||||
if err := io.UnArchive(request.File, request.Path); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -430,8 +433,8 @@ func (r *FileController) Search(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
paths := make(map[string]os.FileInfo)
|
||||
err := filepath.Walk(request.Path, func(path string, info os.FileInfo, err error) error {
|
||||
paths := make(map[string]stdos.FileInfo)
|
||||
err := filepath.Walk(request.Path, func(path string, info stdos.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -465,7 +468,7 @@ func (r *FileController) List(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
fileInfoList, err := os.ReadDir(request.Path)
|
||||
fileInfoList, err := io.ReadDir(request.Path)
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -478,16 +481,16 @@ func (r *FileController) List(ctx http.Context) http.Response {
|
||||
paths = append(paths, map[string]any{
|
||||
"name": info.Name(),
|
||||
"full": filepath.Join(request.Path, info.Name()),
|
||||
"size": tools.FormatBytes(float64(info.Size())),
|
||||
"size": str.FormatBytes(float64(info.Size())),
|
||||
"mode_str": info.Mode().String(),
|
||||
"mode": fmt.Sprintf("%04o", info.Mode().Perm()),
|
||||
"owner": tools.GetUser(stat.Uid),
|
||||
"group": tools.GetGroup(stat.Gid),
|
||||
"owner": os.GetUser(stat.Uid),
|
||||
"group": os.GetGroup(stat.Gid),
|
||||
"uid": stat.Uid,
|
||||
"gid": stat.Gid,
|
||||
"hidden": tools.IsHidden(info.Name()),
|
||||
"symlink": tools.IsSymlink(info.Mode()),
|
||||
"link": tools.GetSymlink(filepath.Join(request.Path, info.Name())),
|
||||
"hidden": io.IsHidden(info.Name()),
|
||||
"symlink": io.IsSymlink(info.Mode()),
|
||||
"link": io.GetSymlink(filepath.Join(request.Path, info.Name())),
|
||||
"dir": info.IsDir(),
|
||||
"modify": carbon.FromStdTime(info.ModTime()).ToDateTimeString(),
|
||||
})
|
||||
@@ -503,6 +506,6 @@ func (r *FileController) List(ctx http.Context) http.Response {
|
||||
|
||||
// setPermission
|
||||
func (r *FileController) setPermission(path string, mode uint, owner, group string) {
|
||||
_ = tools.Chmod(path, os.FileMode(mode))
|
||||
_ = tools.Chown(path, owner, group)
|
||||
_ = io.Chmod(path, stdos.FileMode(mode))
|
||||
_ = io.Chown(path, owner, group)
|
||||
}
|
||||
|
||||
@@ -12,8 +12,10 @@ import (
|
||||
"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/types"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type MenuItem struct {
|
||||
@@ -108,7 +110,7 @@ func (r *InfoController) CountInfo(ctx http.Context) http.Response {
|
||||
}
|
||||
var databaseCount int64
|
||||
if mysqlInstalled {
|
||||
status, err := tools.ServiceStatus("mysqld")
|
||||
status, err := systemctl.Status("mysqld")
|
||||
if status && err == nil {
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
type database struct {
|
||||
@@ -150,9 +152,9 @@ func (r *InfoController) CountInfo(ctx http.Context) http.Response {
|
||||
}
|
||||
}
|
||||
if postgresqlInstalled {
|
||||
status, err := tools.ServiceStatus("postgresql")
|
||||
status, err := systemctl.Status("postgresql")
|
||||
if status && err == nil {
|
||||
raw, err := tools.Exec(`echo "\l" | su - postgres -c "psql"`)
|
||||
raw, err := shell.Execf(`echo "\l" | su - postgres -c "psql"`)
|
||||
if err == nil {
|
||||
databases := strings.Split(raw, "\n")
|
||||
if len(databases) >= 4 {
|
||||
@@ -173,7 +175,7 @@ func (r *InfoController) CountInfo(ctx http.Context) http.Response {
|
||||
var ftpCount int64
|
||||
var ftpPlugin = r.plugin.GetInstalledBySlug("pureftpd")
|
||||
if ftpPlugin.ID != 0 {
|
||||
listRaw, err := tools.Exec("pure-pw list")
|
||||
listRaw, err := shell.Execf("pure-pw list")
|
||||
if len(listRaw) != 0 && err == nil {
|
||||
listArr := strings.Split(listRaw, "\n")
|
||||
ftpCount = int64(len(listArr))
|
||||
|
||||
@@ -12,8 +12,11 @@ import (
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/internal"
|
||||
"github.com/TheTNB/panel/internal/services"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/os"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type Fail2banController struct {
|
||||
@@ -28,7 +31,7 @@ func NewFail2banController() *Fail2banController {
|
||||
|
||||
// List 所有 Fail2ban 规则
|
||||
func (r *Fail2banController) List(ctx http.Context) http.Response {
|
||||
raw, err := tools.Read("/etc/fail2ban/jail.local")
|
||||
raw, err := io.Read("/etc/fail2ban/jail.local")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
}
|
||||
@@ -45,7 +48,7 @@ func (r *Fail2banController) List(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
jailName := jail[1]
|
||||
jailRaw := tools.Cut(raw, "# "+jailName+"-START", "# "+jailName+"-END")
|
||||
jailRaw := str.Cut(raw, "# "+jailName+"-START", "# "+jailName+"-END")
|
||||
if len(jailRaw) == 0 {
|
||||
continue
|
||||
}
|
||||
@@ -97,7 +100,7 @@ func (r *Fail2banController) Add(ctx http.Context) http.Response {
|
||||
jailWebsiteMode := ctx.Request().Input("website_mode")
|
||||
jailWebsitePath := ctx.Request().Input("website_path")
|
||||
|
||||
raw, err := tools.Read("/etc/fail2ban/jail.local")
|
||||
raw, err := io.Read("/etc/fail2ban/jail.local")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
}
|
||||
@@ -136,7 +139,7 @@ logpath = /www/wwwlogs/` + website.Name + `.log
|
||||
# ` + jailWebsiteName + `-` + jailWebsiteMode + `-END
|
||||
`
|
||||
raw += rule
|
||||
if err = tools.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
|
||||
if err = io.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "写入Fail2ban规则失败")
|
||||
}
|
||||
|
||||
@@ -154,7 +157,7 @@ failregex = ^<HOST>\s-.*\s` + jailWebsitePath + `.*HTTP/.*$
|
||||
ignoreregex =
|
||||
`
|
||||
}
|
||||
if err = tools.Write("/etc/fail2ban/filter.d/haozi-"+jailWebsiteName+"-"+jailWebsiteMode+".conf", filter, 0644); err != nil {
|
||||
if err = io.Write("/etc/fail2ban/filter.d/haozi-"+jailWebsiteName+"-"+jailWebsiteMode+".conf", filter, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "写入Fail2ban规则失败")
|
||||
}
|
||||
|
||||
@@ -165,21 +168,21 @@ ignoreregex =
|
||||
var err error
|
||||
switch jailName {
|
||||
case "ssh":
|
||||
if tools.IsDebian() {
|
||||
if os.IsDebian() {
|
||||
logPath = "/var/log/auth.log"
|
||||
} else {
|
||||
logPath = "/var/log/secure"
|
||||
}
|
||||
filter = "sshd"
|
||||
port, err = tools.Exec("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
|
||||
port, err = shell.Execf("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
|
||||
case "mysql":
|
||||
logPath = "/www/server/mysql/mysql-error.log"
|
||||
filter = "mysqld-auth"
|
||||
port, err = tools.Exec("cat /www/server/mysql/conf/my.cnf | grep 'port' | head -n 1 | awk '{print $3}'")
|
||||
port, err = shell.Execf("cat /www/server/mysql/conf/my.cnf | grep 'port' | head -n 1 | awk '{print $3}'")
|
||||
case "pure-ftpd":
|
||||
logPath = "/var/log/messages"
|
||||
filter = "pure-ftpd"
|
||||
port, err = tools.Exec(`cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`)
|
||||
port, err = shell.Execf(`cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`)
|
||||
default:
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "未知服务")
|
||||
}
|
||||
@@ -201,12 +204,12 @@ logpath = ` + logPath + `
|
||||
# ` + jailName + `-END
|
||||
`
|
||||
raw += rule
|
||||
if err := tools.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
|
||||
if err := io.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "写入Fail2ban规则失败")
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := tools.Exec("fail2ban-client reload"); err != nil {
|
||||
if _, err := shell.Execf("fail2ban-client reload"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重载配置失败")
|
||||
}
|
||||
|
||||
@@ -216,7 +219,7 @@ logpath = ` + logPath + `
|
||||
// Delete 删除规则
|
||||
func (r *Fail2banController) Delete(ctx http.Context) http.Response {
|
||||
jailName := ctx.Request().Input("name")
|
||||
raw, err := tools.Read("/etc/fail2ban/jail.local")
|
||||
raw, err := io.Read("/etc/fail2ban/jail.local")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
}
|
||||
@@ -224,14 +227,14 @@ func (r *Fail2banController) Delete(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "规则不存在")
|
||||
}
|
||||
|
||||
rule := tools.Cut(raw, "# "+jailName+"-START", "# "+jailName+"-END")
|
||||
rule := str.Cut(raw, "# "+jailName+"-START", "# "+jailName+"-END")
|
||||
raw = strings.Replace(raw, "\n# "+jailName+"-START"+rule+"# "+jailName+"-END", "", -1)
|
||||
raw = strings.TrimSpace(raw)
|
||||
if err := tools.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
|
||||
if err := io.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "写入Fail2ban规则失败")
|
||||
}
|
||||
|
||||
if _, err := tools.Exec("fail2ban-client reload"); err != nil {
|
||||
if _, err := shell.Execf("fail2ban-client reload"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重载配置失败")
|
||||
}
|
||||
|
||||
@@ -245,15 +248,15 @@ func (r *Fail2banController) BanList(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "缺少参数")
|
||||
}
|
||||
|
||||
currentlyBan, err := tools.Exec(`fail2ban-client status ` + name + ` | grep "Currently banned" | awk '{print $4}'`)
|
||||
currentlyBan, err := shell.Execf(`fail2ban-client status %s | grep "Currently banned" | awk '{print $4}'`, name)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取封禁列表失败")
|
||||
}
|
||||
totalBan, err := tools.Exec(`fail2ban-client status ` + name + ` | grep "Total banned" | awk '{print $4}'`)
|
||||
totalBan, err := shell.Execf(`fail2ban-client status %s | grep "Total banned" | awk '{print $4}'`, name)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取封禁列表失败")
|
||||
}
|
||||
bannedIp, err := tools.Exec(`fail2ban-client status ` + name + ` | grep "Banned IP list" | awk -F ":" '{print $2}'`)
|
||||
bannedIp, err := shell.Execf(`fail2ban-client status %s | grep "Banned IP list" | awk -F ":" '{print $2}'`, name)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取封禁列表失败")
|
||||
}
|
||||
@@ -287,7 +290,7 @@ func (r *Fail2banController) Unban(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "缺少参数")
|
||||
}
|
||||
|
||||
if _, err := tools.Exec("fail2ban-client set " + name + " unbanip " + ip); err != nil {
|
||||
if _, err := shell.Execf("fail2ban-client set %s unbanip %s", name, ip); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "解封失败")
|
||||
}
|
||||
|
||||
@@ -301,7 +304,7 @@ func (r *Fail2banController) SetWhiteList(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "缺少参数")
|
||||
}
|
||||
|
||||
raw, err := tools.Read("/etc/fail2ban/jail.local")
|
||||
raw, err := io.Read("/etc/fail2ban/jail.local")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
}
|
||||
@@ -313,11 +316,11 @@ func (r *Fail2banController) SetWhiteList(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "解析Fail2ban规则失败,Fail2ban可能已损坏")
|
||||
}
|
||||
|
||||
if err := tools.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
|
||||
if err := io.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "写入Fail2ban规则失败")
|
||||
}
|
||||
|
||||
if _, err := tools.Exec("fail2ban-client reload"); err != nil {
|
||||
if _, err := shell.Execf("fail2ban-client reload"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重载配置失败")
|
||||
}
|
||||
return controllers.Success(ctx, nil)
|
||||
@@ -325,7 +328,7 @@ func (r *Fail2banController) SetWhiteList(ctx http.Context) http.Response {
|
||||
|
||||
// GetWhiteList 获取白名单
|
||||
func (r *Fail2banController) GetWhiteList(ctx http.Context) http.Response {
|
||||
raw, err := tools.Read("/etc/fail2ban/jail.local")
|
||||
raw, err := io.Read("/etc/fail2ban/jail.local")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ import (
|
||||
|
||||
"github.com/TheTNB/panel/app/http/controllers"
|
||||
requests "github.com/TheTNB/panel/app/http/requests/plugins/frp"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type FrpController struct {
|
||||
@@ -34,7 +35,7 @@ func (r *FrpController) GetConfig(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
config, err := tools.Read(fmt.Sprintf("/www/server/frp/%s.toml", serviceRequest.Service))
|
||||
config, err := io.Read(fmt.Sprintf("/www/server/frp/%s.toml", serviceRequest.Service))
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -59,11 +60,11 @@ func (r *FrpController) UpdateConfig(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
if err := tools.Write(fmt.Sprintf("/www/server/frp/%s.toml", updateRequest.Service), updateRequest.Config, 0644); err != nil {
|
||||
if err := io.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 {
|
||||
if err := systemctl.Restart(updateRequest.Service); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ import (
|
||||
|
||||
"github.com/TheTNB/panel/app/http/controllers"
|
||||
requests "github.com/TheTNB/panel/app/http/requests/plugins/gitea"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type GiteaController struct {
|
||||
@@ -25,7 +26,7 @@ func NewGiteaController() *GiteaController {
|
||||
// @Success 200 {object} controllers.SuccessResponse
|
||||
// @Router /plugins/gitea/config [get]
|
||||
func (r *GiteaController) GetConfig(ctx http.Context) http.Response {
|
||||
config, err := tools.Read("/www/server/gitea/app.ini")
|
||||
config, err := io.Read("/www/server/gitea/app.ini")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -50,11 +51,11 @@ func (r *GiteaController) UpdateConfig(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
if err := tools.Write("/www/server/gitea/app.ini", updateRequest.Config, 0644); err != nil {
|
||||
if err := io.Write("/www/server/gitea/app.ini", updateRequest.Config, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
if err := tools.ServiceRestart("gitea"); err != nil {
|
||||
if err := systemctl.Restart("gitea"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,11 @@ import (
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/internal"
|
||||
"github.com/TheTNB/panel/internal/services"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type MySQLController struct {
|
||||
@@ -30,7 +33,7 @@ func NewMySQLController() *MySQLController {
|
||||
|
||||
// GetConfig 获取配置
|
||||
func (r *MySQLController) GetConfig(ctx http.Context) http.Response {
|
||||
config, err := tools.Read("/www/server/mysql/conf/my.cnf")
|
||||
config, err := io.Read("/www/server/mysql/conf/my.cnf")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL配置失败")
|
||||
}
|
||||
@@ -45,11 +48,11 @@ func (r *MySQLController) SaveConfig(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "配置不能为空")
|
||||
}
|
||||
|
||||
if err := tools.Write("/www/server/mysql/conf/my.cnf", config, 0644); err != nil {
|
||||
if err := io.Write("/www/server/mysql/conf/my.cnf", config, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "写入MySQL配置失败")
|
||||
}
|
||||
|
||||
if err := tools.ServiceReload("mysqld"); err != nil {
|
||||
if err := systemctl.Reload("mysqld"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重载MySQL失败")
|
||||
}
|
||||
|
||||
@@ -63,12 +66,12 @@ func (r *MySQLController) Load(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码为空")
|
||||
}
|
||||
|
||||
status, _ := tools.ServiceStatus("mysqld")
|
||||
status, _ := systemctl.Status("mysqld")
|
||||
if !status {
|
||||
return controllers.Success(ctx, []types.NV{})
|
||||
}
|
||||
|
||||
raw, err := tools.Exec("/www/server/mysql/bin/mysqladmin -uroot -p" + rootPassword + " extended-status 2>&1")
|
||||
raw, err := shell.Execf("/www/server/mysql/bin/mysqladmin -uroot -p" + rootPassword + " extended-status 2>&1")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL负载失败")
|
||||
}
|
||||
@@ -104,7 +107,7 @@ func (r *MySQLController) Load(ctx http.Context) http.Response {
|
||||
if len(matches) > 1 {
|
||||
d := map[string]string{"name": expression.name, "value": matches[1]}
|
||||
if expression.name == "发送" || expression.name == "接收" {
|
||||
d["value"] = tools.FormatBytes(cast.ToFloat64(matches[1]))
|
||||
d["value"] = str.FormatBytes(cast.ToFloat64(matches[1]))
|
||||
}
|
||||
|
||||
data = append(data, d)
|
||||
@@ -125,7 +128,7 @@ func (r *MySQLController) Load(ctx http.Context) http.Response {
|
||||
|
||||
// ErrorLog 获取错误日志
|
||||
func (r *MySQLController) ErrorLog(ctx http.Context) http.Response {
|
||||
log, err := tools.Exec("tail -n 100 /www/server/mysql/mysql-error.log")
|
||||
log, err := shell.Execf("tail -n 100 /www/server/mysql/mysql-error.log")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, log)
|
||||
}
|
||||
@@ -135,7 +138,7 @@ func (r *MySQLController) ErrorLog(ctx http.Context) http.Response {
|
||||
|
||||
// ClearErrorLog 清空错误日志
|
||||
func (r *MySQLController) ClearErrorLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec("echo '' > /www/server/mysql/mysql-error.log"); err != nil {
|
||||
if out, err := shell.Execf("echo '' > /www/server/mysql/mysql-error.log"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -144,7 +147,7 @@ func (r *MySQLController) ClearErrorLog(ctx http.Context) http.Response {
|
||||
|
||||
// SlowLog 获取慢查询日志
|
||||
func (r *MySQLController) SlowLog(ctx http.Context) http.Response {
|
||||
log, err := tools.Exec("tail -n 100 /www/server/mysql/mysql-slow.log")
|
||||
log, err := shell.Execf("tail -n 100 /www/server/mysql/mysql-slow.log")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, log)
|
||||
}
|
||||
@@ -154,7 +157,7 @@ func (r *MySQLController) SlowLog(ctx http.Context) http.Response {
|
||||
|
||||
// ClearSlowLog 清空慢查询日志
|
||||
func (r *MySQLController) ClearSlowLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec("echo '' > /www/server/mysql/mysql-slow.log"); err != nil {
|
||||
if out, err := shell.Execf("echo '' > /www/server/mysql/mysql-slow.log"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
return controllers.Success(ctx, nil)
|
||||
@@ -172,7 +175,7 @@ func (r *MySQLController) GetRootPassword(ctx http.Context) http.Response {
|
||||
|
||||
// SetRootPassword 设置root密码
|
||||
func (r *MySQLController) SetRootPassword(ctx http.Context) http.Response {
|
||||
status, err := tools.ServiceStatus("mysqld")
|
||||
status, err := systemctl.Status("mysqld")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
|
||||
}
|
||||
@@ -187,15 +190,15 @@ func (r *MySQLController) SetRootPassword(ctx http.Context) http.Response {
|
||||
|
||||
oldRootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
if oldRootPassword != rootPassword {
|
||||
if _, err = tools.Exec(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '%s';"`, oldRootPassword, rootPassword)); err != nil {
|
||||
if _, err = shell.Execf(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '%s';"`, oldRootPassword, rootPassword)); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("设置root密码失败: %v", err))
|
||||
}
|
||||
if _, err = tools.Exec(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "FLUSH PRIVILEGES;"`, rootPassword)); err != nil {
|
||||
if _, err = shell.Execf(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "FLUSH PRIVILEGES;"`, rootPassword)); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "设置root密码失败")
|
||||
}
|
||||
if err = r.setting.Set(models.SettingKeyMysqlRootPassword, rootPassword); err != nil {
|
||||
_, _ = tools.Exec(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '%s';"`, rootPassword, oldRootPassword))
|
||||
_, _ = tools.Exec(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "FLUSH PRIVILEGES;"`, oldRootPassword))
|
||||
_, _ = shell.Execf(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '%s';"`, rootPassword, oldRootPassword))
|
||||
_, _ = shell.Execf(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "FLUSH PRIVILEGES;"`, oldRootPassword))
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("设置保存失败: %v", err))
|
||||
}
|
||||
}
|
||||
@@ -269,16 +272,16 @@ func (r *MySQLController) AddDatabase(ctx http.Context) http.Response {
|
||||
user := ctx.Request().Input("user")
|
||||
password := ctx.Request().Input("password")
|
||||
|
||||
if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE DATABASE IF NOT EXISTS " + database + " DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;\""); err != nil {
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE DATABASE IF NOT EXISTS " + database + " DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\""); err != nil {
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\""); err != nil {
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -295,7 +298,7 @@ func (r *MySQLController) DeleteDatabase(ctx http.Context) http.Response {
|
||||
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
database := ctx.Request().Input("database")
|
||||
if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP DATABASE IF EXISTS " + database + ";\""); err != nil {
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP DATABASE IF EXISTS " + database + ";\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -325,8 +328,8 @@ func (r *MySQLController) UploadBackup(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/mysql"
|
||||
if !tools.Exists(backupPath) {
|
||||
if err = tools.Mkdir(backupPath, 0644); err != nil {
|
||||
if !io.Exists(backupPath) {
|
||||
if err = io.Mkdir(backupPath, 0644); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -366,7 +369,7 @@ func (r *MySQLController) DeleteBackup(ctx http.Context) http.Response {
|
||||
|
||||
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/mysql"
|
||||
fileName := ctx.Request().Input("name")
|
||||
if err := tools.Remove(backupPath + "/" + fileName); err != nil {
|
||||
if err := io.Remove(backupPath + "/" + fileName); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -477,13 +480,13 @@ func (r *MySQLController) AddUser(ctx http.Context) http.Response {
|
||||
user := ctx.Request().Input("user")
|
||||
password := ctx.Request().Input("password")
|
||||
database := ctx.Request().Input("database")
|
||||
if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + ";'\""); err != nil {
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + ";'\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\""); err != nil {
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -500,7 +503,7 @@ func (r *MySQLController) DeleteUser(ctx http.Context) http.Response {
|
||||
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
user := ctx.Request().Input("user")
|
||||
if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP USER '" + user + "'@'localhost';\""); err != nil {
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP USER '" + user + "'@'localhost';\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -519,10 +522,10 @@ func (r *MySQLController) SetUserPassword(ctx http.Context) http.Response {
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
user := ctx.Request().Input("user")
|
||||
password := ctx.Request().Input("password")
|
||||
if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\""); err != nil {
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -541,13 +544,13 @@ func (r *MySQLController) SetUserPrivileges(ctx http.Context) http.Response {
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
user := ctx.Request().Input("user")
|
||||
database := ctx.Request().Input("database")
|
||||
if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"REVOKE ALL PRIVILEGES ON *.* FROM '" + user + "'@'localhost';\""); err != nil {
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"REVOKE ALL PRIVILEGES ON *.* FROM '" + user + "'@'localhost';\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\""); err != nil {
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,11 @@ import (
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/app/http/controllers"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type OpenRestyController struct {
|
||||
@@ -30,7 +33,7 @@ func NewOpenrestyController() *OpenRestyController {
|
||||
// @Success 200 {object} controllers.SuccessResponse
|
||||
// @Router /plugins/openresty/config [get]
|
||||
func (r *OpenRestyController) GetConfig(ctx http.Context) http.Response {
|
||||
config, err := tools.Read("/www/server/openresty/conf/nginx.conf")
|
||||
config, err := io.Read("/www/server/openresty/conf/nginx.conf")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取配置失败")
|
||||
}
|
||||
@@ -53,11 +56,11 @@ func (r *OpenRestyController) SaveConfig(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "配置不能为空")
|
||||
}
|
||||
|
||||
if err := tools.Write("/www/server/openresty/conf/nginx.conf", config, 0644); err != nil {
|
||||
if err := io.Write("/www/server/openresty/conf/nginx.conf", config, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "保存配置失败")
|
||||
}
|
||||
|
||||
if err := tools.ServiceReload("openresty"); err != nil {
|
||||
if err := systemctl.Reload("openresty"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重载服务失败")
|
||||
}
|
||||
|
||||
@@ -73,11 +76,11 @@ func (r *OpenRestyController) SaveConfig(ctx http.Context) http.Response {
|
||||
// @Success 200 {object} controllers.SuccessResponse
|
||||
// @Router /plugins/openresty/errorLog [get]
|
||||
func (r *OpenRestyController) ErrorLog(ctx http.Context) http.Response {
|
||||
if !tools.Exists("/www/wwwlogs/nginx_error.log") {
|
||||
if !io.Exists("/www/wwwlogs/nginx_error.log") {
|
||||
return controllers.Success(ctx, "")
|
||||
}
|
||||
|
||||
out, err := tools.Exec("tail -n 100 /www/wwwlogs/openresty_error.log")
|
||||
out, err := shell.Execf("tail -n 100 /www/wwwlogs/openresty_error.log")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
@@ -94,7 +97,7 @@ func (r *OpenRestyController) ErrorLog(ctx http.Context) http.Response {
|
||||
// @Success 200 {object} controllers.SuccessResponse
|
||||
// @Router /plugins/openresty/clearErrorLog [post]
|
||||
func (r *OpenRestyController) ClearErrorLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec("echo '' > /www/wwwlogs/openresty_error.log"); err != nil {
|
||||
if out, err := shell.Execf("echo '' > /www/wwwlogs/openresty_error.log"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -119,7 +122,7 @@ func (r *OpenRestyController) Load(ctx http.Context) http.Response {
|
||||
raw := resp.String()
|
||||
var data []types.NV
|
||||
|
||||
workers, err := tools.Exec("ps aux | grep nginx | grep 'worker process' | wc -l")
|
||||
workers, err := shell.Execf("ps aux | grep nginx | grep 'worker process' | wc -l")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取负载失败")
|
||||
}
|
||||
@@ -128,11 +131,11 @@ func (r *OpenRestyController) Load(ctx http.Context) http.Response {
|
||||
Value: workers,
|
||||
})
|
||||
|
||||
out, err := tools.Exec("ps aux | grep nginx | grep 'worker process' | awk '{memsum+=$6};END {print memsum}'")
|
||||
out, err := shell.Execf("ps aux | grep nginx | grep 'worker process' | awk '{memsum+=$6};END {print memsum}'")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取负载失败")
|
||||
}
|
||||
mem := tools.FormatBytes(cast.ToFloat64(out))
|
||||
mem := str.FormatBytes(cast.ToFloat64(out))
|
||||
data = append(data, types.NV{
|
||||
Name: "内存占用",
|
||||
Value: mem,
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
|
||||
"github.com/TheTNB/panel/app/http/controllers"
|
||||
"github.com/TheTNB/panel/internal/services"
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
)
|
||||
|
||||
type PHPController struct{}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
@@ -10,7 +9,10 @@ import (
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/app/http/controllers"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/os"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type PhpMyAdminController struct {
|
||||
@@ -21,7 +23,7 @@ func NewPhpMyAdminController() *PhpMyAdminController {
|
||||
}
|
||||
|
||||
func (r *PhpMyAdminController) Info(ctx http.Context) http.Response {
|
||||
files, err := os.ReadDir("/www/server/phpmyadmin")
|
||||
files, err := io.ReadDir("/www/server/phpmyadmin")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "找不到 phpMyAdmin 目录")
|
||||
}
|
||||
@@ -36,7 +38,7 @@ func (r *PhpMyAdminController) Info(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "找不到 phpMyAdmin 目录")
|
||||
}
|
||||
|
||||
conf, err := tools.Read("/www/server/vhost/phpmyadmin.conf")
|
||||
conf, err := io.Read("/www/server/vhost/phpmyadmin.conf")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -52,40 +54,40 @@ func (r *PhpMyAdminController) Info(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
func (r *PhpMyAdminController) SetPort(ctx http.Context) http.Response {
|
||||
port := ctx.Request().Input("port")
|
||||
if len(port) == 0 {
|
||||
port := ctx.Request().InputInt("port")
|
||||
if port == 0 {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "端口不能为空")
|
||||
}
|
||||
|
||||
conf, err := tools.Read("/www/server/vhost/phpmyadmin.conf")
|
||||
conf, err := io.Read("/www/server/vhost/phpmyadmin.conf")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
conf = regexp.MustCompile(`listen\s+(\d+);`).ReplaceAllString(conf, "listen "+port+";")
|
||||
if err := tools.Write("/www/server/vhost/phpmyadmin.conf", conf, 0644); err != nil {
|
||||
conf = regexp.MustCompile(`listen\s+(\d+);`).ReplaceAllString(conf, "listen "+cast.ToString(port)+";")
|
||||
if err := io.Write("/www/server/vhost/phpmyadmin.conf", conf, 0644); err != nil {
|
||||
facades.Log().Request(ctx.Request()).Tags("插件", "phpMyAdmin").With(map[string]any{
|
||||
"error": err.Error(),
|
||||
}).Info("修改 phpMyAdmin 端口失败")
|
||||
return controllers.ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
if tools.IsRHEL() {
|
||||
if out, err := tools.Exec("firewall-cmd --zone=public --add-port=" + port + "/tcp --permanent"); err != nil {
|
||||
if os.IsRHEL() {
|
||||
if out, err := shell.Execf("firewall-cmd --zone=public --add-port=%d/tcp --permanent", port); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("firewall-cmd --reload"); err != nil {
|
||||
if out, err := shell.Execf("firewall-cmd --reload"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
} else {
|
||||
if out, err := tools.Exec("ufw allow " + port + "/tcp"); err != nil {
|
||||
if out, err := shell.Execf("ufw allow %d/tcp", port); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("ufw reload"); err != nil {
|
||||
if out, err := shell.Execf("ufw reload"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
}
|
||||
|
||||
if err := tools.ServiceReload("openresty"); err != nil {
|
||||
if err := systemctl.Reload("openresty"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重载OpenResty失败")
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ import (
|
||||
|
||||
"github.com/TheTNB/panel/app/http/controllers"
|
||||
requests "github.com/TheTNB/panel/app/http/requests/plugins/podman"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type PodmanController struct {
|
||||
@@ -25,7 +26,7 @@ func NewPodmanController() *PodmanController {
|
||||
// @Success 200 {object} controllers.SuccessResponse
|
||||
// @Router /plugins/podman/registryConfig [get]
|
||||
func (r *PodmanController) GetRegistryConfig(ctx http.Context) http.Response {
|
||||
config, err := tools.Read("/etc/containers/registries.conf")
|
||||
config, err := io.Read("/etc/containers/registries.conf")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -50,11 +51,11 @@ func (r *PodmanController) UpdateRegistryConfig(ctx http.Context) http.Response
|
||||
return sanitize
|
||||
}
|
||||
|
||||
if err := tools.Write("/etc/containers/registries.conf", updateRequest.Config, 0644); err != nil {
|
||||
if err := io.Write("/etc/containers/registries.conf", updateRequest.Config, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
if err := tools.ServiceRestart("podman"); err != nil {
|
||||
if err := systemctl.Restart("podman"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -71,7 +72,7 @@ func (r *PodmanController) UpdateRegistryConfig(ctx http.Context) http.Response
|
||||
// @Success 200 {object} controllers.SuccessResponse
|
||||
// @Router /plugins/podman/storageConfig [get]
|
||||
func (r *PodmanController) GetStorageConfig(ctx http.Context) http.Response {
|
||||
config, err := tools.Read("/etc/containers/storage.conf")
|
||||
config, err := io.Read("/etc/containers/storage.conf")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -96,11 +97,11 @@ func (r *PodmanController) UpdateStorageConfig(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
if err := tools.Write("/etc/containers/storage.conf", updateRequest.Config, 0644); err != nil {
|
||||
if err := io.Write("/etc/containers/storage.conf", updateRequest.Config, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
if err := tools.ServiceRestart("podman"); err != nil {
|
||||
if err := systemctl.Restart("podman"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,10 @@ import (
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/internal"
|
||||
"github.com/TheTNB/panel/internal/services"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type PostgreSQLController struct {
|
||||
@@ -30,7 +32,7 @@ func NewPostgreSQLController() *PostgreSQLController {
|
||||
// GetConfig 获取配置
|
||||
func (r *PostgreSQLController) GetConfig(ctx http.Context) http.Response {
|
||||
// 获取配置
|
||||
config, err := tools.Read("/www/server/postgresql/data/postgresql.conf")
|
||||
config, err := io.Read("/www/server/postgresql/data/postgresql.conf")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL配置失败")
|
||||
}
|
||||
@@ -41,7 +43,7 @@ func (r *PostgreSQLController) GetConfig(ctx http.Context) http.Response {
|
||||
// GetUserConfig 获取用户配置
|
||||
func (r *PostgreSQLController) GetUserConfig(ctx http.Context) http.Response {
|
||||
// 获取配置
|
||||
config, err := tools.Read("/www/server/postgresql/data/pg_hba.conf")
|
||||
config, err := io.Read("/www/server/postgresql/data/pg_hba.conf")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL配置失败")
|
||||
}
|
||||
@@ -56,11 +58,11 @@ func (r *PostgreSQLController) SaveConfig(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "配置不能为空")
|
||||
}
|
||||
|
||||
if err := tools.Write("/www/server/postgresql/data/postgresql.conf", config, 0644); err != nil {
|
||||
if err := io.Write("/www/server/postgresql/data/postgresql.conf", config, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败")
|
||||
}
|
||||
|
||||
if err := tools.ServiceReload("postgresql"); err != nil {
|
||||
if err := systemctl.Reload("postgresql"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重载服务失败")
|
||||
}
|
||||
|
||||
@@ -74,11 +76,11 @@ func (r *PostgreSQLController) SaveUserConfig(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "配置不能为空")
|
||||
}
|
||||
|
||||
if err := tools.Write("/www/server/postgresql/data/pg_hba.conf", config, 0644); err != nil {
|
||||
if err := io.Write("/www/server/postgresql/data/pg_hba.conf", config, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败")
|
||||
}
|
||||
|
||||
if err := tools.ServiceReload("postgresql"); err != nil {
|
||||
if err := systemctl.Reload("postgresql"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重载服务失败")
|
||||
}
|
||||
|
||||
@@ -87,28 +89,28 @@ func (r *PostgreSQLController) SaveUserConfig(ctx http.Context) http.Response {
|
||||
|
||||
// Load 获取负载
|
||||
func (r *PostgreSQLController) Load(ctx http.Context) http.Response {
|
||||
status, _ := tools.ServiceStatus("postgresql")
|
||||
status, _ := systemctl.Status("postgresql")
|
||||
if !status {
|
||||
return controllers.Success(ctx, []types.NV{})
|
||||
}
|
||||
|
||||
time, err := tools.Exec(`echo "select pg_postmaster_start_time();" | su - postgres -c "psql" | sed -n 3p | cut -d'.' -f1`)
|
||||
time, err := shell.Execf(`echo "select pg_postmaster_start_time();" | su - postgres -c "psql" | sed -n 3p | cut -d'.' -f1`)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL启动时间失败")
|
||||
}
|
||||
pid, err := tools.Exec(`echo "select pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)
|
||||
pid, err := shell.Execf(`echo "select pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL进程PID失败")
|
||||
}
|
||||
process, err := tools.Exec(`ps aux | grep postgres | grep -v grep | wc -l`)
|
||||
process, err := shell.Execf(`ps aux | grep postgres | grep -v grep | wc -l`)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL进程数失败")
|
||||
}
|
||||
connections, err := tools.Exec(`echo "SELECT count(*) FROM pg_stat_activity WHERE NOT pid=pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)
|
||||
connections, err := shell.Execf(`echo "SELECT count(*) FROM pg_stat_activity WHERE NOT pid=pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL连接数失败")
|
||||
}
|
||||
storage, err := tools.Exec(`echo "select pg_size_pretty(pg_database_size('postgres'));" | su - postgres -c "psql" | sed -n 3p`)
|
||||
storage, err := shell.Execf(`echo "select pg_size_pretty(pg_database_size('postgres'));" | su - postgres -c "psql" | sed -n 3p`)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL空间占用失败")
|
||||
}
|
||||
@@ -126,7 +128,7 @@ func (r *PostgreSQLController) Load(ctx http.Context) http.Response {
|
||||
|
||||
// Log 获取日志
|
||||
func (r *PostgreSQLController) Log(ctx http.Context) http.Response {
|
||||
log, err := tools.Exec("tail -n 100 /www/server/postgresql/logs/postgresql-" + carbon.Now().ToDateString() + ".log")
|
||||
log, err := shell.Execf("tail -n 100 /www/server/postgresql/logs/postgresql-" + carbon.Now().ToDateString() + ".log")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, log)
|
||||
}
|
||||
@@ -136,7 +138,7 @@ func (r *PostgreSQLController) Log(ctx http.Context) http.Response {
|
||||
|
||||
// ClearLog 清空日志
|
||||
func (r *PostgreSQLController) ClearLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec("echo '' > /www/server/postgresql/logs/postgresql-" + carbon.Now().ToDateString() + ".log"); err != nil {
|
||||
if out, err := shell.Execf("echo '' > /www/server/postgresql/logs/postgresql-" + carbon.Now().ToDateString() + ".log"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -211,25 +213,25 @@ func (r *PostgreSQLController) AddDatabase(ctx http.Context) http.Response {
|
||||
user := ctx.Request().Input("user")
|
||||
password := ctx.Request().Input("password")
|
||||
|
||||
if out, err := tools.Exec(`echo "CREATE DATABASE ` + database + `;" | su - postgres -c "psql"`); err != nil {
|
||||
if out, err := shell.Execf(`echo "CREATE DATABASE ` + database + `;" | su - postgres -c "psql"`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`); err != nil {
|
||||
if out, err := shell.Execf(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec(`echo "ALTER DATABASE ` + database + ` OWNER TO ` + user + `;" | su - postgres -c "psql"`); err != nil {
|
||||
if out, err := shell.Execf(`echo "ALTER DATABASE ` + database + ` OWNER TO ` + user + `;" | su - postgres -c "psql"`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`); err != nil {
|
||||
if out, err := shell.Execf(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256"
|
||||
if out, err := tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`); err != nil {
|
||||
if out, err := shell.Execf(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
if err := tools.ServiceReload("postgresql"); err != nil {
|
||||
if err := systemctl.Reload("postgresql"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重载服务失败")
|
||||
}
|
||||
|
||||
@@ -245,7 +247,7 @@ func (r *PostgreSQLController) DeleteDatabase(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
database := ctx.Request().Input("database")
|
||||
if out, err := tools.Exec(`echo "DROP DATABASE ` + database + `;" | su - postgres -c "psql"`); err != nil {
|
||||
if out, err := shell.Execf(`echo "DROP DATABASE ` + database + `;" | su - postgres -c "psql"`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -275,8 +277,8 @@ func (r *PostgreSQLController) UploadBackup(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
|
||||
if !tools.Exists(backupPath) {
|
||||
if err = tools.Mkdir(backupPath, 0644); err != nil {
|
||||
if !io.Exists(backupPath) {
|
||||
if err = io.Mkdir(backupPath, 0644); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -316,7 +318,7 @@ func (r *PostgreSQLController) DeleteBackup(ctx http.Context) http.Response {
|
||||
|
||||
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
|
||||
fileName := ctx.Request().Input("name")
|
||||
if err := tools.Remove(backupPath + "/" + fileName); err != nil {
|
||||
if err := io.Remove(backupPath + "/" + fileName); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -428,19 +430,19 @@ func (r *PostgreSQLController) AddRole(ctx http.Context) http.Response {
|
||||
user := ctx.Request().Input("user")
|
||||
password := ctx.Request().Input("password")
|
||||
database := ctx.Request().Input("database")
|
||||
if out, err := tools.Exec(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`); err != nil {
|
||||
if out, err := shell.Execf(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`); err != nil {
|
||||
if out, err := shell.Execf(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256"
|
||||
if out, err := tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`); err != nil {
|
||||
if out, err := shell.Execf(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
if err := tools.ServiceReload("postgresql"); err != nil {
|
||||
if err := systemctl.Reload("postgresql"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重载服务失败")
|
||||
}
|
||||
|
||||
@@ -456,14 +458,14 @@ func (r *PostgreSQLController) DeleteRole(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
user := ctx.Request().Input("user")
|
||||
if out, err := tools.Exec(`echo "DROP USER ` + user + `;" | su - postgres -c "psql"`); err != nil {
|
||||
if out, err := shell.Execf(`echo "DROP USER ` + user + `;" | su - postgres -c "psql"`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec(`sed -i '/` + user + `/d' /www/server/postgresql/data/pg_hba.conf`); err != nil {
|
||||
if out, err := shell.Execf(`sed -i '/` + user + `/d' /www/server/postgresql/data/pg_hba.conf`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
if err := tools.ServiceReload("postgresql"); err != nil {
|
||||
if err := systemctl.Reload("postgresql"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重载服务失败")
|
||||
}
|
||||
|
||||
@@ -481,7 +483,7 @@ func (r *PostgreSQLController) SetRolePassword(ctx http.Context) http.Response {
|
||||
|
||||
user := ctx.Request().Input("user")
|
||||
password := ctx.Request().Input("password")
|
||||
if out, err := tools.Exec(`echo "ALTER USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`); err != nil {
|
||||
if out, err := shell.Execf(`echo "ALTER USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,11 @@ import (
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/app/http/controllers"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/os"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type PureFtpdController struct {
|
||||
@@ -21,7 +24,7 @@ func NewPureFtpdController() *PureFtpdController {
|
||||
|
||||
// List 获取用户列表
|
||||
func (r *PureFtpdController) List(ctx http.Context) http.Response {
|
||||
listRaw, err := tools.Exec("pure-pw list")
|
||||
listRaw, err := shell.Execf("pure-pw list")
|
||||
if err != nil {
|
||||
return controllers.Success(ctx, http.Json{
|
||||
"total": 0,
|
||||
@@ -68,20 +71,20 @@ func (r *PureFtpdController) Add(ctx http.Context) http.Response {
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
path = "/" + path
|
||||
}
|
||||
if !tools.Exists(path) {
|
||||
if !io.Exists(path) {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "目录不存在")
|
||||
}
|
||||
|
||||
if err := tools.Chmod(path, 0755); err != nil {
|
||||
if err := io.Chmod(path, 0755); err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "修改目录权限失败")
|
||||
}
|
||||
if err := tools.Chown(path, "www", "www"); err != nil {
|
||||
if err := io.Chown(path, "www", "www"); err != nil {
|
||||
return nil
|
||||
}
|
||||
if out, err := tools.Exec(`yes '` + password + `' | pure-pw useradd ` + username + ` -u www -g www -d ` + path); err != nil {
|
||||
if out, err := shell.Execf(`yes '` + password + `' | pure-pw useradd ` + username + ` -u www -g www -d ` + path); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("pure-pw mkdb"); err != nil {
|
||||
if out, err := shell.Execf("pure-pw mkdb"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -98,10 +101,10 @@ func (r *PureFtpdController) Delete(ctx http.Context) http.Response {
|
||||
|
||||
username := ctx.Request().Input("username")
|
||||
|
||||
if out, err := tools.Exec("pure-pw userdel " + username + " -m"); err != nil {
|
||||
if out, err := shell.Execf("pure-pw userdel " + username + " -m"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("pure-pw mkdb"); err != nil {
|
||||
if out, err := shell.Execf("pure-pw mkdb"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -120,10 +123,10 @@ func (r *PureFtpdController) ChangePassword(ctx http.Context) http.Response {
|
||||
username := ctx.Request().Input("username")
|
||||
password := ctx.Request().Input("password")
|
||||
|
||||
if out, err := tools.Exec(`yes '` + password + `' | pure-pw passwd ` + username + ` -m`); err != nil {
|
||||
if out, err := shell.Execf(`yes '` + password + `' | pure-pw passwd ` + username + ` -m`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("pure-pw mkdb"); err != nil {
|
||||
if out, err := shell.Execf("pure-pw mkdb"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -132,7 +135,7 @@ func (r *PureFtpdController) ChangePassword(ctx http.Context) http.Response {
|
||||
|
||||
// GetPort 获取端口
|
||||
func (r *PureFtpdController) GetPort(ctx http.Context) http.Response {
|
||||
port, err := tools.Exec(`cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`)
|
||||
port, err := shell.Execf(`cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd端口失败")
|
||||
}
|
||||
@@ -149,26 +152,26 @@ func (r *PureFtpdController) SetPort(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
port := ctx.Request().Input("port")
|
||||
if out, err := tools.Exec(`sed -i "s/Bind.*/Bind 0.0.0.0,` + port + `/g" /www/server/pure-ftpd/etc/pure-ftpd.conf`); err != nil {
|
||||
if out, err := shell.Execf(`sed -i "s/Bind.*/Bind 0.0.0.0,%s/g" /www/server/pure-ftpd/etc/pure-ftpd.conf`, port); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if tools.IsRHEL() {
|
||||
if out, err := tools.Exec("firewall-cmd --zone=public --add-port=" + port + "/tcp --permanent"); err != nil {
|
||||
if os.IsRHEL() {
|
||||
if out, err := shell.Execf("firewall-cmd --zone=public --add-port=%s/tcp --permanent", port); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("firewall-cmd --reload"); err != nil {
|
||||
if out, err := shell.Execf("firewall-cmd --reload"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
} else {
|
||||
if out, err := tools.Exec("ufw allow " + port + "/tcp"); err != nil {
|
||||
if out, err := shell.Execf("ufw allow %s/tcp", port); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("ufw reload"); err != nil {
|
||||
if out, err := shell.Execf("ufw reload"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
}
|
||||
|
||||
if err := tools.ServiceRestart("pure-ftpd"); err != nil {
|
||||
if err := systemctl.Restart("pure-ftpd"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@ import (
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
|
||||
"github.com/TheTNB/panel/app/http/controllers"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type RedisController struct {
|
||||
@@ -20,7 +22,7 @@ func NewRedisController() *RedisController {
|
||||
// GetConfig 获取配置
|
||||
func (r *RedisController) GetConfig(ctx http.Context) http.Response {
|
||||
// 获取配置
|
||||
config, err := tools.Read("/www/server/redis/redis.conf")
|
||||
config, err := io.Read("/www/server/redis/redis.conf")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取Redis配置失败")
|
||||
}
|
||||
@@ -35,11 +37,11 @@ func (r *RedisController) SaveConfig(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "配置不能为空")
|
||||
}
|
||||
|
||||
if err := tools.Write("/www/server/redis/redis.conf", config, 0644); err != nil {
|
||||
if err := io.Write("/www/server/redis/redis.conf", config, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "写入Redis配置失败")
|
||||
}
|
||||
|
||||
if err := tools.ServiceRestart("redis"); err != nil {
|
||||
if err := systemctl.Restart("redis"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重启Redis失败")
|
||||
}
|
||||
|
||||
@@ -48,7 +50,7 @@ func (r *RedisController) SaveConfig(ctx http.Context) http.Response {
|
||||
|
||||
// Load 获取负载
|
||||
func (r *RedisController) Load(ctx http.Context) http.Response {
|
||||
status, err := tools.ServiceStatus("redis")
|
||||
status, err := systemctl.Status("redis")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取Redis状态失败")
|
||||
}
|
||||
@@ -56,7 +58,7 @@ func (r *RedisController) Load(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "Redis已停止运行")
|
||||
}
|
||||
|
||||
raw, err := tools.Exec("redis-cli info")
|
||||
raw, err := shell.Execf("redis-cli info")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取Redis负载失败")
|
||||
}
|
||||
|
||||
@@ -8,8 +8,11 @@ import (
|
||||
|
||||
"github.com/TheTNB/panel/app/http/controllers"
|
||||
requests "github.com/TheTNB/panel/app/http/requests/plugins/rsync"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type RsyncController struct {
|
||||
@@ -30,7 +33,7 @@ func NewRsyncController() *RsyncController {
|
||||
// @Success 200 {object} controllers.SuccessResponse
|
||||
// @Router /plugins/rsync/modules [get]
|
||||
func (r *RsyncController) List(ctx http.Context) http.Response {
|
||||
config, err := tools.Read("/etc/rsyncd.conf")
|
||||
config, err := io.Read("/etc/rsyncd.conf")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -69,7 +72,7 @@ func (r *RsyncController) List(ctx http.Context) http.Response {
|
||||
currentModule.ReadOnly = value == "yes" || value == "true"
|
||||
case "auth users":
|
||||
currentModule.AuthUser = value
|
||||
currentModule.Secret, err = tools.Exec("grep -E '^" + currentModule.AuthUser + ":.*$' /etc/rsyncd.secrets | awk -F ':' '{print $2}'")
|
||||
currentModule.Secret, err = shell.Execf("grep -E '^" + currentModule.AuthUser + ":.*$' /etc/rsyncd.secrets | awk -F ':' '{print $2}'")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取模块"+currentModule.AuthUser+"的密钥失败")
|
||||
}
|
||||
@@ -109,7 +112,7 @@ func (r *RsyncController) Create(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
config, err := tools.Read("/etc/rsyncd.conf")
|
||||
config, err := io.Read("/etc/rsyncd.conf")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -128,14 +131,14 @@ secrets file = /etc/rsyncd.secrets
|
||||
# ` + createRequest.Name + `-END
|
||||
`
|
||||
|
||||
if err := tools.WriteAppend("/etc/rsyncd.conf", conf); err != nil {
|
||||
if err := io.WriteAppend("/etc/rsyncd.conf", conf); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if out, err := tools.Exec("echo '" + createRequest.AuthUser + ":" + createRequest.Secret + "' >> /etc/rsyncd.secrets"); err != nil {
|
||||
if out, err := shell.Execf("echo '" + createRequest.AuthUser + ":" + createRequest.Secret + "' >> /etc/rsyncd.secrets"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
if err := tools.ServiceRestart("rsyncd"); err != nil {
|
||||
if err := systemctl.Restart("rsyncd"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -158,7 +161,7 @@ func (r *RsyncController) Destroy(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "name 不能为空")
|
||||
}
|
||||
|
||||
config, err := tools.Read("/etc/rsyncd.conf")
|
||||
config, err := io.Read("/etc/rsyncd.conf")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -166,22 +169,22 @@ func (r *RsyncController) Destroy(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "模块 "+name+" 不存在")
|
||||
}
|
||||
|
||||
module := tools.Cut(config, "# "+name+"-START", "# "+name+"-END")
|
||||
module := str.Cut(config, "# "+name+"-START", "# "+name+"-END")
|
||||
config = strings.Replace(config, "\n# "+name+"-START"+module+"# "+name+"-END", "", -1)
|
||||
|
||||
match := regexp.MustCompile(`auth users = ([^\n]+)`).FindStringSubmatch(module)
|
||||
if len(match) == 2 {
|
||||
authUser := match[1]
|
||||
if out, err := tools.Exec("sed -i '/^" + authUser + ":.*$/d' /etc/rsyncd.secrets"); err != nil {
|
||||
if out, err := shell.Execf("sed -i '/^" + authUser + ":.*$/d' /etc/rsyncd.secrets"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
}
|
||||
|
||||
if err = tools.Write("/etc/rsyncd.conf", config, 0644); err != nil {
|
||||
if err = io.Write("/etc/rsyncd.conf", config, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
if err = tools.ServiceRestart("rsyncd"); err != nil {
|
||||
if err = systemctl.Restart("rsyncd"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -206,7 +209,7 @@ func (r *RsyncController) Update(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
config, err := tools.Read("/etc/rsyncd.conf")
|
||||
config, err := io.Read("/etc/rsyncd.conf")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -224,25 +227,25 @@ hosts allow = ` + updateRequest.HostsAllow + `
|
||||
secrets file = /etc/rsyncd.secrets
|
||||
# ` + updateRequest.Name + `-END`
|
||||
|
||||
module := tools.Cut(config, "# "+updateRequest.Name+"-START", "# "+updateRequest.Name+"-END")
|
||||
module := str.Cut(config, "# "+updateRequest.Name+"-START", "# "+updateRequest.Name+"-END")
|
||||
config = strings.Replace(config, "# "+updateRequest.Name+"-START"+module+"# "+updateRequest.Name+"-END", newConf, -1)
|
||||
|
||||
match := regexp.MustCompile(`auth users = ([^\n]+)`).FindStringSubmatch(module)
|
||||
if len(match) == 2 {
|
||||
authUser := match[1]
|
||||
if out, err := tools.Exec("sed -i '/^" + authUser + ":.*$/d' /etc/rsyncd.secrets"); err != nil {
|
||||
if out, err := shell.Execf("sed -i '/^" + authUser + ":.*$/d' /etc/rsyncd.secrets"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
}
|
||||
|
||||
if err = tools.Write("/etc/rsyncd.conf", config, 0644); err != nil {
|
||||
if err = io.Write("/etc/rsyncd.conf", config, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if out, err := tools.Exec("echo '" + updateRequest.AuthUser + ":" + updateRequest.Secret + "' >> /etc/rsyncd.secrets"); err != nil {
|
||||
if out, err := shell.Execf("echo '" + updateRequest.AuthUser + ":" + updateRequest.Secret + "' >> /etc/rsyncd.secrets"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
if err = tools.ServiceRestart("rsyncd"); err != nil {
|
||||
if err = systemctl.Restart("rsyncd"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -259,7 +262,7 @@ secrets file = /etc/rsyncd.secrets
|
||||
// @Success 200 {object} controllers.SuccessResponse
|
||||
// @Router /plugins/rsync/config [get]
|
||||
func (r *RsyncController) GetConfig(ctx http.Context) http.Response {
|
||||
config, err := tools.Read("/etc/rsyncd.conf")
|
||||
config, err := io.Read("/etc/rsyncd.conf")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -284,11 +287,11 @@ func (r *RsyncController) UpdateConfig(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
if err := tools.Write("/etc/rsyncd.conf", updateRequest.Config, 0644); err != nil {
|
||||
if err := io.Write("/etc/rsyncd.conf", updateRequest.Config, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
if err := tools.ServiceRestart("rsyncd"); err != nil {
|
||||
if err := systemctl.Restart("rsyncd"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,9 @@ import (
|
||||
"github.com/TheTNB/panel/app/http/controllers"
|
||||
"github.com/TheTNB/panel/internal"
|
||||
"github.com/TheTNB/panel/internal/services"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type S3fsController struct {
|
||||
@@ -65,12 +66,12 @@ func (r *S3fsController) Add(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// 检查挂载目录是否存在且为空
|
||||
if !tools.Exists(path) {
|
||||
if err := tools.Mkdir(path, 0755); err != nil {
|
||||
if !io.Exists(path) {
|
||||
if err := io.Mkdir(path, 0755); err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载目录创建失败")
|
||||
}
|
||||
}
|
||||
if !tools.Empty(path) {
|
||||
if !io.Empty(path) {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载目录必须为空")
|
||||
}
|
||||
|
||||
@@ -87,19 +88,19 @@ func (r *S3fsController) Add(ctx http.Context) http.Response {
|
||||
|
||||
id := carbon.Now().TimestampMilli()
|
||||
password := ak + ":" + sk
|
||||
if err := tools.Write("/etc/passwd-s3fs-"+cast.ToString(id), password, 0600); err != nil {
|
||||
if err := io.Write("/etc/passwd-s3fs-"+cast.ToString(id), password, 0600); err != nil {
|
||||
return nil
|
||||
}
|
||||
out, err := tools.Exec(`echo 's3fs#` + bucket + ` ` + path + ` fuse _netdev,allow_other,nonempty,url=` + url + `,passwd_file=/etc/passwd-s3fs-` + cast.ToString(id) + ` 0 0' >> /etc/fstab`)
|
||||
out, err := shell.Execf(`echo 's3fs#` + bucket + ` ` + path + ` fuse _netdev,allow_other,nonempty,url=` + url + `,passwd_file=/etc/passwd-s3fs-` + cast.ToString(id) + ` 0 0' >> /etc/fstab`)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if mountCheck, err := tools.Exec("mount -a 2>&1"); err != nil {
|
||||
_, _ = tools.Exec(`sed -i 's@^s3fs#` + bucket + `\s` + path + `.*$@@g' /etc/fstab`)
|
||||
if mountCheck, err := shell.Execf("mount -a 2>&1"); err != nil {
|
||||
_, _ = shell.Execf(`sed -i 's@^s3fs#` + bucket + `\s` + path + `.*$@@g' /etc/fstab`)
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "检测到/etc/fstab有误: "+mountCheck)
|
||||
}
|
||||
if _, err := tools.Exec("df -h | grep " + path + " 2>&1"); err != nil {
|
||||
_, _ = tools.Exec(`sed -i 's@^s3fs#` + bucket + `\s` + path + `.*$@@g' /etc/fstab`)
|
||||
if _, err := shell.Execf("df -h | grep " + path + " 2>&1"); err != nil {
|
||||
_, _ = shell.Execf(`sed -i 's@^s3fs#` + bucket + `\s` + path + `.*$@@g' /etc/fstab`)
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "挂载失败,请检查配置是否正确")
|
||||
}
|
||||
|
||||
@@ -145,19 +146,19 @@ func (r *S3fsController) Delete(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载ID不存在")
|
||||
}
|
||||
|
||||
if out, err := tools.Exec(`fusermount -u '` + mount.Path + `' 2>&1`); err != nil {
|
||||
if out, err := shell.Execf(`fusermount -u '` + mount.Path + `' 2>&1`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec(`umount '` + mount.Path + `' 2>&1`); err != nil {
|
||||
if out, err := shell.Execf(`umount '` + mount.Path + `' 2>&1`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec(`sed -i 's@^s3fs#` + mount.Bucket + `\s` + mount.Path + `.*$@@g' /etc/fstab`); err != nil {
|
||||
if out, err := shell.Execf(`sed -i 's@^s3fs#` + mount.Bucket + `\s` + mount.Path + `.*$@@g' /etc/fstab`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if mountCheck, err := tools.Exec("mount -a 2>&1"); err != nil {
|
||||
if mountCheck, err := shell.Execf("mount -a 2>&1"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "检测到/etc/fstab有误: "+mountCheck)
|
||||
}
|
||||
if err := tools.Remove("/etc/passwd-s3fs-" + cast.ToString(mount.ID)); err != nil {
|
||||
if err := io.Remove("/etc/passwd-s3fs-" + cast.ToString(mount.ID)); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,13 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
|
||||
"github.com/TheTNB/panel/app/http/controllers"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/os"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type SupervisorController struct {
|
||||
@@ -17,7 +20,7 @@ type SupervisorController struct {
|
||||
|
||||
func NewSupervisorController() *SupervisorController {
|
||||
var service string
|
||||
if tools.IsRHEL() {
|
||||
if os.IsRHEL() {
|
||||
service = "supervisord"
|
||||
} else {
|
||||
service = "supervisor"
|
||||
@@ -35,7 +38,7 @@ func (r *SupervisorController) Service(ctx http.Context) http.Response {
|
||||
|
||||
// Log 日志
|
||||
func (r *SupervisorController) Log(ctx http.Context) http.Response {
|
||||
log, err := tools.Exec(`tail -n 200 /var/log/supervisor/supervisord.log`)
|
||||
log, err := shell.Execf(`tail -n 200 /var/log/supervisor/supervisord.log`)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, log)
|
||||
}
|
||||
@@ -45,7 +48,7 @@ func (r *SupervisorController) Log(ctx http.Context) http.Response {
|
||||
|
||||
// ClearLog 清空日志
|
||||
func (r *SupervisorController) ClearLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec(`echo "" > /var/log/supervisor/supervisord.log`); err != nil {
|
||||
if out, err := shell.Execf(`echo "" > /var/log/supervisor/supervisord.log`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -56,10 +59,10 @@ func (r *SupervisorController) ClearLog(ctx http.Context) http.Response {
|
||||
func (r *SupervisorController) Config(ctx http.Context) http.Response {
|
||||
var config string
|
||||
var err error
|
||||
if tools.IsRHEL() {
|
||||
config, err = tools.Read(`/etc/supervisord.conf`)
|
||||
if os.IsRHEL() {
|
||||
config, err = io.Read(`/etc/supervisord.conf`)
|
||||
} else {
|
||||
config, err = tools.Read(`/etc/supervisor/supervisord.conf`)
|
||||
config, err = io.Read(`/etc/supervisor/supervisord.conf`)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -73,17 +76,17 @@ func (r *SupervisorController) Config(ctx http.Context) http.Response {
|
||||
func (r *SupervisorController) SaveConfig(ctx http.Context) http.Response {
|
||||
config := ctx.Request().Input("config")
|
||||
var err error
|
||||
if tools.IsRHEL() {
|
||||
err = tools.Write(`/etc/supervisord.conf`, config, 0644)
|
||||
if os.IsRHEL() {
|
||||
err = io.Write(`/etc/supervisord.conf`, config, 0644)
|
||||
} else {
|
||||
err = tools.Write(`/etc/supervisor/supervisord.conf`, config, 0644)
|
||||
err = io.Write(`/etc/supervisor/supervisord.conf`, config, 0644)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
if err = tools.ServiceRestart(r.service); err != nil {
|
||||
if err = systemctl.Restart(r.service); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("重启 %s 服务失败", r.service))
|
||||
}
|
||||
|
||||
@@ -99,7 +102,7 @@ func (r *SupervisorController) Processes(ctx http.Context) http.Response {
|
||||
Uptime string `json:"uptime"`
|
||||
}
|
||||
|
||||
out, err := tools.Exec(`supervisorctl status | awk '{print $1}'`)
|
||||
out, err := shell.Execf(`supervisorctl status | awk '{print $1}'`)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
@@ -112,13 +115,13 @@ func (r *SupervisorController) Processes(ctx http.Context) http.Response {
|
||||
|
||||
var p process
|
||||
p.Name = line
|
||||
if status, err := tools.Exec(`supervisorctl status ` + line + ` | awk '{print $2}'`); err == nil {
|
||||
if status, err := shell.Execf(`supervisorctl status ` + line + ` | awk '{print $2}'`); err == nil {
|
||||
p.Status = status
|
||||
}
|
||||
if p.Status == "RUNNING" {
|
||||
pid, _ := tools.Exec(`supervisorctl status ` + line + ` | awk '{print $4}'`)
|
||||
pid, _ := shell.Execf(`supervisorctl status ` + line + ` | awk '{print $4}'`)
|
||||
p.Pid = strings.ReplaceAll(pid, ",", "")
|
||||
uptime, _ := tools.Exec(`supervisorctl status ` + line + ` | awk '{print $6}'`)
|
||||
uptime, _ := shell.Execf(`supervisorctl status ` + line + ` | awk '{print $6}'`)
|
||||
p.Uptime = uptime
|
||||
} else {
|
||||
p.Pid = "-"
|
||||
@@ -138,7 +141,7 @@ func (r *SupervisorController) Processes(ctx http.Context) http.Response {
|
||||
// StartProcess 启动进程
|
||||
func (r *SupervisorController) StartProcess(ctx http.Context) http.Response {
|
||||
process := ctx.Request().Input("process")
|
||||
if out, err := tools.Exec(`supervisorctl start ` + process); err != nil {
|
||||
if out, err := shell.Execf(`supervisorctl start %s`, process); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -148,7 +151,7 @@ func (r *SupervisorController) StartProcess(ctx http.Context) http.Response {
|
||||
// StopProcess 停止进程
|
||||
func (r *SupervisorController) StopProcess(ctx http.Context) http.Response {
|
||||
process := ctx.Request().Input("process")
|
||||
if out, err := tools.Exec(`supervisorctl stop ` + process); err != nil {
|
||||
if out, err := shell.Execf(`supervisorctl stop %s`, process); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -158,7 +161,7 @@ func (r *SupervisorController) StopProcess(ctx http.Context) http.Response {
|
||||
// RestartProcess 重启进程
|
||||
func (r *SupervisorController) RestartProcess(ctx http.Context) http.Response {
|
||||
process := ctx.Request().Input("process")
|
||||
if out, err := tools.Exec(`supervisorctl restart ` + process); err != nil {
|
||||
if out, err := shell.Execf(`supervisorctl restart %s`, process); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -170,17 +173,17 @@ func (r *SupervisorController) ProcessLog(ctx http.Context) http.Response {
|
||||
process := ctx.Request().Input("process")
|
||||
var logPath string
|
||||
var err error
|
||||
if tools.IsRHEL() {
|
||||
logPath, err = tools.Exec(`cat '/etc/supervisord.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
|
||||
if os.IsRHEL() {
|
||||
logPath, err = shell.Execf(`cat '/etc/supervisord.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, process)
|
||||
} else {
|
||||
logPath, err = tools.Exec(`cat '/etc/supervisor/conf.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
|
||||
logPath, err = shell.Execf(`cat '/etc/supervisor/conf.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, process)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "无法从进程"+process+"的配置文件中获取日志路径")
|
||||
}
|
||||
|
||||
log, err := tools.Exec(`tail -n 200 ` + logPath)
|
||||
log, err := shell.Execf(`tail -n 200 ` + logPath)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, log)
|
||||
}
|
||||
@@ -193,17 +196,17 @@ func (r *SupervisorController) ClearProcessLog(ctx http.Context) http.Response {
|
||||
process := ctx.Request().Input("process")
|
||||
var logPath string
|
||||
var err error
|
||||
if tools.IsRHEL() {
|
||||
logPath, err = tools.Exec(`cat '/etc/supervisord.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
|
||||
if os.IsRHEL() {
|
||||
logPath, err = shell.Execf(`cat '/etc/supervisord.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, process)
|
||||
} else {
|
||||
logPath, err = tools.Exec(`cat '/etc/supervisor/conf.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
|
||||
logPath, err = shell.Execf(`cat '/etc/supervisor/conf.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, process)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "无法从进程"+process+"的配置文件中获取日志路径")
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("无法从进程%s的配置文件中获取日志路径", process))
|
||||
}
|
||||
|
||||
if out, err := tools.Exec(`echo "" > ` + logPath); err != nil {
|
||||
if out, err := shell.Execf(`echo "" > ` + logPath); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
@@ -215,10 +218,10 @@ func (r *SupervisorController) ProcessConfig(ctx http.Context) http.Response {
|
||||
process := ctx.Request().Query("process")
|
||||
var config string
|
||||
var err error
|
||||
if tools.IsRHEL() {
|
||||
config, err = tools.Read(`/etc/supervisord.d/` + process + `.conf`)
|
||||
if os.IsRHEL() {
|
||||
config, err = io.Read(`/etc/supervisord.d/` + process + `.conf`)
|
||||
} else {
|
||||
config, err = tools.Read(`/etc/supervisor/conf.d/` + process + `.conf`)
|
||||
config, err = io.Read(`/etc/supervisor/conf.d/` + process + `.conf`)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -233,19 +236,19 @@ func (r *SupervisorController) SaveProcessConfig(ctx http.Context) http.Response
|
||||
process := ctx.Request().Input("process")
|
||||
config := ctx.Request().Input("config")
|
||||
var err error
|
||||
if tools.IsRHEL() {
|
||||
err = tools.Write(`/etc/supervisord.d/`+process+`.conf`, config, 0644)
|
||||
if os.IsRHEL() {
|
||||
err = io.Write(`/etc/supervisord.d/`+process+`.conf`, config, 0644)
|
||||
} else {
|
||||
err = tools.Write(`/etc/supervisor/conf.d/`+process+`.conf`, config, 0644)
|
||||
err = io.Write(`/etc/supervisor/conf.d/`+process+`.conf`, config, 0644)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
}
|
||||
|
||||
_, _ = tools.Exec(`supervisorctl reread`)
|
||||
_, _ = tools.Exec(`supervisorctl update`)
|
||||
_, _ = tools.Exec(`supervisorctl restart ` + process)
|
||||
_, _ = shell.Execf(`supervisorctl reread`)
|
||||
_, _ = shell.Execf(`supervisorctl update`)
|
||||
_, _ = shell.Execf(`supervisorctl restart %s`, process)
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
@@ -281,19 +284,19 @@ stdout_logfile_maxbytes=2MB
|
||||
`
|
||||
|
||||
var err error
|
||||
if tools.IsRHEL() {
|
||||
err = tools.Write(`/etc/supervisord.d/`+name+`.conf`, config, 0644)
|
||||
if os.IsRHEL() {
|
||||
err = io.Write(`/etc/supervisord.d/`+name+`.conf`, config, 0644)
|
||||
} else {
|
||||
err = tools.Write(`/etc/supervisor/conf.d/`+name+`.conf`, config, 0644)
|
||||
err = io.Write(`/etc/supervisor/conf.d/`+name+`.conf`, config, 0644)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
}
|
||||
|
||||
_, _ = tools.Exec(`supervisorctl reread`)
|
||||
_, _ = tools.Exec(`supervisorctl update`)
|
||||
_, _ = tools.Exec(`supervisorctl start ` + name)
|
||||
_, _ = shell.Execf(`supervisorctl reread`)
|
||||
_, _ = shell.Execf(`supervisorctl update`)
|
||||
_, _ = shell.Execf(`supervisorctl start %s`, name)
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
@@ -301,20 +304,20 @@ stdout_logfile_maxbytes=2MB
|
||||
// DeleteProcess 删除进程
|
||||
func (r *SupervisorController) DeleteProcess(ctx http.Context) http.Response {
|
||||
process := ctx.Request().Input("process")
|
||||
if out, err := tools.Exec(`supervisorctl stop ` + process); err != nil {
|
||||
if out, err := shell.Execf(`supervisorctl stop %s`, process); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
var logPath string
|
||||
var err error
|
||||
if tools.IsRHEL() {
|
||||
logPath, err = tools.Exec(`cat '/etc/supervisord.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
|
||||
if err := tools.Remove(`/etc/supervisord.d/` + process + `.conf`); err != nil {
|
||||
if os.IsRHEL() {
|
||||
logPath, err = shell.Execf(`cat '/etc/supervisord.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, process)
|
||||
if err := io.Remove(`/etc/supervisord.d/` + process + `.conf`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
} else {
|
||||
logPath, err = tools.Exec(`cat '/etc/supervisor/conf.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
|
||||
if err := tools.Remove(`/etc/supervisor/conf.d/` + process + `.conf`); err != nil {
|
||||
logPath, err = shell.Execf(`cat '/etc/supervisor/conf.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, process)
|
||||
if err := io.Remove(`/etc/supervisor/conf.d/` + process + `.conf`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
}
|
||||
@@ -323,11 +326,11 @@ func (r *SupervisorController) DeleteProcess(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "无法从进程"+process+"的配置文件中获取日志路径")
|
||||
}
|
||||
|
||||
if err := tools.Remove(logPath); err != nil {
|
||||
if err := io.Remove(logPath); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
_, _ = tools.Exec(`supervisorctl reread`)
|
||||
_, _ = tools.Exec(`supervisorctl update`)
|
||||
_, _ = shell.Execf(`supervisorctl reread`)
|
||||
_, _ = shell.Execf(`supervisorctl update`)
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ import (
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/app/http/controllers"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
)
|
||||
|
||||
type ToolBoxController struct {
|
||||
@@ -20,7 +22,7 @@ func NewToolBoxController() *ToolBoxController {
|
||||
|
||||
// GetDNS 获取 DNS 信息
|
||||
func (r *ToolBoxController) GetDNS(ctx http.Context) http.Response {
|
||||
raw, err := tools.Read("/etc/resolv.conf")
|
||||
raw, err := io.Read("/etc/resolv.conf")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -50,7 +52,7 @@ func (r *ToolBoxController) SetDNS(ctx http.Context) http.Response {
|
||||
dns += "nameserver " + dns1 + "\n"
|
||||
dns += "nameserver " + dns2 + "\n"
|
||||
|
||||
if err := tools.Write("/etc/resolv.conf", dns, 0644); err != nil {
|
||||
if err := io.Write("/etc/resolv.conf", dns, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "写入 DNS 信息失败")
|
||||
}
|
||||
|
||||
@@ -61,28 +63,28 @@ func (r *ToolBoxController) SetDNS(ctx http.Context) http.Response {
|
||||
func (r *ToolBoxController) GetSWAP(ctx http.Context) http.Response {
|
||||
var total, used, free string
|
||||
var size int64
|
||||
if tools.Exists("/www/swap") {
|
||||
file, err := tools.FileInfo("/www/swap")
|
||||
if io.Exists("/www/swap") {
|
||||
file, err := io.FileInfo("/www/swap")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取 SWAP 信息失败")
|
||||
}
|
||||
|
||||
size = file.Size() / 1024 / 1024
|
||||
total = tools.FormatBytes(float64(file.Size()))
|
||||
total = str.FormatBytes(float64(file.Size()))
|
||||
} else {
|
||||
size = 0
|
||||
total = "0.00 B"
|
||||
}
|
||||
|
||||
raw, err := tools.Exec("free | grep Swap")
|
||||
raw, err := shell.Execf("free | grep Swap")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取 SWAP 信息失败")
|
||||
}
|
||||
|
||||
match := regexp.MustCompile(`Swap:\s+(\d+)\s+(\d+)\s+(\d+)`).FindStringSubmatch(raw)
|
||||
if len(match) > 0 {
|
||||
used = tools.FormatBytes(cast.ToFloat64(match[2]) * 1024)
|
||||
free = tools.FormatBytes(cast.ToFloat64(match[3]) * 1024)
|
||||
used = str.FormatBytes(cast.ToFloat64(match[2]) * 1024)
|
||||
free = str.FormatBytes(cast.ToFloat64(match[3]) * 1024)
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, http.Json{
|
||||
@@ -97,47 +99,47 @@ func (r *ToolBoxController) GetSWAP(ctx http.Context) http.Response {
|
||||
func (r *ToolBoxController) SetSWAP(ctx http.Context) http.Response {
|
||||
size := ctx.Request().InputInt("size")
|
||||
|
||||
if tools.Exists("/www/swap") {
|
||||
if out, err := tools.Exec("swapoff /www/swap"); err != nil {
|
||||
if io.Exists("/www/swap") {
|
||||
if out, err := shell.Execf("swapoff /www/swap"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
|
||||
}
|
||||
if out, err := tools.Exec("rm -f /www/swap"); err != nil {
|
||||
if out, err := shell.Execf("rm -f /www/swap"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
|
||||
}
|
||||
if out, err := tools.Exec("sed -i '/www\\/swap/d' /etc/fstab"); err != nil {
|
||||
if out, err := shell.Execf("sed -i '/www\\/swap/d' /etc/fstab"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
|
||||
}
|
||||
}
|
||||
|
||||
if size > 1 {
|
||||
free, err := tools.Exec("df -k /www | awk '{print $4}' | tail -n 1")
|
||||
free, err := shell.Execf("df -k /www | awk '{print $4}' | tail -n 1")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取磁盘空间失败")
|
||||
}
|
||||
if cast.ToInt64(free)*1024 < int64(size)*1024*1024 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "磁盘空间不足,当前剩余 "+tools.FormatBytes(cast.ToFloat64(free)))
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "磁盘空间不足,当前剩余 "+str.FormatBytes(cast.ToFloat64(free)))
|
||||
}
|
||||
|
||||
btrfsCheck, _ := tools.Exec("df -T /www | awk '{print $2}' | tail -n 1")
|
||||
btrfsCheck, _ := shell.Execf("df -T /www | awk '{print $2}' | tail -n 1")
|
||||
if strings.Contains(btrfsCheck, "btrfs") {
|
||||
if out, err := tools.Exec("btrfs filesystem mkswapfile --size " + cast.ToString(size) + "M --uuid clear /www/swap"); err != nil {
|
||||
if out, err := shell.Execf("btrfs filesystem mkswapfile --size " + cast.ToString(size) + "M --uuid clear /www/swap"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
|
||||
}
|
||||
} else {
|
||||
if out, err := tools.Exec("dd if=/dev/zero of=/www/swap bs=1M count=" + cast.ToString(size)); err != nil {
|
||||
if out, err := shell.Execf("dd if=/dev/zero of=/www/swap bs=1M count=" + cast.ToString(size)); err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
|
||||
}
|
||||
if out, err := tools.Exec("mkswap -f /www/swap"); err != nil {
|
||||
if out, err := shell.Execf("mkswap -f /www/swap"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
|
||||
}
|
||||
if err := tools.Chmod("/www/swap", 0600); err != nil {
|
||||
if err := io.Chmod("/www/swap", 0600); err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "设置 SWAP 权限失败")
|
||||
}
|
||||
}
|
||||
if out, err := tools.Exec("swapon /www/swap"); err != nil {
|
||||
if out, err := shell.Execf("swapon /www/swap"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
|
||||
}
|
||||
if out, err := tools.Exec("echo '/www/swap swap swap defaults 0 0' >> /etc/fstab"); err != nil {
|
||||
if out, err := shell.Execf("echo '/www/swap swap swap defaults 0 0' >> /etc/fstab"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
|
||||
}
|
||||
}
|
||||
@@ -147,7 +149,7 @@ func (r *ToolBoxController) SetSWAP(ctx http.Context) http.Response {
|
||||
|
||||
// GetTimezone 获取时区
|
||||
func (r *ToolBoxController) GetTimezone(ctx http.Context) http.Response {
|
||||
raw, err := tools.Exec("timedatectl | grep zone")
|
||||
raw, err := shell.Execf("timedatectl | grep zone")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取时区信息失败")
|
||||
}
|
||||
@@ -162,7 +164,7 @@ func (r *ToolBoxController) GetTimezone(ctx http.Context) http.Response {
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
zonesRaw, err := tools.Exec("timedatectl list-timezones")
|
||||
zonesRaw, err := shell.Execf("timedatectl list-timezones")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取时区列表失败")
|
||||
}
|
||||
@@ -189,7 +191,7 @@ func (r *ToolBoxController) SetTimezone(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "时区不能为空")
|
||||
}
|
||||
|
||||
if out, err := tools.Exec("timedatectl set-timezone " + timezone); err != nil {
|
||||
if out, err := shell.Execf("timedatectl set-timezone %s", timezone); err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
|
||||
}
|
||||
|
||||
@@ -198,7 +200,7 @@ func (r *ToolBoxController) SetTimezone(ctx http.Context) http.Response {
|
||||
|
||||
// GetHosts 获取 hosts 信息
|
||||
func (r *ToolBoxController) GetHosts(ctx http.Context) http.Response {
|
||||
hosts, err := tools.Read("/etc/hosts")
|
||||
hosts, err := io.Read("/etc/hosts")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
}
|
||||
@@ -213,7 +215,7 @@ func (r *ToolBoxController) SetHosts(ctx http.Context) http.Response {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "hosts 信息不能为空")
|
||||
}
|
||||
|
||||
if err := tools.Write("/etc/hosts", hosts, 0644); err != nil {
|
||||
if err := io.Write("/etc/hosts", hosts, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "写入 hosts 信息失败")
|
||||
}
|
||||
|
||||
@@ -231,7 +233,7 @@ func (r *ToolBoxController) SetRootPassword(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
password = strings.ReplaceAll(password, `'`, `\'`)
|
||||
if out, err := tools.Exec(`yes '` + password + `' | passwd root`); err != nil {
|
||||
if out, err := shell.Execf(`yes '` + password + `' | passwd root`); err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,10 @@ import (
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/os"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type SafeController struct {
|
||||
@@ -16,7 +19,7 @@ type SafeController struct {
|
||||
|
||||
func NewSafeController() *SafeController {
|
||||
var ssh string
|
||||
if tools.IsRHEL() {
|
||||
if os.IsRHEL() {
|
||||
ssh = "sshd"
|
||||
} else {
|
||||
ssh = "ssh"
|
||||
@@ -36,33 +39,33 @@ func (r *SafeController) GetFirewallStatus(ctx http.Context) http.Response {
|
||||
func (r *SafeController) SetFirewallStatus(ctx http.Context) http.Response {
|
||||
var err error
|
||||
if ctx.Request().InputBool("status") {
|
||||
if tools.IsRHEL() {
|
||||
err = tools.ServiceStart("firewalld")
|
||||
if os.IsRHEL() {
|
||||
err = systemctl.Start("firewalld")
|
||||
if err == nil {
|
||||
err = tools.ServiceEnable("firewalld")
|
||||
err = systemctl.Enable("firewalld")
|
||||
}
|
||||
} else {
|
||||
_, err = tools.Exec("echo y | ufw enable")
|
||||
_, err = shell.Execf("echo y | ufw enable")
|
||||
if err == nil {
|
||||
err = tools.ServiceStart("ufw")
|
||||
err = systemctl.Start("ufw")
|
||||
}
|
||||
if err == nil {
|
||||
err = tools.ServiceEnable("ufw")
|
||||
err = systemctl.Enable("ufw")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if tools.IsRHEL() {
|
||||
err = tools.ServiceStop("firewalld")
|
||||
if os.IsRHEL() {
|
||||
err = systemctl.Stop("firewalld")
|
||||
if err == nil {
|
||||
err = tools.ServiceDisable("firewalld")
|
||||
err = systemctl.Disable("firewalld")
|
||||
}
|
||||
} else {
|
||||
_, err = tools.Exec("ufw disable")
|
||||
_, err = shell.Execf("ufw disable")
|
||||
if err == nil {
|
||||
err = tools.ServiceStop("ufw")
|
||||
err = systemctl.Stop("ufw")
|
||||
}
|
||||
if err == nil {
|
||||
err = tools.ServiceDisable("ufw")
|
||||
err = systemctl.Disable("ufw")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,8 +84,8 @@ func (r *SafeController) GetFirewallRules(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
var rules []map[string]string
|
||||
if tools.IsRHEL() {
|
||||
out, err := tools.Exec("firewall-cmd --list-all 2>&1")
|
||||
if os.IsRHEL() {
|
||||
out, err := shell.Execf("firewall-cmd --list-all")
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
@@ -103,7 +106,7 @@ func (r *SafeController) GetFirewallRules(ctx http.Context) http.Response {
|
||||
})
|
||||
}
|
||||
} else {
|
||||
out, err := tools.Exec("ufw status | grep -v '(v6)' | grep ALLOW | awk '{print $1}'")
|
||||
out, err := shell.Execf("ufw status | grep -v '(v6)' | grep ALLOW | awk '{print $1}'")
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
@@ -157,14 +160,14 @@ func (r *SafeController) AddFirewallRule(ctx http.Context) http.Response {
|
||||
}
|
||||
}
|
||||
|
||||
if tools.IsRHEL() {
|
||||
if out, err := tools.Exec("firewall-cmd --remove-port=" + cast.ToString(port) + "/" + protocol + " --permanent 2>&1"); err != nil {
|
||||
if os.IsRHEL() {
|
||||
if out, err := shell.Execf("firewall-cmd --remove-port=%s/%s --permanent", port, protocol); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("firewall-cmd --add-port=" + cast.ToString(port) + "/" + protocol + " --permanent 2>&1"); err != nil {
|
||||
if out, err := shell.Execf("firewall-cmd --add-port=%s/%s --permanent", port, protocol); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("firewall-cmd --reload"); err != nil {
|
||||
if out, err := shell.Execf("firewall-cmd --reload"); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
} else {
|
||||
@@ -172,13 +175,13 @@ func (r *SafeController) AddFirewallRule(ctx http.Context) http.Response {
|
||||
if strings.Contains(port, "-") {
|
||||
port = strings.ReplaceAll(port, "-", ":")
|
||||
}
|
||||
if out, err := tools.Exec("ufw delete allow " + cast.ToString(port) + "/" + protocol); err != nil {
|
||||
if out, err := shell.Execf("ufw delete allow %s/%s", port, protocol); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("ufw allow " + cast.ToString(port) + "/" + protocol); err != nil {
|
||||
if out, err := shell.Execf("ufw allow %s/%s", port, protocol); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("ufw reload"); err != nil {
|
||||
if out, err := shell.Execf("ufw reload"); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
}
|
||||
@@ -192,24 +195,24 @@ func (r *SafeController) DeleteFirewallRule(ctx http.Context) http.Response {
|
||||
return Error(ctx, http.StatusUnprocessableEntity, "防火墙未启动")
|
||||
}
|
||||
|
||||
port := ctx.Request().InputInt("port", 0)
|
||||
protocol := ctx.Request().Input("protocol", "")
|
||||
if port == 0 || protocol == "" {
|
||||
port := ctx.Request().Input("port")
|
||||
protocol := ctx.Request().Input("protocol")
|
||||
if port == "" || protocol == "" {
|
||||
return Error(ctx, http.StatusUnprocessableEntity, "参数错误")
|
||||
}
|
||||
|
||||
if tools.IsRHEL() {
|
||||
if out, err := tools.Exec("firewall-cmd --remove-port=" + cast.ToString(port) + "/" + protocol + " --permanent 2>&1"); err != nil {
|
||||
if os.IsRHEL() {
|
||||
if out, err := shell.Execf("firewall-cmd --remove-port=%s/%s --permanent", port, protocol); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("firewall-cmd --reload"); err != nil {
|
||||
if out, err := shell.Execf("firewall-cmd --reload"); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
} else {
|
||||
if out, err := tools.Exec("ufw delete allow " + cast.ToString(port) + "/" + protocol); err != nil {
|
||||
if out, err := shell.Execf("ufw delete allow %s/%s", port, protocol); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("ufw reload"); err != nil {
|
||||
if out, err := shell.Execf("ufw reload"); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
}
|
||||
@@ -220,10 +223,10 @@ func (r *SafeController) DeleteFirewallRule(ctx http.Context) http.Response {
|
||||
// firewallStatus 获取防火墙状态
|
||||
func (r *SafeController) firewallStatus() bool {
|
||||
var running bool
|
||||
if tools.IsRHEL() {
|
||||
running, _ = tools.ServiceStatus("firewalld")
|
||||
if os.IsRHEL() {
|
||||
running, _ = systemctl.Status("firewalld")
|
||||
} else {
|
||||
running, _ = tools.ServiceStatus("ufw")
|
||||
running, _ = systemctl.Status("ufw")
|
||||
}
|
||||
|
||||
return running
|
||||
@@ -231,7 +234,7 @@ func (r *SafeController) firewallStatus() bool {
|
||||
|
||||
// GetSshStatus 获取 SSH 状态
|
||||
func (r *SafeController) GetSshStatus(ctx http.Context) http.Response {
|
||||
running, err := tools.ServiceStatus(r.ssh)
|
||||
running, err := systemctl.Status(r.ssh)
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -242,17 +245,17 @@ func (r *SafeController) GetSshStatus(ctx http.Context) http.Response {
|
||||
// SetSshStatus 设置 SSH 状态
|
||||
func (r *SafeController) SetSshStatus(ctx http.Context) http.Response {
|
||||
if ctx.Request().InputBool("status") {
|
||||
if err := tools.ServiceEnable(r.ssh); err != nil {
|
||||
if err := systemctl.Enable(r.ssh); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if err := tools.ServiceStart(r.ssh); err != nil {
|
||||
if err := systemctl.Start(r.ssh); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
} else {
|
||||
if err := tools.ServiceStop(r.ssh); err != nil {
|
||||
if err := systemctl.Stop(r.ssh); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if err := tools.ServiceDisable(r.ssh); err != nil {
|
||||
if err := systemctl.Disable(r.ssh); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
}
|
||||
@@ -262,7 +265,7 @@ func (r *SafeController) SetSshStatus(ctx http.Context) http.Response {
|
||||
|
||||
// GetSshPort 获取 SSH 端口
|
||||
func (r *SafeController) GetSshPort(ctx http.Context) http.Response {
|
||||
out, err := tools.Exec("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
|
||||
out, err := shell.Execf("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
@@ -277,16 +280,16 @@ func (r *SafeController) SetSshPort(ctx http.Context) http.Response {
|
||||
return Error(ctx, http.StatusUnprocessableEntity, "参数错误")
|
||||
}
|
||||
|
||||
oldPort, err := tools.Exec("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
|
||||
oldPort, err := shell.Execf("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, oldPort)
|
||||
}
|
||||
_, _ = tools.Exec("sed -i 's/#Port " + oldPort + "/Port " + cast.ToString(port) + "/g' /etc/ssh/sshd_config")
|
||||
_, _ = tools.Exec("sed -i 's/Port " + oldPort + "/Port " + cast.ToString(port) + "/g' /etc/ssh/sshd_config")
|
||||
_, _ = shell.Execf("sed -i 's/#Port %s/Port %d/g' /etc/ssh/sshd_config", oldPort, port)
|
||||
_, _ = shell.Execf("sed -i 's/Port %s/Port %d/g' /etc/ssh/sshd_config", oldPort, port)
|
||||
|
||||
status, _ := tools.ServiceStatus(r.ssh)
|
||||
status, _ := systemctl.Status(r.ssh)
|
||||
if status {
|
||||
_ = tools.ServiceRestart(r.ssh)
|
||||
_ = systemctl.Restart(r.ssh)
|
||||
}
|
||||
|
||||
return Success(ctx, nil)
|
||||
@@ -294,8 +297,8 @@ func (r *SafeController) SetSshPort(ctx http.Context) http.Response {
|
||||
|
||||
// GetPingStatus 获取 Ping 状态
|
||||
func (r *SafeController) GetPingStatus(ctx http.Context) http.Response {
|
||||
if tools.IsRHEL() {
|
||||
out, err := tools.Exec(`firewall-cmd --list-all 2>&1`)
|
||||
if os.IsRHEL() {
|
||||
out, err := shell.Execf(`firewall-cmd --list-all`)
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
@@ -306,7 +309,7 @@ func (r *SafeController) GetPingStatus(ctx http.Context) http.Response {
|
||||
return Success(ctx, false)
|
||||
}
|
||||
} else {
|
||||
config, err := tools.Read("/etc/ufw/before.rules")
|
||||
config, err := io.Read("/etc/ufw/before.rules")
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -322,17 +325,17 @@ func (r *SafeController) GetPingStatus(ctx http.Context) http.Response {
|
||||
func (r *SafeController) SetPingStatus(ctx http.Context) http.Response {
|
||||
var out string
|
||||
var err error
|
||||
if tools.IsRHEL() {
|
||||
if os.IsRHEL() {
|
||||
if ctx.Request().InputBool("status") {
|
||||
out, err = tools.Exec(`firewall-cmd --permanent --remove-rich-rule='rule protocol value=icmp drop'`)
|
||||
out, err = shell.Execf(`firewall-cmd --permanent --remove-rich-rule='rule protocol value=icmp drop'`)
|
||||
} else {
|
||||
out, err = tools.Exec(`firewall-cmd --permanent --add-rich-rule='rule protocol value=icmp drop'`)
|
||||
out, err = shell.Execf(`firewall-cmd --permanent --add-rich-rule='rule protocol value=icmp drop'`)
|
||||
}
|
||||
} else {
|
||||
if ctx.Request().InputBool("status") {
|
||||
out, err = tools.Exec(`sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j DROP/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/g' /etc/ufw/before.rules`)
|
||||
out, err = shell.Execf(`sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j DROP/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/g' /etc/ufw/before.rules`)
|
||||
} else {
|
||||
out, err = tools.Exec(`sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/-A ufw-before-input -p icmp --icmp-type echo-request -j DROP/g' /etc/ufw/before.rules`)
|
||||
out, err = shell.Execf(`sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/-A ufw-before-input -p icmp --icmp-type echo-request -j DROP/g' /etc/ufw/before.rules`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,10 +343,10 @@ func (r *SafeController) SetPingStatus(ctx http.Context) http.Response {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
if tools.IsRHEL() {
|
||||
out, err = tools.Exec(`firewall-cmd --reload`)
|
||||
if os.IsRHEL() {
|
||||
out, err = shell.Execf(`firewall-cmd --reload`)
|
||||
} else {
|
||||
out, err = tools.Exec(`ufw reload`)
|
||||
out, err = shell.Execf(`ufw reload`)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -9,6 +9,10 @@ import (
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/internal"
|
||||
"github.com/TheTNB/panel/internal/services"
|
||||
"github.com/TheTNB/panel/pkg/cert"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/os"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
)
|
||||
|
||||
@@ -24,13 +28,12 @@ func NewSettingController() *SettingController {
|
||||
|
||||
// List
|
||||
//
|
||||
// @Summary 设置列表
|
||||
// @Description 获取面板设置列表
|
||||
// @Tags 面板设置
|
||||
// @Produce json
|
||||
// @Security BearerToken
|
||||
// @Success 200 {object} SuccessResponse
|
||||
// @Router /panel/setting/list [get]
|
||||
// @Summary 设置列表
|
||||
// @Tags 面板设置
|
||||
// @Produce json
|
||||
// @Security BearerToken
|
||||
// @Success 200 {object} SuccessResponse
|
||||
// @Router /panel/setting/list [get]
|
||||
func (r *SettingController) List(ctx http.Context) http.Response {
|
||||
var settings []models.Setting
|
||||
err := facades.Orm().Query().Get(&settings)
|
||||
@@ -50,7 +53,7 @@ func (r *SettingController) List(ctx http.Context) http.Response {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
port, err := tools.Exec(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
|
||||
port, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
|
||||
if err != nil {
|
||||
facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{
|
||||
"error": err.Error(),
|
||||
@@ -74,15 +77,14 @@ func (r *SettingController) List(ctx http.Context) http.Response {
|
||||
|
||||
// Update
|
||||
//
|
||||
// @Summary 更新设置
|
||||
// @Description 更新面板设置
|
||||
// @Tags 面板设置
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerToken
|
||||
// @Param data body requests.Update true "request"
|
||||
// @Success 200 {object} SuccessResponse
|
||||
// @Router /panel/setting/update [post]
|
||||
// @Summary 更新设置
|
||||
// @Tags 面板设置
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerToken
|
||||
// @Param data body requests.Update true "request"
|
||||
// @Success 200 {object} SuccessResponse
|
||||
// @Router /panel/setting/update [post]
|
||||
func (r *SettingController) Update(ctx http.Context) http.Response {
|
||||
var updateRequest requests.Update
|
||||
sanitize := SanitizeRequest(ctx, &updateRequest)
|
||||
@@ -98,8 +100,8 @@ func (r *SettingController) Update(ctx http.Context) http.Response {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
if !tools.Exists(updateRequest.BackupPath) {
|
||||
if err = tools.Mkdir(updateRequest.BackupPath, 0644); err != nil {
|
||||
if !io.Exists(updateRequest.BackupPath) {
|
||||
if err = io.Mkdir(updateRequest.BackupPath, 0644); err != nil {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
}
|
||||
@@ -110,11 +112,11 @@ func (r *SettingController) Update(ctx http.Context) http.Response {
|
||||
}).Info("保存备份目录失败")
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
if !tools.Exists(updateRequest.WebsitePath) {
|
||||
if err = tools.Mkdir(updateRequest.WebsitePath, 0755); err != nil {
|
||||
if !io.Exists(updateRequest.WebsitePath) {
|
||||
if err = io.Mkdir(updateRequest.WebsitePath, 0755); err != nil {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
if err = tools.Chown(updateRequest.WebsitePath, "www", "www"); err != nil {
|
||||
if err = io.Chown(updateRequest.WebsitePath, "www", "www"); err != nil {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
}
|
||||
@@ -147,7 +149,7 @@ func (r *SettingController) Update(ctx http.Context) http.Response {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
oldPort, err := tools.Exec(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
|
||||
oldPort, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
|
||||
if err != nil {
|
||||
facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{
|
||||
"error": err.Error(),
|
||||
@@ -157,33 +159,33 @@ func (r *SettingController) Update(ctx http.Context) http.Response {
|
||||
|
||||
port := cast.ToString(updateRequest.Port)
|
||||
if oldPort != port {
|
||||
if out, err := tools.Exec("sed -i 's/APP_PORT=" + oldPort + "/APP_PORT=" + port + "/g' /www/panel/panel.conf"); err != nil {
|
||||
if out, err := shell.Execf("sed -i 's/APP_PORT=%s/APP_PORT=%s/g' /www/panel/panel.conf", oldPort, port); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if tools.IsRHEL() {
|
||||
if out, err := tools.Exec("firewall-cmd --remove-port=" + cast.ToString(port) + "/tcp --permanent 2>&1"); err != nil {
|
||||
if os.IsRHEL() {
|
||||
if out, err := shell.Execf("firewall-cmd --remove-port=%s/tcp --permanent", oldPort); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("firewall-cmd --add-port=" + cast.ToString(port) + "/tcp --permanent 2>&1"); err != nil {
|
||||
if out, err := shell.Execf("firewall-cmd --add-port=%s/tcp --permanent", port); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("firewall-cmd --reload"); err != nil {
|
||||
if out, err := shell.Execf("firewall-cmd --reload"); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
} else {
|
||||
if out, err := tools.Exec("ufw delete allow " + cast.ToString(port) + "/tcp"); err != nil {
|
||||
if out, err := shell.Execf("ufw delete allow %s/tcp", oldPort); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("ufw allow " + cast.ToString(port) + "/tcp"); err != nil {
|
||||
if out, err := shell.Execf("ufw allow %s/tcp", port); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := tools.Exec("ufw reload"); err != nil {
|
||||
if out, err := shell.Execf("ufw reload"); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
oldEntrance, err := tools.Exec(`cat /www/panel/panel.conf | grep APP_ENTRANCE | awk -F '=' '{print $2}' | tr -d '\n'`)
|
||||
oldEntrance, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_ENTRANCE | awk -F '=' '{print $2}' | tr -d '\n'`)
|
||||
if err != nil {
|
||||
facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{
|
||||
"error": err.Error(),
|
||||
@@ -192,12 +194,12 @@ func (r *SettingController) Update(ctx http.Context) http.Response {
|
||||
}
|
||||
entrance := cast.ToString(updateRequest.Entrance)
|
||||
if oldEntrance != entrance {
|
||||
if out, err := tools.Exec("sed -i 's!APP_ENTRANCE=" + oldEntrance + "!APP_ENTRANCE=" + entrance + "!g' /www/panel/panel.conf"); err != nil {
|
||||
if out, err := shell.Execf("sed -i 's!APP_ENTRANCE=" + oldEntrance + "!APP_ENTRANCE=" + entrance + "!g' /www/panel/panel.conf"); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
}
|
||||
|
||||
oldLanguage, err := tools.Exec(`cat /www/panel/panel.conf | grep APP_LOCALE | awk -F '=' '{print $2}' | tr -d '\n'`)
|
||||
oldLanguage, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_LOCALE | awk -F '=' '{print $2}' | tr -d '\n'`)
|
||||
if err != nil {
|
||||
facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{
|
||||
"error": err.Error(),
|
||||
@@ -205,24 +207,84 @@ func (r *SettingController) Update(ctx http.Context) http.Response {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
if oldLanguage != updateRequest.Language {
|
||||
if out, err := tools.Exec("sed -i 's/APP_LOCALE=" + oldLanguage + "/APP_LOCALE=" + updateRequest.Language + "/g' /www/panel/panel.conf"); err != nil {
|
||||
if out, err := shell.Execf("sed -i 's/APP_LOCALE=" + oldLanguage + "/APP_LOCALE=" + updateRequest.Language + "/g' /www/panel/panel.conf"); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
}
|
||||
|
||||
if updateRequest.SSL {
|
||||
if out, err := tools.Exec("sed -i 's/APP_SSL=false/APP_SSL=true/g' /www/panel/panel.conf"); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
} else {
|
||||
if out, err := tools.Exec("sed -i 's/APP_SSL=true/APP_SSL=false/g' /www/panel/panel.conf"); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
}
|
||||
|
||||
if oldPort != port || oldEntrance != entrance || oldLanguage != updateRequest.Language || updateRequest.SSL != facades.Config().GetBool("panel.ssl") {
|
||||
if oldPort != port || oldEntrance != entrance || oldLanguage != updateRequest.Language {
|
||||
tools.RestartPanel()
|
||||
}
|
||||
|
||||
return Success(ctx, nil)
|
||||
}
|
||||
|
||||
// GetHttps
|
||||
//
|
||||
// @Summary 获取面板 HTTPS 设置
|
||||
// @Tags 面板设置
|
||||
// @Produce json
|
||||
// @Security BearerToken
|
||||
// @Success 200 {object} SuccessResponse
|
||||
// @Router /panel/setting/https [get]
|
||||
func (r *SettingController) GetHttps(ctx http.Context) http.Response {
|
||||
certPath := facades.Config().GetString("http.tls.ssl.cert")
|
||||
keyPath := facades.Config().GetString("http.tls.ssl.key")
|
||||
crt, err := io.Read(certPath)
|
||||
if err != nil {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
key, err := io.Read(keyPath)
|
||||
if err != nil {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
return Success(ctx, http.Json{
|
||||
"https": facades.Config().GetBool("panel.ssl"),
|
||||
"cert": crt,
|
||||
"key": key,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateHttps
|
||||
//
|
||||
// @Summary 更新面板 HTTPS 设置
|
||||
// @Tags 面板设置
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerToken
|
||||
// @Param data body requests.Https true "request"
|
||||
// @Success 200 {object} SuccessResponse
|
||||
// @Router /panel/setting/https [post]
|
||||
func (r *SettingController) UpdateHttps(ctx http.Context) http.Response {
|
||||
var httpsRequest requests.Https
|
||||
sanitize := SanitizeRequest(ctx, &httpsRequest)
|
||||
if sanitize != nil {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
if httpsRequest.Https {
|
||||
if _, err := cert.ParseCert(httpsRequest.Cert); err != nil {
|
||||
return Error(ctx, http.StatusBadRequest, "证书格式错误")
|
||||
}
|
||||
if _, err := cert.ParseKey(httpsRequest.Key); err != nil {
|
||||
return Error(ctx, http.StatusBadRequest, "密钥格式错误")
|
||||
}
|
||||
if err := io.Write(facades.App().ExecutablePath("storage/ssl.crt"), httpsRequest.Cert, 0700); err != nil {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
if err := io.Write(facades.App().ExecutablePath("storage/ssl.key"), httpsRequest.Key, 0700); err != nil {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
if out, err := shell.Execf("sed -i 's/APP_SSL=false/APP_SSL=true/g' /www/panel/panel.conf"); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
} else {
|
||||
if out, err := shell.Execf("sed -i 's/APP_SSL=true/APP_SSL=false/g' /www/panel/panel.conf"); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
}
|
||||
|
||||
tools.RestartPanel()
|
||||
return Success(ctx, nil)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type SystemController struct {
|
||||
@@ -32,7 +32,7 @@ func (r *SystemController) ServiceStatus(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
service := ctx.Request().Query("service")
|
||||
status, err := tools.ServiceStatus(service)
|
||||
status, err := systemctl.Status(service)
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("获取 %s 服务运行状态失败", service))
|
||||
}
|
||||
@@ -57,7 +57,7 @@ func (r *SystemController) ServiceIsEnabled(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
service := ctx.Request().Query("service")
|
||||
enabled, err := tools.ServiceIsEnabled(service)
|
||||
enabled, err := systemctl.IsEnabled(service)
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("获取 %s 服务启用状态失败", service))
|
||||
}
|
||||
@@ -82,7 +82,7 @@ func (r *SystemController) ServiceEnable(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
service := ctx.Request().Input("service")
|
||||
if err := tools.ServiceEnable(service); err != nil {
|
||||
if err := systemctl.Enable(service); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("启用 %s 服务失败", service))
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ func (r *SystemController) ServiceDisable(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
service := ctx.Request().Input("service")
|
||||
if err := tools.ServiceDisable(service); err != nil {
|
||||
if err := systemctl.Disable(service); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("禁用 %s 服务失败", service))
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ func (r *SystemController) ServiceRestart(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
service := ctx.Request().Input("service")
|
||||
if err := tools.ServiceRestart(service); err != nil {
|
||||
if err := systemctl.Restart(service); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("重启 %s 服务失败", service))
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ func (r *SystemController) ServiceReload(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
service := ctx.Request().Input("service")
|
||||
if err := tools.ServiceReload(service); err != nil {
|
||||
if err := systemctl.Reload(service); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("重载 %s 服务失败", service))
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ func (r *SystemController) ServiceStart(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
service := ctx.Request().Input("service")
|
||||
if err := tools.ServiceStart(service); err != nil {
|
||||
if err := systemctl.Start(service); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("启动 %s 服务失败", service))
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ func (r *SystemController) ServiceStop(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
service := ctx.Request().Input("service")
|
||||
if err := tools.ServiceStop(service); err != nil {
|
||||
if err := systemctl.Stop(service); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("停止 %s 服务失败", service))
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"github.com/goravel/framework/facades"
|
||||
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
)
|
||||
|
||||
type TaskController struct {
|
||||
@@ -63,7 +63,7 @@ func (r *TaskController) Log(ctx http.Context) http.Response {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
log, err := tools.Exec(`tail -n 500 '` + task.Log + `'`)
|
||||
log, err := shell.Execf(`tail -n 500 '` + task.Log + `'`)
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, "日志已被清理")
|
||||
}
|
||||
|
||||
@@ -13,8 +13,10 @@ import (
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/internal"
|
||||
"github.com/TheTNB/panel/internal/services"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type WebsiteController struct {
|
||||
@@ -146,11 +148,11 @@ func (r *WebsiteController) Delete(ctx http.Context) http.Response {
|
||||
// @Success 200 {object} SuccessResponse{data=map[string]string}
|
||||
// @Router /panel/website/defaultConfig [get]
|
||||
func (r *WebsiteController) GetDefaultConfig(ctx http.Context) http.Response {
|
||||
index, err := tools.Read("/www/server/openresty/html/index.html")
|
||||
index, err := io.Read("/www/server/openresty/html/index.html")
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
stop, err := tools.Read("/www/server/openresty/html/stop.html")
|
||||
stop, err := io.Read("/www/server/openresty/html/stop.html")
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -175,14 +177,14 @@ func (r *WebsiteController) SaveDefaultConfig(ctx http.Context) http.Response {
|
||||
index := ctx.Request().Input("index")
|
||||
stop := ctx.Request().Input("stop")
|
||||
|
||||
if err := tools.Write("/www/server/openresty/html/index.html", index, 0644); err != nil {
|
||||
if err := io.Write("/www/server/openresty/html/index.html", index, 0644); err != nil {
|
||||
facades.Log().Request(ctx.Request()).Tags("面板", "网站管理").With(map[string]any{
|
||||
"error": err.Error(),
|
||||
}).Info("保存默认首页配置失败")
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
if err := tools.Write("/www/server/openresty/html/stop.html", stop, 0644); err != nil {
|
||||
if err := io.Write("/www/server/openresty/html/stop.html", stop, 0644); err != nil {
|
||||
facades.Log().Request(ctx.Request()).Tags("面板", "网站管理").With(map[string]any{
|
||||
"error": err.Error(),
|
||||
}).Info("保存默认停止页配置失败")
|
||||
@@ -270,7 +272,7 @@ func (r *WebsiteController) ClearLog(ctx http.Context) http.Response {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
if err := tools.Remove("/www/wwwlogs/" + website.Name + ".log"); err != nil {
|
||||
if err := io.Remove("/www/wwwlogs/" + website.Name + ".log"); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -398,8 +400,8 @@ func (r *WebsiteController) UploadBackup(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/website"
|
||||
if !tools.Exists(backupPath) {
|
||||
if err = tools.Mkdir(backupPath, 0644); err != nil {
|
||||
if !io.Exists(backupPath) {
|
||||
if err = io.Mkdir(backupPath, 0644); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -468,13 +470,13 @@ func (r *WebsiteController) DeleteBackup(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/website"
|
||||
if !tools.Exists(backupPath) {
|
||||
if err := tools.Mkdir(backupPath, 0644); err != nil {
|
||||
if !io.Exists(backupPath) {
|
||||
if err := io.Mkdir(backupPath, 0644); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := tools.Remove(backupPath + "/" + deleteBackupRequest.Name); err != nil {
|
||||
if err := io.Remove(backupPath + "/" + deleteBackupRequest.Name); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -547,9 +549,12 @@ server
|
||||
# waf标记位结束
|
||||
|
||||
# 错误页配置,可自行设置
|
||||
#error_page 404 /404.html;
|
||||
error_page 404 /404.html;
|
||||
#error_page 502 /502.html;
|
||||
|
||||
# acme证书签发配置,不可修改
|
||||
include /www/server/vhost/acme/%s.conf;
|
||||
|
||||
# 伪静态规则引入,修改后将导致面板设置的伪静态规则失效
|
||||
include /www/server/vhost/rewrite/%s.conf;
|
||||
|
||||
@@ -570,14 +575,17 @@ server
|
||||
error_log /www/wwwlogs/%s.log;
|
||||
}
|
||||
|
||||
`, website.Path, website.Php, website.Name, website.Name, website.Name)
|
||||
if err := tools.Write("/www/server/vhost/"+website.Name+".conf", raw, 0644); err != nil {
|
||||
`, website.Path, website.Php, website.Name, website.Name, website.Name, website.Name)
|
||||
if err := io.Write("/www/server/vhost/"+website.Name+".conf", raw, 0644); err != nil {
|
||||
return nil
|
||||
}
|
||||
if err := tools.Write("/www/server/vhost/rewrite"+website.Name+".conf", "", 0644); err != nil {
|
||||
if err := io.Write("/www/server/vhost/rewrite/"+website.Name+".conf", "", 0644); err != nil {
|
||||
return nil
|
||||
}
|
||||
if err := tools.ServiceReload("openresty"); err != nil {
|
||||
if err := io.Write("/www/server/vhost/acme/"+website.Name+".conf", "", 0644); err != nil {
|
||||
return nil
|
||||
}
|
||||
if err := systemctl.Reload("openresty"); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
@@ -611,13 +619,13 @@ func (r *WebsiteController) Status(ctx http.Context) http.Response {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
raw, err := tools.Read("/www/server/vhost/" + website.Name + ".conf")
|
||||
raw, err := io.Read("/www/server/vhost/" + website.Name + ".conf")
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
// 运行目录
|
||||
rootConfig := tools.Cut(raw, "# root标记位开始\n", "# root标记位结束")
|
||||
rootConfig := str.Cut(raw, "# root标记位开始\n", "# root标记位结束")
|
||||
match := regexp.MustCompile(`root\s+(.+);`).FindStringSubmatch(rootConfig)
|
||||
if len(match) == 2 {
|
||||
if website.Status {
|
||||
@@ -629,7 +637,7 @@ func (r *WebsiteController) Status(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// 默认文件
|
||||
indexConfig := tools.Cut(raw, "# index标记位开始\n", "# index标记位结束")
|
||||
indexConfig := str.Cut(raw, "# index标记位开始\n", "# index标记位结束")
|
||||
match = regexp.MustCompile(`index\s+(.+);`).FindStringSubmatch(indexConfig)
|
||||
if len(match) == 2 {
|
||||
if website.Status {
|
||||
@@ -640,10 +648,10 @@ func (r *WebsiteController) Status(ctx http.Context) http.Response {
|
||||
}
|
||||
}
|
||||
|
||||
if err = tools.Write("/www/server/vhost/"+website.Name+".conf", raw, 0644); err != nil {
|
||||
if err = io.Write("/www/server/vhost/"+website.Name+".conf", raw, 0644); err != nil {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
if err = tools.ServiceReload("openresty"); err != nil {
|
||||
if err = systemctl.Reload("openresty"); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/facades"
|
||||
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
// Status 检查程序状态
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/contracts/validation"
|
||||
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type ContainerCreate struct {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/contracts/validation"
|
||||
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type ContainerUpdate struct {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/contracts/validation"
|
||||
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type NetworkCreate struct {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/contracts/validation"
|
||||
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type VolumeCreate struct {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package requests
|
||||
|
||||
import (
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/contracts/validation"
|
||||
)
|
||||
|
||||
type Https struct {
|
||||
Https bool `form:"https" json:"https"`
|
||||
Cert string `form:"cert" json:"cert"`
|
||||
Key string `form:"key" json:"key"`
|
||||
}
|
||||
|
||||
func (r *Https) Authorize(ctx http.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Https) Rules(ctx http.Context) map[string]string {
|
||||
return map[string]string{
|
||||
"https": "bool",
|
||||
"cert": "string",
|
||||
"key": "string",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Https) Messages(ctx http.Context) map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (r *Https) Attributes(ctx http.Context) map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (r *Https) PrepareForValidation(ctx http.Context, data validation.Data) error {
|
||||
return nil
|
||||
}
|
||||
@@ -12,7 +12,6 @@ type Update struct {
|
||||
BackupPath string `form:"backup_path" json:"backup_path"`
|
||||
WebsitePath string `form:"website_path" json:"website_path"`
|
||||
Entrance string `form:"entrance" json:"entrance"`
|
||||
SSL bool `form:"ssl" json:"ssl"`
|
||||
UserName string `form:"username" json:"username"`
|
||||
Email string `form:"email" json:"email"`
|
||||
Password string `form:"password" json:"password"`
|
||||
@@ -30,7 +29,6 @@ func (r *Update) Rules(ctx http.Context) map[string]string {
|
||||
"backup_path": "required|string:2,255",
|
||||
"website_path": "required|string:2,255",
|
||||
"entrance": `required|regex:^/(\w+)?$|not_in:/api`,
|
||||
"ssl": "bool",
|
||||
"username": "required|string:2,20",
|
||||
"email": "required|email",
|
||||
"password": "string:8,255",
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package jobs
|
||||
|
||||
import (
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/goravel/framework/facades"
|
||||
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
)
|
||||
|
||||
// ProcessTask 处理面板任务
|
||||
@@ -48,7 +48,7 @@ func (receiver *ProcessTask) Handle(args ...any) error {
|
||||
"task_id": taskID,
|
||||
}).Infof("开始执行任务")
|
||||
|
||||
if _, err := tools.Exec(task.Shell); err != nil {
|
||||
if _, err := shell.Execf(task.Shell); err != nil {
|
||||
task.Status = models.TaskStatusFailed
|
||||
if err := facades.Orm().Query().Save(&task); err != nil {
|
||||
facades.Log().Tags("面板", "异步任务").With(map[string]any{
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/goravel/framework/contracts/validation"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
)
|
||||
|
||||
type PathExists struct {
|
||||
@@ -27,7 +28,7 @@ func (receiver *PathExists) Passes(_ validation.Data, val any, options ...any) b
|
||||
return false
|
||||
}
|
||||
|
||||
return tools.Exists(requestValue)
|
||||
return io.Exists(requestValue)
|
||||
}
|
||||
|
||||
// Message Get the validation error message.
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/goravel/framework/contracts/validation"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
)
|
||||
|
||||
type PathNotExists struct {
|
||||
@@ -27,7 +28,7 @@ func (receiver *PathNotExists) Passes(_ validation.Data, val any, options ...any
|
||||
return false
|
||||
}
|
||||
|
||||
return !tools.Exists(requestValue)
|
||||
return !io.Exists(requestValue)
|
||||
}
|
||||
|
||||
// Message Get the validation error message.
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ func init() {
|
||||
config := facades.Config()
|
||||
config.Add("panel", map[string]any{
|
||||
"name": "耗子面板",
|
||||
"version": "v2.2.10",
|
||||
"version": "v2.2.14",
|
||||
"ssl": config.Env("APP_SSL", false),
|
||||
})
|
||||
}
|
||||
|
||||
+74
-5
@@ -2830,6 +2830,66 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/panel/setting/https": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"面板设置"
|
||||
],
|
||||
"summary": "获取面板 HTTPS 设置",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.SuccessResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"面板设置"
|
||||
],
|
||||
"summary": "更新面板 HTTPS 设置",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "data",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/requests.Https"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.SuccessResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/panel/setting/list": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -2837,7 +2897,6 @@ const docTemplate = `{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"description": "获取面板设置列表",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
@@ -2862,7 +2921,6 @@ const docTemplate = `{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"description": "更新面板设置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
@@ -5006,9 +5064,6 @@ const docTemplate = `{
|
||||
"port": {
|
||||
"type": "integer"
|
||||
},
|
||||
"ssl": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -5467,6 +5522,20 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"requests.Https": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"cert": {
|
||||
"type": "string"
|
||||
},
|
||||
"https": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requests.ImagePull": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
+74
-5
@@ -2823,6 +2823,66 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/panel/setting/https": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"面板设置"
|
||||
],
|
||||
"summary": "获取面板 HTTPS 设置",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.SuccessResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"面板设置"
|
||||
],
|
||||
"summary": "更新面板 HTTPS 设置",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "data",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/requests.Https"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.SuccessResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/panel/setting/list": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -2830,7 +2890,6 @@
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"description": "获取面板设置列表",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
@@ -2855,7 +2914,6 @@
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"description": "更新面板设置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
@@ -4999,9 +5057,6 @@
|
||||
"port": {
|
||||
"type": "integer"
|
||||
},
|
||||
"ssl": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -5460,6 +5515,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"requests.Https": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"cert": {
|
||||
"type": "string"
|
||||
},
|
||||
"https": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requests.ImagePull": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
+45
-4
@@ -84,8 +84,6 @@ definitions:
|
||||
type: string
|
||||
port:
|
||||
type: integer
|
||||
ssl:
|
||||
type: boolean
|
||||
username:
|
||||
type: string
|
||||
website_path:
|
||||
@@ -389,6 +387,15 @@ definitions:
|
||||
path:
|
||||
type: string
|
||||
type: object
|
||||
requests.Https:
|
||||
properties:
|
||||
cert:
|
||||
type: string
|
||||
https:
|
||||
type: boolean
|
||||
key:
|
||||
type: string
|
||||
type: object
|
||||
requests.ImagePull:
|
||||
properties:
|
||||
auth:
|
||||
@@ -2384,9 +2391,44 @@ paths:
|
||||
summary: 更新插件首页显示状态
|
||||
tags:
|
||||
- 插件
|
||||
/panel/setting/https:
|
||||
get:
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/controllers.SuccessResponse'
|
||||
security:
|
||||
- BearerToken: []
|
||||
summary: 获取面板 HTTPS 设置
|
||||
tags:
|
||||
- 面板设置
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: data
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/requests.Https'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/controllers.SuccessResponse'
|
||||
security:
|
||||
- BearerToken: []
|
||||
summary: 更新面板 HTTPS 设置
|
||||
tags:
|
||||
- 面板设置
|
||||
/panel/setting/list:
|
||||
get:
|
||||
description: 获取面板设置列表
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
@@ -2403,7 +2445,6 @@ paths:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 更新面板设置
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ package internal
|
||||
|
||||
import (
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type Backup interface {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
|
||||
requests "github.com/TheTNB/panel/app/http/requests/container"
|
||||
paneltypes "github.com/TheTNB/panel/types"
|
||||
paneltypes "github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type Container interface {
|
||||
|
||||
+3
-1
@@ -1,6 +1,8 @@
|
||||
package internal
|
||||
|
||||
import "github.com/TheTNB/panel/types"
|
||||
import (
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type PHP interface {
|
||||
Status() (bool, error)
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ package internal
|
||||
|
||||
import (
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type Plugin interface {
|
||||
|
||||
+45
-43
@@ -11,8 +11,10 @@ import (
|
||||
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/internal"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type BackupImpl struct {
|
||||
@@ -33,8 +35,8 @@ func (s *BackupImpl) WebsiteList() ([]types.BackupFile, error) {
|
||||
}
|
||||
|
||||
backupPath += "/website"
|
||||
if !tools.Exists(backupPath) {
|
||||
if err := tools.Mkdir(backupPath, 0644); err != nil {
|
||||
if !io.Exists(backupPath) {
|
||||
if err := io.Mkdir(backupPath, 0644); err != nil {
|
||||
return []types.BackupFile{}, err
|
||||
}
|
||||
}
|
||||
@@ -51,7 +53,7 @@ func (s *BackupImpl) WebsiteList() ([]types.BackupFile, error) {
|
||||
}
|
||||
backupList = append(backupList, types.BackupFile{
|
||||
Name: file.Name(),
|
||||
Size: tools.FormatBytes(float64(info.Size())),
|
||||
Size: str.FormatBytes(float64(info.Size())),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -66,14 +68,14 @@ func (s *BackupImpl) WebSiteBackup(website models.Website) error {
|
||||
}
|
||||
|
||||
backupPath += "/website"
|
||||
if !tools.Exists(backupPath) {
|
||||
if err := tools.Mkdir(backupPath, 0644); err != nil {
|
||||
if !io.Exists(backupPath) {
|
||||
if err := io.Mkdir(backupPath, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
backupFile := backupPath + "/" + website.Name + "_" + carbon.Now().ToShortDateTimeString() + ".zip"
|
||||
if _, err := tools.Exec(`cd '` + website.Path + `' && zip -r '` + backupFile + `' .`); err != nil {
|
||||
if _, err := shell.Execf(`cd '` + website.Path + `' && zip -r '` + backupFile + `' .`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -88,27 +90,27 @@ func (s *BackupImpl) WebsiteRestore(website models.Website, backupFile string) e
|
||||
}
|
||||
|
||||
backupPath += "/website"
|
||||
if !tools.Exists(backupPath) {
|
||||
if err := tools.Mkdir(backupPath, 0644); err != nil {
|
||||
if !io.Exists(backupPath) {
|
||||
if err := io.Mkdir(backupPath, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
backupFile = backupPath + "/" + backupFile
|
||||
if !tools.Exists(backupFile) {
|
||||
if !io.Exists(backupFile) {
|
||||
return errors.New("备份文件不存在")
|
||||
}
|
||||
|
||||
if err := tools.Remove(website.Path); err != nil {
|
||||
if err := io.Remove(website.Path); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tools.UnArchive(backupFile, website.Path); err != nil {
|
||||
if err := io.UnArchive(backupFile, website.Path); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tools.Chmod(website.Path, 0755); err != nil {
|
||||
if err := io.Chmod(website.Path, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tools.Chown(website.Path, "www", "www"); err != nil {
|
||||
if err := io.Chown(website.Path, "www", "www"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -123,8 +125,8 @@ func (s *BackupImpl) MysqlList() ([]types.BackupFile, error) {
|
||||
}
|
||||
|
||||
backupPath += "/mysql"
|
||||
if !tools.Exists(backupPath) {
|
||||
if err := tools.Mkdir(backupPath, 0644); err != nil {
|
||||
if !io.Exists(backupPath) {
|
||||
if err := io.Mkdir(backupPath, 0644); err != nil {
|
||||
return []types.BackupFile{}, err
|
||||
}
|
||||
}
|
||||
@@ -141,7 +143,7 @@ func (s *BackupImpl) MysqlList() ([]types.BackupFile, error) {
|
||||
}
|
||||
backupList = append(backupList, types.BackupFile{
|
||||
Name: file.Name(),
|
||||
Size: tools.FormatBytes(float64(info.Size())),
|
||||
Size: str.FormatBytes(float64(info.Size())),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -153,8 +155,8 @@ func (s *BackupImpl) MysqlBackup(database string) error {
|
||||
backupPath := s.setting.Get(models.SettingKeyBackupPath) + "/mysql"
|
||||
rootPassword := s.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
backupFile := database + "_" + carbon.Now().ToShortDateTimeString() + ".sql"
|
||||
if !tools.Exists(backupPath) {
|
||||
if err := tools.Mkdir(backupPath, 0644); err != nil {
|
||||
if !io.Exists(backupPath) {
|
||||
if err := io.Mkdir(backupPath, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -163,13 +165,13 @@ func (s *BackupImpl) MysqlBackup(database string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tools.Exec("/www/server/mysql/bin/mysqldump -uroot " + database + " > " + backupPath + "/" + backupFile); err != nil {
|
||||
if _, err := shell.Execf("/www/server/mysql/bin/mysqldump -uroot " + database + " > " + backupPath + "/" + backupFile); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := tools.Exec("cd " + backupPath + " && zip -r " + backupPath + "/" + backupFile + ".zip " + backupFile); err != nil {
|
||||
if _, err := shell.Execf("cd " + backupPath + " && zip -r " + backupPath + "/" + backupFile + ".zip " + backupFile); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tools.Remove(backupPath + "/" + backupFile); err != nil {
|
||||
if err := io.Remove(backupPath + "/" + backupFile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -181,7 +183,7 @@ func (s *BackupImpl) MysqlRestore(database string, backupFile string) error {
|
||||
backupPath := s.setting.Get(models.SettingKeyBackupPath) + "/mysql"
|
||||
rootPassword := s.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
backupFullPath := filepath.Join(backupPath, backupFile)
|
||||
if !tools.Exists(backupFullPath) {
|
||||
if !io.Exists(backupFullPath) {
|
||||
return errors.New("备份文件不存在")
|
||||
}
|
||||
|
||||
@@ -189,14 +191,14 @@ func (s *BackupImpl) MysqlRestore(database string, backupFile string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
tempDir, err := tools.TempDir(backupFile)
|
||||
tempDir, err := io.TempDir(backupFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(backupFile, ".sql") {
|
||||
backupFile = "" // 置空,防止干扰后续判断
|
||||
if err = tools.UnArchive(backupFullPath, tempDir); err != nil {
|
||||
if err = io.UnArchive(backupFullPath, tempDir); err != nil {
|
||||
return err
|
||||
}
|
||||
if files, err := os.ReadDir(tempDir); err == nil {
|
||||
@@ -208,7 +210,7 @@ func (s *BackupImpl) MysqlRestore(database string, backupFile string) error {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err = tools.Cp(backupFullPath, filepath.Join(tempDir, backupFile)); err != nil {
|
||||
if err = io.Cp(backupFullPath, filepath.Join(tempDir, backupFile)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -217,11 +219,11 @@ func (s *BackupImpl) MysqlRestore(database string, backupFile string) error {
|
||||
return errors.New("无法找到备份文件")
|
||||
}
|
||||
|
||||
if _, err = tools.Exec("/www/server/mysql/bin/mysql -uroot " + database + " < " + filepath.Join(tempDir, backupFile)); err != nil {
|
||||
if _, err = shell.Execf("/www/server/mysql/bin/mysql -uroot " + database + " < " + filepath.Join(tempDir, backupFile)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = tools.Remove(tempDir); err != nil {
|
||||
if err = io.Remove(tempDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -236,8 +238,8 @@ func (s *BackupImpl) PostgresqlList() ([]types.BackupFile, error) {
|
||||
}
|
||||
|
||||
backupPath += "/postgresql"
|
||||
if !tools.Exists(backupPath) {
|
||||
if err := tools.Mkdir(backupPath, 0644); err != nil {
|
||||
if !io.Exists(backupPath) {
|
||||
if err := io.Mkdir(backupPath, 0644); err != nil {
|
||||
return []types.BackupFile{}, err
|
||||
}
|
||||
}
|
||||
@@ -254,7 +256,7 @@ func (s *BackupImpl) PostgresqlList() ([]types.BackupFile, error) {
|
||||
}
|
||||
backupList = append(backupList, types.BackupFile{
|
||||
Name: file.Name(),
|
||||
Size: tools.FormatBytes(float64(info.Size())),
|
||||
Size: str.FormatBytes(float64(info.Size())),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -265,38 +267,38 @@ func (s *BackupImpl) PostgresqlList() ([]types.BackupFile, error) {
|
||||
func (s *BackupImpl) PostgresqlBackup(database string) error {
|
||||
backupPath := s.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
|
||||
backupFile := database + "_" + carbon.Now().ToShortDateTimeString() + ".sql"
|
||||
if !tools.Exists(backupPath) {
|
||||
if err := tools.Mkdir(backupPath, 0644); err != nil {
|
||||
if !io.Exists(backupPath) {
|
||||
if err := io.Mkdir(backupPath, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := tools.Exec(`su - postgres -c "pg_dump ` + database + `" > ` + backupPath + "/" + backupFile); err != nil {
|
||||
if _, err := shell.Execf(`su - postgres -c "pg_dump ` + database + `" > ` + backupPath + "/" + backupFile); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := tools.Exec("cd " + backupPath + " && zip -r " + backupPath + "/" + backupFile + ".zip " + backupFile); err != nil {
|
||||
if _, err := shell.Execf("cd " + backupPath + " && zip -r " + backupPath + "/" + backupFile + ".zip " + backupFile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tools.Remove(backupPath + "/" + backupFile)
|
||||
return io.Remove(backupPath + "/" + backupFile)
|
||||
}
|
||||
|
||||
// PostgresqlRestore PostgreSQL恢复
|
||||
func (s *BackupImpl) PostgresqlRestore(database string, backupFile string) error {
|
||||
backupPath := s.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
|
||||
backupFullPath := filepath.Join(backupPath, backupFile)
|
||||
if !tools.Exists(backupFullPath) {
|
||||
if !io.Exists(backupFullPath) {
|
||||
return errors.New("备份文件不存在")
|
||||
}
|
||||
|
||||
tempDir, err := tools.TempDir(backupFile)
|
||||
tempDir, err := io.TempDir(backupFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(backupFile, ".sql") {
|
||||
backupFile = "" // 置空,防止干扰后续判断
|
||||
if err = tools.UnArchive(backupFullPath, tempDir); err != nil {
|
||||
if err = io.UnArchive(backupFullPath, tempDir); err != nil {
|
||||
return err
|
||||
}
|
||||
if files, err := os.ReadDir(tempDir); err == nil {
|
||||
@@ -308,7 +310,7 @@ func (s *BackupImpl) PostgresqlRestore(database string, backupFile string) error
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err = tools.Cp(backupFullPath, filepath.Join(tempDir, backupFile)); err != nil {
|
||||
if err = io.Cp(backupFullPath, filepath.Join(tempDir, backupFile)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -317,11 +319,11 @@ func (s *BackupImpl) PostgresqlRestore(database string, backupFile string) error
|
||||
return errors.New("无法找到备份文件")
|
||||
}
|
||||
|
||||
if _, err = tools.Exec(`su - postgres -c "psql ` + database + `" < ` + filepath.Join(tempDir, backupFile)); err != nil {
|
||||
if _, err = shell.Execf(`su - postgres -c "psql ` + database + `" < ` + filepath.Join(tempDir, backupFile)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = tools.Remove(tempDir); err != nil {
|
||||
if err = io.Remove(tempDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
+22
-17
@@ -4,6 +4,7 @@ package services
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -12,7 +13,9 @@ import (
|
||||
requests "github.com/TheTNB/panel/app/http/requests/cert"
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/pkg/acme"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/cert"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type CertImpl struct {
|
||||
@@ -53,7 +56,7 @@ func (s *CertImpl) UserStore(request requests.UserStore) error {
|
||||
return errors.New("向 CA 注册账号失败,请检查参数是否正确")
|
||||
}
|
||||
|
||||
privateKey, err := acme.EncodePrivateKey(client.Account.PrivateKey)
|
||||
privateKey, err := cert.EncodeKey(client.Account.PrivateKey)
|
||||
if err != nil {
|
||||
return errors.New("获取私钥失败")
|
||||
}
|
||||
@@ -96,7 +99,7 @@ func (s *CertImpl) UserUpdate(request requests.UserUpdate) error {
|
||||
return errors.New("向 CA 注册账号失败,请检查参数是否正确")
|
||||
}
|
||||
|
||||
privateKey, err := acme.EncodePrivateKey(client.Account.PrivateKey)
|
||||
privateKey, err := cert.EncodeKey(client.Account.PrivateKey)
|
||||
if err != nil {
|
||||
return errors.New("获取私钥失败")
|
||||
}
|
||||
@@ -253,7 +256,8 @@ func (s *CertImpl) ObtainAuto(ID uint) (acme.Certificate, error) {
|
||||
return acme.Certificate{}, errors.New("通配符域名无法使用 HTTP 验证")
|
||||
}
|
||||
}
|
||||
client.UseHTTP(cert.Website.Path)
|
||||
conf := fmt.Sprintf("/www/server/vhost/acme/%s.conf", cert.Website.Name)
|
||||
client.UseHTTP(conf, cert.Website.Path)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,13 +275,13 @@ func (s *CertImpl) ObtainAuto(ID uint) (acme.Certificate, error) {
|
||||
}
|
||||
|
||||
if cert.Website != nil {
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", cert.Cert, 0644); err != nil {
|
||||
if err = io.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", cert.Cert, 0644); err != nil {
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", cert.Key, 0644); err != nil {
|
||||
if err = io.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", cert.Key, 0644); err != nil {
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
if err = tools.ServiceReload("openresty"); err != nil {
|
||||
if err = systemctl.Reload("openresty"); err != nil {
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
}
|
||||
@@ -311,13 +315,13 @@ func (s *CertImpl) ObtainManual(ID uint) (acme.Certificate, error) {
|
||||
}
|
||||
|
||||
if cert.Website != nil {
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", cert.Cert, 0644); err != nil {
|
||||
if err = io.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", cert.Cert, 0644); err != nil {
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", cert.Key, 0644); err != nil {
|
||||
if err = io.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", cert.Key, 0644); err != nil {
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
if err = tools.ServiceReload("openresty"); err != nil {
|
||||
if err = systemctl.Reload("openresty"); err != nil {
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
}
|
||||
@@ -378,7 +382,8 @@ func (s *CertImpl) Renew(ID uint) (acme.Certificate, error) {
|
||||
return acme.Certificate{}, errors.New("通配符域名无法使用 HTTP 验证")
|
||||
}
|
||||
}
|
||||
client.UseHTTP(cert.Website.Path)
|
||||
conf := fmt.Sprintf("/www/server/vhost/acme/%s.conf", cert.Website.Name)
|
||||
client.UseHTTP(conf, cert.Website.Path)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,13 +401,13 @@ func (s *CertImpl) Renew(ID uint) (acme.Certificate, error) {
|
||||
}
|
||||
|
||||
if cert.Website != nil {
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", cert.Cert, 0644); err != nil {
|
||||
if err = io.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", cert.Cert, 0644); err != nil {
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", cert.Key, 0644); err != nil {
|
||||
if err = io.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", cert.Key, 0644); err != nil {
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
if err = tools.ServiceReload("openresty"); err != nil {
|
||||
if err = systemctl.Reload("openresty"); err != nil {
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
}
|
||||
@@ -428,13 +433,13 @@ func (s *CertImpl) Deploy(ID, WebsiteID uint) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+website.Name+".pem", cert.Cert, 0644); err != nil {
|
||||
if err = io.Write("/www/server/vhost/ssl/"+website.Name+".pem", cert.Cert, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+website.Name+".key", cert.Key, 0644); err != nil {
|
||||
if err = io.Write("/www/server/vhost/ssl/"+website.Name+".key", cert.Key, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = tools.ServiceReload("openresty"); err != nil {
|
||||
if err = systemctl.Reload("openresty"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
"github.com/goravel/framework/support/json"
|
||||
|
||||
requests "github.com/TheTNB/panel/app/http/requests/container"
|
||||
paneltypes "github.com/TheTNB/panel/types"
|
||||
paneltypes "github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type Container struct {
|
||||
|
||||
+15
-13
@@ -6,7 +6,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/os"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type CronImpl struct {
|
||||
@@ -18,18 +20,18 @@ func NewCronImpl() *CronImpl {
|
||||
|
||||
// AddToSystem 添加到系统
|
||||
func (r *CronImpl) AddToSystem(cron models.Cron) error {
|
||||
if tools.IsRHEL() {
|
||||
if _, err := tools.Exec(fmt.Sprintf(`echo "%s %s >> %s 2>&1" >> /var/spool/cron/root`, cron.Time, cron.Shell, cron.Log)); err != nil {
|
||||
if os.IsRHEL() {
|
||||
if _, err := shell.Execf(fmt.Sprintf(`echo "%s %s >> %s 2>&1" >> /var/spool/cron/root`, cron.Time, cron.Shell, cron.Log)); err != nil {
|
||||
return err
|
||||
}
|
||||
return tools.ServiceRestart("crond")
|
||||
return systemctl.Restart("crond")
|
||||
}
|
||||
|
||||
if tools.IsDebian() {
|
||||
if _, err := tools.Exec(fmt.Sprintf(`echo "%s %s >> %s 2>&1" >> /var/spool/cron/crontabs/root`, cron.Time, cron.Shell, cron.Log)); err != nil {
|
||||
if os.IsDebian() {
|
||||
if _, err := shell.Execf(fmt.Sprintf(`echo "%s %s >> %s 2>&1" >> /var/spool/cron/crontabs/root`, cron.Time, cron.Shell, cron.Log)); err != nil {
|
||||
return err
|
||||
}
|
||||
return tools.ServiceRestart("cron")
|
||||
return systemctl.Restart("cron")
|
||||
}
|
||||
|
||||
return errors.New("不支持的系统")
|
||||
@@ -39,18 +41,18 @@ func (r *CronImpl) AddToSystem(cron models.Cron) error {
|
||||
func (r *CronImpl) DeleteFromSystem(cron models.Cron) error {
|
||||
// 需要转义 shell 路径的/为\/
|
||||
cron.Shell = strings.ReplaceAll(cron.Shell, "/", "\\/")
|
||||
if tools.IsRHEL() {
|
||||
if _, err := tools.Exec("sed -i '/" + cron.Shell + "/d' /var/spool/cron/root"); err != nil {
|
||||
if os.IsRHEL() {
|
||||
if _, err := shell.Execf("sed -i '/" + cron.Shell + "/d' /var/spool/cron/root"); err != nil {
|
||||
return err
|
||||
}
|
||||
return tools.ServiceRestart("crond")
|
||||
return systemctl.Restart("crond")
|
||||
}
|
||||
|
||||
if tools.IsDebian() {
|
||||
if _, err := tools.Exec("sed -i '/" + cron.Shell + "/d' /var/spool/cron/crontabs/root"); err != nil {
|
||||
if os.IsDebian() {
|
||||
if _, err := shell.Execf("sed -i '/" + cron.Shell + "/d' /var/spool/cron/crontabs/root"); err != nil {
|
||||
return err
|
||||
}
|
||||
return tools.ServiceRestart("cron")
|
||||
return systemctl.Restart("cron")
|
||||
}
|
||||
|
||||
return errors.New("不支持的系统")
|
||||
|
||||
+14
-12
@@ -13,8 +13,10 @@ import (
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type PHPImpl struct {
|
||||
@@ -28,15 +30,15 @@ func NewPHPImpl(version uint) *PHPImpl {
|
||||
}
|
||||
|
||||
func (r *PHPImpl) Reload() error {
|
||||
return tools.ServiceReload("php-fpm-" + r.version)
|
||||
return systemctl.Reload("php-fpm-" + r.version)
|
||||
}
|
||||
|
||||
func (r *PHPImpl) GetConfig() (string, error) {
|
||||
return tools.Read("/www/server/php/" + r.version + "/etc/php.ini")
|
||||
return io.Read("/www/server/php/" + r.version + "/etc/php.ini")
|
||||
}
|
||||
|
||||
func (r *PHPImpl) SaveConfig(config string) error {
|
||||
if err := tools.Write("/www/server/php/"+r.version+"/etc/php.ini", config, 0644); err != nil {
|
||||
if err := io.Write("/www/server/php/"+r.version+"/etc/php.ini", config, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -44,11 +46,11 @@ func (r *PHPImpl) SaveConfig(config string) error {
|
||||
}
|
||||
|
||||
func (r *PHPImpl) GetFPMConfig() (string, error) {
|
||||
return tools.Read("/www/server/php/" + r.version + "/etc/php-fpm.conf")
|
||||
return io.Read("/www/server/php/" + r.version + "/etc/php-fpm.conf")
|
||||
}
|
||||
|
||||
func (r *PHPImpl) SaveFPMConfig(config string) error {
|
||||
if err := tools.Write("/www/server/php/"+r.version+"/etc/php-fpm.conf", config, 0644); err != nil {
|
||||
if err := io.Write("/www/server/php/"+r.version+"/etc/php-fpm.conf", config, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -82,15 +84,15 @@ func (r *PHPImpl) Load() ([]types.NV, error) {
|
||||
}
|
||||
|
||||
func (r *PHPImpl) GetErrorLog() (string, error) {
|
||||
return tools.Exec("tail -n 500 /www/server/php/" + r.version + "/var/log/php-fpm.log")
|
||||
return shell.Execf("tail -n 500 /www/server/php/%s/var/log/php-fpm.log", r.version)
|
||||
}
|
||||
|
||||
func (r *PHPImpl) GetSlowLog() (string, error) {
|
||||
return tools.Exec("tail -n 500 /www/server/php/" + r.version + "/var/log/slow.log")
|
||||
return shell.Execf("tail -n 500 /www/server/php/%s/var/log/slow.log", r.version)
|
||||
}
|
||||
|
||||
func (r *PHPImpl) ClearErrorLog() error {
|
||||
if out, err := tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/php-fpm.log"); err != nil {
|
||||
if out, err := shell.Execf("echo '' > /www/server/php/%s/var/log/php-fpm.log", r.version); err != nil {
|
||||
return errors.New(out)
|
||||
}
|
||||
|
||||
@@ -98,7 +100,7 @@ func (r *PHPImpl) ClearErrorLog() error {
|
||||
}
|
||||
|
||||
func (r *PHPImpl) ClearSlowLog() error {
|
||||
if out, err := tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/slow.log"); err != nil {
|
||||
if out, err := shell.Execf("echo '' > /www/server/php/%s/var/log/slow.log", r.version); err != nil {
|
||||
return errors.New(out)
|
||||
}
|
||||
|
||||
@@ -270,7 +272,7 @@ func (r *PHPImpl) GetExtensions() ([]types.PHPExtension, error) {
|
||||
})
|
||||
}
|
||||
|
||||
raw, err := tools.Exec("/www/server/php/" + r.version + "/bin/php -m")
|
||||
raw, err := shell.Execf("/www/server/php/%s/bin/php -m", r.version)
|
||||
if err != nil {
|
||||
return extensions, err
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/internal"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type PluginImpl struct {
|
||||
@@ -122,7 +122,7 @@ func (r *PluginImpl) Install(slug string) error {
|
||||
return errors.New("创建任务失败")
|
||||
}
|
||||
|
||||
_ = tools.Remove(task.Log)
|
||||
_ = io.Remove(task.Log)
|
||||
return r.task.Process(task.ID)
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ func (r *PluginImpl) Uninstall(slug string) error {
|
||||
return errors.New("创建任务失败")
|
||||
}
|
||||
|
||||
_ = tools.Remove(task.Log)
|
||||
_ = io.Remove(task.Log)
|
||||
return r.task.Process(task.ID)
|
||||
}
|
||||
|
||||
@@ -214,6 +214,6 @@ func (r *PluginImpl) Update(slug string) error {
|
||||
return errors.New("创建任务失败")
|
||||
}
|
||||
|
||||
_ = tools.Remove(task.Log)
|
||||
_ = io.Remove(task.Log)
|
||||
return r.task.Process(task.ID)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"github.com/goravel/framework/facades"
|
||||
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
)
|
||||
|
||||
type SettingImpl struct {
|
||||
@@ -19,11 +19,11 @@ func NewSettingImpl() *SettingImpl {
|
||||
func (r *SettingImpl) Get(key string, defaultValue ...string) string {
|
||||
var setting models.Setting
|
||||
if err := facades.Orm().Query().Where("key", key).FirstOrFail(&setting); err != nil {
|
||||
return tools.FirstElement(defaultValue)
|
||||
return str.FirstElement(defaultValue)
|
||||
}
|
||||
|
||||
if len(setting.Value) == 0 {
|
||||
return tools.FirstElement(defaultValue)
|
||||
return str.FirstElement(defaultValue)
|
||||
}
|
||||
|
||||
return setting.Value
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
@@ -16,8 +14,12 @@ import (
|
||||
requests "github.com/TheTNB/panel/app/http/requests/website"
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/internal"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/cert"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type WebsiteImpl struct {
|
||||
@@ -55,7 +57,7 @@ func (r *WebsiteImpl) Add(website types.Website) (models.Website, error) {
|
||||
return models.Website{}, err
|
||||
}
|
||||
|
||||
if err := tools.Mkdir(website.Path, 0755); err != nil {
|
||||
if err := io.Mkdir(website.Path, 0755); err != nil {
|
||||
return models.Website{}, err
|
||||
}
|
||||
|
||||
@@ -113,7 +115,7 @@ func (r *WebsiteImpl) Add(website types.Website) (models.Website, error) {
|
||||
</html>
|
||||
|
||||
`
|
||||
if err := tools.Write(website.Path+"/index.html", index, 0644); err != nil {
|
||||
if err := io.Write(website.Path+"/index.html", index, 0644); err != nil {
|
||||
return models.Website{}, err
|
||||
}
|
||||
|
||||
@@ -174,7 +176,7 @@ func (r *WebsiteImpl) Add(website types.Website) (models.Website, error) {
|
||||
</html>
|
||||
|
||||
`
|
||||
if err := tools.Write(website.Path+"/404.html", notFound, 0644); err != nil {
|
||||
if err := io.Write(website.Path+"/404.html", notFound, 0644); err != nil {
|
||||
return models.Website{}, err
|
||||
}
|
||||
|
||||
@@ -238,6 +240,9 @@ server
|
||||
error_page 404 /404.html;
|
||||
#error_page 502 /502.html;
|
||||
|
||||
# acme证书签发配置,不可修改
|
||||
include /www/server/vhost/acme/%s.conf;
|
||||
|
||||
# 伪静态规则引入,修改后将导致面板设置的伪静态规则失效
|
||||
include /www/server/vhost/rewrite/%s.conf;
|
||||
|
||||
@@ -257,47 +262,47 @@ server
|
||||
access_log /www/wwwlogs/%s.log;
|
||||
error_log /www/wwwlogs/%s.log;
|
||||
}
|
||||
`, portList, domainList, website.Path, website.Php, website.Name, website.Name, website.Name)
|
||||
`, portList, domainList, website.Path, website.Php, website.Name, website.Name, website.Name, website.Name)
|
||||
|
||||
if err := tools.Write("/www/server/vhost/"+website.Name+".conf", nginxConf, 0644); err != nil {
|
||||
if err := io.Write("/www/server/vhost/"+website.Name+".conf", nginxConf, 0644); err != nil {
|
||||
return models.Website{}, err
|
||||
}
|
||||
if err := tools.Write("/www/server/vhost/rewrite/"+website.Name+".conf", "", 0644); err != nil {
|
||||
if err := io.Write("/www/server/vhost/rewrite/"+website.Name+".conf", "", 0644); err != nil {
|
||||
return models.Website{}, err
|
||||
}
|
||||
if err := tools.Write("/www/server/vhost/ssl/"+website.Name+".pem", "", 0644); err != nil {
|
||||
if err := io.Write("/www/server/vhost/ssl/"+website.Name+".pem", "", 0644); err != nil {
|
||||
return models.Website{}, err
|
||||
}
|
||||
if err := tools.Write("/www/server/vhost/ssl/"+website.Name+".key", "", 0644); err != nil {
|
||||
if err := io.Write("/www/server/vhost/ssl/"+website.Name+".key", "", 0644); err != nil {
|
||||
return models.Website{}, err
|
||||
}
|
||||
|
||||
if err := tools.Chmod(website.Path, 0755); err != nil {
|
||||
if err := io.Chmod(website.Path, 0755); err != nil {
|
||||
return models.Website{}, err
|
||||
}
|
||||
if err := tools.Chown(website.Path, "www", "www"); err != nil {
|
||||
if err := io.Chown(website.Path, "www", "www"); err != nil {
|
||||
return models.Website{}, err
|
||||
}
|
||||
|
||||
if err := tools.ServiceReload("openresty"); err != nil {
|
||||
if err := systemctl.Reload("openresty"); err != nil {
|
||||
return models.Website{}, err
|
||||
}
|
||||
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
if website.Db && website.DbType == "mysql" {
|
||||
_, _ = tools.Exec(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "CREATE DATABASE IF NOT EXISTS ` + website.DbName + ` DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;"`)
|
||||
_, _ = tools.Exec(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "CREATE USER '` + website.DbUser + `'@'localhost' IDENTIFIED BY '` + website.DbPassword + `';"`)
|
||||
_, _ = tools.Exec(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "GRANT ALL PRIVILEGES ON ` + website.DbName + `.* TO '` + website.DbUser + `'@'localhost';"`)
|
||||
_, _ = tools.Exec(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "FLUSH PRIVILEGES;"`)
|
||||
_, _ = shell.Execf(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "CREATE DATABASE IF NOT EXISTS ` + website.DbName + ` DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;"`)
|
||||
_, _ = shell.Execf(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "CREATE USER '` + website.DbUser + `'@'localhost' IDENTIFIED BY '` + website.DbPassword + `';"`)
|
||||
_, _ = shell.Execf(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "GRANT ALL PRIVILEGES ON ` + website.DbName + `.* TO '` + website.DbUser + `'@'localhost';"`)
|
||||
_, _ = shell.Execf(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "FLUSH PRIVILEGES;"`)
|
||||
}
|
||||
if website.Db && website.DbType == "postgresql" {
|
||||
_, _ = tools.Exec(`echo "CREATE DATABASE ` + website.DbName + `;" | su - postgres -c "psql"`)
|
||||
_, _ = tools.Exec(`echo "CREATE USER ` + website.DbUser + ` WITH PASSWORD '` + website.DbPassword + `';" | su - postgres -c "psql"`)
|
||||
_, _ = tools.Exec(`echo "ALTER DATABASE ` + website.DbName + ` OWNER TO ` + website.DbUser + `;" | su - postgres -c "psql"`)
|
||||
_, _ = tools.Exec(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + website.DbName + ` TO ` + website.DbUser + `;" | su - postgres -c "psql"`)
|
||||
_, _ = shell.Execf(`echo "CREATE DATABASE ` + website.DbName + `;" | su - postgres -c "psql"`)
|
||||
_, _ = shell.Execf(`echo "CREATE USER ` + website.DbUser + ` WITH PASSWORD '` + website.DbPassword + `';" | su - postgres -c "psql"`)
|
||||
_, _ = shell.Execf(`echo "ALTER DATABASE ` + website.DbName + ` OWNER TO ` + website.DbUser + `;" | su - postgres -c "psql"`)
|
||||
_, _ = shell.Execf(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + website.DbName + ` TO ` + website.DbUser + `;" | su - postgres -c "psql"`)
|
||||
userConfig := "host " + website.DbName + " " + website.DbUser + " 127.0.0.1/32 scram-sha-256"
|
||||
_, _ = tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`)
|
||||
_ = tools.ServiceReload("postgresql")
|
||||
_, _ = shell.Execf(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`)
|
||||
_ = systemctl.Reload("postgresql")
|
||||
}
|
||||
|
||||
return w, nil
|
||||
@@ -315,15 +320,15 @@ func (r *WebsiteImpl) SaveConfig(config requests.SaveConfig) error {
|
||||
}
|
||||
|
||||
// 原文
|
||||
raw, err := tools.Read("/www/server/vhost/" + website.Name + ".conf")
|
||||
raw, err := io.Read("/www/server/vhost/" + website.Name + ".conf")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.TrimSpace(raw) != strings.TrimSpace(config.Raw) {
|
||||
if err = tools.Write("/www/server/vhost/"+website.Name+".conf", config.Raw, 0644); err != nil {
|
||||
if err = io.Write("/www/server/vhost/"+website.Name+".conf", config.Raw, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = tools.ServiceReload("openresty"); err != nil {
|
||||
if err = systemctl.Reload("openresty"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -332,7 +337,7 @@ func (r *WebsiteImpl) SaveConfig(config requests.SaveConfig) error {
|
||||
|
||||
// 目录
|
||||
path := config.Path
|
||||
if !tools.Exists(path) {
|
||||
if !io.Exists(path) {
|
||||
return errors.New("网站目录不存在")
|
||||
}
|
||||
website.Path = path
|
||||
@@ -347,7 +352,7 @@ func (r *WebsiteImpl) SaveConfig(config requests.SaveConfig) error {
|
||||
domain += " " + v
|
||||
}
|
||||
domain += ";"
|
||||
domainConfigOld := tools.Cut(raw, "# server_name标记位开始", "# server_name标记位结束")
|
||||
domainConfigOld := str.Cut(raw, "# server_name标记位开始", "# server_name标记位结束")
|
||||
if len(strings.TrimSpace(domainConfigOld)) == 0 {
|
||||
return errors.New("配置文件中缺少server_name标记位")
|
||||
}
|
||||
@@ -377,14 +382,14 @@ func (r *WebsiteImpl) SaveConfig(config requests.SaveConfig) error {
|
||||
port.WriteString(" listen [::]:" + vStr + ";")
|
||||
}
|
||||
}
|
||||
portConfigOld := tools.Cut(raw, "# port标记位开始", "# port标记位结束")
|
||||
portConfigOld := str.Cut(raw, "# port标记位开始", "# port标记位结束")
|
||||
if len(strings.TrimSpace(portConfigOld)) == 0 {
|
||||
return errors.New("配置文件中缺少port标记位")
|
||||
}
|
||||
raw = strings.Replace(raw, portConfigOld, "\n"+port.String()+"\n ", -1)
|
||||
|
||||
// 运行目录
|
||||
root := tools.Cut(raw, "# root标记位开始", "# root标记位结束")
|
||||
root := str.Cut(raw, "# root标记位开始", "# root标记位结束")
|
||||
if len(strings.TrimSpace(root)) == 0 {
|
||||
return errors.New("配置文件中缺少root标记位")
|
||||
}
|
||||
@@ -396,7 +401,7 @@ func (r *WebsiteImpl) SaveConfig(config requests.SaveConfig) error {
|
||||
raw = strings.Replace(raw, root, rootNew, -1)
|
||||
|
||||
// 默认文件
|
||||
index := tools.Cut(raw, "# index标记位开始", "# index标记位结束")
|
||||
index := str.Cut(raw, "# index标记位开始", "# index标记位结束")
|
||||
if len(strings.TrimSpace(index)) == 0 {
|
||||
return errors.New("配置文件中缺少index标记位")
|
||||
}
|
||||
@@ -413,12 +418,12 @@ func (r *WebsiteImpl) SaveConfig(config requests.SaveConfig) error {
|
||||
root += "/"
|
||||
}
|
||||
if config.OpenBasedir {
|
||||
if err := tools.Write(root+".user.ini", "open_basedir="+path+":/tmp/", 0644); err != nil {
|
||||
if err := io.Write(root+".user.ini", "open_basedir="+path+":/tmp/", 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if tools.Exists(root + ".user.ini") {
|
||||
if err := tools.Remove(root + ".user.ini"); err != nil {
|
||||
if io.Exists(root + ".user.ini") {
|
||||
if err := io.Remove(root + ".user.ini"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -440,7 +445,7 @@ func (r *WebsiteImpl) SaveConfig(config requests.SaveConfig) error {
|
||||
waf_cc_deny ` + wafCcDeny + `;
|
||||
waf_cache ` + wafCache + `;
|
||||
`
|
||||
wafConfigOld := tools.Cut(raw, "# waf标记位开始", "# waf标记位结束")
|
||||
wafConfigOld := str.Cut(raw, "# waf标记位开始", "# waf标记位结束")
|
||||
if len(strings.TrimSpace(wafConfigOld)) != 0 {
|
||||
raw = strings.Replace(raw, wafConfigOld, "", -1)
|
||||
}
|
||||
@@ -449,10 +454,18 @@ func (r *WebsiteImpl) SaveConfig(config requests.SaveConfig) error {
|
||||
// SSL
|
||||
ssl := config.Ssl
|
||||
website.Ssl = ssl
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+website.Name+".pem", config.SslCertificate, 0644); err != nil {
|
||||
if ssl {
|
||||
if _, err = cert.ParseCert(config.SslCertificate); err != nil {
|
||||
return errors.New("TLS证书格式错误")
|
||||
}
|
||||
if _, err = cert.ParseKey(config.SslCertificateKey); err != nil {
|
||||
return errors.New("TLS私钥格式错误")
|
||||
}
|
||||
}
|
||||
if err = io.Write("/www/server/vhost/ssl/"+website.Name+".pem", config.SslCertificate, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+website.Name+".key", config.SslCertificateKey, 0644); err != nil {
|
||||
if err = io.Write("/www/server/vhost/ssl/"+website.Name+".key", config.SslCertificateKey, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if ssl {
|
||||
@@ -481,13 +494,13 @@ func (r *WebsiteImpl) SaveConfig(config requests.SaveConfig) error {
|
||||
# hsts标记位结束
|
||||
`
|
||||
}
|
||||
sslConfigOld := tools.Cut(raw, "# ssl标记位开始", "# ssl标记位结束")
|
||||
sslConfigOld := str.Cut(raw, "# ssl标记位开始", "# ssl标记位结束")
|
||||
if len(strings.TrimSpace(sslConfigOld)) != 0 {
|
||||
raw = strings.Replace(raw, sslConfigOld, "", -1)
|
||||
}
|
||||
raw = strings.Replace(raw, "# ssl标记位开始", sslConfig, -1)
|
||||
} else {
|
||||
sslConfigOld := tools.Cut(raw, "# ssl标记位开始", "# ssl标记位结束")
|
||||
sslConfigOld := str.Cut(raw, "# ssl标记位开始", "# ssl标记位结束")
|
||||
if len(strings.TrimSpace(sslConfigOld)) != 0 {
|
||||
raw = strings.Replace(raw, sslConfigOld, "\n ", -1)
|
||||
}
|
||||
@@ -495,7 +508,7 @@ func (r *WebsiteImpl) SaveConfig(config requests.SaveConfig) error {
|
||||
|
||||
if website.Php != config.Php {
|
||||
website.Php = config.Php
|
||||
phpConfigOld := tools.Cut(raw, "# php标记位开始", "# php标记位结束")
|
||||
phpConfigOld := str.Cut(raw, "# php标记位开始", "# php标记位结束")
|
||||
phpConfig := `
|
||||
include enable-php-` + strconv.Itoa(website.Php) + `.conf;
|
||||
`
|
||||
@@ -508,14 +521,14 @@ func (r *WebsiteImpl) SaveConfig(config requests.SaveConfig) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tools.Write("/www/server/vhost/"+website.Name+".conf", raw, 0644); err != nil {
|
||||
if err := io.Write("/www/server/vhost/"+website.Name+".conf", raw, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tools.Write("/www/server/vhost/rewrite/"+website.Name+".conf", config.Rewrite, 0644); err != nil {
|
||||
if err := io.Write("/www/server/vhost/rewrite/"+website.Name+".conf", config.Rewrite, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tools.ServiceReload("openresty")
|
||||
return systemctl.Reload("openresty")
|
||||
}
|
||||
|
||||
// Delete 删除网站
|
||||
@@ -533,23 +546,14 @@ func (r *WebsiteImpl) Delete(id uint) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tools.Remove("/www/server/vhost/" + website.Name + ".conf"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tools.Remove("/www/server/vhost/rewrite/" + website.Name + ".conf"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tools.Remove("/www/server/vhost/ssl/" + website.Name + ".pem"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tools.Remove("/www/server/vhost/ssl/" + website.Name + ".key"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tools.Remove(website.Path); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = io.Remove("/www/server/vhost/" + website.Name + ".conf")
|
||||
_ = io.Remove("/www/server/vhost/rewrite/" + website.Name + ".conf")
|
||||
_ = io.Remove("/www/server/vhost/acme/" + website.Name + ".conf")
|
||||
_ = io.Remove("/www/server/vhost/ssl/" + website.Name + ".pem")
|
||||
_ = io.Remove("/www/server/vhost/ssl/" + website.Name + ".key")
|
||||
_ = io.Remove(website.Path)
|
||||
|
||||
return tools.ServiceReload("openresty")
|
||||
return systemctl.Reload("openresty")
|
||||
}
|
||||
|
||||
// GetConfig 获取网站配置
|
||||
@@ -559,7 +563,7 @@ func (r *WebsiteImpl) GetConfig(id uint) (types.WebsiteSetting, error) {
|
||||
return types.WebsiteSetting{}, err
|
||||
}
|
||||
|
||||
config, err := tools.Read("/www/server/vhost/" + website.Name + ".conf")
|
||||
config, err := io.Read("/www/server/vhost/" + website.Name + ".conf")
|
||||
if err != nil {
|
||||
return types.WebsiteSetting{}, err
|
||||
}
|
||||
@@ -571,7 +575,7 @@ func (r *WebsiteImpl) GetConfig(id uint) (types.WebsiteSetting, error) {
|
||||
setting.Php = strconv.Itoa(website.Php)
|
||||
setting.Raw = config
|
||||
|
||||
ports := tools.Cut(config, "# port标记位开始", "# port标记位结束")
|
||||
ports := str.Cut(config, "# port标记位开始", "# port标记位结束")
|
||||
matches := regexp.MustCompile(`listen\s+(.*);`).FindAllStringSubmatch(ports, -1)
|
||||
for _, match := range matches {
|
||||
if len(match) < 2 {
|
||||
@@ -590,24 +594,24 @@ func (r *WebsiteImpl) GetConfig(id uint) (types.WebsiteSetting, error) {
|
||||
setting.Ports = append(setting.Ports, cast.ToUint(ports[0]))
|
||||
}
|
||||
}
|
||||
serverName := tools.Cut(config, "# server_name标记位开始", "# server_name标记位结束")
|
||||
serverName := str.Cut(config, "# server_name标记位开始", "# server_name标记位结束")
|
||||
match := regexp.MustCompile(`server_name\s+(.*);`).FindStringSubmatch(serverName)
|
||||
if len(match) > 1 {
|
||||
setting.Domains = strings.Split(match[1], " ")
|
||||
}
|
||||
root := tools.Cut(config, "# root标记位开始", "# root标记位结束")
|
||||
root := str.Cut(config, "# root标记位开始", "# root标记位结束")
|
||||
match = regexp.MustCompile(`root\s+(.*);`).FindStringSubmatch(root)
|
||||
if len(match) > 1 {
|
||||
setting.Root = match[1]
|
||||
}
|
||||
index := tools.Cut(config, "# index标记位开始", "# index标记位结束")
|
||||
index := str.Cut(config, "# index标记位开始", "# index标记位结束")
|
||||
match = regexp.MustCompile(`index\s+(.*);`).FindStringSubmatch(index)
|
||||
if len(match) > 1 {
|
||||
setting.Index = match[1]
|
||||
}
|
||||
|
||||
if tools.Exists(setting.Root + "/.user.ini") {
|
||||
userIni, _ := tools.Read(setting.Root + "/.user.ini")
|
||||
if io.Exists(setting.Root + "/.user.ini") {
|
||||
userIni, _ := io.Read(setting.Root + "/.user.ini")
|
||||
if strings.Contains(userIni, "open_basedir") {
|
||||
setting.OpenBasedir = true
|
||||
} else {
|
||||
@@ -617,32 +621,27 @@ func (r *WebsiteImpl) GetConfig(id uint) (types.WebsiteSetting, error) {
|
||||
setting.OpenBasedir = false
|
||||
}
|
||||
|
||||
cert, _ := tools.Read("/www/server/vhost/ssl/" + website.Name + ".pem")
|
||||
setting.SslCertificate = cert
|
||||
key, _ := tools.Read("/www/server/vhost/ssl/" + website.Name + ".key")
|
||||
crt, _ := io.Read("/www/server/vhost/ssl/" + website.Name + ".pem")
|
||||
setting.SslCertificate = crt
|
||||
key, _ := io.Read("/www/server/vhost/ssl/" + website.Name + ".key")
|
||||
setting.SslCertificateKey = key
|
||||
if setting.Ssl {
|
||||
ssl := tools.Cut(config, "# ssl标记位开始", "# ssl标记位结束")
|
||||
ssl := str.Cut(config, "# ssl标记位开始", "# ssl标记位结束")
|
||||
setting.HttpRedirect = strings.Contains(ssl, "# http重定向标记位")
|
||||
setting.Hsts = strings.Contains(ssl, "# hsts标记位")
|
||||
|
||||
block, _ := pem.Decode([]byte(cert))
|
||||
if block != nil {
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err == nil {
|
||||
setting.SslNotBefore = cert.NotBefore.Format("2006-01-02 15:04:05")
|
||||
setting.SslNotAfter = cert.NotAfter.Format("2006-01-02 15:04:05")
|
||||
setting.SslIssuer = cert.Issuer.CommonName
|
||||
setting.SslOCSPServer = cert.OCSPServer
|
||||
setting.SSlDNSNames = cert.DNSNames
|
||||
}
|
||||
if decode, err := cert.ParseCert(crt); err == nil {
|
||||
setting.SslNotBefore = decode.NotBefore.Format("2006-01-02 15:04:05")
|
||||
setting.SslNotAfter = decode.NotAfter.Format("2006-01-02 15:04:05")
|
||||
setting.SslIssuer = decode.Issuer.CommonName
|
||||
setting.SslOCSPServer = decode.OCSPServer
|
||||
setting.SSlDNSNames = decode.DNSNames
|
||||
}
|
||||
} else {
|
||||
setting.HttpRedirect = false
|
||||
setting.Hsts = false
|
||||
}
|
||||
|
||||
waf := tools.Cut(config, "# waf标记位开始", "# waf标记位结束")
|
||||
waf := str.Cut(config, "# waf标记位开始", "# waf标记位结束")
|
||||
setting.Waf = strings.Contains(waf, "waf on;")
|
||||
match = regexp.MustCompile(`waf_mode\s+(.+);`).FindStringSubmatch(waf)
|
||||
if len(match) > 1 {
|
||||
@@ -657,9 +656,9 @@ func (r *WebsiteImpl) GetConfig(id uint) (types.WebsiteSetting, error) {
|
||||
setting.WafCache = match[1]
|
||||
}
|
||||
|
||||
rewrite, _ := tools.Read("/www/server/vhost/rewrite/" + website.Name + ".conf")
|
||||
rewrite, _ := io.Read("/www/server/vhost/rewrite/" + website.Name + ".conf")
|
||||
setting.Rewrite = rewrite
|
||||
log, _ := tools.Exec(`tail -n 100 '/www/wwwlogs/` + website.Name + `.log'`)
|
||||
log, _ := shell.Execf(`tail -n 100 '/www/wwwlogs/%s.log'`, website.Name)
|
||||
setting.Log = log
|
||||
|
||||
return setting, err
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ package internal
|
||||
import (
|
||||
requests "github.com/TheTNB/panel/app/http/requests/website"
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/types"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type Website interface {
|
||||
|
||||
+3
-64
@@ -4,20 +4,17 @@ import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/mholt/acmez/v2"
|
||||
"github.com/mholt/acmez/v2/acme"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/cert"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -77,7 +74,7 @@ func NewPrivateKeyAccount(email string, privateKey string, CA string, eab *EAB)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key, err := parsePrivateKey([]byte(privateKey))
|
||||
key, err := cert.ParseKey(privateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -102,36 +99,6 @@ func NewPrivateKeyAccount(email string, privateKey string, CA string, eab *EAB)
|
||||
return &Client{Account: account, zClient: client}, nil
|
||||
}
|
||||
|
||||
func parsePrivateKey(key []byte) (crypto.Signer, error) {
|
||||
keyBlockDER, _ := pem.Decode(key)
|
||||
if keyBlockDER == nil {
|
||||
return nil, errors.New("invalid PEM block")
|
||||
}
|
||||
|
||||
if keyBlockDER.Type != "PRIVATE KEY" && !strings.HasSuffix(keyBlockDER.Type, " PRIVATE KEY") {
|
||||
return nil, fmt.Errorf("unknown PEM header %q", keyBlockDER.Type)
|
||||
}
|
||||
|
||||
if parse, err := x509.ParsePKCS1PrivateKey(keyBlockDER.Bytes); err == nil {
|
||||
return parse, nil
|
||||
}
|
||||
|
||||
if parse, err := x509.ParsePKCS8PrivateKey(keyBlockDER.Bytes); err == nil {
|
||||
switch parse.(type) {
|
||||
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
|
||||
return parse.(crypto.Signer), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("found unknown private key type in PKCS#8 wrapping: %T", key)
|
||||
}
|
||||
}
|
||||
|
||||
if parse, err := x509.ParseECPrivateKey(keyBlockDER.Bytes); err == nil {
|
||||
return parse, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("解析私钥失败")
|
||||
}
|
||||
|
||||
func generatePrivateKey(keyType KeyType) (crypto.Signer, error) {
|
||||
switch keyType {
|
||||
case KeyEC256:
|
||||
@@ -149,34 +116,6 @@ func generatePrivateKey(keyType KeyType) (crypto.Signer, error) {
|
||||
return nil, errors.New("未知的密钥类型")
|
||||
}
|
||||
|
||||
func EncodePrivateKey(key crypto.Signer) ([]byte, error) {
|
||||
var pemType string
|
||||
var keyBytes []byte
|
||||
switch key := key.(type) {
|
||||
case *ecdsa.PrivateKey:
|
||||
var err error
|
||||
pemType = "EC"
|
||||
keyBytes, err = x509.MarshalECPrivateKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *rsa.PrivateKey:
|
||||
pemType = "RSA"
|
||||
keyBytes = x509.MarshalPKCS1PrivateKey(key)
|
||||
case ed25519.PrivateKey:
|
||||
var err error
|
||||
pemType = "ED25519"
|
||||
keyBytes, err = x509.MarshalPKCS8PrivateKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("未知的密钥类型 %T", key)
|
||||
}
|
||||
pemKey := pem.Block{Type: pemType + " PRIVATE KEY", Bytes: keyBytes}
|
||||
return pem.EncodeToMemory(&pemKey), nil
|
||||
}
|
||||
|
||||
func getClient(CA string) (acmez.Client, error) {
|
||||
logger, err := zap.NewProduction()
|
||||
if err != nil {
|
||||
|
||||
+7
-2
@@ -7,6 +7,8 @@ import (
|
||||
"github.com/libdns/libdns"
|
||||
"github.com/mholt/acmez/v2"
|
||||
"github.com/mholt/acmez/v2/acme"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/cert"
|
||||
)
|
||||
|
||||
type Certificate struct {
|
||||
@@ -47,9 +49,12 @@ func (c *Client) UseManualDns(total int, check ...bool) {
|
||||
}
|
||||
|
||||
// UseHTTP 使用 HTTP 验证
|
||||
func (c *Client) UseHTTP(path string) {
|
||||
// conf openresty 配置文件路径
|
||||
// path 验证文件存放路径
|
||||
func (c *Client) UseHTTP(conf, path string) {
|
||||
c.zClient.ChallengeSolvers = map[string]acmez.Solver{
|
||||
acme.ChallengeTypeHTTP01: httpSolver{
|
||||
conf: conf,
|
||||
path: path,
|
||||
},
|
||||
}
|
||||
@@ -61,7 +66,7 @@ func (c *Client) ObtainSSL(ctx context.Context, domains []string, keyType KeyTyp
|
||||
if err != nil {
|
||||
return Certificate{}, err
|
||||
}
|
||||
pemPrivateKey, err := EncodePrivateKey(certPrivateKey)
|
||||
pemPrivateKey, err := cert.EncodeKey(certPrivateKey)
|
||||
if err != nil {
|
||||
return Certificate{}, err
|
||||
}
|
||||
|
||||
+34
-13
@@ -13,9 +13,12 @@ import (
|
||||
"github.com/libdns/libdns"
|
||||
"github.com/mholt/acmez/v2/acme"
|
||||
"golang.org/x/net/publicsuffix"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type httpSolver struct {
|
||||
conf string
|
||||
path string
|
||||
}
|
||||
|
||||
@@ -26,12 +29,24 @@ func (s httpSolver) Present(_ context.Context, challenge acme.Challenge) error {
|
||||
}
|
||||
|
||||
challengeFilePath := filepath.Join(s.path, challenge.HTTP01ResourcePath())
|
||||
if err = os.MkdirAll(filepath.Dir(challengeFilePath), 0o755); err != nil {
|
||||
return fmt.Errorf("无法在网站目录创建 HTTP 挑战所需的目录: %w", err)
|
||||
if err = os.MkdirAll(filepath.Dir(challengeFilePath), 0755); err != nil {
|
||||
return fmt.Errorf("无法在网站目录创建HTTP挑战所需的目录: %w", err)
|
||||
}
|
||||
|
||||
if err = os.WriteFile(challengeFilePath, []byte(challenge.KeyAuthorization), 0o644); err != nil {
|
||||
return fmt.Errorf("无法在网站目录创建 HTTP 挑战所需的文件: %w", err)
|
||||
if err = os.WriteFile(challengeFilePath, []byte(challenge.KeyAuthorization), 0644); err != nil {
|
||||
return fmt.Errorf("无法在网站目录创建HTTP挑战所需的文件: %w", err)
|
||||
}
|
||||
|
||||
conf := fmt.Sprintf(`location = /.well-known/acme-challenge/%s {
|
||||
default_type text/plain;
|
||||
return 200 %q;
|
||||
}
|
||||
`, challenge.Token, challenge.KeyAuthorization)
|
||||
if err = os.WriteFile(s.conf, []byte(conf), 0644); err != nil {
|
||||
return fmt.Errorf("无法写入OpenResty配置文件: %w", err)
|
||||
}
|
||||
if err = systemctl.Reload("openresty"); err != nil {
|
||||
return fmt.Errorf("无法重载OpenResty: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -44,7 +59,13 @@ func (s httpSolver) CleanUp(_ context.Context, challenge acme.Challenge) error {
|
||||
}
|
||||
|
||||
if err := os.Remove(filepath.Join(s.path, challenge.HTTP01ResourcePath())); err != nil {
|
||||
return fmt.Errorf("无法删除 HTTP 挑战文件: %w", err)
|
||||
return fmt.Errorf("无法删除HTTP挑战文件: %w", err)
|
||||
}
|
||||
if err := os.WriteFile(s.conf, []byte{}, 0644); err != nil {
|
||||
return fmt.Errorf("无法清空OpenResty配置文件: %w", err)
|
||||
}
|
||||
if err := systemctl.Reload("openresty"); err != nil {
|
||||
return fmt.Errorf("无法重载OpenResty: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -61,11 +82,11 @@ func (s dnsSolver) Present(ctx context.Context, challenge acme.Challenge) error
|
||||
keyAuth := challenge.DNS01KeyAuthorization()
|
||||
provider, err := s.getDNSProvider()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取 DNS 提供商失败: %w", err)
|
||||
return fmt.Errorf("获取DNS提供商失败: %w", err)
|
||||
}
|
||||
zone, err := publicsuffix.EffectiveTLDPlusOne(dnsName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取域名 %q 的顶级域失败: %w", dnsName, err)
|
||||
return fmt.Errorf("获取域名%q的顶级域失败: %w", dnsName, err)
|
||||
}
|
||||
|
||||
rec := libdns.Record{
|
||||
@@ -76,10 +97,10 @@ func (s dnsSolver) Present(ctx context.Context, challenge acme.Challenge) error
|
||||
|
||||
results, err := provider.AppendRecords(ctx, zone+".", []libdns.Record{rec})
|
||||
if err != nil {
|
||||
return fmt.Errorf("域名 %q 添加临时记录 %q 失败: %w", zone, dnsName, err)
|
||||
return fmt.Errorf("域名%q添加临时记录%q失败: %w", zone, dnsName, err)
|
||||
}
|
||||
if len(results) != 1 {
|
||||
return fmt.Errorf("预期添加 1 条记录,但实际添加了 %d 条记录", len(results))
|
||||
return fmt.Errorf("预期添加1条记录,但实际添加了%d条记录", len(results))
|
||||
}
|
||||
|
||||
s.records = &results
|
||||
@@ -90,11 +111,11 @@ func (s dnsSolver) CleanUp(ctx context.Context, challenge acme.Challenge) error
|
||||
dnsName := challenge.DNS01TXTRecordName()
|
||||
provider, err := s.getDNSProvider()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取 DNS 提供商失败: %w", err)
|
||||
return fmt.Errorf("获取DNS提供商失败: %w", err)
|
||||
}
|
||||
zone, err := publicsuffix.EffectiveTLDPlusOne(dnsName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取域名 %q 的顶级域失败: %w", dnsName, err)
|
||||
return fmt.Errorf("获取域名%q的顶级域失败: %w", dnsName, err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
|
||||
@@ -102,7 +123,7 @@ func (s dnsSolver) CleanUp(ctx context.Context, challenge acme.Challenge) error
|
||||
|
||||
_, err = provider.DeleteRecords(ctx, zone+".", *s.records)
|
||||
if err != nil {
|
||||
return fmt.Errorf("域名 %q 删除临时记录 %q 失败: %w", zone, dnsName, err)
|
||||
return fmt.Errorf("域名%q删除临时记录%q失败: %w", zone, dnsName, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -126,7 +147,7 @@ func (s dnsSolver) getDNSProvider() (DNSProvider, error) {
|
||||
APIToken: s.param.APIkey,
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("未知的 DNS 提供商 %q", s.dns)
|
||||
return nil, fmt.Errorf("未知的DNS提供商 %q", s.dns)
|
||||
}
|
||||
|
||||
return dns, nil
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package cert
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ParseCert(crt string) (x509.Certificate, error) {
|
||||
certBlock, _ := pem.Decode([]byte(crt))
|
||||
if certBlock == nil {
|
||||
return x509.Certificate{}, errors.New("invalid PEM block")
|
||||
}
|
||||
cert, err := x509.ParseCertificate(certBlock.Bytes)
|
||||
if err != nil {
|
||||
return x509.Certificate{}, err
|
||||
}
|
||||
|
||||
return *cert, nil
|
||||
}
|
||||
|
||||
func ParseKey(key string) (crypto.Signer, error) {
|
||||
keyBlockDER, _ := pem.Decode([]byte(key))
|
||||
if keyBlockDER == nil {
|
||||
return nil, errors.New("invalid PEM block")
|
||||
}
|
||||
|
||||
if keyBlockDER.Type != "PRIVATE KEY" && !strings.HasSuffix(keyBlockDER.Type, " PRIVATE KEY") {
|
||||
return nil, fmt.Errorf("unknown PEM header %q", keyBlockDER.Type)
|
||||
}
|
||||
|
||||
if parse, err := x509.ParsePKCS1PrivateKey(keyBlockDER.Bytes); err == nil {
|
||||
return parse, nil
|
||||
}
|
||||
|
||||
if parse, err := x509.ParsePKCS8PrivateKey(keyBlockDER.Bytes); err == nil {
|
||||
switch parse.(type) {
|
||||
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
|
||||
return parse.(crypto.Signer), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("found unknown private key type in PKCS#8 wrapping: %T", key)
|
||||
}
|
||||
}
|
||||
|
||||
if parse, err := x509.ParseECPrivateKey(keyBlockDER.Bytes); err == nil {
|
||||
return parse, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("解析私钥失败")
|
||||
}
|
||||
|
||||
func EncodeCert(cert x509.Certificate) ([]byte, error) {
|
||||
pemCert := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
|
||||
return pem.EncodeToMemory(&pemCert), nil
|
||||
}
|
||||
|
||||
func EncodeKey(key crypto.Signer) ([]byte, error) {
|
||||
var pemType string
|
||||
var keyBytes []byte
|
||||
switch key := key.(type) {
|
||||
case *ecdsa.PrivateKey:
|
||||
var err error
|
||||
pemType = "EC"
|
||||
keyBytes, err = x509.MarshalECPrivateKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *rsa.PrivateKey:
|
||||
pemType = "RSA"
|
||||
keyBytes = x509.MarshalPKCS1PrivateKey(key)
|
||||
case ed25519.PrivateKey:
|
||||
var err error
|
||||
pemType = "ED25519"
|
||||
keyBytes, err = x509.MarshalPKCS8PrivateKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("未知的密钥类型 %T", key)
|
||||
}
|
||||
pemKey := pem.Block{Type: pemType + " PRIVATE KEY", Bytes: keyBytes}
|
||||
return pem.EncodeToMemory(&pemKey), nil
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package io
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/mholt/archiver/v3"
|
||||
)
|
||||
|
||||
// Write 写入文件
|
||||
func Write(path string, data string, permission os.FileMode) error {
|
||||
if err := os.MkdirAll(filepath.Dir(path), permission); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err := os.WriteFile(path, []byte(data), permission)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteAppend 追加写入文件
|
||||
func WriteAppend(path string, data string) error {
|
||||
file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.WriteString(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read 读取文件
|
||||
func Read(path string) (string, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
return string(data), err
|
||||
}
|
||||
|
||||
// FileInfo 获取文件大小
|
||||
func FileInfo(path string) (os.FileInfo, error) {
|
||||
return os.Stat(path)
|
||||
}
|
||||
|
||||
// UnArchive 智能解压文件
|
||||
func UnArchive(file string, dst string) error {
|
||||
return archiver.Unarchive(file, dst)
|
||||
}
|
||||
|
||||
// Archive 智能压缩文件
|
||||
func Archive(src []string, dst string) error {
|
||||
return archiver.Archive(src, dst)
|
||||
}
|
||||
|
||||
// TempFile 创建临时文件
|
||||
func TempFile(prefix string) (*os.File, error) {
|
||||
return os.CreateTemp("", prefix)
|
||||
}
|
||||
|
||||
// IsSymlink 判读是否为软链接
|
||||
func IsSymlink(mode os.FileMode) bool {
|
||||
return mode&os.ModeSymlink != 0
|
||||
}
|
||||
|
||||
// IsHidden 判断是否为隐藏文件
|
||||
func IsHidden(path string) bool {
|
||||
_, file := filepath.Split(path)
|
||||
return strings.HasPrefix(file, ".")
|
||||
}
|
||||
|
||||
// GetSymlink 获取软链接目标
|
||||
func GetSymlink(path string) string {
|
||||
linkPath, err := os.Readlink(path)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return linkPath
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
package io
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Remove 删除文件/目录
|
||||
func Remove(path string) error {
|
||||
return os.RemoveAll(path)
|
||||
}
|
||||
|
||||
// Mkdir 创建目录
|
||||
func Mkdir(path string, permission os.FileMode) error {
|
||||
return os.MkdirAll(path, permission)
|
||||
}
|
||||
|
||||
// Chmod 修改文件/目录权限
|
||||
func Chmod(path string, permission os.FileMode) error {
|
||||
cmd := exec.Command("chmod", "-R", fmt.Sprintf("%o", permission), path)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// Chown 修改文件或目录所有者
|
||||
func Chown(path, user, group string) error {
|
||||
cmd := exec.Command("chown", "-R", user+":"+group, path)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// Exists 判断路径是否存在
|
||||
func Exists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Empty 判断路径是否为空
|
||||
func Empty(path string) bool {
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return len(files) == 0
|
||||
}
|
||||
|
||||
func Mv(src, dst string) error {
|
||||
err := os.Rename(src, dst)
|
||||
if err != nil {
|
||||
// 如果在不同的文件系统中移动文件,os.Rename 可能会失败
|
||||
err = Cp(src, dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.RemoveAll(src)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Cp 复制文件或目录
|
||||
func Cp(src, dst string) error {
|
||||
srcInfo, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if srcInfo.IsDir() {
|
||||
return copyDir(src, dst)
|
||||
}
|
||||
return copyFile(src, dst)
|
||||
}
|
||||
|
||||
func copyFile(src, dst string) error {
|
||||
srcFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
dstFile, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dstFile.Close()
|
||||
|
||||
_, err = io.Copy(dstFile, srcFile)
|
||||
return err
|
||||
}
|
||||
|
||||
func copyDir(src, dst string) error {
|
||||
srcInfo, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.MkdirAll(dst, srcInfo.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
srcPath := filepath.Join(src, entry.Name())
|
||||
dstPath := filepath.Join(dst, entry.Name())
|
||||
|
||||
if entry.IsDir() {
|
||||
err = copyDir(srcPath, dstPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = copyFile(srcPath, dstPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Size 获取路径大小
|
||||
func Size(path string) (int64, error) {
|
||||
var size int64
|
||||
|
||||
err := filepath.Walk(path, func(filePath string, info os.FileInfo, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return walkErr
|
||||
}
|
||||
size += info.Size()
|
||||
return nil
|
||||
})
|
||||
|
||||
return size, err
|
||||
}
|
||||
|
||||
// TempDir 创建临时目录
|
||||
func TempDir(prefix string) (string, error) {
|
||||
return os.MkdirTemp("", prefix)
|
||||
}
|
||||
|
||||
// ReadDir 读取目录
|
||||
func ReadDir(path string) ([]os.DirEntry, error) {
|
||||
return os.ReadDir(path)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package tools
|
||||
package os
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -1,9 +1,8 @@
|
||||
package tools
|
||||
package os
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/goravel/framework/support/env"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
@@ -16,15 +15,9 @@ func TestOSHelperTestSuite(t *testing.T) {
|
||||
}
|
||||
|
||||
func (s *OSHelperTestSuite) TestIsDebian() {
|
||||
if env.IsWindows() {
|
||||
return
|
||||
}
|
||||
s.True(IsDebian())
|
||||
}
|
||||
|
||||
func (s *OSHelperTestSuite) TestIsRHEL() {
|
||||
if env.IsWindows() {
|
||||
return
|
||||
}
|
||||
s.False(IsRHEL())
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package os
|
||||
|
||||
import (
|
||||
"os/user"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
// GetUser 通过 uid 获取用户名
|
||||
func GetUser(uid uint32) string {
|
||||
usr, err := user.LookupId(cast.ToString(uid))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return usr.Username
|
||||
}
|
||||
|
||||
// GetGroup 通过 gid 获取组名
|
||||
func GetGroup(gid uint32) string {
|
||||
usr, err := user.LookupGroupId(cast.ToString(gid))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return usr.Name
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/goravel/framework/support"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/slice"
|
||||
)
|
||||
|
||||
// Execf 执行 shell 命令
|
||||
func Execf(shell string, args ...any) (string, error) {
|
||||
if !CheckArgs(slice.ToString(args)...) {
|
||||
return "", errors.New("你想干什么?")
|
||||
}
|
||||
|
||||
var cmd *exec.Cmd
|
||||
_ = os.Setenv("LC_ALL", "C")
|
||||
cmd = exec.Command("bash", "-c", fmt.Sprintf(shell, args...))
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", errors.New(strings.TrimSpace(stderr.String()))
|
||||
}
|
||||
|
||||
return strings.TrimSpace(stdout.String()), err
|
||||
}
|
||||
|
||||
// ExecfAsync 异步执行 shell 命令
|
||||
func ExecfAsync(shell string, args ...any) error {
|
||||
if !CheckArgs(slice.ToString(args)...) {
|
||||
return errors.New("你想干什么?")
|
||||
}
|
||||
|
||||
var cmd *exec.Cmd
|
||||
_ = os.Setenv("LC_ALL", "C")
|
||||
cmd = exec.Command("bash", "-c", fmt.Sprintf(shell, args...))
|
||||
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
err := cmd.Wait()
|
||||
if err != nil {
|
||||
if support.Env == support.EnvTest {
|
||||
fmt.Println(err.Error())
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExecfWithTimeout 执行 shell 命令并设置超时时间
|
||||
func ExecfWithTimeout(timeout time.Duration, shell string, args ...any) (string, error) {
|
||||
if !CheckArgs(slice.ToString(args)...) {
|
||||
return "", errors.New("你想干什么?")
|
||||
}
|
||||
|
||||
var cmd *exec.Cmd
|
||||
_ = os.Setenv("LC_ALL", "C")
|
||||
cmd = exec.Command("bash", "-c", fmt.Sprintf(shell, args...))
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
done := make(chan error)
|
||||
go func() {
|
||||
done <- cmd.Wait()
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(timeout):
|
||||
_ = cmd.Process.Kill()
|
||||
return "", errors.New("执行超时")
|
||||
case err = <-done:
|
||||
if err != nil {
|
||||
return "", errors.New(strings.TrimSpace(stderr.String()))
|
||||
}
|
||||
}
|
||||
|
||||
return strings.TrimSpace(stdout.String()), err
|
||||
}
|
||||
|
||||
// CheckArgs 检查危险的参数
|
||||
func CheckArgs(args ...string) bool {
|
||||
if len(args) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
dangerous := []string{"&", "|", ";", "$", "'", `"`, "(", ")", "`", "\n", "\r", ">", "<", "{", "}", "[", "]", "\\"}
|
||||
for _, arg := range args {
|
||||
for _, char := range dangerous {
|
||||
if strings.Contains(arg, char) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package slice
|
||||
|
||||
import "github.com/spf13/cast"
|
||||
|
||||
// ToAny 将任意类型切片转换为 []any
|
||||
func ToAny[T any](s []T) []any {
|
||||
result := make([]any, len(s))
|
||||
for i, v := range s {
|
||||
result[i] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ToString 将任意类型切片转换为 []string
|
||||
func ToString[T any](s []T) []string {
|
||||
result := make([]string, len(s))
|
||||
for i, v := range s {
|
||||
result[i] = cast.ToString(v)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ToInt 将任意类型切片转换为 []int
|
||||
func ToInt[T any](s []T) []int {
|
||||
result := make([]int, len(s))
|
||||
for i, v := range s {
|
||||
result[i] = cast.ToInt(v)
|
||||
}
|
||||
return result
|
||||
}
|
||||
+2
-2
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
)
|
||||
|
||||
type AuthMethod int8
|
||||
@@ -68,7 +68,7 @@ func NewSSHClient(conf *ClientConfig) (*ssh.Client, error) {
|
||||
}
|
||||
|
||||
func getKey(keyPath string) (ssh.Signer, error) {
|
||||
key, err := tools.Read(keyPath)
|
||||
key, err := io.Read(keyPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ func (t *Turn) Write(p []byte) (n int, err error) {
|
||||
|
||||
return writer.Write(p)
|
||||
}
|
||||
|
||||
func (t *Turn) Close() error {
|
||||
if t.Session != nil {
|
||||
t.Session.Close()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package tools
|
||||
package str
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
@@ -1,4 +1,4 @@
|
||||
package tools
|
||||
package str
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -0,0 +1,73 @@
|
||||
package systemctl
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
)
|
||||
|
||||
// Status 获取服务状态
|
||||
func Status(name string) (bool, error) {
|
||||
output, err := shell.Execf("systemctl status %s | grep Active | grep -v grep | awk '{print $2}'", name)
|
||||
return output == "active", err
|
||||
}
|
||||
|
||||
// IsEnabled 服务是否启用
|
||||
func IsEnabled(name string) (bool, error) {
|
||||
cmd := exec.Command("systemctl", "is-enabled", name)
|
||||
output, _ := cmd.CombinedOutput()
|
||||
status := strings.TrimSpace(string(output))
|
||||
|
||||
switch status {
|
||||
case "enabled":
|
||||
return true, nil
|
||||
case "disabled":
|
||||
return false, nil
|
||||
case "masked":
|
||||
return false, errors.New("服务已被屏蔽")
|
||||
case "static":
|
||||
return false, errors.New("服务已被静态启用")
|
||||
case "indirect":
|
||||
return false, errors.New("服务已被间接启用")
|
||||
default:
|
||||
return false, errors.New("无法确定服务状态")
|
||||
}
|
||||
}
|
||||
|
||||
// Start 启动服务
|
||||
func Start(name string) error {
|
||||
_, err := shell.Execf("systemctl start %s", name)
|
||||
return err
|
||||
}
|
||||
|
||||
// Stop 停止服务
|
||||
func Stop(name string) error {
|
||||
_, err := shell.Execf("systemctl stop %s", name)
|
||||
return err
|
||||
}
|
||||
|
||||
// Restart 重启服务
|
||||
func Restart(name string) error {
|
||||
_, err := shell.Execf("systemctl restart %s", name)
|
||||
return err
|
||||
}
|
||||
|
||||
// Reload 重载服务
|
||||
func Reload(name string) error {
|
||||
_, err := shell.Execf("systemctl reload %s", name)
|
||||
return err
|
||||
}
|
||||
|
||||
// Enable 启用服务
|
||||
func Enable(name string) error {
|
||||
_, err := shell.Execf("systemctl enable %s", name)
|
||||
return err
|
||||
}
|
||||
|
||||
// Disable 禁用服务
|
||||
func Disable(name string) error {
|
||||
_, err := shell.Execf("systemctl disable %s", name)
|
||||
return err
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ServiceStatus 获取服务状态
|
||||
func ServiceStatus(name string) (bool, error) {
|
||||
output, err := Exec(fmt.Sprintf("systemctl status %s | grep Active | grep -v grep | awk '{print $2}'", name))
|
||||
return output == "active", err
|
||||
}
|
||||
|
||||
// ServiceIsEnabled 服务是否启用
|
||||
func ServiceIsEnabled(name string) (bool, error) {
|
||||
cmd := exec.Command("systemctl", "is-enabled", name)
|
||||
output, _ := cmd.CombinedOutput()
|
||||
status := strings.TrimSpace(string(output))
|
||||
|
||||
switch status {
|
||||
case "enabled":
|
||||
return true, nil
|
||||
case "disabled":
|
||||
return false, nil
|
||||
case "masked":
|
||||
return false, errors.New("服务已被屏蔽")
|
||||
case "static":
|
||||
return false, errors.New("服务已被静态启用")
|
||||
case "indirect":
|
||||
return false, errors.New("服务已被间接启用")
|
||||
default:
|
||||
return false, errors.New("无法确定服务状态")
|
||||
}
|
||||
}
|
||||
|
||||
// ServiceStart 启动服务
|
||||
func ServiceStart(name string) error {
|
||||
_, err := Exec(fmt.Sprintf("systemctl start %s", name))
|
||||
return err
|
||||
}
|
||||
|
||||
// ServiceStop 停止服务
|
||||
func ServiceStop(name string) error {
|
||||
_, err := Exec(fmt.Sprintf("systemctl stop %s", name))
|
||||
return err
|
||||
}
|
||||
|
||||
// ServiceRestart 重启服务
|
||||
func ServiceRestart(name string) error {
|
||||
_, err := Exec(fmt.Sprintf("systemctl restart %s", name))
|
||||
return err
|
||||
}
|
||||
|
||||
// ServiceReload 重载服务
|
||||
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
|
||||
}
|
||||
@@ -1,305 +0,0 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/goravel/framework/support"
|
||||
"github.com/goravel/framework/support/env"
|
||||
"github.com/mholt/archiver/v3"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
// Write 写入文件
|
||||
func Write(path string, data string, permission os.FileMode) error {
|
||||
if err := os.MkdirAll(filepath.Dir(path), permission); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err := os.WriteFile(path, []byte(data), permission)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteAppend 追加写入文件
|
||||
func WriteAppend(path string, data string) error {
|
||||
file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.WriteString(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read 读取文件
|
||||
func Read(path string) (string, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
return string(data), err
|
||||
}
|
||||
|
||||
// Remove 删除文件/目录
|
||||
func Remove(path string) error {
|
||||
return os.RemoveAll(path)
|
||||
}
|
||||
|
||||
// Exec 执行 shell 命令
|
||||
func Exec(shell string) (string, error) {
|
||||
var cmd *exec.Cmd
|
||||
if env.IsLinux() {
|
||||
cmd = exec.Command("bash", "-c", "LC_ALL=C "+shell)
|
||||
} else {
|
||||
cmd = exec.Command("cmd", "/C", "chcp 65001 >nul && "+shell)
|
||||
}
|
||||
|
||||
var stdoutBuf, stderrBuf bytes.Buffer
|
||||
cmd.Stdout = &stdoutBuf
|
||||
cmd.Stderr = &stderrBuf
|
||||
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", errors.New(strings.TrimSpace(stderrBuf.String()))
|
||||
}
|
||||
|
||||
return strings.TrimSpace(stdoutBuf.String()), err
|
||||
}
|
||||
|
||||
// ExecAsync 异步执行 shell 命令
|
||||
func ExecAsync(shell string) error {
|
||||
var cmd *exec.Cmd
|
||||
if env.IsLinux() {
|
||||
cmd = exec.Command("bash", "-c", "LC_ALL=C "+shell)
|
||||
} else {
|
||||
cmd = exec.Command("cmd", "/C", "chcp 65001 >nul && "+shell)
|
||||
}
|
||||
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
err := cmd.Wait()
|
||||
if err != nil {
|
||||
if support.Env == support.EnvTest {
|
||||
fmt.Println(err.Error())
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Mkdir 创建目录
|
||||
func Mkdir(path string, permission os.FileMode) error {
|
||||
return os.MkdirAll(path, permission)
|
||||
}
|
||||
|
||||
// Chmod 修改文件/目录权限
|
||||
func Chmod(path string, permission os.FileMode) error {
|
||||
if env.IsWindows() {
|
||||
return errors.New("chmod is not supported on Windows")
|
||||
}
|
||||
|
||||
cmd := exec.Command("chmod", "-R", fmt.Sprintf("%o", permission), path)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// Chown 修改文件或目录所有者
|
||||
func Chown(path, user, group string) error {
|
||||
if env.IsWindows() {
|
||||
return errors.New("chown is not supported on Windows")
|
||||
}
|
||||
|
||||
cmd := exec.Command("chown", "-R", user+":"+group, path)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// Exists 判断路径是否存在
|
||||
func Exists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Empty 判断路径是否为空
|
||||
func Empty(path string) bool {
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return len(files) == 0
|
||||
}
|
||||
|
||||
func Mv(src, dst string) error {
|
||||
err := os.Rename(src, dst)
|
||||
if err != nil {
|
||||
// 如果在不同的文件系统中移动文件,os.Rename 可能会失败
|
||||
err = Cp(src, dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.RemoveAll(src)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Cp 复制文件或目录
|
||||
func Cp(src, dst string) error {
|
||||
srcInfo, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if srcInfo.IsDir() {
|
||||
return copyDir(src, dst)
|
||||
}
|
||||
return copyFile(src, dst)
|
||||
}
|
||||
|
||||
func copyFile(src, dst string) error {
|
||||
srcFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
dstFile, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dstFile.Close()
|
||||
|
||||
_, err = io.Copy(dstFile, srcFile)
|
||||
return err
|
||||
}
|
||||
|
||||
func copyDir(src, dst string) error {
|
||||
srcInfo, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.MkdirAll(dst, srcInfo.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
srcPath := filepath.Join(src, entry.Name())
|
||||
dstPath := filepath.Join(dst, entry.Name())
|
||||
|
||||
if entry.IsDir() {
|
||||
err = copyDir(srcPath, dstPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = copyFile(srcPath, dstPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Size 获取路径大小
|
||||
func Size(path string) (int64, error) {
|
||||
var size int64
|
||||
|
||||
err := filepath.Walk(path, func(filePath string, info os.FileInfo, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return walkErr
|
||||
}
|
||||
size += info.Size()
|
||||
return nil
|
||||
})
|
||||
|
||||
return size, err
|
||||
}
|
||||
|
||||
// FileInfo 获取文件大小
|
||||
func FileInfo(path string) (os.FileInfo, error) {
|
||||
return os.Stat(path)
|
||||
}
|
||||
|
||||
// UnArchive 智能解压文件
|
||||
func UnArchive(file string, dst string) error {
|
||||
return archiver.Unarchive(file, dst)
|
||||
}
|
||||
|
||||
// Archive 智能压缩文件
|
||||
func Archive(src []string, dst string) error {
|
||||
return archiver.Archive(src, dst)
|
||||
}
|
||||
|
||||
// TempDir 创建临时目录
|
||||
func TempDir(prefix string) (string, error) {
|
||||
return os.MkdirTemp("", prefix)
|
||||
}
|
||||
|
||||
// TempFile 创建临时文件
|
||||
func TempFile(prefix string) (*os.File, error) {
|
||||
return os.CreateTemp("", prefix)
|
||||
}
|
||||
|
||||
// IsSymlink 判读是否为软链接
|
||||
func IsSymlink(mode os.FileMode) bool {
|
||||
return mode&os.ModeSymlink != 0
|
||||
}
|
||||
|
||||
// IsHidden 判断是否为隐藏文件
|
||||
func IsHidden(path string) bool {
|
||||
_, file := filepath.Split(path)
|
||||
return strings.HasPrefix(file, ".")
|
||||
}
|
||||
|
||||
// GetSymlink 获取软链接目标
|
||||
func GetSymlink(path string) string {
|
||||
linkPath, err := os.Readlink(path)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return linkPath
|
||||
}
|
||||
|
||||
// GetUser 通过 uid 获取用户名
|
||||
func GetUser(uid uint32) string {
|
||||
usr, err := user.LookupId(cast.ToString(uid))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return usr.Username
|
||||
}
|
||||
|
||||
// GetGroup 通过 gid 获取组名
|
||||
func GetGroup(gid uint32) string {
|
||||
usr, err := user.LookupGroupId(cast.ToString(gid))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return usr.Name
|
||||
}
|
||||
@@ -1,330 +0,0 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/goravel/framework/support/env"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type SystemHelperTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func TestSystemHelperTestSuite(t *testing.T) {
|
||||
suite.Run(t, &SystemHelperTestSuite{})
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) WriteCreatesFileWithCorrectContent() {
|
||||
filePath, _ := TempFile("testfile")
|
||||
|
||||
err := Write(filePath.Name(), "test data", 0644)
|
||||
s.Nil(err)
|
||||
|
||||
content, _ := Read(filePath.Name())
|
||||
s.Equal("test data", content)
|
||||
s.Nil(filePath.Close())
|
||||
s.Nil(Remove(filePath.Name()))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) WriteCreatesDirectoriesIfNeeded() {
|
||||
filePath, _ := TempFile("testdir/testfile")
|
||||
|
||||
err := Write(filePath.Name(), "test data", 0644)
|
||||
s.Nil(err)
|
||||
|
||||
content, _ := Read(filePath.Name())
|
||||
s.Equal("test data", content)
|
||||
s.Nil(filePath.Close())
|
||||
s.Nil(Remove(filePath.Name()))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) WriteFailsIfDirectoryCannotBeCreated() {
|
||||
filePath := "/nonexistent/testfile"
|
||||
|
||||
err := Write(filePath, "test data", 0644)
|
||||
s.NotNil(err)
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) WriteFailsIfFileCannotBeWritten() {
|
||||
filePath, _ := TempFile("testfile")
|
||||
s.Nil(filePath.Close())
|
||||
s.Nil(Chmod(filePath.Name(), 0400))
|
||||
|
||||
err := Write(filePath.Name(), "test data", 0644)
|
||||
s.NotNil(err)
|
||||
|
||||
s.Nil(Chmod(filePath.Name(), 0644))
|
||||
s.Nil(Remove(filePath.Name()))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) WriteAppendSuccessfullyAppendsDataToFile() {
|
||||
filePath, _ := TempFile("testfile")
|
||||
|
||||
err := Write(filePath.Name(), "initial data", 0644)
|
||||
s.Nil(err)
|
||||
|
||||
err = WriteAppend(filePath.Name(), " appended data")
|
||||
s.Nil(err)
|
||||
|
||||
content, _ := Read(filePath.Name())
|
||||
s.Equal("initial data appended data", content)
|
||||
s.Nil(filePath.Close())
|
||||
s.Nil(Remove(filePath.Name()))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) WriteAppendCreatesFileIfNotExists() {
|
||||
filePath, _ := TempFile("testfile")
|
||||
s.Nil(filePath.Close())
|
||||
s.Nil(Remove(filePath.Name()))
|
||||
|
||||
err := WriteAppend(filePath.Name(), "test data")
|
||||
s.Nil(err)
|
||||
|
||||
content, _ := Read(filePath.Name())
|
||||
s.Equal("test data", content)
|
||||
s.Nil(Remove(filePath.Name()))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) WriteAppendReturnsErrorIfPathIsADirectory() {
|
||||
dirPath, _ := TempDir("testdir")
|
||||
|
||||
err := WriteAppend(dirPath, "test data")
|
||||
s.NotNil(err)
|
||||
|
||||
s.Nil(Remove(dirPath))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) ReadSuccessfullyReadsFileContent() {
|
||||
filePath, _ := TempFile("testfile")
|
||||
|
||||
err := Write(filePath.Name(), "test data", 0644)
|
||||
s.Nil(err)
|
||||
|
||||
content, err := Read(filePath.Name())
|
||||
s.Nil(err)
|
||||
s.Equal("test data", content)
|
||||
|
||||
s.Nil(filePath.Close())
|
||||
s.Nil(Remove(filePath.Name()))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) ReadReturnsErrorForNonExistentFile() {
|
||||
_, err := Read("/nonexistent/testfile")
|
||||
s.NotNil(err)
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) RemoveSuccessfullyRemovesFile() {
|
||||
filePath, _ := TempFile("testfile")
|
||||
|
||||
err := Write(filePath.Name(), "test data", 0644)
|
||||
s.Nil(err)
|
||||
|
||||
err = Remove(filePath.Name())
|
||||
s.Nil(err)
|
||||
|
||||
s.False(Exists(filePath.Name()))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) RemoveReturnsErrorForNonExistentFile() {
|
||||
err := Remove("/nonexistent/testfile")
|
||||
s.NotNil(err)
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) TestExec() {
|
||||
output, err := Exec("echo test")
|
||||
s.Equal("test", output)
|
||||
s.Nil(err)
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) TestExecAsync() {
|
||||
command := "echo test > test.txt"
|
||||
if env.IsWindows() {
|
||||
command = "echo test> test.txt"
|
||||
}
|
||||
|
||||
err := ExecAsync(command)
|
||||
s.Nil(err)
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
content, err := Read("test.txt")
|
||||
s.Nil(err)
|
||||
|
||||
condition := "test\n"
|
||||
if env.IsWindows() {
|
||||
condition = "test\r\n"
|
||||
}
|
||||
s.Equal(condition, content)
|
||||
s.Nil(Remove("test.txt"))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) TestMkdir() {
|
||||
dirPath, _ := TempDir("testdir")
|
||||
|
||||
s.Nil(Mkdir(dirPath, 0755))
|
||||
s.Nil(Remove(dirPath))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) TestChmod() {
|
||||
filePath, _ := TempFile("testfile")
|
||||
|
||||
err := Write(filePath.Name(), "test data", 0644)
|
||||
s.Nil(err)
|
||||
|
||||
s.Nil(filePath.Close())
|
||||
s.Nil(Chmod(filePath.Name(), 0755))
|
||||
s.Nil(Remove(filePath.Name()))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) TestChown() {
|
||||
filePath, _ := TempFile("testfile")
|
||||
|
||||
err := Write(filePath.Name(), "test data", 0644)
|
||||
s.Nil(err)
|
||||
|
||||
currentUser, err := user.Current()
|
||||
s.Nil(err)
|
||||
groups, err := currentUser.GroupIds()
|
||||
s.Nil(err)
|
||||
|
||||
err = Chown(filePath.Name(), currentUser.Username, groups[0])
|
||||
if env.IsWindows() {
|
||||
s.NotNil(err)
|
||||
} else {
|
||||
s.Nil(err)
|
||||
}
|
||||
s.Nil(filePath.Close())
|
||||
s.Nil(Remove(filePath.Name()))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) TestExists() {
|
||||
filePath, _ := TempFile("testfile")
|
||||
|
||||
s.True(Exists(filePath.Name()))
|
||||
s.False(Exists("123"))
|
||||
s.Nil(filePath.Close())
|
||||
s.Nil(Remove(filePath.Name()))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) TestEmpty() {
|
||||
filePath, _ := TempFile("testfile")
|
||||
|
||||
s.True(Empty(filePath.Name()))
|
||||
if env.IsWindows() {
|
||||
s.True(Empty("C:\\Windows\\System32\\drivers\\etc\\hosts"))
|
||||
} else {
|
||||
s.True(Empty("/etc/hosts"))
|
||||
}
|
||||
s.Nil(filePath.Close())
|
||||
s.Nil(Remove(filePath.Name()))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) TestMv() {
|
||||
filePath, _ := TempFile("testfile")
|
||||
|
||||
err := Write(filePath.Name(), "test data", 0644)
|
||||
s.Nil(err)
|
||||
|
||||
newFilePath, _ := TempFile("testfile2")
|
||||
|
||||
s.Nil(newFilePath.Close())
|
||||
s.Nil(filePath.Close())
|
||||
|
||||
s.Nil(Mv(filePath.Name(), newFilePath.Name()))
|
||||
s.False(Exists(filePath.Name()))
|
||||
s.Nil(Remove(newFilePath.Name()))
|
||||
s.Nil(Remove(filePath.Name()))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) TestCp() {
|
||||
tempDir, _ := TempDir("testdir")
|
||||
|
||||
err := Write(filepath.Join(tempDir, "testfile"), "test data", 0644)
|
||||
s.Nil(err)
|
||||
|
||||
s.Nil(Cp(filepath.Join(tempDir, "testfile"), filepath.Join(tempDir, "testfile2")))
|
||||
s.True(Exists(filepath.Join(tempDir, "testfile2")))
|
||||
s.Nil(Remove(tempDir))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) TestSize() {
|
||||
filePath, _ := TempFile("testfile")
|
||||
|
||||
err := Write(filePath.Name(), "test data", 0644)
|
||||
s.Nil(err)
|
||||
|
||||
size, err := Size(filePath.Name())
|
||||
s.Nil(err)
|
||||
s.Equal(int64(len("test data")), size)
|
||||
s.Nil(filePath.Close())
|
||||
s.Nil(Remove(filePath.Name()))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) TestFileInfo() {
|
||||
filePath, _ := TempFile("testfile")
|
||||
|
||||
err := Write(filePath.Name(), "test data", 0644)
|
||||
s.Nil(err)
|
||||
|
||||
info, err := FileInfo(filePath.Name())
|
||||
s.Nil(err)
|
||||
s.Equal(filepath.Base(filePath.Name()), info.Name())
|
||||
s.Nil(filePath.Close())
|
||||
s.Nil(Remove(filePath.Name()))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) TestUnArchiveSuccessfullyUnarchivesFile() {
|
||||
file, _ := TempFile("test")
|
||||
dstDir, _ := TempDir("archive")
|
||||
|
||||
err := Write(file.Name(), "test data", 0644)
|
||||
s.Nil(err)
|
||||
|
||||
err = Archive([]string{file.Name()}, filepath.Join(dstDir, "test.zip"))
|
||||
s.Nil(err)
|
||||
s.FileExists(filepath.Join(dstDir, "test.zip"))
|
||||
|
||||
err = UnArchive(filepath.Join(dstDir, "test.zip"), dstDir)
|
||||
s.Nil(err)
|
||||
s.FileExists(filepath.Join(dstDir, filepath.Base(file.Name())))
|
||||
s.Nil(file.Close())
|
||||
s.Nil(Remove(file.Name()))
|
||||
s.Nil(Remove(dstDir))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) TestUnArchiveFailsForNonExistentFile() {
|
||||
srcFile := "nonexistent.zip"
|
||||
dstDir, _ := TempDir("unarchived")
|
||||
|
||||
err := UnArchive(srcFile, dstDir)
|
||||
s.NotNil(err)
|
||||
s.Nil(Remove(dstDir))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) TestArchiveSuccessfullyArchivesFiles() {
|
||||
srcFile, _ := TempFile("test")
|
||||
dstDir, _ := TempDir("archive")
|
||||
|
||||
err := Write(srcFile.Name(), "test data", 0644)
|
||||
s.Nil(err)
|
||||
|
||||
err = Archive([]string{srcFile.Name()}, filepath.Join(dstDir, "test.zip"))
|
||||
s.Nil(err)
|
||||
s.FileExists(filepath.Join(dstDir, "test.zip"))
|
||||
s.Nil(srcFile.Close())
|
||||
s.Nil(Remove(srcFile.Name()))
|
||||
s.Nil(Remove(dstDir))
|
||||
}
|
||||
|
||||
func (s *SystemHelperTestSuite) TestArchiveFailsForNonExistentFiles() {
|
||||
srcFile := "nonexistent"
|
||||
dstDir, _ := TempDir("archive")
|
||||
|
||||
err := Archive([]string{srcFile}, filepath.Join(dstDir, "test.zip"))
|
||||
s.NotNil(err)
|
||||
s.Nil(Remove(dstDir))
|
||||
}
|
||||
+79
-73
@@ -4,7 +4,6 @@ package tools
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/spf13/cast"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -19,6 +18,10 @@ import (
|
||||
"github.com/shirou/gopsutil/load"
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
"github.com/shirou/gopsutil/net"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
)
|
||||
|
||||
// MonitoringInfo 监控信息
|
||||
@@ -48,8 +51,8 @@ func GetMonitoringInfo() MonitoringInfo {
|
||||
res.Disk, _ = disk.Partitions(true)
|
||||
|
||||
ioCounters, _ := disk.IOCounters()
|
||||
for _, io := range ioCounters {
|
||||
res.DiskIO = append(res.DiskIO, io)
|
||||
for _, info := range ioCounters {
|
||||
res.DiskIO = append(res.DiskIO, info)
|
||||
}
|
||||
|
||||
for _, partition := range res.Disk {
|
||||
@@ -80,9 +83,12 @@ func VersionCompare(ver1, ver2, operator string) bool {
|
||||
}
|
||||
|
||||
for i := 0; i < len(v1s); i++ {
|
||||
if v1s[i] > v2s[i] {
|
||||
v1i := cast.ToInt(v1s[i])
|
||||
v2i := cast.ToInt(v2s[i])
|
||||
|
||||
if v1i > v2i {
|
||||
return operator == ">" || operator == ">=" || operator == "!="
|
||||
} else if v1s[i] < v2s[i] {
|
||||
} else if v1i < v2i {
|
||||
return operator == "<" || operator == "<=" || operator == "!="
|
||||
}
|
||||
}
|
||||
@@ -156,9 +162,9 @@ func GetLatestPanelVersion() (PanelInfo, error) {
|
||||
isChina := IsChina()
|
||||
|
||||
if isChina {
|
||||
output, err = Exec(`curl -sSL "https://git.haozi.net/api/v4/projects/opensource%2Fpanel/releases/permalink/latest"`)
|
||||
output, err = shell.Execf(`curl -sSL "https://git.haozi.net/api/v4/projects/opensource%%2Fpanel/releases/permalink/latest"`)
|
||||
} else {
|
||||
output, err = Exec(`curl -sSL "https://api.github.com/repos/TheTNB/panel/releases/latest"`)
|
||||
output, err = shell.Execf(`curl -sSL "https://api.github.com/repos/TheTNB/panel/releases/latest"`)
|
||||
}
|
||||
|
||||
if len(output) == 0 || err != nil {
|
||||
@@ -182,70 +188,70 @@ func GetLatestPanelVersion() (PanelInfo, error) {
|
||||
|
||||
var name, version, body, date, downloadName, downloadUrl, checksums, checksumsUrl string
|
||||
if isChina {
|
||||
if name, err = Exec("jq -r '.name' " + fileName); err != nil {
|
||||
if name, err = shell.Execf("jq -r '.name' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
if version, err = Exec("jq -r '.tag_name' " + fileName); err != nil {
|
||||
if version, err = shell.Execf("jq -r '.tag_name' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
if body, err = Exec("jq -r '.description' " + fileName); err != nil {
|
||||
if body, err = shell.Execf("jq -r '.description' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
if date, err = Exec("jq -r '.created_at' " + fileName); err != nil {
|
||||
if date, err = shell.Execf("jq -r '.created_at' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
if checksums, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"checksums\")) | .name' " + fileName); err != nil {
|
||||
if checksums, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"checksums\")) | .name' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
if checksumsUrl, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"checksums\")) | .direct_asset_url' " + fileName); err != nil {
|
||||
if checksumsUrl, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"checksums\")) | .direct_asset_url' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
if env.IsArm() {
|
||||
if downloadName, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"arm64\")) | .name' " + fileName); err != nil {
|
||||
if downloadName, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"arm64\")) | .name' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
if downloadUrl, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"arm64\")) | .direct_asset_url' " + fileName); err != nil {
|
||||
if downloadUrl, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"arm64\")) | .direct_asset_url' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
} else {
|
||||
if downloadName, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"amd64v2\")) | .name' " + fileName); err != nil {
|
||||
if downloadName, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"amd64v2\")) | .name' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
if downloadUrl, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"amd64v2\")) | .direct_asset_url' " + fileName); err != nil {
|
||||
if downloadUrl, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"amd64v2\")) | .direct_asset_url' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if name, err = Exec("jq -r '.name' " + fileName); err != nil {
|
||||
if name, err = shell.Execf("jq -r '.name' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
if version, err = Exec("jq -r '.tag_name' " + fileName); err != nil {
|
||||
if version, err = shell.Execf("jq -r '.tag_name' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
if body, err = Exec("jq -r '.body' " + fileName); err != nil {
|
||||
if body, err = shell.Execf("jq -r '.body' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
if date, err = Exec("jq -r '.published_at' " + fileName); err != nil {
|
||||
if date, err = shell.Execf("jq -r '.published_at' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
if checksums, err = Exec("jq -r '.assets[] | select(.name | contains(\"checksums\")) | .name' " + fileName); err != nil {
|
||||
if checksums, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"checksums\")) | .name' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
if checksumsUrl, err = Exec("jq -r '.assets[] | select(.name | contains(\"checksums\")) | .browser_download_url' " + fileName); err != nil {
|
||||
if checksumsUrl, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"checksums\")) | .browser_download_url' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
if env.IsArm() {
|
||||
if downloadName, err = Exec("jq -r '.assets[] | select(.name | contains(\"arm64\")) | .name' " + fileName); err != nil {
|
||||
if downloadName, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"arm64\")) | .name' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
if downloadUrl, err = Exec("jq -r '.assets[] | select(.name | contains(\"arm64\")) | .browser_download_url' " + fileName); err != nil {
|
||||
if downloadUrl, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"arm64\")) | .browser_download_url' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
} else {
|
||||
if downloadName, err = Exec("jq -r '.assets[] | select(.name | contains(\"amd64v2\")) | .name' " + fileName); err != nil {
|
||||
if downloadName, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"amd64v2\")) | .name' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
if downloadUrl, err = Exec("jq -r '.assets[] | select(.name | contains(\"amd64v2\")) | .browser_download_url' " + fileName); err != nil {
|
||||
if downloadUrl, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"amd64v2\")) | .browser_download_url' " + fileName); err != nil {
|
||||
return info, errors.New("获取最新版本失败")
|
||||
}
|
||||
}
|
||||
@@ -275,9 +281,9 @@ func GetPanelVersion(version string) (PanelInfo, error) {
|
||||
}
|
||||
|
||||
if isChina {
|
||||
output, err = Exec(`curl -sSL "https://git.haozi.net/api/v4/projects/opensource%2Fpanel/releases/` + version + `"`)
|
||||
output, err = shell.Execf(`curl -sSL "https://git.haozi.net/api/v4/projects/opensource%%2Fpanel/releases/` + version + `"`)
|
||||
} else {
|
||||
output, err = Exec(`curl -sSL "https://api.github.com/repos/TheTNB/panel/releases/tags/` + version + `"`)
|
||||
output, err = shell.Execf(`curl -sSL "https://api.github.com/repos/TheTNB/panel/releases/tags/` + version + `"`)
|
||||
}
|
||||
|
||||
if len(output) == 0 || err != nil {
|
||||
@@ -301,70 +307,70 @@ func GetPanelVersion(version string) (PanelInfo, error) {
|
||||
|
||||
var name, version2, body, date, downloadName, downloadUrl, checksums, checksumsUrl string
|
||||
if isChina {
|
||||
if name, err = Exec("jq -r '.name' " + fileName); err != nil {
|
||||
if name, err = shell.Execf("jq -r '.name' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
if version2, err = Exec("jq -r '.tag_name' " + fileName); err != nil {
|
||||
if version2, err = shell.Execf("jq -r '.tag_name' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
if body, err = Exec("jq -r '.description' " + fileName); err != nil {
|
||||
if body, err = shell.Execf("jq -r '.description' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
if date, err = Exec("jq -r '.created_at' " + fileName); err != nil {
|
||||
if date, err = shell.Execf("jq -r '.created_at' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
if checksums, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"checksums\")) | .name' " + fileName); err != nil {
|
||||
if checksums, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"checksums\")) | .name' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
if checksumsUrl, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"checksums\")) | .direct_asset_url' " + fileName); err != nil {
|
||||
if checksumsUrl, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"checksums\")) | .direct_asset_url' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
if env.IsArm() {
|
||||
if downloadName, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"arm64\")) | .name' " + fileName); err != nil {
|
||||
if downloadName, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"arm64\")) | .name' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
if downloadUrl, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"arm64\")) | .direct_asset_url' " + fileName); err != nil {
|
||||
if downloadUrl, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"arm64\")) | .direct_asset_url' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
} else {
|
||||
if downloadName, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"amd64v2\")) | .name' " + fileName); err != nil {
|
||||
if downloadName, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"amd64v2\")) | .name' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
if downloadUrl, err = Exec("jq -r '.assets.links[] | select(.name | contains(\"amd64v2\")) | .direct_asset_url' " + fileName); err != nil {
|
||||
if downloadUrl, err = shell.Execf("jq -r '.assets.links[] | select(.name | contains(\"amd64v2\")) | .direct_asset_url' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if name, err = Exec("jq -r '.name' " + fileName); err != nil {
|
||||
if name, err = shell.Execf("jq -r '.name' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
if version2, err = Exec("jq -r '.tag_name' " + fileName); err != nil {
|
||||
if version2, err = shell.Execf("jq -r '.tag_name' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
if body, err = Exec("jq -r '.body' " + fileName); err != nil {
|
||||
if body, err = shell.Execf("jq -r '.body' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
if date, err = Exec("jq -r '.published_at' " + fileName); err != nil {
|
||||
if date, err = shell.Execf("jq -r '.published_at' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
if checksums, err = Exec("jq -r '.assets[] | select(.name | contains(\"checksums\")) | .name' " + fileName); err != nil {
|
||||
if checksums, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"checksums\")) | .name' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
if checksumsUrl, err = Exec("jq -r '.assets[] | select(.name | contains(\"checksums\")) | .browser_download_url' " + fileName); err != nil {
|
||||
if checksumsUrl, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"checksums\")) | .browser_download_url' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
if env.IsArm() {
|
||||
if downloadName, err = Exec("jq -r '.assets[] | select(.name | contains(\"arm64\")) | .name' " + fileName); err != nil {
|
||||
if downloadName, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"arm64\")) | .name' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
if downloadUrl, err = Exec("jq -r '.assets[] | select(.name | contains(\"arm64\")) | .browser_download_url' " + fileName); err != nil {
|
||||
if downloadUrl, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"arm64\")) | .browser_download_url' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
} else {
|
||||
if downloadName, err = Exec("jq -r '.assets[] | select(.name | contains(\"amd64v2\")) | .name' " + fileName); err != nil {
|
||||
if downloadName, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"amd64v2\")) | .name' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
if downloadUrl, err = Exec("jq -r '.assets[] | select(.name | contains(\"amd64v2\")) | .browser_download_url' " + fileName); err != nil {
|
||||
if downloadUrl, err = shell.Execf("jq -r '.assets[] | select(.name | contains(\"amd64v2\")) | .browser_download_url' " + fileName); err != nil {
|
||||
return info, errors.New("获取面板版本失败")
|
||||
}
|
||||
}
|
||||
@@ -388,112 +394,112 @@ func UpdatePanel(panelInfo PanelInfo) error {
|
||||
color.Green().Printfln("下载链接: " + panelInfo.DownloadUrl)
|
||||
|
||||
color.Green().Printfln("前置检查...")
|
||||
if Exists("/tmp/panel-storage.zip") || Exists("/tmp/panel.conf.bak") {
|
||||
if io.Exists("/tmp/panel-storage.zip") || io.Exists("/tmp/panel.conf.bak") {
|
||||
return errors.New("检测到 /tmp 存在临时文件,可能是上次更新失败导致的,请谨慎排除后重试")
|
||||
}
|
||||
|
||||
color.Green().Printfln("备份面板数据...")
|
||||
// 备份面板
|
||||
if err := Archive([]string{"/www/panel"}, "/www/backup/panel/panel-"+carbon.Now().ToShortDateTimeString()+".zip"); err != nil {
|
||||
if err := io.Archive([]string{"/www/panel"}, "/www/backup/panel/panel-"+carbon.Now().ToShortDateTimeString()+".zip"); err != nil {
|
||||
color.Red().Printfln("备份面板失败")
|
||||
return err
|
||||
}
|
||||
if _, err := Exec("cd /www/panel/storage && zip -r /tmp/panel-storage.zip *"); err != nil {
|
||||
if _, err := shell.Execf("cd /www/panel/storage && zip -r /tmp/panel-storage.zip *"); err != nil {
|
||||
color.Red().Printfln("备份面板数据失败")
|
||||
return err
|
||||
}
|
||||
if _, err := Exec("cp -f /www/panel/panel.conf /tmp/panel.conf.bak"); err != nil {
|
||||
if _, err := shell.Execf("cp -f /www/panel/panel.conf /tmp/panel.conf.bak"); err != nil {
|
||||
color.Red().Printfln("备份面板配置失败")
|
||||
return err
|
||||
}
|
||||
if !Exists("/tmp/panel-storage.zip") || !Exists("/tmp/panel.conf.bak") {
|
||||
if !io.Exists("/tmp/panel-storage.zip") || !io.Exists("/tmp/panel.conf.bak") {
|
||||
return errors.New("备份面板数据失败")
|
||||
}
|
||||
color.Green().Printfln("备份完成")
|
||||
|
||||
color.Green().Printfln("清理旧版本...")
|
||||
if _, err := Exec("rm -rf /www/panel/*"); err != nil {
|
||||
if _, err := shell.Execf("rm -rf /www/panel/*"); err != nil {
|
||||
color.Red().Printfln("清理旧版本失败")
|
||||
return err
|
||||
}
|
||||
color.Green().Printfln("清理完成")
|
||||
|
||||
color.Green().Printfln("正在下载...")
|
||||
if _, err := Exec("wget -T 120 -t 3 -O /www/panel/" + panelInfo.DownloadName + " " + panelInfo.DownloadUrl); err != nil {
|
||||
if _, err := shell.Execf("wget -T 120 -t 3 -O /www/panel/" + panelInfo.DownloadName + " " + panelInfo.DownloadUrl); err != nil {
|
||||
color.Red().Printfln("下载失败")
|
||||
return err
|
||||
}
|
||||
if _, err := Exec("wget -T 20 -t 3 -O /www/panel/" + panelInfo.Checksums + " " + panelInfo.ChecksumsUrl); err != nil {
|
||||
if _, err := shell.Execf("wget -T 20 -t 3 -O /www/panel/" + panelInfo.Checksums + " " + panelInfo.ChecksumsUrl); err != nil {
|
||||
color.Red().Printfln("下载失败")
|
||||
return err
|
||||
}
|
||||
if !Exists("/www/panel/"+panelInfo.DownloadName) || !Exists("/www/panel/"+panelInfo.Checksums) {
|
||||
if !io.Exists("/www/panel/"+panelInfo.DownloadName) || !io.Exists("/www/panel/"+panelInfo.Checksums) {
|
||||
return errors.New("下载失败")
|
||||
}
|
||||
color.Green().Printfln("下载完成")
|
||||
|
||||
color.Green().Printfln("校验下载文件...")
|
||||
check, err := Exec("cd /www/panel && sha256sum -c " + panelInfo.Checksums + " --ignore-missing")
|
||||
check, err := shell.Execf("cd /www/panel && sha256sum -c " + panelInfo.Checksums + " --ignore-missing")
|
||||
if check != panelInfo.DownloadName+": OK" || err != nil {
|
||||
return errors.New("下载文件校验失败")
|
||||
}
|
||||
if err = Remove("/www/panel/" + panelInfo.Checksums); err != nil {
|
||||
if err = io.Remove("/www/panel/" + panelInfo.Checksums); err != nil {
|
||||
color.Red().Printfln("清理临时文件失败")
|
||||
return err
|
||||
}
|
||||
color.Green().Printfln("文件校验完成")
|
||||
|
||||
color.Green().Printfln("更新新版本...")
|
||||
if _, err = Exec("cd /www/panel && unzip -o " + panelInfo.DownloadName + " && rm -rf " + panelInfo.DownloadName); err != nil {
|
||||
if _, err = shell.Execf("cd /www/panel && unzip -o " + panelInfo.DownloadName + " && rm -rf " + panelInfo.DownloadName); err != nil {
|
||||
color.Red().Printfln("更新失败")
|
||||
return err
|
||||
}
|
||||
if !Exists("/www/panel/panel") {
|
||||
if !io.Exists("/www/panel/panel") {
|
||||
return errors.New("更新失败,可能是下载过程中出现了问题")
|
||||
}
|
||||
color.Green().Printfln("更新完成")
|
||||
|
||||
color.Green().Printfln("恢复面板数据...")
|
||||
if _, err = Exec("cp -f /tmp/panel-storage.zip /www/panel/storage/panel-storage.zip && cd /www/panel/storage && unzip -o panel-storage.zip && rm -rf panel-storage.zip"); err != nil {
|
||||
if _, err = shell.Execf("cp -f /tmp/panel-storage.zip /www/panel/storage/panel-storage.zip && cd /www/panel/storage && unzip -o panel-storage.zip && rm -rf panel-storage.zip"); err != nil {
|
||||
color.Red().Printfln("恢复面板数据失败")
|
||||
return err
|
||||
}
|
||||
if _, err = Exec("cp -f /tmp/panel.conf.bak /www/panel/panel.conf"); err != nil {
|
||||
if _, err = shell.Execf("cp -f /tmp/panel.conf.bak /www/panel/panel.conf"); err != nil {
|
||||
color.Red().Printfln("恢复面板配置失败")
|
||||
return err
|
||||
}
|
||||
if _, err = Exec("cp -f /www/panel/scripts/panel.sh /usr/bin/panel"); err != nil {
|
||||
if _, err = shell.Execf("cp -f /www/panel/scripts/panel.sh /usr/bin/panel"); err != nil {
|
||||
color.Red().Printfln("恢复面板脚本失败")
|
||||
return err
|
||||
}
|
||||
if !Exists("/www/panel/storage/panel.db") || !Exists("/www/panel/panel.conf") {
|
||||
if !io.Exists("/www/panel/storage/panel.db") || !io.Exists("/www/panel/panel.conf") {
|
||||
return errors.New("恢复面板数据失败")
|
||||
}
|
||||
color.Green().Printfln("恢复完成")
|
||||
|
||||
color.Green().Printfln("设置面板文件权限...")
|
||||
_, _ = Exec("chmod -R 700 /www/panel")
|
||||
_, _ = Exec("chmod -R 700 /usr/bin/panel")
|
||||
_, _ = shell.Execf("chmod -R 700 /www/panel")
|
||||
_, _ = shell.Execf("chmod -R 700 /usr/bin/panel")
|
||||
color.Green().Printfln("设置完成")
|
||||
|
||||
if _, err = Exec("bash /www/panel/scripts/update_panel.sh"); err != nil {
|
||||
if _, err = shell.Execf("bash /www/panel/scripts/update_panel.sh"); err != nil {
|
||||
color.Red().Printfln("执行面板升级后脚本失败")
|
||||
return err
|
||||
}
|
||||
if _, err = Exec("panel writeSetting version " + panelInfo.Version); err != nil {
|
||||
if _, err = shell.Execf("panel writeSetting version " + panelInfo.Version); err != nil {
|
||||
color.Red().Printfln("写入面板版本号失败")
|
||||
return err
|
||||
}
|
||||
|
||||
_, _ = Exec("rm -rf /tmp/panel-storage.zip")
|
||||
_, _ = Exec("rm -rf /tmp/panel.conf.bak")
|
||||
_, _ = shell.Execf("rm -rf /tmp/panel-storage.zip")
|
||||
_, _ = shell.Execf("rm -rf /tmp/panel.conf.bak")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func RestartPanel() {
|
||||
color.Green().Printfln("重启面板...")
|
||||
err := ExecAsync("sleep 2 && systemctl restart panel")
|
||||
err := shell.ExecfAsync("sleep 2 && systemctl restart panel")
|
||||
if err != nil {
|
||||
color.Red().Printfln("重启失败")
|
||||
return
|
||||
|
||||
@@ -3,7 +3,6 @@ package tools
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/goravel/framework/support/env"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
@@ -44,6 +43,9 @@ func (s *HelperTestSuite) TestVersionCompare() {
|
||||
s.True(VersionCompare("v1.0.0", "1.0.0", "=="))
|
||||
s.True(VersionCompare("1.0.0", "v1.0.0", "=="))
|
||||
s.True(VersionCompare("v1.0.0", "v1.0.0", "=="))
|
||||
s.False(VersionCompare("v2.2.9", "v2.2.10", ">="))
|
||||
s.True(VersionCompare("v2.2.10", "v2.2.9", ">="))
|
||||
|
||||
}
|
||||
|
||||
func (s *HelperTestSuite) TestGenerateVersions() {
|
||||
@@ -77,18 +79,12 @@ func (s *HelperTestSuite) TestGenerateVersions() {
|
||||
}
|
||||
|
||||
func (s *HelperTestSuite) TestGetLatestPanelVersion() {
|
||||
if env.IsWindows() {
|
||||
return
|
||||
}
|
||||
version, err := GetLatestPanelVersion()
|
||||
s.NotEmpty(version)
|
||||
s.Nil(err)
|
||||
}
|
||||
|
||||
func (s *HelperTestSuite) TestGetPanelVersion() {
|
||||
if env.IsWindows() {
|
||||
return
|
||||
}
|
||||
version, err := GetPanelVersion("v2.1.29")
|
||||
s.NotEmpty(version)
|
||||
s.Nil(err)
|
||||
|
||||
@@ -199,6 +199,8 @@ func Api() {
|
||||
settingController := controllers.NewSettingController()
|
||||
r.Get("list", settingController.List)
|
||||
r.Post("update", settingController.Update)
|
||||
r.Get("https", settingController.GetHttps)
|
||||
r.Post("https", settingController.UpdateHttps)
|
||||
})
|
||||
r.Prefix("system").Middleware(middleware.Jwt()).Group(func(r route.Router) {
|
||||
controller := controllers.NewSystemController()
|
||||
|
||||
@@ -282,6 +282,7 @@ mkdir -p /www/server/vhost
|
||||
mkdir -p /www/server/vhost
|
||||
mkdir -p /www/server/vhost/rewrite
|
||||
mkdir -p /www/server/vhost/ssl
|
||||
mkdir -p /www/server/vhost/acme
|
||||
|
||||
# 写入主配置文件
|
||||
cat > ${openrestyPath}/conf/nginx.conf << EOF
|
||||
@@ -561,7 +562,7 @@ cat > ${openrestyPath}/html/block.html << EOF
|
||||
<div class="container">
|
||||
<h1>耗子面板</h1>
|
||||
<p>本次请求判断为危险的攻击请求,已被拦截!</p>
|
||||
<p>当您看到此页面,说明您的请求被WAF拦截,可能是由于您的请求中包含了危险的攻击内容,或者您的请求被误判为攻击请求。</p>
|
||||
<p>可能您的请求中包含了危险的攻击内容,或者您的请求被误判为攻击请求。</p>
|
||||
<p>如果您认为这是误判,请联系服务器管理员解决。</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -124,6 +124,15 @@ if version_lt "$oldVersion" "2.2.10"; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if version_lt "$oldVersion" "2.2.14"; then
|
||||
echo "更新面板到 v2.2.14 ..."
|
||||
echo "Update panel to v2.2.14 ..."
|
||||
if [ -f "/www/server/openresty/bin/openresty" ]; then
|
||||
mkdir -p /www/server/vhost/acme
|
||||
chmod -R 644 /www/server/vhost/acme
|
||||
fi
|
||||
fi
|
||||
|
||||
echo $HR
|
||||
echo "更新结束"
|
||||
echo "Update finished"
|
||||
|
||||
Reference in New Issue
Block a user