Compare commits

...

32 Commits

Author SHA1 Message Date
耗子 83f58e0a2e feat: 发布v2.2.4 2024-06-08 16:09:59 +08:00
耗子 e941ab6661 feat: 更新redis和php版本 2024-06-08 15:59:01 +08:00
耗子 688801eebb fix: lint 2024-06-08 15:37:39 +08:00
耗子 8ad42f8c0e fix: frp 相关接口 2024-06-08 15:26:57 +08:00
耗子 02c8809785 Merge remote-tracking branch 'origin/main' 2024-06-08 14:48:23 +08:00
耗子 fd0e43fb26 fix: 容器列表空切片 2024-06-08 14:48:15 +08:00
机器人 96ec795096 Merge branch 'renovate/github.com-docker-docker-26.x' into 'main'
chore(deps): Update module github.com/docker/docker to v26.1.4+incompatible

See merge request opensource/panel!75
2024-06-05 22:18:50 +00:00
机器人 5ea191b550 Merge branch 'renovate/golang.org-x-net-0.x' into 'main'
chore(deps): Update module golang.org/x/net to v0.26.0

See merge request opensource/panel!73
2024-06-05 22:16:57 +00:00
机器人 c37cc3bd2c chore(deps): Update module golang.org/x/net to v0.26.0 2024-06-05 22:11:16 +00:00
机器人 110f305f16 chore(deps): Update module github.com/docker/docker to v26.1.4+incompatible 2024-06-05 22:10:40 +00:00
机器人 179b6ff83e Merge branch 'renovate/golang.org-x-crypto-0.x' into 'main'
chore(deps): Update module golang.org/x/crypto to v0.24.0

See merge request opensource/panel!72
2024-06-05 16:13:59 +00:00
机器人 0da1b0b565 chore(deps): Update module golang.org/x/crypto to v0.24.0 2024-06-05 16:09:16 +00:00
耗子 0da3f7b8e0 feat: 切换镜像源 2024-06-05 19:58:50 +08:00
耗子 c6f9a585b0 fix: lint 2024-06-04 22:47:47 +08:00
耗子 686bdd9ae2 fix: frp安装 2024-06-03 02:58:39 +08:00
耗子 e3a22f924e fix: lint 2024-06-03 02:07:47 +08:00
耗子 0c3c47361b feat: frp管理器 2024-06-03 02:07:21 +08:00
耗子 c3f9f56f11 feat: 调整定时任务时间 2024-06-03 01:27:40 +08:00
耗子 76e992cc9e chore: fix lint 2024-05-30 02:27:35 +08:00
耗子 73cc5a35ad refactor: move types 2024-05-30 01:58:17 +08:00
耗子 873520e9f4 chore: update framework 2024-05-30 01:44:44 +08:00
耗子 af3254c11a docs: update README 2024-05-30 00:30:37 +08:00
耗子 5575d34ab4 fix: test 2024-05-29 21:47:35 +08:00
耗子 a995c56f1b fix: lint 2024-05-29 21:38:59 +08:00
耗子 1fbea148e3 refactor: 重命名软件包名 2024-05-29 21:31:01 +08:00
耗子 ca504e1acc feat: optimize task 2024-05-29 21:19:39 +08:00
耗子 18fe84117d feat: 使用新队列 2024-05-29 19:20:06 +08:00
耗子 9eb9a2c7d6 fix(安全): SSH端口类型 2024-05-26 14:19:56 +08:00
耗子 de7d50ce51 feat: optimize info api 2024-05-26 14:14:48 +08:00
耗子 6e0d93257f feat: add language api 2024-05-26 13:37:58 +08:00
耗子 96806d4e94 feat: bump v2.2.3 2024-05-24 00:05:18 +08:00
耗子 3de78c95bf ci: 优化upx压缩速度 2024-05-23 23:52:55 +08:00
117 changed files with 2591 additions and 1696 deletions
+2 -2
View File
@@ -59,7 +59,7 @@ fetch:
stage: build
image: alpine:latest
before_script:
- sed -i 's/dl-cdn.alpinelinux.org/mirror.sjtu.edu.cn/g' /etc/apk/repositories
- sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
- apk add --no-cache curl jq unzip zip
script:
- curl -sSL "https://git.haozi.net/api/v4/projects/opensource%2Fpanel-frontend/releases" | jq -r '.[0].assets.links[] | select(.name | contains("dist")) | .direct_asset_url' | xargs curl -L -o frontend.zip
@@ -87,6 +87,6 @@ release:
# generate a changelog.
GIT_DEPTH: 0
script:
- sed -i 's/dl-cdn.alpinelinux.org/mirror.sjtu.edu.cn/g' /etc/apk/repositories
- sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
- apk add --no-cache upx
- goreleaser release --clean
+1 -1
View File
@@ -26,7 +26,7 @@ upx:
# Whether to try LZMA (slower).
lzma: true
# Whether to try all methods and filters (slow).
brute: true
brute: false
archives:
- id: panel-archive
+3 -5
View File
@@ -12,9 +12,9 @@
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2FTheTNB%2Fpanel?ref=badge_shield"><img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2FTheTNB%2Fpanel.svg?type=shield" alt="FOSSA Status"></a>
</p>
耗子 Linux 面板是使用 Golang + Vue 开发的轻量 Linux 服务器运维管理面板,以 GNU Affero General Public License v3.0 开源
耗子 Linux 面板是使用 Golang + Vue 开发的开源轻量 Linux 服务器运维管理面板。
交流QQ群:[12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | QQ频道:[pd.qq.com/s/fyol46wfy](https://pd.qq.com/s/fyol46wfy)
交流QQ群:[12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | 论坛:[tom.moe](https://tom.moe) | 赞助:[爱发电](https://afdian.net/a/TheTNB)
## 优势
@@ -36,9 +36,7 @@
低配机器建议使用 `Debian`,资源占用较 `RHEL` 系更低。其他机器建议使用 `AlmaLinux` | `RockyLinux`,维护周期更长也更稳定。
不在下表中的其他 RHEL 9.x 系统,可自行尝试安装,但不保证能够正常运行,且不提供无偿技术支持(理论上不会有大问题)。
CentOS Stream 可使用迁移脚本迁移至支持的系统: [CentOS 8/9 迁移脚本](https://github.com/TheTNB/byecentos)
不在下表中的其他 RHEL 9.x 系统,可自行尝试安装,但不保证能够正常运行,且不提供技术支持(理论上不会有大问题)。
| 系统 | 版本 |
|------------|----|
+3 -5
View File
@@ -12,9 +12,9 @@
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2FTheTNB%2Fpanel?ref=badge_shield"><img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2FTheTNB%2Fpanel.svg?type=shield" alt="FOSSA Status"></a>
</p>
The HaoZi Linux Panel is a lightweight Linux server operation and maintenance management panel developed using Golang and Vue. It is open source under the GNU Affero General Public License v3.0 protocol.
The HaoZi Linux Panel is an open source lightweight Linux server operation and maintenance management panel developed using Golang and Vue.
Communication QQ group: [12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | QQ channel: [pd.qq.com/s/fyol46wfy](https://pd.qq.com/s/fyol46wfy)
Communication QQ group: [12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | Forum: [tom.moe](https://tom.moe) | Sponsor: [Open Collective](https://opencollective.com/tnb)
## Advantages
@@ -36,9 +36,7 @@ HaoZi Linux Panel only supports the latest version of mainstream systems under t
Recommended to use `Debian` for low-configuration machines, as its resource usage is lower than that of the `RHEL` system. For other machines, recommended to use `RockyLinux` | `AlmaLinux`, which has a longer maintenance cycle and is more stable.
For other RHEL 9.x systems not in the table, you can try to install it yourself, but normal operation is not guaranteed, and free technical support is not provided (theoretically there will be no major question).
CentOS Stream can be migrated to a supported system using the migration script: [CentOS 8/9 Migration Script](https://github.com/TheTNB/byecentos)
For other RHEL 9.x systems not in the table, you can try to install it yourself, but normal operation is not guaranteed, and technical support is not provided (theoretically there will be no major question).
| OS | Version |
|------------|---------|
+4 -4
View File
@@ -10,9 +10,9 @@ import (
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/carbon"
"panel/app/models"
"panel/internal"
"panel/internal/services"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/types"
)
// CertRenew 证书续签
@@ -39,7 +39,7 @@ func (receiver *CertRenew) Extend() command.Extend {
// Handle Execute the console command.
func (receiver *CertRenew) Handle(console.Context) error {
if internal.Status != internal.StatusNormal {
if types.Status != types.StatusNormal {
return nil
}
+20 -19
View File
@@ -2,19 +2,18 @@ package commands
import (
"context"
"strconv"
"github.com/gookit/color"
"github.com/goravel/framework/contracts/console"
"github.com/goravel/framework/contracts/console/command"
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/carbon"
"github.com/goravel/framework/support/color"
"github.com/spf13/cast"
"panel/app/models"
"panel/internal"
"panel/internal/services"
"panel/pkg/tools"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/tools"
"github.com/TheTNB/panel/types"
)
// Monitoring 系统监控
@@ -41,10 +40,14 @@ func (receiver *Monitoring) Extend() command.Extend {
// Handle Execute the console command.
func (receiver *Monitoring) Handle(console.Context) error {
if internal.Status != internal.StatusNormal {
if types.Status != types.StatusNormal {
return nil
}
// 将等待中的任务分发
task := services.NewTaskImpl()
_ = task.DispatchWaiting()
setting := services.NewSettingImpl()
monitor := setting.Get(models.SettingKeyMonitor)
if !cast.ToBool(monitor) {
@@ -52,7 +55,6 @@ func (receiver *Monitoring) Handle(console.Context) error {
}
info := tools.GetMonitoringInfo()
translate := facades.Lang(context.Background())
// 去除部分数据以减少数据库存储
@@ -66,30 +68,29 @@ func (receiver *Monitoring) Handle(console.Context) error {
cpu.Flags = nil
}
if internal.Status != internal.StatusNormal {
if types.Status != types.StatusNormal {
return nil
}
err := facades.Orm().Query().Create(&models.Monitor{
Info: info,
})
if err != nil {
facades.Log().Infof("[面板] 系统监控保存失败: %s", err.Error())
color.Redf(translate.Get("commands.panel:monitoring.fail")+": %s", err.Error())
facades.Log().Tags("面板", "系统监控").With(map[string]any{
"error": err.Error(),
}).Infof("保存失败")
color.Red().Printfln(translate.Get("commands.panel:monitoring.fail")+": %s", err.Error())
return nil
}
// 删除过期数据
monitorDays := setting.Get(models.SettingKeyMonitorDays)
days, err := strconv.Atoi(monitorDays)
if err != nil {
return nil
}
if days <= 0 || internal.Status != internal.StatusNormal {
days := cast.ToInt(setting.Get(models.SettingKeyMonitorDays))
if days <= 0 || types.Status != types.StatusNormal {
return nil
}
if _, err = facades.Orm().Query().Where("created_at < ?", carbon.Now().SubDays(days).ToDateTimeString()).Delete(&models.Monitor{}); err != nil {
facades.Log().Infof("[面板] 系统监控删除过期数据失败: %s", err.Error())
facades.Log().Tags("面板", "系统监控").With(map[string]any{
"error": err.Error(),
}).Infof("删除过期数据失败")
return nil
}
+181 -174
View File
@@ -8,17 +8,17 @@ import (
"sort"
"strings"
"github.com/gookit/color"
"github.com/goravel/framework/contracts/console"
"github.com/goravel/framework/contracts/console/command"
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/carbon"
"github.com/goravel/framework/support/color"
"github.com/spf13/cast"
"panel/app/models"
"panel/internal"
"panel/internal/services"
"panel/pkg/tools"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/tools"
"github.com/TheTNB/panel/types"
)
// Panel 面板命令行
@@ -59,69 +59,76 @@ func (receiver *Panel) Handle(ctx console.Context) error {
var check models.User
err := facades.Orm().Query().FirstOrFail(&check)
if err == nil {
color.Redln(translate.Get("commands.panel.init.exist"))
color.Red().Printfln(translate.Get("commands.panel.init.exist"))
return nil
}
settings := []models.Setting{{Key: models.SettingKeyName, Value: "耗子 Linux 面板"}, {Key: models.SettingKeyMonitor, Value: "1"}, {Key: models.SettingKeyMonitorDays, Value: "30"}, {Key: models.SettingKeyBackupPath, Value: "/www/backup"}, {Key: models.SettingKeyWebsitePath, Value: "/www/wwwroot"}, {Key: models.SettingKeyVersion, Value: facades.Config().GetString("panel.version")}}
err = facades.Orm().Query().Create(&settings)
if err != nil {
color.Redln(translate.Get("commands.panel.init.fail"))
color.Red().Printfln(translate.Get("commands.panel.init.fail"))
return nil
}
hash, err := facades.Hash().Make(tools.RandomString(32))
if err != nil {
color.Redln(translate.Get("commands.panel.init.fail"))
color.Red().Printfln(translate.Get("commands.panel.init.fail"))
return nil
}
user := services.NewUserImpl()
_, err = user.Create("admin", hash)
if err != nil {
color.Redln(translate.Get("commands.panel.init.adminFail"))
color.Red().Printfln(translate.Get("commands.panel.init.adminFail"))
return nil
}
color.Greenln(translate.Get("commands.panel.init.success"))
color.Green().Printfln(translate.Get("commands.panel.init.success"))
case "update":
var task models.Task
err := facades.Orm().Query().Where("status", models.TaskStatusRunning).OrWhere("status", models.TaskStatusWaiting).FirstOrFail(&task)
if err == nil {
color.Redln(translate.Get("commands.panel.update.taskCheck"))
if err := facades.Orm().Query().Where("status", models.TaskStatusRunning).OrWhere("status", models.TaskStatusWaiting).FirstOrFail(&task); err == nil {
color.Red().Printfln(translate.Get("commands.panel.update.taskCheck"))
return nil
}
if _, err := facades.Orm().Query().Exec("PRAGMA wal_checkpoint(TRUNCATE)"); err != nil {
types.Status = types.StatusFailed
color.Red().Printfln(translate.Get("commands.panel.update.dbFail"))
return nil
}
panel, err := tools.GetLatestPanelVersion()
if err != nil {
color.Redln(translate.Get("commands.panel.update.versionFail"))
color.Red().Printfln(translate.Get("commands.panel.update.versionFail"))
return err
}
internal.Status = internal.StatusUpgrade
// 停止面板服务,因为在shell中运行的和systemd的不同
_ = tools.ServiceStop("panel")
types.Status = types.StatusUpgrade
if err = tools.UpdatePanel(panel); err != nil {
internal.Status = internal.StatusFailed
color.Redln(translate.Get("commands.panel.update.fail") + ": " + err.Error())
types.Status = types.StatusFailed
color.Red().Printfln(translate.Get("commands.panel.update.fail") + ": " + err.Error())
return nil
}
internal.Status = internal.StatusNormal
color.Greenln(translate.Get("commands.panel.update.success"))
types.Status = types.StatusNormal
color.Green().Printfln(translate.Get("commands.panel.update.success"))
tools.RestartPanel()
case "getInfo":
var user models.User
err := facades.Orm().Query().Where("id", 1).FirstOrFail(&user)
if err != nil {
color.Redln(translate.Get("commands.panel.getInfo.adminGetFail"))
color.Red().Printfln(translate.Get("commands.panel.getInfo.adminGetFail"))
return nil
}
password := tools.RandomString(16)
hash, err := facades.Hash().Make(password)
if err != nil {
color.Redln(translate.Get("commands.panel.getInfo.passwordGenerationFail"))
color.Red().Printfln(translate.Get("commands.panel.getInfo.passwordGenerationFail"))
return nil
}
user.Username = tools.RandomString(8)
@@ -132,13 +139,13 @@ func (receiver *Panel) Handle(ctx console.Context) error {
err = facades.Orm().Query().Save(&user)
if err != nil {
color.Redln(translate.Get("commands.panel.getInfo.adminSaveFail"))
color.Red().Printfln(translate.Get("commands.panel.getInfo.adminSaveFail"))
return nil
}
port, err := tools.Exec(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
if err != nil {
color.Redln(translate.Get("commands.panel.portFail"))
color.Red().Printfln(translate.Get("commands.panel.portFail"))
return nil
}
ip, err := tools.GetPublicIP()
@@ -150,42 +157,42 @@ func (receiver *Panel) Handle(ctx console.Context) error {
protocol = "https"
}
color.Greenln(translate.Get("commands.panel.getInfo.username") + ": " + user.Username)
color.Greenln(translate.Get("commands.panel.getInfo.password") + ": " + password)
color.Greenln(translate.Get("commands.panel.port") + ": " + port)
color.Greenln(translate.Get("commands.panel.entrance") + ": " + facades.Config().GetString("http.entrance"))
color.Greenln(translate.Get("commands.panel.getInfo.address") + ": " + protocol + "://" + ip + ":" + port + facades.Config().GetString("http.entrance"))
color.Green().Printfln(translate.Get("commands.panel.getInfo.username") + ": " + user.Username)
color.Green().Printfln(translate.Get("commands.panel.getInfo.password") + ": " + password)
color.Green().Printfln(translate.Get("commands.panel.port") + ": " + port)
color.Green().Printfln(translate.Get("commands.panel.entrance") + ": " + facades.Config().GetString("http.entrance"))
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'`)
if err != nil {
color.Redln(translate.Get("commands.panel.portFail"))
color.Red().Printfln(translate.Get("commands.panel.portFail"))
return nil
}
color.Greenln(translate.Get("commands.panel.port") + ": " + port)
color.Green().Printfln(translate.Get("commands.panel.port") + ": " + port)
case "getEntrance":
color.Greenln(translate.Get("commands.panel.entrance") + ": " + facades.Config().GetString("http.entrance"))
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'`)
if err != nil {
color.Redln(translate.Get("commands.panel.deleteEntrance.fail"))
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 {
color.Redln(translate.Get("commands.panel.deleteEntrance.fail"))
color.Red().Printfln(translate.Get("commands.panel.deleteEntrance.fail"))
return nil
}
color.Greenln(translate.Get("commands.panel.deleteEntrance.success"))
color.Green().Printfln(translate.Get("commands.panel.deleteEntrance.success"))
case "writePlugin":
slug := arg1
version := arg2
if len(slug) == 0 || len(version) == 0 {
color.Redln(translate.Get("commands.panel.writePlugin.paramFail"))
color.Red().Printfln(translate.Get("commands.panel.writePlugin.paramFail"))
return nil
}
@@ -197,31 +204,31 @@ func (receiver *Panel) Handle(ctx console.Context) error {
})
if err != nil {
color.Redln(translate.Get("commands.panel.writePlugin.fail"))
color.Red().Printfln(translate.Get("commands.panel.writePlugin.fail"))
return nil
}
color.Greenln(translate.Get("commands.panel.writePlugin.success"))
color.Green().Printfln(translate.Get("commands.panel.writePlugin.success"))
case "deletePlugin":
slug := arg1
if len(slug) == 0 {
color.Redln(translate.Get("commands.panel.deletePlugin.paramFail"))
color.Red().Printfln(translate.Get("commands.panel.deletePlugin.paramFail"))
return nil
}
_, err := facades.Orm().Query().Where("slug", slug).Delete(&models.Plugin{})
if err != nil {
color.Redln(translate.Get("commands.panel.deletePlugin.fail"))
color.Red().Printfln(translate.Get("commands.panel.deletePlugin.fail"))
return nil
}
color.Greenln(translate.Get("commands.panel.deletePlugin.success"))
color.Green().Printfln(translate.Get("commands.panel.deletePlugin.success"))
case "writeMysqlPassword":
password := arg1
if len(password) == 0 {
color.Redln(translate.Get("commands.panel.writeMysqlPassword.paramFail"))
color.Red().Printfln(translate.Get("commands.panel.writeMysqlPassword.paramFail"))
return nil
}
@@ -233,20 +240,20 @@ func (receiver *Panel) Handle(ctx console.Context) error {
})
if err != nil {
color.Redln(translate.Get("commands.panel.writeMysqlPassword.fail"))
color.Red().Printfln(translate.Get("commands.panel.writeMysqlPassword.fail"))
return nil
}
color.Greenln(translate.Get("commands.panel.writeMysqlPassword.success"))
color.Green().Printfln(translate.Get("commands.panel.writeMysqlPassword.success"))
case "cleanTask":
_, err := facades.Orm().Query().Model(&models.Task{}).Where("status", models.TaskStatusRunning).OrWhere("status", models.TaskStatusWaiting).Update("status", models.TaskStatusFailed)
if err != nil {
color.Redln(translate.Get("commands.panel.cleanTask.fail"))
color.Red().Printfln(translate.Get("commands.panel.cleanTask.fail"))
return nil
}
color.Greenln(translate.Get("commands.panel.cleanTask.success"))
color.Green().Printfln(translate.Get("commands.panel.cleanTask.success"))
case "backup":
backupType := arg1
@@ -255,37 +262,37 @@ func (receiver *Panel) Handle(ctx console.Context) error {
save := arg4
hr := `+----------------------------------------------------`
if len(backupType) == 0 || len(name) == 0 || len(path) == 0 || len(save) == 0 {
color.Redln(translate.Get("commands.panel.backup.paramFail"))
color.Red().Printfln(translate.Get("commands.panel.backup.paramFail"))
return nil
}
color.Greenln(hr)
color.Greenln("★ " + translate.Get("commands.panel.backup.start") + " [" + carbon.Now().ToDateTimeString() + "]")
color.Greenln(hr)
color.Green().Printfln(hr)
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 {
color.Redln("|-" + translate.Get("commands.panel.backup.backupDirFail") + ": " + err.Error())
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.backupDirFail") + ": " + err.Error())
return nil
}
}
switch backupType {
case "website":
color.Yellowln("|-" + translate.Get("commands.panel.backup.targetSite") + ": " + name)
color.Yellow().Printfln("|-" + translate.Get("commands.panel.backup.targetSite") + ": " + name)
var website models.Website
if err := facades.Orm().Query().Where("name", name).FirstOrFail(&website); err != nil {
color.Redln("|-" + translate.Get("commands.panel.backup.siteNotExist"))
color.Greenln(hr)
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.siteNotExist"))
color.Green().Printfln(hr)
return nil
}
backupFile := path + "/" + website.Name + "_" + carbon.Now().ToShortDateTimeString() + ".zip"
if _, err := tools.Exec(`cd '` + website.Path + `' && zip -r '` + backupFile + `' .`); err != nil {
color.Redln("|-" + translate.Get("commands.panel.backup.backupFail") + ": " + err.Error())
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.backupFail") + ": " + err.Error())
return nil
}
color.Greenln("|-" + translate.Get("commands.panel.backup.backupSuccess"))
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.backupSuccess"))
case "mysql":
rootPassword := services.NewSettingImpl().Get(models.SettingKeyMysqlRootPassword)
@@ -293,81 +300,81 @@ func (receiver *Panel) Handle(ctx console.Context) error {
err := os.Setenv("MYSQL_PWD", rootPassword)
if err != nil {
color.Redln("|-" + translate.Get("commands.panel.backup.mysqlBackupFail") + ": " + err.Error())
color.Greenln(hr)
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.mysqlBackupFail") + ": " + err.Error())
color.Green().Printfln(hr)
return nil
}
color.Greenln("|-" + translate.Get("commands.panel.backup.targetMysql") + ": " + name)
color.Greenln("|-" + translate.Get("commands.panel.backup.startExport"))
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 {
color.Redln("|-" + translate.Get("commands.panel.backup.exportFail") + ": " + err.Error())
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.exportFail") + ": " + err.Error())
return nil
}
color.Greenln("|-" + translate.Get("commands.panel.backup.exportSuccess"))
color.Greenln("|-" + translate.Get("commands.panel.backup.startCompress"))
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 {
color.Redln("|-" + translate.Get("commands.panel.backup.compressFail") + ": " + err.Error())
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.compressFail") + ": " + err.Error())
return nil
}
if err := tools.Remove("/tmp/" + backupFile); err != nil {
color.Redln("|-" + translate.Get("commands.panel.backup.deleteFail") + ": " + err.Error())
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.deleteFail") + ": " + err.Error())
return nil
}
color.Greenln("|-" + translate.Get("commands.panel.backup.compressSuccess"))
color.Greenln("|-" + translate.Get("commands.panel.backup.startMove"))
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 {
color.Redln("|-" + translate.Get("commands.panel.backup.moveFail") + ": " + err.Error())
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.moveFail") + ": " + err.Error())
return nil
}
color.Greenln("|-" + translate.Get("commands.panel.backup.moveSuccess"))
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.moveSuccess"))
_ = os.Unsetenv("MYSQL_PWD")
color.Greenln("|-" + translate.Get("commands.panel.backup.success"))
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.success"))
case "postgresql":
backupFile := name + "_" + carbon.Now().ToShortDateTimeString() + ".sql"
check, err := tools.Exec(`su - postgres -c "psql -l" 2>&1`)
if err != nil {
color.Redln("|-" + translate.Get("commands.panel.backup.databaseGetFail") + ": " + err.Error())
color.Greenln(hr)
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.databaseGetFail") + ": " + err.Error())
color.Green().Printfln(hr)
return nil
}
if !strings.Contains(check, name) {
color.Redln("|-" + translate.Get("commands.panel.backup.databaseNotExist"))
color.Greenln(hr)
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.databaseNotExist"))
color.Green().Printfln(hr)
return nil
}
color.Greenln("|-" + translate.Get("commands.panel.backup.targetPostgres") + ": " + name)
color.Greenln("|-" + translate.Get("commands.panel.backup.startExport"))
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 {
color.Redln("|-" + translate.Get("commands.panel.backup.exportFail") + ": " + err.Error())
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.exportFail") + ": " + err.Error())
return nil
}
color.Greenln("|-" + translate.Get("commands.panel.backup.exportSuccess"))
color.Greenln("|-" + translate.Get("commands.panel.backup.startCompress"))
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 {
color.Redln("|-" + translate.Get("commands.panel.backup.compressFail") + ": " + err.Error())
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.compressFail") + ": " + err.Error())
return nil
}
if err := tools.Remove("/tmp/" + backupFile); err != nil {
color.Redln("|-" + translate.Get("commands.panel.backup.deleteFail") + ": " + err.Error())
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.deleteFail") + ": " + err.Error())
return nil
}
color.Greenln("|-" + translate.Get("commands.panel.backup.compressSuccess"))
color.Greenln("|-" + translate.Get("commands.panel.backup.startMove"))
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 {
color.Redln("|-" + translate.Get("commands.panel.backup.moveFail") + ": " + err.Error())
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.moveFail") + ": " + err.Error())
return nil
}
color.Greenln("|-" + translate.Get("commands.panel.backup.moveSuccess"))
color.Greenln("|-" + translate.Get("commands.panel.backup.success"))
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.moveSuccess"))
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.success"))
}
color.Greenln(hr)
color.Green().Printfln(hr)
files, err := os.ReadDir(path)
if err != nil {
color.Redln("|-" + translate.Get("commands.panel.backup.cleanupFail") + ": " + err.Error())
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.cleanupFail") + ": " + err.Error())
return nil
}
var filteredFiles []os.FileInfo
@@ -385,60 +392,60 @@ 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.Yellowln("|-" + translate.Get("commands.panel.backup.cleanBackup") + ": " + fileToDelete)
color.Yellow().Printfln("|-" + translate.Get("commands.panel.backup.cleanBackup") + ": " + fileToDelete)
if err := tools.Remove(fileToDelete); err != nil {
color.Redln("|-" + translate.Get("commands.panel.backup.cleanupFail") + ": " + err.Error())
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.cleanupFail") + ": " + err.Error())
return nil
}
}
color.Greenln("|-" + translate.Get("commands.panel.backup.cleanupSuccess"))
color.Greenln(hr)
color.Greenln("☆ " + translate.Get("commands.panel.backup.success") + " [" + carbon.Now().ToDateTimeString() + "]")
color.Greenln(hr)
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.cleanupSuccess"))
color.Green().Printfln(hr)
color.Green().Printfln("☆ " + translate.Get("commands.panel.backup.success") + " [" + carbon.Now().ToDateTimeString() + "]")
color.Green().Printfln(hr)
case "cutoff":
name := arg1
save := arg2
hr := `+----------------------------------------------------`
if len(name) == 0 || len(save) == 0 {
color.Redln(translate.Get("commands.panel.cutoff.paramFail"))
color.Red().Printfln(translate.Get("commands.panel.cutoff.paramFail"))
return nil
}
color.Greenln(hr)
color.Greenln("★ " + translate.Get("commands.panel.cutoff.start") + " [" + carbon.Now().ToDateTimeString() + "]")
color.Greenln(hr)
color.Green().Printfln(hr)
color.Green().Printfln("★ " + translate.Get("commands.panel.cutoff.start") + " [" + carbon.Now().ToDateTimeString() + "]")
color.Green().Printfln(hr)
color.Yellowln("|-" + translate.Get("commands.panel.cutoff.targetSite") + ": " + name)
color.Yellow().Printfln("|-" + translate.Get("commands.panel.cutoff.targetSite") + ": " + name)
var website models.Website
if err := facades.Orm().Query().Where("name", name).FirstOrFail(&website); err != nil {
color.Redln("|-" + translate.Get("commands.panel.cutoff.siteNotExist"))
color.Greenln(hr)
color.Red().Printfln("|-" + translate.Get("commands.panel.cutoff.siteNotExist"))
color.Green().Printfln(hr)
return nil
}
logPath := "/www/wwwlogs/" + website.Name + ".log"
if !tools.Exists(logPath) {
color.Redln("|-" + translate.Get("commands.panel.cutoff.logNotExist"))
color.Greenln(hr)
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 {
color.Redln("|-" + translate.Get("commands.panel.cutoff.backupFail") + ": " + err.Error())
color.Red().Printfln("|-" + translate.Get("commands.panel.cutoff.backupFail") + ": " + err.Error())
return nil
}
if _, err := tools.Exec(`echo "" > ` + logPath); err != nil {
color.Redln("|-" + translate.Get("commands.panel.cutoff.clearFail") + ": " + err.Error())
color.Red().Printfln("|-" + translate.Get("commands.panel.cutoff.clearFail") + ": " + err.Error())
return nil
}
color.Greenln("|-" + translate.Get("commands.panel.cutoff.cutSuccess"))
color.Green().Printfln("|-" + translate.Get("commands.panel.cutoff.cutSuccess"))
color.Greenln(hr)
color.Green().Printfln(hr)
files, err := os.ReadDir("/www/wwwlogs")
if err != nil {
color.Redln("|-" + translate.Get("commands.panel.cutoff.cleanupFail") + ": " + err.Error())
color.Red().Printfln("|-" + translate.Get("commands.panel.cutoff.cleanupFail") + ": " + err.Error())
return nil
}
var filteredFiles []os.FileInfo
@@ -456,16 +463,16 @@ 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.Yellowln("|-" + translate.Get("commands.panel.cutoff.clearLog") + ": " + fileToDelete)
color.Yellow().Printfln("|-" + translate.Get("commands.panel.cutoff.clearLog") + ": " + fileToDelete)
if err := tools.Remove(fileToDelete); err != nil {
color.Redln("|-" + translate.Get("commands.panel.cutoff.cleanupFail") + ": " + err.Error())
color.Red().Printfln("|-" + translate.Get("commands.panel.cutoff.cleanupFail") + ": " + err.Error())
return nil
}
}
color.Greenln("|-" + translate.Get("commands.panel.cutoff.cleanupSuccess"))
color.Greenln(hr)
color.Greenln("☆ " + translate.Get("commands.panel.cutoff.end") + " [" + carbon.Now().ToDateTimeString() + "]")
color.Greenln(hr)
color.Green().Printfln("|-" + translate.Get("commands.panel.cutoff.cleanupSuccess"))
color.Green().Printfln(hr)
color.Green().Printfln("☆ " + translate.Get("commands.panel.cutoff.end") + " [" + carbon.Now().ToDateTimeString() + "]")
color.Green().Printfln(hr)
case "writeSite":
name := arg1
@@ -474,19 +481,19 @@ func (receiver *Panel) Handle(ctx console.Context) error {
php := cast.ToInt(arg4)
ssl := cast.ToBool(ctx.Argument(5))
if len(name) == 0 || len(path) == 0 {
color.Redln(translate.Get("commands.panel.writeSite.paramFail"))
color.Red().Printfln(translate.Get("commands.panel.writeSite.paramFail"))
return nil
}
var website models.Website
if err := facades.Orm().Query().Where("name", name).FirstOrFail(&website); err == nil {
color.Redln(translate.Get("commands.panel.writeSite.siteExist"))
color.Red().Printfln(translate.Get("commands.panel.writeSite.siteExist"))
return nil
}
_, err := os.Stat(path)
if os.IsNotExist(err) {
color.Redln(translate.Get("commands.panel.writeSite.pathNotExist"))
color.Red().Printfln(translate.Get("commands.panel.writeSite.pathNotExist"))
return nil
}
@@ -498,32 +505,32 @@ func (receiver *Panel) Handle(ctx console.Context) error {
Ssl: ssl,
})
if err != nil {
color.Redln(translate.Get("commands.panel.writeSite.fail"))
color.Red().Printfln(translate.Get("commands.panel.writeSite.fail"))
return nil
}
color.Greenln(translate.Get("commands.panel.writeSite.success"))
color.Green().Printfln(translate.Get("commands.panel.writeSite.success"))
case "deleteSite":
name := arg1
if len(name) == 0 {
color.Redln(translate.Get("commands.panel.deleteSite.paramFail"))
color.Red().Printfln(translate.Get("commands.panel.deleteSite.paramFail"))
return nil
}
_, err := facades.Orm().Query().Where("name", name).Delete(&models.Website{})
if err != nil {
color.Redln(translate.Get("commands.panel.deleteSite.fail"))
color.Red().Printfln(translate.Get("commands.panel.deleteSite.fail"))
return nil
}
color.Greenln(translate.Get("commands.panel.deleteSite.success"))
color.Green().Printfln(translate.Get("commands.panel.deleteSite.success"))
case "writeSetting":
key := arg1
value := arg2
if len(key) == 0 || len(value) == 0 {
color.Redln(translate.Get("commands.panel.writeSetting.paramFail"))
color.Red().Printfln(translate.Get("commands.panel.writeSetting.paramFail"))
return nil
}
@@ -534,16 +541,16 @@ func (receiver *Panel) Handle(ctx console.Context) error {
Value: value,
})
if err != nil {
color.Redln(translate.Get("commands.panel.writeSetting.fail"))
color.Red().Printfln(translate.Get("commands.panel.writeSetting.fail"))
return nil
}
color.Greenln(translate.Get("commands.panel.writeSetting.success"))
color.Green().Printfln(translate.Get("commands.panel.writeSetting.success"))
case "getSetting":
key := arg1
if len(key) == 0 {
color.Redln(translate.Get("commands.panel.getSetting.paramFail"))
color.Red().Printfln(translate.Get("commands.panel.getSetting.paramFail"))
return nil
}
@@ -557,17 +564,17 @@ func (receiver *Panel) Handle(ctx console.Context) error {
case "deleteSetting":
key := arg1
if len(key) == 0 {
color.Redln(translate.Get("commands.panel.deleteSetting.paramFail"))
color.Red().Printfln(translate.Get("commands.panel.deleteSetting.paramFail"))
return nil
}
_, err := facades.Orm().Query().Where("key", key).Delete(&models.Setting{})
if err != nil {
color.Redln(translate.Get("commands.panel.deleteSetting.fail"))
color.Red().Printfln(translate.Get("commands.panel.deleteSetting.fail"))
return nil
}
color.Greenln(translate.Get("commands.panel.deleteSetting.success"))
color.Green().Printfln(translate.Get("commands.panel.deleteSetting.success"))
case "addSite":
name := arg1
@@ -576,14 +583,14 @@ func (receiver *Panel) Handle(ctx console.Context) error {
path := arg4
php := arg5
if len(name) == 0 || len(domain) == 0 || len(port) == 0 || len(path) == 0 {
color.Redln(translate.Get("commands.panel.addSite.paramFail"))
color.Red().Printfln(translate.Get("commands.panel.addSite.paramFail"))
return nil
}
domains := strings.Split(domain, ",")
ports := strings.Split(port, ",")
if len(domains) == 0 || len(ports) == 0 {
color.Redln(translate.Get("commands.panel.addSite.paramFail"))
color.Red().Printfln(translate.Get("commands.panel.addSite.paramFail"))
return nil
}
@@ -595,15 +602,15 @@ func (receiver *Panel) Handle(ctx console.Context) error {
website := services.NewWebsiteImpl()
id, err := website.GetIDByName(name)
if err != nil {
color.Redln(err.Error())
color.Red().Printfln(err.Error())
return nil
}
if id != 0 {
color.Redln(translate.Get("commands.panel.addSite.siteExist"))
color.Red().Printfln(translate.Get("commands.panel.addSite.siteExist"))
return nil
}
_, err = website.Add(internal.PanelWebsite{
_, err = website.Add(types.Website{
Name: name,
Status: true,
Domains: domains,
@@ -614,108 +621,108 @@ func (receiver *Panel) Handle(ctx console.Context) error {
Db: false,
})
if err != nil {
color.Redln(err.Error())
color.Red().Printfln(err.Error())
return nil
}
color.Greenln(translate.Get("commands.panel.addSite.success"))
color.Green().Printfln(translate.Get("commands.panel.addSite.success"))
case "removeSite":
name := arg1
if len(name) == 0 {
color.Redln(translate.Get("commands.panel.removeSite.paramFail"))
color.Red().Printfln(translate.Get("commands.panel.removeSite.paramFail"))
return nil
}
website := services.NewWebsiteImpl()
id, err := website.GetIDByName(name)
if err != nil {
color.Redln(err.Error())
color.Red().Printfln(err.Error())
return nil
}
if id == 0 {
color.Redln(translate.Get("commands.panel.removeSite.siteNotExist"))
color.Red().Printfln(translate.Get("commands.panel.removeSite.siteNotExist"))
return nil
}
if err = website.Delete(id); err != nil {
color.Redln(err.Error())
color.Red().Printfln(err.Error())
return nil
}
color.Greenln(translate.Get("commands.panel.removeSite.success"))
color.Green().Printfln(translate.Get("commands.panel.removeSite.success"))
case "installPlugin":
slug := arg1
if len(slug) == 0 {
color.Redln(translate.Get("commands.panel.installPlugin.paramFail"))
color.Red().Printfln(translate.Get("commands.panel.installPlugin.paramFail"))
return nil
}
plugin := services.NewPluginImpl()
if err := plugin.Install(slug); err != nil {
color.Redln(err.Error())
color.Red().Printfln(err.Error())
return nil
}
color.Greenln(translate.Get("commands.panel.installPlugin.success"))
color.Green().Printfln(translate.Get("commands.panel.installPlugin.success"))
case "uninstallPlugin":
slug := arg1
if len(slug) == 0 {
color.Redln(translate.Get("commands.panel.uninstallPlugin.paramFail"))
color.Red().Printfln(translate.Get("commands.panel.uninstallPlugin.paramFail"))
return nil
}
plugin := services.NewPluginImpl()
if err := plugin.Uninstall(slug); err != nil {
color.Redln(err.Error())
color.Red().Printfln(err.Error())
return nil
}
color.Greenln(translate.Get("commands.panel.uninstallPlugin.success"))
color.Green().Printfln(translate.Get("commands.panel.uninstallPlugin.success"))
case "updatePlugin":
slug := arg1
if len(slug) == 0 {
color.Redln(translate.Get("commands.panel.updatePlugin.paramFail"))
color.Red().Printfln(translate.Get("commands.panel.updatePlugin.paramFail"))
return nil
}
plugin := services.NewPluginImpl()
if err := plugin.Update(slug); err != nil {
color.Redln(err.Error())
color.Red().Printfln(err.Error())
return nil
}
color.Greenln(translate.Get("commands.panel.updatePlugin.success"))
color.Green().Printfln(translate.Get("commands.panel.updatePlugin.success"))
default:
color.Yellowln(facades.Config().GetString("panel.name") + " - " + translate.Get("commands.panel.tool") + " - " + facades.Config().GetString("panel.version"))
color.Greenln(translate.Get("commands.panel.use") + "")
color.Greenln("panel update " + translate.Get("commands.panel.update.description"))
color.Greenln("panel getInfo " + translate.Get("commands.panel.getInfo.description"))
color.Greenln("panel getPort " + translate.Get("commands.panel.getPort.description"))
color.Greenln("panel getEntrance " + translate.Get("commands.panel.getEntrance.description"))
color.Greenln("panel deleteEntrance " + translate.Get("commands.panel.deleteEntrance.description"))
color.Greenln("panel cleanTask " + translate.Get("commands.panel.cleanTask.description"))
color.Greenln("panel backup {website/mysql/postgresql} {name} {path} {save_copies} " + translate.Get("commands.panel.backup.description"))
color.Greenln("panel cutoff {website_name} {save_copies} " + translate.Get("commands.panel.cutoff.description"))
color.Greenln("panel installPlugin {slug} " + translate.Get("commands.panel.installPlugin.description"))
color.Greenln("panel uninstallPlugin {slug} " + translate.Get("commands.panel.uninstallPlugin.description"))
color.Greenln("panel updatePlugin {slug} " + translate.Get("commands.panel.updatePlugin.description"))
color.Greenln("panel addSite {name} {domain} {port} {path} {php} " + translate.Get("commands.panel.addSite.description"))
color.Greenln("panel removeSite {name} " + translate.Get("commands.panel.removeSite.description"))
color.Redln(translate.Get("commands.panel.forDeveloper") + ":")
color.Yellowln("panel init " + translate.Get("commands.panel.init.description"))
color.Yellowln("panel writePlugin {slug} {version} " + translate.Get("commands.panel.writePlugin.description"))
color.Yellowln("panel deletePlugin {slug} " + translate.Get("commands.panel.deletePlugin.description"))
color.Yellowln("panel writeMysqlPassword {password} " + translate.Get("commands.panel.writeMysqlPassword.description"))
color.Yellowln("panel writeSite {name} {status} {path} {php} {ssl} " + translate.Get("commands.panel.writeSite.description"))
color.Yellowln("panel deleteSite {name} " + translate.Get("commands.panel.deleteSite.description"))
color.Yellowln("panel getSetting {name} " + translate.Get("commands.panel.getSetting.description"))
color.Yellowln("panel writeSetting {name} {value} " + translate.Get("commands.panel.writeSetting.description"))
color.Yellowln("panel deleteSetting {name} " + translate.Get("commands.panel.deleteSetting.description"))
color.Yellow().Printfln(facades.Config().GetString("panel.name") + " - " + translate.Get("commands.panel.tool") + " - " + facades.Config().GetString("panel.version"))
color.Green().Printfln(translate.Get("commands.panel.use") + "")
color.Green().Printfln("panel update " + translate.Get("commands.panel.update.description"))
color.Green().Printfln("panel getInfo " + translate.Get("commands.panel.getInfo.description"))
color.Green().Printfln("panel getPort " + translate.Get("commands.panel.getPort.description"))
color.Green().Printfln("panel getEntrance " + translate.Get("commands.panel.getEntrance.description"))
color.Green().Printfln("panel deleteEntrance " + translate.Get("commands.panel.deleteEntrance.description"))
color.Green().Printfln("panel cleanTask " + translate.Get("commands.panel.cleanTask.description"))
color.Green().Printfln("panel backup {website/mysql/postgresql} {name} {path} {save_copies} " + translate.Get("commands.panel.backup.description"))
color.Green().Printfln("panel cutoff {website_name} {save_copies} " + translate.Get("commands.panel.cutoff.description"))
color.Green().Printfln("panel installPlugin {slug} " + translate.Get("commands.panel.installPlugin.description"))
color.Green().Printfln("panel uninstallPlugin {slug} " + translate.Get("commands.panel.uninstallPlugin.description"))
color.Green().Printfln("panel updatePlugin {slug} " + translate.Get("commands.panel.updatePlugin.description"))
color.Green().Printfln("panel addSite {name} {domain} {port} {path} {php} " + translate.Get("commands.panel.addSite.description"))
color.Green().Printfln("panel removeSite {name} " + translate.Get("commands.panel.removeSite.description"))
color.Red().Printfln(translate.Get("commands.panel.forDeveloper") + ":")
color.Yellow().Printfln("panel init " + translate.Get("commands.panel.init.description"))
color.Yellow().Printfln("panel writePlugin {slug} {version} " + translate.Get("commands.panel.writePlugin.description"))
color.Yellow().Printfln("panel deletePlugin {slug} " + translate.Get("commands.panel.deletePlugin.description"))
color.Yellow().Printfln("panel writeMysqlPassword {password} " + translate.Get("commands.panel.writeMysqlPassword.description"))
color.Yellow().Printfln("panel writeSite {name} {status} {path} {php} {ssl} " + translate.Get("commands.panel.writeSite.description"))
color.Yellow().Printfln("panel deleteSite {name} " + translate.Get("commands.panel.deleteSite.description"))
color.Yellow().Printfln("panel getSetting {name} " + translate.Get("commands.panel.getSetting.description"))
color.Yellow().Printfln("panel writeSetting {name} {value} " + translate.Get("commands.panel.writeSetting.description"))
color.Yellow().Printfln("panel deleteSetting {name} " + translate.Get("commands.panel.deleteSetting.description"))
}
return nil
+7 -7
View File
@@ -8,8 +8,8 @@ import (
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/carbon"
"panel/internal"
"panel/pkg/tools"
"github.com/TheTNB/panel/pkg/tools"
"github.com/TheTNB/panel/types"
)
// PanelTask 面板每日任务
@@ -35,11 +35,11 @@ func (receiver *PanelTask) Extend() command.Extend {
// Handle Execute the console command.
func (receiver *PanelTask) Handle(console.Context) error {
internal.Status = internal.StatusMaintain
types.Status = types.StatusMaintain
// 优化数据库
if _, err := facades.Orm().Query().Exec("VACUUM"); err != nil {
internal.Status = internal.StatusFailed
types.Status = types.StatusFailed
facades.Log().Tags("面板", "每日任务").
With(map[string]any{
"error": err.Error(),
@@ -49,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 {
internal.Status = internal.StatusFailed
types.Status = types.StatusFailed
facades.Log().Tags("面板", "每日任务").
With(map[string]any{
"error": err.Error(),
@@ -59,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 {
internal.Status = internal.StatusFailed
types.Status = types.StatusFailed
facades.Log().Tags("面板", "每日任务").
With(map[string]any{
"error": err.Error(),
@@ -67,6 +67,6 @@ func (receiver *PanelTask) Handle(console.Context) error {
return err
}
internal.Status = internal.StatusNormal
types.Status = types.StatusNormal
return nil
}
+2 -2
View File
@@ -5,7 +5,7 @@ import (
"github.com/goravel/framework/contracts/schedule"
"github.com/goravel/framework/facades"
"panel/app/console/commands"
"github.com/TheTNB/panel/app/console/commands"
)
type Kernel struct {
@@ -15,7 +15,7 @@ func (kernel *Kernel) Schedule() []schedule.Event {
return []schedule.Event{
facades.Schedule().Command("panel:monitoring").EveryMinute().SkipIfStillRunning(),
facades.Schedule().Command("panel:cert-renew").DailyAt("04:00").SkipIfStillRunning(),
facades.Schedule().Command("panel:task").Daily().SkipIfStillRunning(),
facades.Schedule().Command("panel:task").DailyAt("03:30").SkipIfStillRunning(),
}
}
+3 -3
View File
@@ -7,9 +7,9 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"panel/internal"
"panel/internal/services"
"panel/pkg/tools"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/tools"
)
type AssetController struct {
+6 -6
View File
@@ -4,12 +4,12 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
requests "panel/app/http/requests/cert"
commonrequests "panel/app/http/requests/common"
"panel/app/models"
"panel/internal"
"panel/internal/services"
"panel/pkg/acme"
requests "github.com/TheTNB/panel/app/http/requests/cert"
commonrequests "github.com/TheTNB/panel/app/http/requests/common"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/acme"
)
type CertController struct {
+5 -8
View File
@@ -5,6 +5,7 @@ import (
"strconv"
"strings"
"github.com/TheTNB/panel/pkg/tools"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image"
@@ -13,11 +14,10 @@ import (
"github.com/docker/go-connections/nat"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/support/carbon"
"panel/pkg/tools"
commonrequests "panel/app/http/requests/common"
requests "panel/app/http/requests/container"
"panel/internal/services"
commonrequests "github.com/TheTNB/panel/app/http/requests/common"
requests "github.com/TheTNB/panel/app/http/requests/container"
"github.com/TheTNB/panel/internal/services"
)
type ContainerController struct {
@@ -63,11 +63,8 @@ func (r *ContainerController) ContainerList(ctx http.Context) http.Response {
endIndex = len(containers)
}
paged := containers[startIndex:endIndex]
if paged == nil {
paged = []types.Container{}
}
var items []any
items := make([]any, 0)
for _, item := range paged {
var name string
if len(item.Names) > 0 {
+4 -4
View File
@@ -9,10 +9,10 @@ import (
"github.com/goravel/framework/support/carbon"
"github.com/spf13/cast"
"panel/app/models"
"panel/internal"
"panel/internal/services"
"panel/pkg/tools"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/tools"
)
type CronController struct {
+3 -3
View File
@@ -8,12 +8,12 @@ import (
"strings"
"syscall"
commonrequests "github.com/TheTNB/panel/app/http/requests/common"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/support/carbon"
commonrequests "panel/app/http/requests/common"
requests "panel/app/http/requests/file"
"panel/pkg/tools"
requests "github.com/TheTNB/panel/app/http/requests/file"
"github.com/TheTNB/panel/pkg/tools"
)
type FileController struct {
+17 -12
View File
@@ -9,10 +9,11 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"panel/app/models"
"panel/internal"
"panel/internal/services"
"panel/pkg/tools"
"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"
)
type MenuItem struct {
@@ -34,10 +35,11 @@ func NewInfoController() *InfoController {
}
}
// Name 获取面板名称
func (r *InfoController) Name(ctx http.Context) http.Response {
// Panel 获取面板信息
func (r *InfoController) Panel(ctx http.Context) http.Response {
return Success(ctx, http.Json{
"name": r.setting.Get(models.SettingKeyName),
"name": r.setting.Get(models.SettingKeyName),
"language": facades.Config().GetString("app.locale"),
})
}
@@ -296,10 +298,13 @@ func (r *InfoController) UpdateInfo(ctx http.Context) http.Response {
// Update 更新面板
func (r *InfoController) Update(ctx http.Context) http.Response {
var task models.Task
err := facades.Orm().Query().Where("status", models.TaskStatusRunning).OrWhere("status", models.TaskStatusWaiting).FirstOrFail(&task)
if err == nil {
if err := facades.Orm().Query().Where("status", models.TaskStatusRunning).OrWhere("status", models.TaskStatusWaiting).FirstOrFail(&task); err == nil {
return Error(ctx, http.StatusInternalServerError, "当前有任务正在执行,禁止更新")
}
if _, err := facades.Orm().Query().Exec("PRAGMA wal_checkpoint(TRUNCATE)"); err != nil {
types.Status = types.StatusFailed
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("面板数据库异常,已终止操作:%s", err.Error()))
}
panel, err := tools.GetLatestPanelVersion()
if err != nil {
@@ -309,16 +314,16 @@ func (r *InfoController) Update(ctx http.Context) http.Response {
return Error(ctx, http.StatusInternalServerError, "获取最新版本失败")
}
internal.Status = internal.StatusUpgrade
types.Status = types.StatusUpgrade
if err = tools.UpdatePanel(panel); err != nil {
internal.Status = internal.StatusFailed
types.Status = types.StatusFailed
facades.Log().Request(ctx.Request()).Tags("面板", "基础信息").With(map[string]any{
"error": err.Error(),
}).Info("更新面板失败")
return Error(ctx, http.StatusInternalServerError, err.Error())
}
internal.Status = internal.StatusNormal
types.Status = types.StatusNormal
tools.RestartPanel()
return Success(ctx, nil)
}
+3 -3
View File
@@ -8,9 +8,9 @@ import (
"github.com/goravel/framework/support/carbon"
"github.com/spf13/cast"
"panel/app/models"
"panel/internal"
"panel/internal/services"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
)
type MonitorController struct {
+3 -3
View File
@@ -4,9 +4,9 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"panel/app/models"
"panel/internal"
"panel/internal/services"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
)
type PluginController struct {
@@ -8,12 +8,12 @@ import (
"github.com/goravel/framework/facades"
"github.com/spf13/cast"
"panel/app/http/controllers"
"panel/app/models"
"panel/internal"
"panel/internal/services"
"panel/pkg/tools"
"panel/types"
"github.com/TheTNB/panel/app/http/controllers"
"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"
)
type Fail2banController struct {
@@ -0,0 +1,251 @@
package plugins
import (
"fmt"
"github.com/goravel/framework/contracts/http"
"github.com/TheTNB/panel/app/http/controllers"
requests "github.com/TheTNB/panel/app/http/requests/plugins/frp"
"github.com/TheTNB/panel/pkg/tools"
)
type FrpController struct {
}
func NewFrpController() *FrpController {
return &FrpController{}
}
// Status
//
// @Summary 服务状态
// @Description 获取 Frp 服务状态
// @Tags 插件-Frp
// @Produce json
// @Security BearerToken
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/frp/status [get]
func (r *FrpController) Status(ctx http.Context) http.Response {
frps, err := tools.ServiceStatus("frps")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取 frps 服务运行状态失败")
}
frpc, err := tools.ServiceStatus("frpc")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取 frpc 服务运行状态失败")
}
return controllers.Success(ctx, http.Json{
"frps": frps,
"frpc": frpc,
})
}
// IsEnabled
//
// @Summary 是否启用服务
// @Description 获取是否启用 Frp 服务
// @Tags 插件-Frp
// @Produce json
// @Security BearerToken
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/frp/isEnabled [get]
func (r *FrpController) IsEnabled(ctx http.Context) http.Response {
frps, err := tools.ServiceIsEnabled("frps")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取 frps 服务启用状态失败")
}
frpc, err := tools.ServiceIsEnabled("frpc")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取 frpc 服务启用状态失败")
}
return controllers.Success(ctx, http.Json{
"frps": frps,
"frpc": frpc,
})
}
// Enable
//
// @Summary 启用服务
// @Description 启用 Frp 服务
// @Tags 插件-Frp
// @Produce json
// @Security BearerToken
// @Param data body requests.Service true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/frp/enable [post]
func (r *FrpController) Enable(ctx http.Context) http.Response {
var serviceRequest requests.Service
sanitize := controllers.Sanitize(ctx, &serviceRequest)
if sanitize != nil {
return sanitize
}
if err := tools.ServiceEnable(serviceRequest.Service); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("启用 %s 服务失败", serviceRequest.Service))
}
return controllers.Success(ctx, nil)
}
// Disable
//
// @Summary 禁用服务
// @Description 禁用 Frp 服务
// @Tags 插件-Frp
// @Produce json
// @Security BearerToken
// @Param data body requests.Service true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/frp/disable [post]
func (r *FrpController) Disable(ctx http.Context) http.Response {
var serviceRequest requests.Service
sanitize := controllers.Sanitize(ctx, &serviceRequest)
if sanitize != nil {
return sanitize
}
if err := tools.ServiceDisable(serviceRequest.Service); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("禁用 %s 服务失败", serviceRequest.Service))
}
return controllers.Success(ctx, nil)
}
// Restart
//
// @Summary 重启服务
// @Description 重启 Frp 服务
// @Tags 插件-Frp
// @Produce json
// @Security BearerToken
// @param data body requests.Service true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/frp/restart [post]
func (r *FrpController) Restart(ctx http.Context) http.Response {
var serviceRequest requests.Service
sanitize := controllers.Sanitize(ctx, &serviceRequest)
if sanitize != nil {
return sanitize
}
if err := tools.ServiceRestart(serviceRequest.Service); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("重启 %s 服务失败", serviceRequest.Service))
}
return controllers.Success(ctx, nil)
}
// Start
//
// @Summary 启动服务
// @Description 启动 Frp 服务
// @Tags 插件-Frp
// @Produce json
// @Security BearerToken
// @Param data body requests.Service true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/frp/start [post]
func (r *FrpController) Start(ctx http.Context) http.Response {
var serviceRequest requests.Service
sanitize := controllers.Sanitize(ctx, &serviceRequest)
if sanitize != nil {
return sanitize
}
if err := tools.ServiceStart(serviceRequest.Service); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("启动 %s 服务失败", serviceRequest.Service))
}
status, err := tools.ServiceStatus(serviceRequest.Service)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("获取 %s 服务运行状态失败", serviceRequest.Service))
}
return controllers.Success(ctx, status)
}
// Stop
//
// @Summary 停止服务
// @Description 停止 Frp 服务
// @Tags 插件-Frp
// @Produce json
// @Security BearerToken
// @Param data body requests.Service true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/frp/stop [post]
func (r *FrpController) Stop(ctx http.Context) http.Response {
var serviceRequest requests.Service
sanitize := controllers.Sanitize(ctx, &serviceRequest)
if sanitize != nil {
return sanitize
}
if err := tools.ServiceStop(serviceRequest.Service); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("停止 %s 服务失败", serviceRequest.Service))
}
status, err := tools.ServiceStatus(serviceRequest.Service)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("获取 %s 服务运行状态失败", serviceRequest.Service))
}
return controllers.Success(ctx, !status)
}
// GetConfig
//
// @Summary 获取配置
// @Description 获取 Frp 配置
// @Tags 插件-Frp
// @Produce json
// @Security BearerToken
// @Param service query string false "服务"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/frp/config [get]
func (r *FrpController) GetConfig(ctx http.Context) http.Response {
var serviceRequest requests.Service
sanitize := controllers.Sanitize(ctx, &serviceRequest)
if sanitize != nil {
return sanitize
}
config, err := tools.Read(fmt.Sprintf("/www/server/frp/%s.toml", serviceRequest.Service))
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, config)
}
// UpdateConfig
//
// @Summary 更新配置
// @Description 更新 Frp 配置
// @Tags 插件-Frp
// @Produce json
// @Security BearerToken
// @Param data body requests.UpdateConfig true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/frp/config [post]
func (r *FrpController) UpdateConfig(ctx http.Context) http.Response {
var updateRequest requests.UpdateConfig
sanitize := controllers.Sanitize(ctx, &updateRequest)
if sanitize != nil {
return sanitize
}
if err := tools.Write(fmt.Sprintf("/www/server/frp/%s.toml", updateRequest.Service), updateRequest.Config, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err := tools.ServiceRestart(updateRequest.Service); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
@@ -8,11 +8,12 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/spf13/cast"
"panel/app/http/controllers"
"panel/app/models"
"panel/internal"
"panel/internal/services"
"panel/pkg/tools"
"github.com/TheTNB/panel/app/http/controllers"
"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"
)
type MySQLController struct {
@@ -377,7 +378,7 @@ func (r *MySQLController) BackupList(ctx http.Context) http.Response {
if startIndex > len(backupList) {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []internal.BackupFile{},
"items": []types.BackupFile{},
})
}
if endIndex > len(backupList) {
@@ -385,7 +386,7 @@ func (r *MySQLController) BackupList(ctx http.Context) http.Response {
}
pagedBackupList := backupList[startIndex:endIndex]
if pagedBackupList == nil {
pagedBackupList = []internal.BackupFile{}
pagedBackupList = []types.BackupFile{}
}
return controllers.Success(ctx, http.Json{
@@ -8,8 +8,8 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/spf13/cast"
"panel/app/http/controllers"
"panel/pkg/tools"
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/pkg/tools"
)
type OpenRestyController struct {
@@ -1,10 +1,10 @@
package plugins
import (
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/goravel/framework/contracts/http"
"panel/app/http/controllers"
"panel/internal"
"panel/internal/services"
)
type PHPController struct {
@@ -9,8 +9,8 @@ import (
"github.com/goravel/framework/facades"
"github.com/spf13/cast"
"panel/app/http/controllers"
"panel/pkg/tools"
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/pkg/tools"
)
type PhpMyAdminController struct {
@@ -6,12 +6,12 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/support/carbon"
"panel/app/http/controllers"
"panel/app/models"
"panel/internal"
"panel/internal/services"
"panel/pkg/tools"
"panel/types"
"github.com/TheTNB/panel/app/http/controllers"
"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"
)
type Postgresql15Controller struct {
@@ -321,7 +321,7 @@ func (r *Postgresql15Controller) BackupList(ctx http.Context) http.Response {
if startIndex > len(backupList) {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []internal.BackupFile{},
"items": []types.BackupFile{},
})
}
if endIndex > len(backupList) {
@@ -329,7 +329,7 @@ func (r *Postgresql15Controller) BackupList(ctx http.Context) http.Response {
}
pagedBackupList := backupList[startIndex:endIndex]
if pagedBackupList == nil {
pagedBackupList = []internal.BackupFile{}
pagedBackupList = []types.BackupFile{}
}
return controllers.Success(ctx, http.Json{
@@ -6,12 +6,12 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/support/carbon"
"panel/app/http/controllers"
"panel/app/models"
"panel/internal"
"panel/internal/services"
"panel/pkg/tools"
"panel/types"
"github.com/TheTNB/panel/app/http/controllers"
"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"
)
type Postgresql16Controller struct {
@@ -321,7 +321,7 @@ func (r *Postgresql16Controller) BackupList(ctx http.Context) http.Response {
if startIndex > len(backupList) {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []internal.BackupFile{},
"items": []types.BackupFile{},
})
}
if endIndex > len(backupList) {
@@ -329,7 +329,7 @@ func (r *Postgresql16Controller) BackupList(ctx http.Context) http.Response {
}
pagedBackupList := backupList[startIndex:endIndex]
if pagedBackupList == nil {
pagedBackupList = []internal.BackupFile{}
pagedBackupList = []types.BackupFile{}
}
return controllers.Success(ctx, http.Json{
@@ -7,8 +7,8 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/spf13/cast"
"panel/app/http/controllers"
"panel/pkg/tools"
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/pkg/tools"
)
type PureFtpdController struct {
@@ -5,9 +5,9 @@ import (
"github.com/goravel/framework/contracts/http"
"panel/app/http/controllers"
"panel/pkg/tools"
"panel/types"
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/pkg/tools"
"github.com/TheTNB/panel/types"
)
type RedisController struct {
@@ -6,23 +6,18 @@ import (
"github.com/goravel/framework/contracts/http"
"panel/app/http/controllers"
commonrequests "panel/app/http/requests/common"
requests "panel/app/http/requests/plugins/rsync"
"panel/internal"
"panel/internal/services"
"panel/pkg/tools"
"panel/types"
"github.com/TheTNB/panel/app/http/controllers"
commonrequests "github.com/TheTNB/panel/app/http/requests/common"
requests "github.com/TheTNB/panel/app/http/requests/plugins/rsync"
"github.com/TheTNB/panel/pkg/tools"
"github.com/TheTNB/panel/types"
)
type RsyncController struct {
setting internal.Setting
}
func NewRsyncController() *RsyncController {
return &RsyncController{
setting: services.NewSettingImpl(),
}
return &RsyncController{}
}
// Status
@@ -8,11 +8,11 @@ import (
"github.com/goravel/framework/support/json"
"github.com/spf13/cast"
"panel/app/http/controllers"
"panel/internal"
"panel/internal/services"
"panel/pkg/tools"
"panel/types"
"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"
)
type S3fsController struct {
@@ -4,10 +4,10 @@ import (
"strconv"
"strings"
"github.com/TheTNB/panel/pkg/tools"
"github.com/goravel/framework/contracts/http"
"panel/pkg/tools"
"panel/app/http/controllers"
"github.com/TheTNB/panel/app/http/controllers"
)
type SupervisorController struct {
@@ -7,8 +7,8 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/spf13/cast"
"panel/app/http/controllers"
"panel/pkg/tools"
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/pkg/tools"
)
type ToolBoxController struct {
+3 -3
View File
@@ -4,11 +4,11 @@ import (
"regexp"
"strings"
commonrequests "github.com/TheTNB/panel/app/http/requests/common"
"github.com/goravel/framework/contracts/http"
"github.com/spf13/cast"
commonrequests "panel/app/http/requests/common"
"panel/pkg/tools"
"github.com/TheTNB/panel/pkg/tools"
)
type SafeController struct {
@@ -302,7 +302,7 @@ func (r *SafeController) GetSshPort(ctx http.Context) http.Response {
return Error(ctx, http.StatusInternalServerError, out)
}
return Success(ctx, out)
return Success(ctx, cast.ToInt(out))
}
// SetSshPort 设置 SSH 端口
+20 -7
View File
@@ -5,11 +5,11 @@ import (
"github.com/goravel/framework/facades"
"github.com/spf13/cast"
requests "panel/app/http/requests/setting"
"panel/app/models"
"panel/internal"
"panel/internal/services"
"panel/pkg/tools"
requests "github.com/TheTNB/panel/app/http/requests/setting"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/tools"
)
type SettingController struct {
@@ -60,6 +60,7 @@ func (r *SettingController) List(ctx http.Context) http.Response {
return Success(ctx, http.Json{
"name": r.setting.Get(models.SettingKeyName),
"language": facades.Config().GetString("app.locale"),
"entrance": facades.Config().GetString("http.entrance"),
"ssl": facades.Config().GetBool("panel.ssl"),
"website_path": r.setting.Get(models.SettingKeyWebsitePath),
@@ -189,7 +190,6 @@ func (r *SettingController) Update(ctx http.Context) http.Response {
}).Info("获取面板入口失败")
return ErrorSystem(ctx)
}
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 {
@@ -197,6 +197,19 @@ func (r *SettingController) Update(ctx http.Context) http.Response {
}
}
oldLanguage, err := tools.Exec(`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(),
}).Info("获取面板语言失败")
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 {
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)
@@ -207,7 +220,7 @@ func (r *SettingController) Update(ctx http.Context) http.Response {
}
}
if oldPort != port || oldEntrance != entrance || updateRequest.SSL != facades.Config().GetBool("panel.ssl") {
if oldPort != port || oldEntrance != entrance || oldLanguage != updateRequest.Language || updateRequest.SSL != facades.Config().GetBool("panel.ssl") {
tools.RestartPanel()
}
+5 -5
View File
@@ -12,10 +12,10 @@ import (
"github.com/gorilla/websocket"
"github.com/spf13/cast"
"panel/app/models"
"panel/internal"
"panel/internal/services"
"panel/pkg/ssh"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/ssh"
)
type SshController struct {
@@ -103,7 +103,7 @@ func (r *SshController) Session(ctx http.Context) http.Response {
}
defer ws.Close()
config := ssh.SSHClientConfigPassword(
config := ssh.ClientConfigPassword(
r.setting.Get(models.SettingKeySshHost)+":"+r.setting.Get(models.SettingKeySshPort),
r.setting.Get(models.SettingKeySshUser),
r.setting.Get(models.SettingKeySshPassword),
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"github.com/goravel/framework/facades"
"github.com/swaggo/http-swagger/v2"
_ "panel/docs"
_ "github.com/TheTNB/panel/docs"
)
type SwaggerController struct {
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"panel/app/models"
"panel/pkg/tools"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/pkg/tools"
)
type TaskController struct {
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"panel/app/http/requests/user"
"panel/app/models"
"github.com/TheTNB/panel/app/http/requests/user"
"github.com/TheTNB/panel/app/models"
)
type UserController struct {
+12 -11
View File
@@ -8,12 +8,13 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
commonrequests "panel/app/http/requests/common"
requests "panel/app/http/requests/website"
"panel/app/models"
"panel/internal"
"panel/internal/services"
"panel/pkg/tools"
commonrequests "github.com/TheTNB/panel/app/http/requests/common"
requests "github.com/TheTNB/panel/app/http/requests/website"
"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"
)
type WebsiteController struct {
@@ -83,7 +84,7 @@ func (r *WebsiteController) Add(ctx http.Context) http.Response {
addRequest.Path = r.setting.Get(models.SettingKeyWebsitePath) + "/" + addRequest.Name
}
website := internal.PanelWebsite{
website := types.Website{
Name: addRequest.Name,
Status: true,
Domains: addRequest.Domains,
@@ -205,7 +206,7 @@ func (r *WebsiteController) SaveDefaultConfig(ctx http.Context) http.Response {
// @Produce json
// @Security BearerToken
// @Param id path int true "网站 ID"
// @Success 200 {object} SuccessResponse{data=internal.PanelWebsite}
// @Success 200 {object} SuccessResponse{data=types.Website}
// @Router /panel/websites/{id}/config [get]
func (r *WebsiteController) GetConfig(ctx http.Context) http.Response {
var idRequest requests.ID
@@ -328,7 +329,7 @@ func (r *WebsiteController) UpdateRemark(ctx http.Context) http.Response {
// @Produce json
// @Security BearerToken
// @Param data query commonrequests.Paginate true "request"
// @Success 200 {object} SuccessResponse{data=[]internal.BackupFile}
// @Success 200 {object} SuccessResponse{data=[]types.BackupFile}
// @Router /panel/website/backupList [get]
func (r *WebsiteController) BackupList(ctx http.Context) http.Response {
var paginateRequest commonrequests.Paginate
@@ -350,7 +351,7 @@ func (r *WebsiteController) BackupList(ctx http.Context) http.Response {
if startIndex > len(backupList) {
return Success(ctx, http.Json{
"total": 0,
"items": []internal.BackupFile{},
"items": []types.BackupFile{},
})
}
if endIndex > len(backupList) {
@@ -358,7 +359,7 @@ func (r *WebsiteController) BackupList(ctx http.Context) http.Response {
}
pagedBackupList := backupList[startIndex:endIndex]
if pagedBackupList == nil {
pagedBackupList = []internal.BackupFile{}
pagedBackupList = []types.BackupFile{}
}
return Success(ctx, http.Json{
+1 -1
View File
@@ -3,7 +3,7 @@ package http
import (
"github.com/goravel/framework/contracts/http"
"panel/app/http/middleware"
"github.com/TheTNB/panel/app/http/middleware"
)
type Kernel struct {
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"github.com/goravel/framework/contracts/translation"
"github.com/goravel/framework/facades"
"panel/internal/services"
"github.com/TheTNB/panel/internal/services"
)
// MustInstall 确保已安装插件
+6 -6
View File
@@ -4,30 +4,30 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"panel/internal"
"github.com/TheTNB/panel/types"
)
// Status 检查程序状态
func Status() http.Middleware {
return func(ctx http.Context) {
translate := facades.Lang(ctx)
switch internal.Status {
case internal.StatusUpgrade:
switch types.Status {
case types.StatusUpgrade:
ctx.Request().AbortWithStatusJson(http.StatusServiceUnavailable, http.Json{
"message": translate.Get("status.upgrade"),
})
return
case internal.StatusMaintain:
case types.StatusMaintain:
ctx.Request().AbortWithStatusJson(http.StatusServiceUnavailable, http.Json{
"message": translate.Get("status.maintain"),
})
return
case internal.StatusClosed:
case types.StatusClosed:
ctx.Request().AbortWithStatusJson(http.StatusForbidden, http.Json{
"message": translate.Get("status.closed"),
})
return
case internal.StatusFailed:
case types.StatusFailed:
ctx.Request().AbortWithStatusJson(http.StatusInternalServerError, http.Json{
"message": translate.Get("status.failed"),
})
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
"panel/pkg/acme"
"github.com/TheTNB/panel/pkg/acme"
)
type DNSStore struct {
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
"panel/pkg/acme"
"github.com/TheTNB/panel/pkg/acme"
)
type DNSUpdate struct {
@@ -4,7 +4,7 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
"panel/types"
"github.com/TheTNB/panel/types"
)
type ContainerCreate struct {
@@ -4,7 +4,7 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
"panel/types"
"github.com/TheTNB/panel/types"
)
type ContainerUpdate struct {
@@ -4,7 +4,7 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
"panel/types"
"github.com/TheTNB/panel/types"
)
type NetworkCreate struct {
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
"panel/types"
"github.com/TheTNB/panel/types"
)
type VolumeCreate struct {
+32
View File
@@ -0,0 +1,32 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type Service struct {
Service string `form:"service" json:"service"`
}
func (r *Service) Authorize(ctx http.Context) error {
return nil
}
func (r *Service) Rules(ctx http.Context) map[string]string {
return map[string]string{
"service": "required|string|in:frps,frpc",
}
}
func (r *Service) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Service) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Service) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
@@ -0,0 +1,34 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type UpdateConfig struct {
Service string `form:"service" json:"service"`
Config string `form:"config" json:"config"`
}
func (r *UpdateConfig) Authorize(ctx http.Context) error {
return nil
}
func (r *UpdateConfig) Rules(ctx http.Context) map[string]string {
return map[string]string{
"service": "required|string|in:frps,frpc",
"config": "required|string",
}
}
func (r *UpdateConfig) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UpdateConfig) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UpdateConfig) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
+2
View File
@@ -7,6 +7,7 @@ import (
type Update struct {
Name string `form:"name" json:"name"`
Language string `form:"language" json:"language"`
Port uint `form:"port" json:"port" filter:"uint"`
BackupPath string `form:"backup_path" json:"backup_path"`
WebsitePath string `form:"website_path" json:"website_path"`
@@ -24,6 +25,7 @@ func (r *Update) Authorize(ctx http.Context) error {
func (r *Update) Rules(ctx http.Context) map[string]string {
return map[string]string{
"name": "required|string:2,20",
"language": "required|in:zh_CN,en",
"port": "required|int:1000,65535",
"backup_path": "required|string:2,255",
"website_path": "required|string:2,255",
+33 -37
View File
@@ -1,12 +1,10 @@
package jobs
import (
"os/exec"
"time"
"github.com/TheTNB/panel/pkg/tools"
"github.com/goravel/framework/facades"
"panel/app/models"
"github.com/TheTNB/panel/app/models"
)
// ProcessTask 处理面板任务
@@ -22,63 +20,61 @@ func (receiver *ProcessTask) Signature() string {
func (receiver *ProcessTask) Handle(args ...any) error {
taskID, ok := args[0].(uint)
if !ok {
facades.Log().Info("[面板][ProcessTask] 任务ID参数错误")
facades.Log().Tags("面板", "异步任务").With(map[string]any{
"args": args,
}).Infof("参数错误")
return nil
}
for {
if !haveRunningTask() {
break
}
time.Sleep(5 * time.Second)
}
var task models.Task
if err := facades.Orm().Query().Where("id = ?", taskID).Get(&task); err != nil {
facades.Log().Infof("[面板][ProcessTask] 获取任务%d失败: %s", taskID, err.Error())
facades.Log().Tags("面板", "异步任务").With(map[string]any{
"task_id": taskID,
"error": err.Error(),
}).Infof("获取任务失败")
return nil
}
task.Status = models.TaskStatusRunning
if err := facades.Orm().Query().Save(&task); err != nil {
facades.Log().Infof("[面板][ProcessTask] 更新任务%d失败: %s", taskID, err.Error())
facades.Log().Tags("面板", "异步任务").With(map[string]any{
"task_id": taskID,
"error": err.Error(),
}).Infof("更新任务失败")
return nil
}
facades.Log().Infof("[面板][ProcessTask] 开始执行任务%d", taskID)
cmd := exec.Command("bash", "-c", task.Shell)
err := cmd.Run()
if err != nil {
facades.Log().Tags("面板", "异步任务").With(map[string]any{
"task_id": taskID,
}).Infof("开始执行任务")
if _, err := tools.Exec(task.Shell); err != nil {
task.Status = models.TaskStatusFailed
if err := facades.Orm().Query().Save(&task); err != nil {
facades.Log().Infof("[面板][ProcessTask] 更新任务%d失败: %s", taskID, err.Error())
facades.Log().Tags("面板", "异步任务").With(map[string]any{
"task_id": taskID,
"error": err.Error(),
}).Infof("更新任务失败")
return nil
}
facades.Log().Infof("[面板][ProcessTask] 任务%d执行失败: %s", taskID, err.Error())
facades.Log().Tags("面板", "异步任务").With(map[string]any{
"task_id": taskID,
"error": err.Error(),
}).Infof("执行任务失败")
return nil
}
task.Status = models.TaskStatusSuccess
if err := facades.Orm().Query().Save(&task); err != nil {
facades.Log().Infof("[面板][ProcessTask] 更新任务%d失败: %s", taskID, err.Error())
facades.Log().Tags("面板", "异步任务").With(map[string]any{
"task_id": taskID,
"error": err.Error(),
}).Infof("更新任务失败")
return nil
}
facades.Log().Infof("[面板][ProcessTask] 任务%d执行成功", taskID)
facades.Log().Tags("面板", "异步任务").With(map[string]any{
"task_id": taskID,
}).Infof("执行任务成功")
return nil
}
// haveRunningTask 是否有任务正在执行
func haveRunningTask() bool {
var task models.Task
if err := facades.Orm().Query().Where("status = ?", models.TaskStatusRunning).Get(&task); err != nil {
facades.Log().Info("[面板][ProcessTask] 获取任务失败: " + err.Error())
return true
}
if task.ID != 0 {
return true
}
return false
}
+1 -1
View File
@@ -3,7 +3,7 @@ package models
import (
"github.com/goravel/framework/support/carbon"
"panel/pkg/acme"
"github.com/TheTNB/panel/pkg/acme"
)
type CertDNS struct {
+1 -1
View File
@@ -3,7 +3,7 @@ package models
import (
"github.com/goravel/framework/support/carbon"
"panel/pkg/tools"
"github.com/TheTNB/panel/pkg/tools"
)
type Monitor struct {
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"github.com/goravel/framework/contracts/foundation"
"github.com/goravel/framework/facades"
"panel/app/console"
"github.com/TheTNB/panel/app/console"
)
type ConsoleServiceProvider struct {
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"github.com/goravel/framework/contracts/foundation"
"github.com/goravel/framework/facades"
"panel/database/seeders"
"github.com/TheTNB/panel/database/seeders"
)
type DatabaseServiceProvider struct {
+4 -2
View File
@@ -5,14 +5,16 @@ import (
"github.com/goravel/framework/contracts/queue"
"github.com/goravel/framework/facades"
"panel/app/jobs"
"github.com/TheTNB/panel/app/jobs"
)
type QueueServiceProvider struct {
}
func (receiver *QueueServiceProvider) Register(app foundation.Application) {
facades.Queue().Register(receiver.Jobs())
if err := facades.Queue().Register(receiver.Jobs()); err != nil {
panic(err.Error())
}
}
func (receiver *QueueServiceProvider) Boot(app foundation.Application) {
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"github.com/goravel/framework/contracts/foundation"
"github.com/goravel/framework/facades"
"panel/app/http"
"panel/routes"
"github.com/TheTNB/panel/app/http"
"github.com/TheTNB/panel/routes"
)
type RouteServiceProvider struct {
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"github.com/goravel/framework/contracts/validation"
"github.com/goravel/framework/facades"
"panel/app/rules"
"github.com/TheTNB/panel/app/rules"
)
type ValidationServiceProvider struct {
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"github.com/goravel/framework/contracts/validation"
"panel/pkg/captcha"
"github.com/TheTNB/panel/pkg/captcha"
)
type Captcha struct {
+1 -1
View File
@@ -1,9 +1,9 @@
package rules
import (
"github.com/TheTNB/panel/pkg/tools"
"github.com/goravel/framework/contracts/validation"
"github.com/spf13/cast"
"panel/pkg/tools"
)
type PathExists struct {
+1 -1
View File
@@ -1,9 +1,9 @@
package rules
import (
"github.com/TheTNB/panel/pkg/tools"
"github.com/goravel/framework/contracts/validation"
"github.com/spf13/cast"
"panel/pkg/tools"
)
type PathNotExists struct {
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"github.com/gookit/validate/locales/zhcn"
"github.com/goravel/framework/foundation"
"panel/config"
"github.com/TheTNB/panel/config"
)
func Boot() {
+1 -1
View File
@@ -23,7 +23,7 @@ import (
"github.com/goravel/framework/validation"
"github.com/goravel/gin"
"panel/app/providers"
"github.com/TheTNB/panel/app/providers"
)
// Boot Start all init methods of the current folder to bootstrap all config.
+1 -1
View File
@@ -8,7 +8,7 @@ func init() {
config := facades.Config()
config.Add("panel", map[string]any{
"name": "耗子 Linux 面板",
"version": "v2.2.2",
"version": "v2.2.4",
"ssl": config.Env("APP_SSL", false),
})
}
+4 -4
View File
@@ -8,15 +8,15 @@ func init() {
config := facades.Config()
config.Add("queue", map[string]any{
// Default Queue Connection Name
"default": "sync",
"default": "async",
// Queue Connections
//
// Here you may configure the connection information for each server that is used by your application.
// Drivers: "sync", "redis"
// Drivers: "sync", "async", "custom"
"connections": map[string]any{
"sync": map[string]any{
"driver": "sync",
"async": map[string]any{
"driver": "async",
},
},
})
+411 -117
View File
@@ -11,7 +11,7 @@ const docTemplate = `{
"title": "{{.Title}}",
"contact": {
"name": "耗子科技",
"email": "i@haozi.net"
"email": "admin@haozi.net"
},
"license": {
"name": "GNU Affero General Public License v3"
@@ -1122,7 +1122,7 @@ const docTemplate = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_container.ID"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID"
}
}
],
@@ -1190,7 +1190,7 @@ const docTemplate = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_container.ID"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID"
}
}
],
@@ -1538,7 +1538,7 @@ const docTemplate = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_container.ID"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID"
}
}
],
@@ -1599,7 +1599,7 @@ const docTemplate = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_container.ID"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID"
}
}
],
@@ -1671,7 +1671,7 @@ const docTemplate = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_container.ID"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID"
}
}
],
@@ -1741,7 +1741,7 @@ const docTemplate = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_container.ID"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID"
}
}
],
@@ -1809,7 +1809,7 @@ const docTemplate = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_container.ID"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID"
}
}
],
@@ -1845,7 +1845,7 @@ const docTemplate = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_container.ID"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID"
}
}
],
@@ -2046,7 +2046,7 @@ const docTemplate = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_container.ID"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID"
}
}
],
@@ -2693,7 +2693,7 @@ const docTemplate = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_setting.Update"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_setting.Update"
}
}
],
@@ -2819,7 +2819,7 @@ const docTemplate = `{
"data": {
"type": "array",
"items": {
"$ref": "#/definitions/internal.BackupFile"
"$ref": "#/definitions/types.BackupFile"
}
}
}
@@ -3136,7 +3136,7 @@ const docTemplate = `{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/internal.PanelWebsite"
"$ref": "#/definitions/types.Website"
}
}
}
@@ -3412,6 +3412,278 @@ const docTemplate = `{
}
}
},
"/plugins/frp/config": {
"get": {
"security": [
{
"BearerToken": []
}
],
"description": "获取 Frp 配置",
"produces": [
"application/json"
],
"tags": [
"插件-Frp"
],
"summary": "获取配置",
"parameters": [
{
"type": "string",
"description": "服务",
"name": "service",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
},
"post": {
"security": [
{
"BearerToken": []
}
],
"description": "更新 Frp 配置",
"produces": [
"application/json"
],
"tags": [
"插件-Frp"
],
"summary": "更新配置",
"parameters": [
{
"description": "request",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_plugins_frp.UpdateConfig"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
}
},
"/plugins/frp/disable": {
"post": {
"security": [
{
"BearerToken": []
}
],
"description": "禁用 Frp 服务",
"produces": [
"application/json"
],
"tags": [
"插件-Frp"
],
"summary": "禁用服务",
"parameters": [
{
"description": "request",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.Service"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
}
},
"/plugins/frp/enable": {
"post": {
"security": [
{
"BearerToken": []
}
],
"description": "启用 Frp 服务",
"produces": [
"application/json"
],
"tags": [
"插件-Frp"
],
"summary": "启用服务",
"parameters": [
{
"description": "request",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.Service"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
}
},
"/plugins/frp/restart": {
"post": {
"security": [
{
"BearerToken": []
}
],
"description": "重启 Frp 服务",
"produces": [
"application/json"
],
"tags": [
"插件-Frp"
],
"summary": "重启服务",
"parameters": [
{
"description": "request",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.Service"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
}
},
"/plugins/frp/start": {
"post": {
"security": [
{
"BearerToken": []
}
],
"description": "启动 Frp 服务",
"produces": [
"application/json"
],
"tags": [
"插件-Frp"
],
"summary": "启动服务",
"parameters": [
{
"description": "request",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.Service"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
}
},
"/plugins/frp/status": {
"get": {
"security": [
{
"BearerToken": []
}
],
"description": "获取 Frp 服务状态",
"produces": [
"application/json"
],
"tags": [
"插件-Frp"
],
"summary": "服务状态",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
}
},
"/plugins/frp/stop": {
"post": {
"security": [
{
"BearerToken": []
}
],
"description": "停止 Frp 服务",
"produces": [
"application/json"
],
"tags": [
"插件-Frp"
],
"summary": "停止服务",
"parameters": [
{
"description": "request",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.Service"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
}
},
"/plugins/rsync/config": {
"get": {
"security": [
@@ -3457,7 +3729,7 @@ const docTemplate = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.UpdateConfig"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_plugins_rsync.UpdateConfig"
}
}
],
@@ -3571,7 +3843,7 @@ const docTemplate = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_plugins_rsync.Update"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_plugins_rsync.Update"
}
}
],
@@ -3784,64 +4056,88 @@ const docTemplate = `{
}
}
},
"internal.BackupFile": {
"github_com_TheTNB_panel_app_http_requests_container.ID": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"size": {
"id": {
"type": "string"
}
}
},
"internal.PanelWebsite": {
"github_com_TheTNB_panel_app_http_requests_plugins_frp.UpdateConfig": {
"type": "object",
"properties": {
"db": {
"type": "boolean"
},
"db_name": {
"config": {
"type": "string"
},
"db_password": {
"service": {
"type": "string"
}
}
},
"github_com_TheTNB_panel_app_http_requests_plugins_rsync.Update": {
"type": "object",
"properties": {
"auth_user": {
"type": "string"
},
"db_type": {
"comment": {
"type": "string"
},
"db_user": {
"hosts_allow": {
"type": "string"
},
"domains": {
"type": "array",
"items": {
"type": "string"
}
},
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"php": {
"secret": {
"type": "string"
}
}
},
"github_com_TheTNB_panel_app_http_requests_plugins_rsync.UpdateConfig": {
"type": "object",
"properties": {
"config": {
"type": "string"
}
}
},
"github_com_TheTNB_panel_app_http_requests_setting.Update": {
"type": "object",
"properties": {
"backup_path": {
"type": "string"
},
"ports": {
"type": "array",
"items": {
"type": "integer"
}
},
"remark": {
"email": {
"type": "string"
},
"entrance": {
"type": "string"
},
"language": {
"type": "string"
},
"name": {
"type": "string"
},
"password": {
"type": "string"
},
"port": {
"type": "integer"
},
"ssl": {
"type": "boolean"
},
"status": {
"type": "boolean"
"username": {
"type": "string"
},
"website_path": {
"type": "string"
}
}
},
@@ -3999,69 +4295,6 @@ const docTemplate = `{
}
}
},
"panel_app_http_requests_container.ID": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
}
},
"panel_app_http_requests_plugins_rsync.Update": {
"type": "object",
"properties": {
"auth_user": {
"type": "string"
},
"comment": {
"type": "string"
},
"hosts_allow": {
"type": "string"
},
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"secret": {
"type": "string"
}
}
},
"panel_app_http_requests_setting.Update": {
"type": "object",
"properties": {
"backup_path": {
"type": "string"
},
"email": {
"type": "string"
},
"entrance": {
"type": "string"
},
"name": {
"type": "string"
},
"password": {
"type": "string"
},
"port": {
"type": "integer"
},
"ssl": {
"type": "boolean"
},
"username": {
"type": "string"
},
"website_path": {
"type": "string"
}
}
},
"requests.Add": {
"type": "object",
"properties": {
@@ -4274,10 +4507,10 @@ const docTemplate = `{
"requests.Copy": {
"type": "object",
"properties": {
"new": {
"source": {
"type": "string"
},
"old": {
"target": {
"type": "string"
}
}
@@ -4562,6 +4795,14 @@ const docTemplate = `{
}
}
},
"requests.Service": {
"type": "object",
"properties": {
"service": {
"type": "string"
}
}
},
"requests.UnArchive": {
"type": "object",
"properties": {
@@ -4573,14 +4814,6 @@ const docTemplate = `{
}
}
},
"requests.UpdateConfig": {
"type": "object",
"properties": {
"config": {
"type": "string"
}
}
},
"requests.UserStore": {
"type": "object",
"properties": {
@@ -4647,6 +4880,17 @@ const docTemplate = `{
}
}
},
"types.BackupFile": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"size": {
"type": "string"
}
}
},
"types.ContainerNetwork": {
"type": "object",
"properties": {
@@ -4711,6 +4955,56 @@ const docTemplate = `{
"type": "string"
}
}
},
"types.Website": {
"type": "object",
"properties": {
"db": {
"type": "boolean"
},
"db_name": {
"type": "string"
},
"db_password": {
"type": "string"
},
"db_type": {
"type": "string"
},
"db_user": {
"type": "string"
},
"domains": {
"type": "array",
"items": {
"type": "string"
}
},
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"php": {
"type": "string"
},
"ports": {
"type": "array",
"items": {
"type": "integer"
}
},
"remark": {
"type": "string"
},
"ssl": {
"type": "boolean"
},
"status": {
"type": "boolean"
}
}
}
},
"securityDefinitions": {
+411 -117
View File
@@ -5,7 +5,7 @@
"title": "耗子 Linux 面板 API",
"contact": {
"name": "耗子科技",
"email": "i@haozi.net"
"email": "admin@haozi.net"
},
"license": {
"name": "GNU Affero General Public License v3"
@@ -1115,7 +1115,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_container.ID"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID"
}
}
],
@@ -1183,7 +1183,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_container.ID"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID"
}
}
],
@@ -1531,7 +1531,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_container.ID"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID"
}
}
],
@@ -1592,7 +1592,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_container.ID"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID"
}
}
],
@@ -1664,7 +1664,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_container.ID"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID"
}
}
],
@@ -1734,7 +1734,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_container.ID"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID"
}
}
],
@@ -1802,7 +1802,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_container.ID"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID"
}
}
],
@@ -1838,7 +1838,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_container.ID"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID"
}
}
],
@@ -2039,7 +2039,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_container.ID"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID"
}
}
],
@@ -2686,7 +2686,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_setting.Update"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_setting.Update"
}
}
],
@@ -2812,7 +2812,7 @@
"data": {
"type": "array",
"items": {
"$ref": "#/definitions/internal.BackupFile"
"$ref": "#/definitions/types.BackupFile"
}
}
}
@@ -3129,7 +3129,7 @@
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/internal.PanelWebsite"
"$ref": "#/definitions/types.Website"
}
}
}
@@ -3405,6 +3405,278 @@
}
}
},
"/plugins/frp/config": {
"get": {
"security": [
{
"BearerToken": []
}
],
"description": "获取 Frp 配置",
"produces": [
"application/json"
],
"tags": [
"插件-Frp"
],
"summary": "获取配置",
"parameters": [
{
"type": "string",
"description": "服务",
"name": "service",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
},
"post": {
"security": [
{
"BearerToken": []
}
],
"description": "更新 Frp 配置",
"produces": [
"application/json"
],
"tags": [
"插件-Frp"
],
"summary": "更新配置",
"parameters": [
{
"description": "request",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_plugins_frp.UpdateConfig"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
}
},
"/plugins/frp/disable": {
"post": {
"security": [
{
"BearerToken": []
}
],
"description": "禁用 Frp 服务",
"produces": [
"application/json"
],
"tags": [
"插件-Frp"
],
"summary": "禁用服务",
"parameters": [
{
"description": "request",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.Service"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
}
},
"/plugins/frp/enable": {
"post": {
"security": [
{
"BearerToken": []
}
],
"description": "启用 Frp 服务",
"produces": [
"application/json"
],
"tags": [
"插件-Frp"
],
"summary": "启用服务",
"parameters": [
{
"description": "request",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.Service"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
}
},
"/plugins/frp/restart": {
"post": {
"security": [
{
"BearerToken": []
}
],
"description": "重启 Frp 服务",
"produces": [
"application/json"
],
"tags": [
"插件-Frp"
],
"summary": "重启服务",
"parameters": [
{
"description": "request",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.Service"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
}
},
"/plugins/frp/start": {
"post": {
"security": [
{
"BearerToken": []
}
],
"description": "启动 Frp 服务",
"produces": [
"application/json"
],
"tags": [
"插件-Frp"
],
"summary": "启动服务",
"parameters": [
{
"description": "request",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.Service"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
}
},
"/plugins/frp/status": {
"get": {
"security": [
{
"BearerToken": []
}
],
"description": "获取 Frp 服务状态",
"produces": [
"application/json"
],
"tags": [
"插件-Frp"
],
"summary": "服务状态",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
}
},
"/plugins/frp/stop": {
"post": {
"security": [
{
"BearerToken": []
}
],
"description": "停止 Frp 服务",
"produces": [
"application/json"
],
"tags": [
"插件-Frp"
],
"summary": "停止服务",
"parameters": [
{
"description": "request",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.Service"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
}
},
"/plugins/rsync/config": {
"get": {
"security": [
@@ -3450,7 +3722,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.UpdateConfig"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_plugins_rsync.UpdateConfig"
}
}
],
@@ -3564,7 +3836,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/panel_app_http_requests_plugins_rsync.Update"
"$ref": "#/definitions/github_com_TheTNB_panel_app_http_requests_plugins_rsync.Update"
}
}
],
@@ -3777,64 +4049,88 @@
}
}
},
"internal.BackupFile": {
"github_com_TheTNB_panel_app_http_requests_container.ID": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"size": {
"id": {
"type": "string"
}
}
},
"internal.PanelWebsite": {
"github_com_TheTNB_panel_app_http_requests_plugins_frp.UpdateConfig": {
"type": "object",
"properties": {
"db": {
"type": "boolean"
},
"db_name": {
"config": {
"type": "string"
},
"db_password": {
"service": {
"type": "string"
}
}
},
"github_com_TheTNB_panel_app_http_requests_plugins_rsync.Update": {
"type": "object",
"properties": {
"auth_user": {
"type": "string"
},
"db_type": {
"comment": {
"type": "string"
},
"db_user": {
"hosts_allow": {
"type": "string"
},
"domains": {
"type": "array",
"items": {
"type": "string"
}
},
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"php": {
"secret": {
"type": "string"
}
}
},
"github_com_TheTNB_panel_app_http_requests_plugins_rsync.UpdateConfig": {
"type": "object",
"properties": {
"config": {
"type": "string"
}
}
},
"github_com_TheTNB_panel_app_http_requests_setting.Update": {
"type": "object",
"properties": {
"backup_path": {
"type": "string"
},
"ports": {
"type": "array",
"items": {
"type": "integer"
}
},
"remark": {
"email": {
"type": "string"
},
"entrance": {
"type": "string"
},
"language": {
"type": "string"
},
"name": {
"type": "string"
},
"password": {
"type": "string"
},
"port": {
"type": "integer"
},
"ssl": {
"type": "boolean"
},
"status": {
"type": "boolean"
"username": {
"type": "string"
},
"website_path": {
"type": "string"
}
}
},
@@ -3992,69 +4288,6 @@
}
}
},
"panel_app_http_requests_container.ID": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
}
},
"panel_app_http_requests_plugins_rsync.Update": {
"type": "object",
"properties": {
"auth_user": {
"type": "string"
},
"comment": {
"type": "string"
},
"hosts_allow": {
"type": "string"
},
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"secret": {
"type": "string"
}
}
},
"panel_app_http_requests_setting.Update": {
"type": "object",
"properties": {
"backup_path": {
"type": "string"
},
"email": {
"type": "string"
},
"entrance": {
"type": "string"
},
"name": {
"type": "string"
},
"password": {
"type": "string"
},
"port": {
"type": "integer"
},
"ssl": {
"type": "boolean"
},
"username": {
"type": "string"
},
"website_path": {
"type": "string"
}
}
},
"requests.Add": {
"type": "object",
"properties": {
@@ -4267,10 +4500,10 @@
"requests.Copy": {
"type": "object",
"properties": {
"new": {
"source": {
"type": "string"
},
"old": {
"target": {
"type": "string"
}
}
@@ -4555,6 +4788,14 @@
}
}
},
"requests.Service": {
"type": "object",
"properties": {
"service": {
"type": "string"
}
}
},
"requests.UnArchive": {
"type": "object",
"properties": {
@@ -4566,14 +4807,6 @@
}
}
},
"requests.UpdateConfig": {
"type": "object",
"properties": {
"config": {
"type": "string"
}
}
},
"requests.UserStore": {
"type": "object",
"properties": {
@@ -4640,6 +4873,17 @@
}
}
},
"types.BackupFile": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"size": {
"type": "string"
}
}
},
"types.ContainerNetwork": {
"type": "object",
"properties": {
@@ -4704,6 +4948,56 @@
"type": "string"
}
}
},
"types.Website": {
"type": "object",
"properties": {
"db": {
"type": "boolean"
},
"db_name": {
"type": "string"
},
"db_password": {
"type": "string"
},
"db_type": {
"type": "string"
},
"db_user": {
"type": "string"
},
"domains": {
"type": "array",
"items": {
"type": "string"
}
},
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"php": {
"type": "string"
},
"ports": {
"type": "array",
"items": {
"type": "integer"
}
},
"remark": {
"type": "string"
},
"ssl": {
"type": "boolean"
},
"status": {
"type": "boolean"
}
}
}
},
"securityDefinitions": {
+266 -86
View File
@@ -31,45 +31,60 @@ definitions:
message:
type: string
type: object
internal.BackupFile:
github_com_TheTNB_panel_app_http_requests_container.ID:
properties:
name:
type: string
size:
id:
type: string
type: object
internal.PanelWebsite:
github_com_TheTNB_panel_app_http_requests_plugins_frp.UpdateConfig:
properties:
db:
type: boolean
db_name:
config:
type: string
db_password:
service:
type: string
db_type:
type: object
github_com_TheTNB_panel_app_http_requests_plugins_rsync.Update:
properties:
auth_user:
type: string
db_user:
comment:
type: string
hosts_allow:
type: string
domains:
items:
type: string
type: array
name:
type: string
path:
type: string
php:
secret:
type: string
ports:
items:
type: integer
type: array
remark:
type: object
github_com_TheTNB_panel_app_http_requests_plugins_rsync.UpdateConfig:
properties:
config:
type: string
type: object
github_com_TheTNB_panel_app_http_requests_setting.Update:
properties:
backup_path:
type: string
email:
type: string
entrance:
type: string
language:
type: string
name:
type: string
password:
type: string
port:
type: integer
ssl:
type: boolean
status:
type: boolean
username:
type: string
website_path:
type: string
type: object
models.Cert:
properties:
@@ -176,47 +191,6 @@ definitions:
updated_at:
type: string
type: object
panel_app_http_requests_container.ID:
properties:
id:
type: string
type: object
panel_app_http_requests_plugins_rsync.Update:
properties:
auth_user:
type: string
comment:
type: string
hosts_allow:
type: string
name:
type: string
path:
type: string
secret:
type: string
type: object
panel_app_http_requests_setting.Update:
properties:
backup_path:
type: string
email:
type: string
entrance:
type: string
name:
type: string
password:
type: string
port:
type: integer
ssl:
type: boolean
username:
type: string
website_path:
type: string
type: object
requests.Add:
properties:
db:
@@ -356,9 +330,9 @@ definitions:
type: object
requests.Copy:
properties:
new:
source:
type: string
old:
target:
type: string
type: object
requests.Create:
@@ -542,6 +516,11 @@ definitions:
path:
type: string
type: object
requests.Service:
properties:
service:
type: string
type: object
requests.UnArchive:
properties:
file:
@@ -549,11 +528,6 @@ definitions:
path:
type: string
type: object
requests.UpdateConfig:
properties:
config:
type: string
type: object
requests.UserStore:
properties:
ca:
@@ -597,6 +571,13 @@ definitions:
$ref: '#/definitions/types.KV'
type: array
type: object
types.BackupFile:
properties:
name:
type: string
size:
type: string
type: object
types.ContainerNetwork:
properties:
enabled:
@@ -639,9 +620,42 @@ definitions:
value:
type: string
type: object
types.Website:
properties:
db:
type: boolean
db_name:
type: string
db_password:
type: string
db_type:
type: string
db_user:
type: string
domains:
items:
type: string
type: array
name:
type: string
path:
type: string
php:
type: string
ports:
items:
type: integer
type: array
remark:
type: string
ssl:
type: boolean
status:
type: boolean
type: object
info:
contact:
email: i@haozi.net
email: admin@haozi.net
name: 耗子科技
description: 耗子 Linux 面板的 API 信息
license:
@@ -1311,7 +1325,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/panel_app_http_requests_container.ID'
$ref: '#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID'
produces:
- application/json
responses:
@@ -1352,7 +1366,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/panel_app_http_requests_container.ID'
$ref: '#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID'
produces:
- application/json
responses:
@@ -1562,7 +1576,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/panel_app_http_requests_container.ID'
$ref: '#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID'
produces:
- application/json
responses:
@@ -1599,7 +1613,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/panel_app_http_requests_container.ID'
$ref: '#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID'
produces:
- application/json
responses:
@@ -1643,7 +1657,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/panel_app_http_requests_container.ID'
$ref: '#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID'
produces:
- application/json
responses:
@@ -1686,7 +1700,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/panel_app_http_requests_container.ID'
$ref: '#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID'
produces:
- application/json
responses:
@@ -1727,7 +1741,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/panel_app_http_requests_container.ID'
$ref: '#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID'
produces:
- application/json
responses:
@@ -1749,7 +1763,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/panel_app_http_requests_container.ID'
$ref: '#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID'
produces:
- application/json
responses:
@@ -1870,7 +1884,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/panel_app_http_requests_container.ID'
$ref: '#/definitions/github_com_TheTNB_panel_app_http_requests_container.ID'
produces:
- application/json
responses:
@@ -2269,7 +2283,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/panel_app_http_requests_setting.Update'
$ref: '#/definitions/github_com_TheTNB_panel_app_http_requests_setting.Update'
produces:
- application/json
responses:
@@ -2348,7 +2362,7 @@ paths:
- properties:
data:
items:
$ref: '#/definitions/internal.BackupFile'
$ref: '#/definitions/types.BackupFile'
type: array
type: object
security:
@@ -2539,7 +2553,7 @@ paths:
- $ref: '#/definitions/controllers.SuccessResponse'
- properties:
data:
$ref: '#/definitions/internal.PanelWebsite'
$ref: '#/definitions/types.Website'
type: object
security:
- BearerToken: []
@@ -2712,6 +2726,172 @@ paths:
summary: 更新备注
tags:
- 网站管理
/plugins/frp/config:
get:
description: 获取 Frp 配置
parameters:
- description: 服务
in: query
name: service
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controllers.SuccessResponse'
security:
- BearerToken: []
summary: 获取配置
tags:
- 插件-Frp
post:
description: 更新 Frp 配置
parameters:
- description: request
in: body
name: data
required: true
schema:
$ref: '#/definitions/github_com_TheTNB_panel_app_http_requests_plugins_frp.UpdateConfig'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controllers.SuccessResponse'
security:
- BearerToken: []
summary: 更新配置
tags:
- 插件-Frp
/plugins/frp/disable:
post:
description: 禁用 Frp 服务
parameters:
- description: request
in: body
name: data
required: true
schema:
$ref: '#/definitions/requests.Service'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controllers.SuccessResponse'
security:
- BearerToken: []
summary: 禁用服务
tags:
- 插件-Frp
/plugins/frp/enable:
post:
description: 启用 Frp 服务
parameters:
- description: request
in: body
name: data
required: true
schema:
$ref: '#/definitions/requests.Service'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controllers.SuccessResponse'
security:
- BearerToken: []
summary: 启用服务
tags:
- 插件-Frp
/plugins/frp/restart:
post:
description: 重启 Frp 服务
parameters:
- description: request
in: body
name: data
required: true
schema:
$ref: '#/definitions/requests.Service'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controllers.SuccessResponse'
security:
- BearerToken: []
summary: 重启服务
tags:
- 插件-Frp
/plugins/frp/start:
post:
description: 启动 Frp 服务
parameters:
- description: request
in: body
name: data
required: true
schema:
$ref: '#/definitions/requests.Service'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controllers.SuccessResponse'
security:
- BearerToken: []
summary: 启动服务
tags:
- 插件-Frp
/plugins/frp/status:
get:
description: 获取 Frp 服务状态
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controllers.SuccessResponse'
security:
- BearerToken: []
summary: 服务状态
tags:
- 插件-Frp
/plugins/frp/stop:
post:
description: 停止 Frp 服务
parameters:
- description: request
in: body
name: data
required: true
schema:
$ref: '#/definitions/requests.Service'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controllers.SuccessResponse'
security:
- BearerToken: []
summary: 停止服务
tags:
- 插件-Frp
/plugins/rsync/config:
get:
description: 获取 Rsync 配置
@@ -2735,7 +2915,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/requests.UpdateConfig'
$ref: '#/definitions/github_com_TheTNB_panel_app_http_requests_plugins_rsync.UpdateConfig'
produces:
- application/json
responses:
@@ -2825,7 +3005,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/panel_app_http_requests_plugins_rsync.Update'
$ref: '#/definitions/github_com_TheTNB_panel_app_http_requests_plugins_rsync.Update'
produces:
- application/json
responses:
+49 -54
View File
@@ -1,14 +1,13 @@
module panel
module github.com/TheTNB/panel
go 1.22
require (
github.com/docker/docker v26.1.3+incompatible
github.com/docker/docker v26.1.4+incompatible
github.com/docker/go-connections v0.5.0
github.com/go-resty/resty/v2 v2.13.1
github.com/gookit/color v1.5.4
github.com/gookit/validate v1.5.2
github.com/goravel/framework v1.13.1-0.20240430025652-cf76911d0286
github.com/goravel/framework v1.13.1-0.20240529174304-bddcb8bcc28a
github.com/goravel/gin v1.1.6-0.20240426085159-718e4d6d5f4f
github.com/gorilla/websocket v1.5.1
github.com/libdns/alidns v1.0.3
@@ -24,16 +23,14 @@ require (
github.com/swaggo/http-swagger/v2 v2.0.2
github.com/swaggo/swag v1.16.3
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.23.0
golang.org/x/net v0.25.0
golang.org/x/crypto v0.24.0
golang.org/x/net v0.26.0
)
require (
cloud.google.com/go v0.112.0 // indirect
cloud.google.com/go/compute v1.24.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.1.6 // indirect
cloud.google.com/go/pubsub v1.36.1 // indirect
atomicgo.dev/cursor v0.2.0 // indirect
atomicgo.dev/keyboard v0.2.9 // indirect
atomicgo.dev/schedule v0.1.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 // indirect
@@ -44,27 +41,37 @@ require (
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae // indirect
github.com/RichardKnop/machinery/v2 v2.0.13 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/aws/aws-sdk-go v1.49.6 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/bytedance/sonic v1.11.6 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/catppuccin/go v0.2.0 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/charmbracelet/bubbles v0.18.0 // indirect
github.com/charmbracelet/bubbletea v0.26.3 // indirect
github.com/charmbracelet/huh v0.4.2 // indirect
github.com/charmbracelet/huh/spinner v0.0.0-20240508140610-13957916abf0 // indirect
github.com/charmbracelet/lipgloss v0.11.0 // indirect
github.com/charmbracelet/x/ansi v0.1.1 // indirect
github.com/charmbracelet/x/exp/strings v0.0.0-20240524151031-ff83003bf67a // indirect
github.com/charmbracelet/x/input v0.1.1 // indirect
github.com/charmbracelet/x/term v0.1.1 // indirect
github.com/charmbracelet/x/windows v0.1.2 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gabriel-vasile/mimetype v1.4.4 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.9.1 // indirect
github.com/glebarez/go-sqlite v1.22.0 // indirect
@@ -79,7 +86,6 @@ require (
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.15.5 // indirect
github.com/go-redsync/redsync/v4 v4.8.1 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
@@ -90,16 +96,12 @@ require (
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/gomodule/redigo v2.0.0+incompatible // indirect
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/google/wire v0.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/gookit/filter v1.2.1 // indirect
github.com/gookit/goutil v0.6.15 // indirect
github.com/goravel/file-rotatelogs/v2 v2.4.2 // indirect
@@ -113,44 +115,49 @@ require (
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/klauspost/compress v1.17.7 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/lithammer/fuzzysearch v1.1.8 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/microsoft/go-mssqldb v1.6.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/montanaflynn/stats v0.7.0 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/nrdcg/dnspod-go v0.4.0 // indirect
github.com/nwaples/rardecode v1.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pierrec/lz4/v4 v4.1.16 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rabbitmq/amqp091-go v1.9.0 // indirect
github.com/redis/go-redis/v9 v9.5.1 // indirect
github.com/pterm/pterm v0.12.79 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rotisserie/eris v0.5.4 // indirect
github.com/rs/cors v1.11.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/samber/do/v2 v2.0.0-beta.7 // indirect
github.com/samber/go-type-to-string v1.7.0 // indirect
github.com/savioxavier/termlink v1.3.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
@@ -160,7 +167,6 @@ require (
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/swaggo/files/v2 v2.0.0 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
@@ -168,43 +174,32 @@ require (
github.com/ulikunitz/xz v0.5.11 // indirect
github.com/unrolled/secure v1.14.0 // indirect
github.com/urfave/cli/v2 v2.27.2 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.1 // indirect
github.com/xdg-go/stringprep v1.0.3 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
go.mongodb.org/mongo-driver v1.11.3 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect
go.opentelemetry.io/otel v1.22.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0 // indirect
go.opentelemetry.io/otel/metric v1.22.0 // indirect
go.opentelemetry.io/otel/trace v1.22.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.22.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.7.0 // indirect
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
golang.org/x/image v0.13.0 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/oauth2 v0.17.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.19.0 // indirect
google.golang.org/api v0.162.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/term v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/grpc v1.63.2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
google.golang.org/grpc v1.64.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/mysql v1.5.6 // indirect
gorm.io/driver/postgres v1.5.7 // indirect
+134 -535
View File
File diff suppressed because it is too large Load Diff
+7 -9
View File
@@ -1,20 +1,18 @@
package internal
import "panel/app/models"
import (
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/types"
)
type Backup interface {
WebsiteList() ([]BackupFile, error)
WebsiteList() ([]types.BackupFile, error)
WebSiteBackup(website models.Website) error
WebsiteRestore(website models.Website, backupFile string) error
MysqlList() ([]BackupFile, error)
MysqlList() ([]types.BackupFile, error)
MysqlBackup(database string) error
MysqlRestore(database string, backupFile string) error
PostgresqlList() ([]BackupFile, error)
PostgresqlList() ([]types.BackupFile, error)
PostgresqlBackup(database string) error
PostgresqlRestore(database string, backupFile string) error
}
type BackupFile struct {
Name string `json:"name"`
Size string `json:"size"`
}
+3 -3
View File
@@ -1,9 +1,9 @@
package internal
import (
requests "panel/app/http/requests/cert"
"panel/app/models"
"panel/pkg/acme"
requests "github.com/TheTNB/panel/app/http/requests/cert"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/pkg/acme"
)
type Cert interface {
+2 -2
View File
@@ -7,8 +7,8 @@ import (
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/volume"
requests "panel/app/http/requests/container"
paneltypes "panel/types"
requests "github.com/TheTNB/panel/app/http/requests/container"
paneltypes "github.com/TheTNB/panel/types"
)
type Container interface {
+1 -1
View File
@@ -1,6 +1,6 @@
package internal
import "panel/app/models"
import "github.com/TheTNB/panel/app/models"
type Cron interface {
AddToSystem(cron models.Cron) error
+2 -9
View File
@@ -1,13 +1,6 @@
package internal
import "panel/types"
type PHPExtension struct {
Name string `json:"name"`
Slug string `json:"slug"`
Description string `json:"description"`
Installed bool `json:"installed"`
}
import "github.com/TheTNB/panel/types"
type PHP interface {
Status() (bool, error)
@@ -24,7 +17,7 @@ type PHP interface {
GetSlowLog() (string, error)
ClearErrorLog() error
ClearSlowLog() error
GetExtensions() ([]PHPExtension, error)
GetExtensions() ([]types.PHPExtension, error)
InstallExtension(slug string) error
UninstallExtension(slug string) error
}
+6 -16
View File
@@ -1,24 +1,14 @@
package internal
import "panel/app/models"
// PanelPlugin 插件元数据结构
type PanelPlugin struct {
Name string
Description string
Slug string
Version string
Requires []string
Excludes []string
Install string
Uninstall string
Update string
}
import (
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/types"
)
type Plugin interface {
AllInstalled() ([]models.Plugin, error)
All() []PanelPlugin
GetBySlug(slug string) PanelPlugin
All() []types.Plugin
GetBySlug(slug string) types.Plugin
GetInstalledBySlug(slug string) models.Plugin
Install(slug string) error
Uninstall(slug string) error
+22 -21
View File
@@ -9,9 +9,10 @@ import (
"github.com/goravel/framework/support/carbon"
"panel/app/models"
"panel/internal"
"panel/pkg/tools"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/pkg/tools"
"github.com/TheTNB/panel/types"
)
type BackupImpl struct {
@@ -25,30 +26,30 @@ func NewBackupImpl() *BackupImpl {
}
// WebsiteList 网站备份列表
func (s *BackupImpl) WebsiteList() ([]internal.BackupFile, error) {
func (s *BackupImpl) WebsiteList() ([]types.BackupFile, error) {
backupPath := s.setting.Get(models.SettingKeyBackupPath)
if len(backupPath) == 0 {
return []internal.BackupFile{}, nil
return []types.BackupFile{}, nil
}
backupPath += "/website"
if !tools.Exists(backupPath) {
if err := tools.Mkdir(backupPath, 0644); err != nil {
return []internal.BackupFile{}, err
return []types.BackupFile{}, err
}
}
files, err := os.ReadDir(backupPath)
if err != nil {
return []internal.BackupFile{}, err
return []types.BackupFile{}, err
}
var backupList []internal.BackupFile
var backupList []types.BackupFile
for _, file := range files {
info, err := file.Info()
if err != nil {
continue
}
backupList = append(backupList, internal.BackupFile{
backupList = append(backupList, types.BackupFile{
Name: file.Name(),
Size: tools.FormatBytes(float64(info.Size())),
})
@@ -115,30 +116,30 @@ func (s *BackupImpl) WebsiteRestore(website models.Website, backupFile string) e
}
// MysqlList MySQL备份列表
func (s *BackupImpl) MysqlList() ([]internal.BackupFile, error) {
func (s *BackupImpl) MysqlList() ([]types.BackupFile, error) {
backupPath := s.setting.Get(models.SettingKeyBackupPath)
if len(backupPath) == 0 {
return []internal.BackupFile{}, nil
return []types.BackupFile{}, nil
}
backupPath += "/mysql"
if !tools.Exists(backupPath) {
if err := tools.Mkdir(backupPath, 0644); err != nil {
return []internal.BackupFile{}, err
return []types.BackupFile{}, err
}
}
files, err := os.ReadDir(backupPath)
if err != nil {
return []internal.BackupFile{}, err
return []types.BackupFile{}, err
}
var backupList []internal.BackupFile
var backupList []types.BackupFile
for _, file := range files {
info, err := file.Info()
if err != nil {
continue
}
backupList = append(backupList, internal.BackupFile{
backupList = append(backupList, types.BackupFile{
Name: file.Name(),
Size: tools.FormatBytes(float64(info.Size())),
})
@@ -228,30 +229,30 @@ func (s *BackupImpl) MysqlRestore(database string, backupFile string) error {
}
// PostgresqlList PostgreSQL备份列表
func (s *BackupImpl) PostgresqlList() ([]internal.BackupFile, error) {
func (s *BackupImpl) PostgresqlList() ([]types.BackupFile, error) {
backupPath := s.setting.Get(models.SettingKeyBackupPath)
if len(backupPath) == 0 {
return []internal.BackupFile{}, nil
return []types.BackupFile{}, nil
}
backupPath += "/postgresql"
if !tools.Exists(backupPath) {
if err := tools.Mkdir(backupPath, 0644); err != nil {
return []internal.BackupFile{}, err
return []types.BackupFile{}, err
}
}
files, err := os.ReadDir(backupPath)
if err != nil {
return []internal.BackupFile{}, err
return []types.BackupFile{}, err
}
var backupList []internal.BackupFile
var backupList []types.BackupFile
for _, file := range files {
info, err := file.Info()
if err != nil {
continue
}
backupList = append(backupList, internal.BackupFile{
backupList = append(backupList, types.BackupFile{
Name: file.Name(),
Size: tools.FormatBytes(float64(info.Size())),
})
+4 -4
View File
@@ -9,10 +9,10 @@ import (
"github.com/goravel/framework/facades"
requests "panel/app/http/requests/cert"
"panel/app/models"
"panel/pkg/acme"
"panel/pkg/tools"
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"
)
type CertImpl struct {
+2 -2
View File
@@ -15,8 +15,8 @@ import (
"github.com/docker/docker/client"
"github.com/goravel/framework/support/json"
requests "panel/app/http/requests/container"
paneltypes "panel/types"
requests "github.com/TheTNB/panel/app/http/requests/container"
paneltypes "github.com/TheTNB/panel/types"
)
type Container struct {
+21 -23
View File
@@ -1,10 +1,12 @@
package services
import (
"errors"
"fmt"
"strings"
"panel/app/models"
"panel/pkg/tools"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/pkg/tools"
)
type CronImpl struct {
@@ -17,43 +19,39 @@ func NewCronImpl() *CronImpl {
// AddToSystem 添加到系统
func (r *CronImpl) AddToSystem(cron models.Cron) error {
if tools.IsRHEL() {
if _, err := tools.Exec("echo \"" + cron.Time + " " + cron.Shell + " >> " + cron.Log + " 2>&1\" >> /var/spool/cron/root"); err != nil {
return err
}
if _, err := tools.Exec("systemctl restart crond"); err != nil {
return err
}
} else {
if _, err := tools.Exec("echo \"" + cron.Time + " " + cron.Shell + " >> " + cron.Log + " 2>&1\" >> /var/spool/cron/crontabs/root"); err != nil {
return err
}
if _, err := tools.Exec("systemctl restart cron"); err != nil {
if _, err := tools.Exec(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 nil
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 {
return err
}
return tools.ServiceRestart("cron")
}
return errors.New("不支持的系统")
}
// DeleteFromSystem 从系统中删除
func (r *CronImpl) DeleteFromSystem(cron models.Cron) error {
// 需要转义Shell路径的/为\/
// 需要转义 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 {
return err
}
if _, err := tools.Exec("systemctl restart crond"); err != nil {
return err
}
} else {
return tools.ServiceRestart("crond")
}
if tools.IsDebian() {
if _, err := tools.Exec("sed -i '/" + cron.Shell + "/d' /var/spool/cron/crontabs/root"); err != nil {
return err
}
if _, err := tools.Exec("systemctl restart cron"); err != nil {
return err
}
return tools.ServiceRestart("cron")
}
return nil
return errors.New("不支持的系统")
}
+11 -14
View File
@@ -12,10 +12,9 @@ import (
"github.com/goravel/framework/facades"
"github.com/spf13/cast"
"panel/app/models"
"panel/internal"
"panel/pkg/tools"
"panel/types"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/pkg/tools"
"github.com/TheTNB/panel/types"
)
type PHPImpl struct {
@@ -122,8 +121,8 @@ func (r *PHPImpl) ClearSlowLog() error {
return nil
}
func (r *PHPImpl) GetExtensions() ([]internal.PHPExtension, error) {
extensions := []internal.PHPExtension{
func (r *PHPImpl) GetExtensions() ([]types.PHPExtension, error) {
extensions := []types.PHPExtension{
{
Name: "fileinfo",
Slug: "fileinfo",
@@ -264,7 +263,7 @@ func (r *PHPImpl) GetExtensions() ([]internal.PHPExtension, error) {
// ionCube 只支持 PHP 8.3 以下版本
if cast.ToUint(r.version) < 83 {
extensions = append(extensions, internal.PHPExtension{
extensions = append(extensions, types.PHPExtension{
Name: "ionCube",
Slug: "ionCube Loader",
Description: "ionCube 是一个专业级的 PHP 加密解密工具。",
@@ -273,13 +272,13 @@ func (r *PHPImpl) GetExtensions() ([]internal.PHPExtension, error) {
}
// Swoole 和 Swow 不支持 PHP 8.0 以下版本
if cast.ToUint(r.version) >= 80 {
extensions = append(extensions, internal.PHPExtension{
extensions = append(extensions, types.PHPExtension{
Name: "Swoole",
Slug: "swoole",
Description: "Swoole 是一个用于构建高性能的异步并发服务器的 PHP 扩展。",
Installed: false,
})
extensions = append(extensions, internal.PHPExtension{
extensions = append(extensions, types.PHPExtension{
Name: "Swow",
Slug: "Swow",
Description: "Swow 是一个用于构建高性能的异步并发服务器的 PHP 扩展。",
@@ -292,7 +291,7 @@ func (r *PHPImpl) GetExtensions() ([]internal.PHPExtension, error) {
return extensions, err
}
extensionMap := make(map[string]*internal.PHPExtension)
extensionMap := make(map[string]*types.PHPExtension)
for i := range extensions {
extensionMap[extensions[i].Slug] = &extensions[i]
}
@@ -328,8 +327,7 @@ func (r *PHPImpl) InstallExtension(slug string) error {
return err
}
NewTaskImpl().Process(task.ID)
return nil
return NewTaskImpl().Process(task.ID)
}
func (r *PHPImpl) UninstallExtension(slug string) error {
@@ -353,8 +351,7 @@ func (r *PHPImpl) UninstallExtension(slug string) error {
return err
}
NewTaskImpl().Process(task.ID)
return nil
return NewTaskImpl().Process(task.ID)
}
func (r *PHPImpl) checkExtension(slug string) bool {
+32 -33
View File
@@ -6,8 +6,9 @@ import (
"github.com/goravel/framework/facades"
"panel/app/models"
"panel/internal"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/types"
)
type PluginImpl struct {
@@ -31,44 +32,45 @@ func (r *PluginImpl) AllInstalled() ([]models.Plugin, error) {
}
// All 获取所有插件
func (r *PluginImpl) All() []internal.PanelPlugin {
var plugins = []internal.PanelPlugin{
internal.PluginOpenResty,
internal.PluginMySQL57,
internal.PluginMySQL80,
internal.PluginMySQL84,
internal.PluginPostgreSQL15,
internal.PluginPostgreSQL16,
internal.PluginPHP74,
internal.PluginPHP80,
internal.PluginPHP81,
internal.PluginPHP82,
internal.PluginPHP83,
internal.PluginPHPMyAdmin,
internal.PluginPureFTPd,
internal.PluginRedis,
internal.PluginS3fs,
internal.PluginRsync,
internal.PluginSupervisor,
internal.PluginFail2ban,
internal.PluginToolBox,
func (r *PluginImpl) All() []types.Plugin {
var plugins = []types.Plugin{
types.PluginOpenResty,
types.PluginMySQL57,
types.PluginMySQL80,
types.PluginMySQL84,
types.PluginPostgreSQL15,
types.PluginPostgreSQL16,
types.PluginPHP74,
types.PluginPHP80,
types.PluginPHP81,
types.PluginPHP82,
types.PluginPHP83,
types.PluginPHPMyAdmin,
types.PluginPureFTPd,
types.PluginRedis,
types.PluginS3fs,
types.PluginRsync,
types.PluginSupervisor,
types.PluginFail2ban,
types.PluginFrp,
types.PluginToolBox,
}
return plugins
}
// GetBySlug 根据slug获取插件
func (r *PluginImpl) GetBySlug(slug string) internal.PanelPlugin {
// GetBySlug 根据 slug 获取插件
func (r *PluginImpl) GetBySlug(slug string) types.Plugin {
for _, item := range r.All() {
if item.Slug == slug {
return item
}
}
return internal.PanelPlugin{}
return types.Plugin{}
}
// GetInstalledBySlug 根据slug获取已安装的插件
// GetInstalledBySlug 根据 slug 获取已安装的插件
func (r *PluginImpl) GetInstalledBySlug(slug string) models.Plugin {
var plugin models.Plugin
if err := facades.Orm().Query().Where("slug", slug).Get(&plugin); err != nil {
@@ -120,8 +122,7 @@ func (r *PluginImpl) Install(slug string) error {
return errors.New("创建任务失败")
}
r.task.Process(task.ID)
return nil
return r.task.Process(task.ID)
}
// Uninstall 卸载插件
@@ -166,8 +167,7 @@ func (r *PluginImpl) Uninstall(slug string) error {
return errors.New("创建任务失败")
}
r.task.Process(task.ID)
return nil
return r.task.Process(task.ID)
}
// Update 更新插件
@@ -212,6 +212,5 @@ func (r *PluginImpl) Update(slug string) error {
return errors.New("创建任务失败")
}
r.task.Process(task.ID)
return nil
return r.task.Process(task.ID)
}
+5 -11
View File
@@ -3,7 +3,9 @@ package services
import (
"github.com/goravel/framework/facades"
"panel/app/models"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/pkg/tools"
)
type SettingImpl struct {
@@ -17,19 +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 {
if len(defaultValue) == 0 {
return ""
}
return defaultValue[0]
return tools.FirstElement(defaultValue)
}
if len(setting.Value) == 0 {
if len(defaultValue) == 0 {
return ""
}
return defaultValue[0]
return tools.FirstElement(defaultValue)
}
return setting.Value
+19 -12
View File
@@ -1,10 +1,10 @@
package services
import (
"github.com/goravel/framework/contracts/queue"
"github.com/goravel/framework/facades"
"panel/app/jobs"
"github.com/TheTNB/panel/app/jobs"
"github.com/TheTNB/panel/app/models"
)
type TaskImpl struct {
@@ -14,14 +14,21 @@ func NewTaskImpl() *TaskImpl {
return &TaskImpl{}
}
func (r *TaskImpl) Process(taskID uint) {
go func() {
err := facades.Queue().Job(&jobs.ProcessTask{}, []queue.Arg{
{Type: "uint", Value: taskID},
}).Dispatch()
if err != nil {
facades.Log().Info("[面板][TaskService] 运行任务失败: " + err.Error())
return
}
}()
func (r *TaskImpl) Process(taskID uint) error {
return facades.Queue().Job(&jobs.ProcessTask{}, []any{taskID}).Dispatch()
}
func (r *TaskImpl) DispatchWaiting() error {
var tasks []models.Task
if err := facades.Orm().Query().Where("status = ?", models.TaskStatusWaiting).Find(&tasks); err != nil {
return err
}
for _, task := range tasks {
if err := r.Process(task.ID); err != nil {
return err
}
}
return nil
}
+1 -1
View File
@@ -4,7 +4,7 @@ package services
import (
"github.com/goravel/framework/facades"
"panel/app/models"
"github.com/TheTNB/panel/app/models"
)
type UserImpl struct {
+14 -15
View File
@@ -12,11 +12,12 @@ import (
"github.com/goravel/framework/facades"
"github.com/spf13/cast"
requests "panel/app/http/requests/website"
"panel/app/models"
"panel/internal"
"panel/pkg/tools"
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"
)
type WebsiteImpl struct {
@@ -41,7 +42,7 @@ func (r *WebsiteImpl) List(page, limit int) (int64, []models.Website, error) {
}
// Add 添加网站
func (r *WebsiteImpl) Add(website internal.PanelWebsite) (models.Website, error) {
func (r *WebsiteImpl) Add(website types.Website) (models.Website, error) {
w := models.Website{
Name: website.Name,
Status: website.Status,
@@ -459,8 +460,7 @@ func (r *WebsiteImpl) SaveConfig(config requests.SaveConfig) error {
return err
}
_, err = tools.Exec("systemctl reload openresty")
return err
return tools.ServiceReload("openresty")
}
// Delete 删除网站
@@ -494,23 +494,22 @@ func (r *WebsiteImpl) Delete(id uint) error {
return err
}
_, err := tools.Exec("systemctl reload openresty")
return err
return tools.ServiceReload("openresty")
}
// GetConfig 获取网站配置
func (r *WebsiteImpl) GetConfig(id uint) (internal.WebsiteSetting, error) {
func (r *WebsiteImpl) GetConfig(id uint) (types.WebsiteSetting, error) {
var website models.Website
if err := facades.Orm().Query().Where("id", id).First(&website); err != nil {
return internal.WebsiteSetting{}, err
return types.WebsiteSetting{}, err
}
config, err := tools.Read("/www/server/vhost/" + website.Name + ".conf")
if err != nil {
return internal.WebsiteSetting{}, err
return types.WebsiteSetting{}, err
}
var setting internal.WebsiteSetting
var setting types.WebsiteSetting
setting.Name = website.Name
setting.Path = website.Path
setting.Ssl = website.Ssl
@@ -612,10 +611,10 @@ func (r *WebsiteImpl) GetConfig(id uint) (internal.WebsiteSetting, error) {
}
// GetConfigByName 根据网站名称获取网站配置
func (r *WebsiteImpl) GetConfigByName(name string) (internal.WebsiteSetting, error) {
func (r *WebsiteImpl) GetConfigByName(name string) (types.WebsiteSetting, error) {
var website models.Website
if err := facades.Orm().Query().Where("name", name).First(&website); err != nil {
return internal.WebsiteSetting{}, err
return types.WebsiteSetting{}, err
}
return r.GetConfig(website.ID)
+2 -1
View File
@@ -1,5 +1,6 @@
package internal
type Task interface {
Process(taskID uint)
Process(taskID uint) error
DispatchWaiting() error
}
+1 -1
View File
@@ -1,6 +1,6 @@
package internal
import "panel/app/models"
import "github.com/TheTNB/panel/app/models"
type User interface {
Create(name, password string) (models.User, error)
+6 -50
View File
@@ -1,61 +1,17 @@
package internal
import (
requests "panel/app/http/requests/website"
"panel/app/models"
requests "github.com/TheTNB/panel/app/http/requests/website"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/types"
)
type Website interface {
List(page int, limit int) (int64, []models.Website, error)
Add(website PanelWebsite) (models.Website, error)
Add(website types.Website) (models.Website, error)
SaveConfig(config requests.SaveConfig) error
Delete(id uint) error
GetConfig(id uint) (WebsiteSetting, error)
GetConfigByName(name string) (WebsiteSetting, error)
GetConfig(id uint) (types.WebsiteSetting, error)
GetConfigByName(name string) (types.WebsiteSetting, error)
GetIDByName(name string) (uint, error)
}
type PanelWebsite struct {
Name string `json:"name"`
Status bool `json:"status"`
Domains []string `json:"domains"`
Ports []uint `json:"ports"`
Path string `json:"path"`
Php string `json:"php"`
Ssl bool `json:"ssl"`
Remark string `json:"remark"`
Db bool `json:"db"`
DbType string `json:"db_type"`
DbName string `json:"db_name"`
DbUser string `json:"db_user"`
DbPassword string `json:"db_password"`
}
// WebsiteSetting 网站设置
type WebsiteSetting struct {
Name string `json:"name"`
Domains []string `json:"domains"`
Ports []uint `json:"ports"`
Root string `json:"root"`
Path string `json:"path"`
Index string `json:"index"`
Php string `json:"php"`
OpenBasedir bool `json:"open_basedir"`
Ssl bool `json:"ssl"`
SslCertificate string `json:"ssl_certificate"`
SslCertificateKey string `json:"ssl_certificate_key"`
SslNotBefore string `json:"ssl_not_before"`
SslNotAfter string `json:"ssl_not_after"`
SSlDNSNames []string `json:"ssl_dns_names"`
SslIssuer string `json:"ssl_issuer"`
SslOCSPServer []string `json:"ssl_ocsp_server"`
HttpRedirect bool `json:"http_redirect"`
Hsts bool `json:"hsts"`
Waf bool `json:"waf"`
WafMode string `json:"waf_mode"`
WafCcDeny string `json:"waf_cc_deny"`
WafCache string `json:"waf_cache"`
Rewrite string `json:"rewrite"`
Raw string `json:"raw"`
Log string `json:"log"`
}
+2 -1
View File
@@ -24,6 +24,7 @@
"update": {
"description": "update/fix panel to latest version",
"taskCheck": "There is currently a task being executed and updates are prohibited.",
"dbFail": "panel database exception, operation terminated",
"versionFail": "failed to get latest version",
"fail": "update failed",
"success": "update completed"
@@ -76,7 +77,7 @@
"databaseGetFail": "failed to get database",
"databaseNotExist": "database does not exist",
"targetPostgres": "target PostgreSQL database",
"cleanBackup" : "clean backup",
"cleanBackup": "clean backup",
"cleanupFail": "cleanup failed",
"cleanupSuccess": "cleanup successful",
"deleteFail": "failed to delete",
+1
View File
@@ -24,6 +24,7 @@
"update": {
"description": "更新 / 修复面板到最新版本",
"taskCheck": "当前有任务正在执行,禁止更新",
"dbFail": "面板数据库异常,已终止操作",
"versionFail": "获取最新版本失败",
"fail": "更新失败",
"success": "更新成功"
+9 -2
View File
@@ -19,7 +19,7 @@ package main
import (
"github.com/goravel/framework/facades"
"panel/bootstrap"
"github.com/TheTNB/panel/bootstrap"
)
// @title 耗子 Linux 面板 API
@@ -27,7 +27,7 @@ import (
// @description 耗子 Linux 面板的 API 信息
// @contact.name 耗子科技
// @contact.email i@haozi.net
// @contact.email admin@haozi.net
// @license.name GNU Affero General Public License v3
// @license url https://www.gnu.org/licenses/agpl-3.0.html
@@ -59,5 +59,12 @@ func main() {
// 启动计划任务
go facades.Schedule().Run()
// 启动队列
go func() {
if err := facades.Queue().Worker(nil).Run(); err != nil {
facades.Log().Errorf("Queue run error: %v", err)
}
}()
select {}
}
+3 -8
View File
@@ -26,13 +26,11 @@ func (s httpSolver) Present(_ context.Context, challenge acme.Challenge) error {
}
challengeFilePath := filepath.Join(s.path, challenge.HTTP01ResourcePath())
err = os.MkdirAll(filepath.Dir(challengeFilePath), 0o755)
if err != nil {
if err = os.MkdirAll(filepath.Dir(challengeFilePath), 0o755); err != nil {
return fmt.Errorf("无法在网站目录创建 HTTP 挑战所需的目录: %w", err)
}
err = os.WriteFile(challengeFilePath, []byte(challenge.KeyAuthorization), 0o644)
if err != nil {
if err = os.WriteFile(challengeFilePath, []byte(challenge.KeyAuthorization), 0o644); err != nil {
return fmt.Errorf("无法在网站目录创建 HTTP 挑战所需的文件: %w", err)
}
@@ -45,8 +43,7 @@ func (s httpSolver) CleanUp(_ context.Context, challenge acme.Challenge) error {
return nil
}
err := os.Remove(filepath.Join(s.path, challenge.HTTP01ResourcePath()))
if err != nil {
if err := os.Remove(filepath.Join(s.path, challenge.HTTP01ResourcePath())); err != nil {
return fmt.Errorf("无法删除 HTTP 挑战文件: %w", err)
}
@@ -167,7 +164,6 @@ func (s manualDNSSolver) Present(ctx context.Context, challenge acme.Challenge)
dnsName := challenge.DNS01TXTRecordName()
keyAuth := challenge.DNS01KeyAuthorization()
// 追加记录到 records 中
*s.records = append(*s.records, DNSRecord{
Key: dnsName,
Value: keyAuth,
@@ -177,7 +173,6 @@ func (s manualDNSSolver) Present(ctx context.Context, challenge acme.Challenge)
_, cancel := context.WithTimeout(ctx, 2*time.Minute)
defer cancel()
// 等待信号以继续
<-s.controlChan
return nil
}
+7 -7
View File
@@ -5,7 +5,7 @@ import (
"golang.org/x/crypto/ssh"
"panel/pkg/tools"
"github.com/TheTNB/panel/pkg/tools"
)
type AuthMethod int8
@@ -15,7 +15,7 @@ const (
PUBLICKEY
)
type SSHClientConfig struct {
type ClientConfig struct {
AuthMethod AuthMethod
HostAddr string
User string
@@ -24,8 +24,8 @@ type SSHClientConfig struct {
Timeout time.Duration
}
func SSHClientConfigPassword(hostAddr, user, Password string) *SSHClientConfig {
return &SSHClientConfig{
func ClientConfigPassword(hostAddr, user, Password string) *ClientConfig {
return &ClientConfig{
Timeout: time.Second * 5,
AuthMethod: PASSWORD,
HostAddr: hostAddr,
@@ -34,8 +34,8 @@ func SSHClientConfigPassword(hostAddr, user, Password string) *SSHClientConfig {
}
}
func SSHClientConfigPulicKey(hostAddr, user, keyPath string) *SSHClientConfig {
return &SSHClientConfig{
func ClientConfigPublicKey(hostAddr, user, keyPath string) *ClientConfig {
return &ClientConfig{
Timeout: time.Second * 5,
AuthMethod: PUBLICKEY,
HostAddr: hostAddr,
@@ -44,7 +44,7 @@ func SSHClientConfigPulicKey(hostAddr, user, keyPath string) *SSHClientConfig {
}
}
func NewSSHClient(conf *SSHClientConfig) (*ssh.Client, error) {
func NewSSHClient(conf *ClientConfig) (*ssh.Client, error) {
config := &ssh.ClientConfig{
Timeout: conf.Timeout,
User: conf.User,
+40 -1
View File
@@ -1,6 +1,11 @@
package tools
import "fmt"
import (
"errors"
"fmt"
"os/exec"
"strings"
)
// ServiceStatus 获取服务状态
func ServiceStatus(name string) (bool, error) {
@@ -8,6 +13,28 @@ func ServiceStatus(name string) (bool, error) {
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))
@@ -31,3 +58,15 @@ func ServiceReload(name string) error {
_, err := Exec(fmt.Sprintf("systemctl reload %s", name))
return err
}
// ServiceEnable 启用服务
func ServiceEnable(name string) error {
_, err := Exec(fmt.Sprintf("systemctl enable %s", name))
return err
}
// ServiceDisable 禁用服务
func ServiceDisable(name string) error {
_, err := Exec(fmt.Sprintf("systemctl disable %s", name))
return err
}
+42 -60
View File
@@ -4,14 +4,14 @@ package tools
import (
"errors"
"fmt"
"github.com/spf13/cast"
"os"
"strconv"
"strings"
"time"
"github.com/go-resty/resty/v2"
"github.com/gookit/color"
"github.com/goravel/framework/support/carbon"
"github.com/goravel/framework/support/color"
"github.com/goravel/framework/support/env"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
@@ -101,30 +101,12 @@ func GenerateVersions(start, end string) ([]string, error) {
return nil, fmt.Errorf("版本格式错误")
}
startMajor, err := strconv.Atoi(startParts[0])
if err != nil {
return nil, fmt.Errorf("无效的起始主版本号: %v", err)
}
startMinor, err := strconv.Atoi(startParts[1])
if err != nil {
return nil, fmt.Errorf("无效的起始次版本号: %v", err)
}
startPatch, err := strconv.Atoi(startParts[2])
if err != nil {
return nil, fmt.Errorf("无效的起始修订号: %v", err)
}
endMajor, err := strconv.Atoi(endParts[0])
if err != nil {
return nil, fmt.Errorf("无效的结束主版本号: %v", err)
}
endMinor, err := strconv.Atoi(endParts[1])
if err != nil {
return nil, fmt.Errorf("无效的结束次版本号: %v", err)
}
endPatch, err := strconv.Atoi(endParts[2])
if err != nil {
return nil, fmt.Errorf("无效的结束修订号: %v", err)
}
startMajor := cast.ToInt(startParts[0])
startMinor := cast.ToInt(startParts[1])
startPatch := cast.ToInt(startParts[2])
endMajor := cast.ToInt(endParts[0])
endMinor := cast.ToInt(endParts[1])
endPatch := cast.ToInt(endParts[2])
for major := startMajor; major <= endMajor; major++ {
for minor := 0; minor <= 99; minor++ {
@@ -402,108 +384,108 @@ func GetPanelVersion(version string) (PanelInfo, error) {
// UpdatePanel 更新面板
func UpdatePanel(panelInfo PanelInfo) error {
color.Greenln("目标版本: " + panelInfo.Version)
color.Greenln("下载链接: " + panelInfo.DownloadUrl)
color.Green().Printfln("目标版本: " + panelInfo.Version)
color.Green().Printfln("下载链接: " + panelInfo.DownloadUrl)
color.Greenln("前置检查...")
color.Green().Printfln("前置检查...")
if Exists("/tmp/panel-storage.zip") || Exists("/tmp/panel.conf.bak") {
return errors.New("检测到 /tmp 存在临时文件,可能是上次更新失败导致的,请谨慎排除后重试")
}
color.Greenln("备份面板数据...")
color.Green().Printfln("备份面板数据...")
// 备份面板
if err := Archive([]string{"/www/panel"}, "/www/backup/panel/panel-"+carbon.Now().ToShortDateTimeString()+".zip"); err != nil {
color.Redln("备份面板失败")
color.Red().Printfln("备份面板失败")
return err
}
if _, err := Exec("cd /www/panel/storage && zip -r /tmp/panel-storage.zip *"); err != nil {
color.Redln("备份面板数据失败")
color.Red().Printfln("备份面板数据失败")
return err
}
if _, err := Exec("cp -f /www/panel/panel.conf /tmp/panel.conf.bak"); err != nil {
color.Redln("备份面板配置失败")
color.Red().Printfln("备份面板配置失败")
return err
}
if !Exists("/tmp/panel-storage.zip") || !Exists("/tmp/panel.conf.bak") {
return errors.New("备份面板数据失败")
}
color.Greenln("备份完成")
color.Green().Printfln("备份完成")
color.Greenln("清理旧版本...")
color.Green().Printfln("清理旧版本...")
if _, err := Exec("rm -rf /www/panel/*"); err != nil {
color.Redln("清理旧版本失败")
color.Red().Printfln("清理旧版本失败")
return err
}
color.Greenln("清理完成")
color.Green().Printfln("清理完成")
color.Greenln("正在下载...")
color.Green().Printfln("正在下载...")
if _, err := Exec("wget -T 120 -t 3 -O /www/panel/" + panelInfo.DownloadName + " " + panelInfo.DownloadUrl); err != nil {
color.Redln("下载失败")
color.Red().Printfln("下载失败")
return err
}
if _, err := Exec("wget -T 20 -t 3 -O /www/panel/" + panelInfo.Checksums + " " + panelInfo.ChecksumsUrl); err != nil {
color.Redln("下载失败")
color.Red().Printfln("下载失败")
return err
}
if !Exists("/www/panel/"+panelInfo.DownloadName) || !Exists("/www/panel/"+panelInfo.Checksums) {
return errors.New("下载失败")
}
color.Greenln("下载完成")
color.Green().Printfln("下载完成")
color.Greenln("校验下载文件...")
color.Green().Printfln("校验下载文件...")
check, err := Exec("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 {
color.Redln("清理临时文件失败")
color.Red().Printfln("清理临时文件失败")
return err
}
color.Greenln("文件校验完成")
color.Green().Printfln("文件校验完成")
color.Greenln("更新新版本...")
color.Green().Printfln("更新新版本...")
if _, err = Exec("cd /www/panel && unzip -o " + panelInfo.DownloadName + " && rm -rf " + panelInfo.DownloadName); err != nil {
color.Redln("更新失败")
color.Red().Printfln("更新失败")
return err
}
if !Exists("/www/panel/panel") {
return errors.New("更新失败,可能是下载过程中出现了问题")
}
color.Greenln("更新完成")
color.Green().Printfln("更新完成")
color.Greenln("恢复面板数据...")
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 {
color.Redln("恢复面板数据失败")
color.Red().Printfln("恢复面板数据失败")
return err
}
if _, err = Exec("cp -f /tmp/panel.conf.bak /www/panel/panel.conf"); err != nil {
color.Redln("恢复面板配置失败")
color.Red().Printfln("恢复面板配置失败")
return err
}
if _, err = Exec("cp -f /www/panel/scripts/panel.sh /usr/bin/panel"); err != nil {
color.Redln("恢复面板脚本失败")
color.Red().Printfln("恢复面板脚本失败")
return err
}
if !Exists("/www/panel/storage/panel.db") || !Exists("/www/panel/panel.conf") {
return errors.New("恢复面板数据失败")
}
color.Greenln("恢复完成")
color.Green().Printfln("恢复完成")
color.Greenln("设置面板文件权限...")
color.Green().Printfln("设置面板文件权限...")
_, _ = Exec("chmod -R 700 /www/panel")
_, _ = Exec("chmod -R 700 /usr/bin/panel")
color.Greenln("设置完成")
color.Green().Printfln("设置完成")
if _, err = Exec("/www/panel/panel --env=panel.conf artisan migrate"); err != nil {
color.Redln("运行面板数据库迁移失败")
color.Red().Printfln("运行面板数据库迁移失败")
return err
}
if _, err = Exec("bash /www/panel/scripts/update_panel.sh"); err != nil {
color.Redln("执行面板升级后脚本失败")
color.Red().Printfln("执行面板升级后脚本失败")
return err
}
if _, err = Exec("panel writeSetting version " + panelInfo.Version); err != nil {
color.Redln("写入面板版本号失败")
color.Red().Printfln("写入面板版本号失败")
return err
}
@@ -514,14 +496,14 @@ func UpdatePanel(panelInfo PanelInfo) error {
}
func RestartPanel() {
color.Greenln("重启面板...")
color.Green().Printfln("重启面板...")
err := ExecAsync("sleep 2 && systemctl restart panel")
if err != nil {
color.Redln("重启失败")
color.Red().Printfln("重启失败")
return
}
color.Greenln("重启完成")
color.Green().Printfln("重启完成")
}
// IsChina 是否中国大陆
-8
View File
@@ -74,14 +74,6 @@ func (s *HelperTestSuite) TestGenerateVersions() {
"1.0.91", "1.0.92", "1.0.93", "1.0.94", "1.0.95", "1.0.96", "1.0.97", "1.0.98", "1.0.99", "1.1.0",
"1.1.1",
}, versions)
versions, err = GenerateVersions("1..0", "1.0.1")
s.Error(err)
s.Nil(versions)
versions, err = GenerateVersions("1.0.0", "1..1")
s.Error(err)
s.Nil(versions)
}
func (s *HelperTestSuite) TestGetLatestPanelVersion() {
+3 -3
View File
@@ -4,15 +4,15 @@ import (
"github.com/goravel/framework/contracts/route"
"github.com/goravel/framework/facades"
"panel/app/http/controllers"
"panel/app/http/middleware"
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/app/http/middleware"
)
func Api() {
facades.Route().Prefix("api/panel").Group(func(r route.Router) {
r.Prefix("info").Group(func(r route.Router) {
infoController := controllers.NewInfoController()
r.Get("name", infoController.Name)
r.Get("panel", infoController.Panel)
r.Middleware(middleware.Jwt()).Get("homePlugins", infoController.HomePlugins)
r.Middleware(middleware.Jwt()).Get("nowMonitor", infoController.NowMonitor)
r.Middleware(middleware.Jwt()).Get("systemInfo", infoController.SystemInfo)
+14 -2
View File
@@ -4,8 +4,8 @@ import (
"github.com/goravel/framework/contracts/route"
"github.com/goravel/framework/facades"
"panel/app/http/controllers/plugins"
"panel/app/http/middleware"
"github.com/TheTNB/panel/app/http/controllers/plugins"
"github.com/TheTNB/panel/app/http/middleware"
)
// Plugin 加载插件路由
@@ -353,6 +353,18 @@ func Plugin() {
route.Get("config", rsyncController.GetConfig)
route.Post("config", rsyncController.UpdateConfig)
})
r.Prefix("frp").Group(func(route route.Router) {
frpController := plugins.NewFrpController()
route.Get("status", frpController.Status)
route.Get("isEnabled", frpController.IsEnabled)
route.Post("enable", frpController.Enable)
route.Post("disable", frpController.Disable)
route.Post("start", frpController.Start)
route.Post("stop", frpController.Stop)
route.Post("restart", frpController.Restart)
route.Get("config", frpController.GetConfig)
route.Post("config", frpController.UpdateConfig)
})
r.Prefix("toolbox").Group(func(route route.Router) {
toolboxController := plugins.NewToolBoxController()
route.Get("dns", toolboxController.GetDNS)
+99
View File
@@ -0,0 +1,99 @@
#!/bin/bash
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/www/server/bin:/www/server/sbin:$PATH
: '
Copyright (C) 2022 - now HaoZi Technology Co., Ltd.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'
HR="+----------------------------------------------------"
ARCH=$(uname -m)
downloadUrl="https://dl.cdn.haozi.net/panel/frp"
frpPath="/www/server/frp"
frpVersion="0.58.0"
if [ ! -d "${frpPath}" ]; then
mkdir -p ${frpPath}
fi
# 架构判断
if [ "${ARCH}" == "x86_64" ]; then
frpFile="frp_${frpVersion}_linux_amd64.7z"
elif [ "${ARCH}" == "aarch64" ]; then
frpFile="frp_${frpVersion}_linux_arm64.7z"
else
echo -e $HR
echo "错误:不支持的架构"
exit 1
fi
# 下载frp
cd ${frpPath}
wget -T 120 -t 3 -O ${frpPath}/${frpFile} ${downloadUrl}/${frpFile}
wget -T 20 -t 3 -O ${frpPath}/${frpFile}.checksum.txt ${downloadUrl}/${frpFile}.checksum.txt
if ! sha256sum --status -c ${frpPath}/${frpFile}.checksum.txt; then
echo -e $HR
echo "错误:frp checksum 校验失败,文件可能被篡改或不完整,已终止操作"
rm -rf ${frpPath}
exit 1
fi
# 解压frp
cd ${frpPath}
7z x ${frpFile}
chmod -R 700 ${frpPath}
rm -f ${frpFile} ${frpFile}.checksum.txt
# 配置systemd
cat >/etc/systemd/system/frps.service <<EOF
[Unit]
Description=Frp Server Service
After=network.target syslog.target
Wants=network.target
[Service]
Type=simple
Restart=on-failure
RestartSec=5s
ExecStart=/www/server/frp/frps -c /www/server/frp/frps.toml
[Install]
WantedBy=multi-user.target
EOF
cat >/etc/systemd/system/frpc.service <<EOF
[Unit]
Description=Frp Client Service
After=network.target syslog.target
Wants=network.target
[Service]
Type=simple
Restart=on-failure
RestartSec=5s
ExecStart=/www/server/frp/frpc -c /www/server/frp/frpc.toml
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable frps
systemctl enable frpc
systemctl start frps
systemctl start frpc
panel writePlugin frp ${frpVersion}
echo -e "${HR}\nfrp 安装完成\n${HR}"

Some files were not shown because too many files have changed in this diff Show More