Compare commits

...

46 Commits

Author SHA1 Message Date
耗子 b0c072f78b feat: 发布 v2.1.10 2023-11-09 17:53:21 +08:00
耗子 8559fb9d0c fix: 修复入口设置 2023-11-09 17:53:01 +08:00
耗子 1bbec2b637 feat: 发布 v2.1.9 2023-11-09 17:25:31 +08:00
耗子 1426fb48ff feat: 发布 v2.1.8 2023-11-09 17:25:08 +08:00
耗子 0509397332 fix: 修复入口问题 2023-11-09 17:24:50 +08:00
耗子 57de4cb988 fix(证书管理): 修复证书保存及更新 2023-11-09 15:53:26 +08:00
耗子 d05d3eb2aa fix(证书管理): 修复DNS和用户删除 2023-11-09 02:02:53 +08:00
耗子 876fdc419f feat(证书管理): 输出错误信息 2023-11-09 01:58:28 +08:00
耗子 3f64d81250 feat: 同步前端修改 2023-11-09 01:51:28 +08:00
耗子 c4d304f2c9 fix: build 2023-11-08 16:15:06 +08:00
耗子 33d5d48b59 chore: 文档更新 2023-11-08 16:00:14 +08:00
耗子 d90423db4a feat: 规范部分控制器方法 2023-11-08 02:01:28 +08:00
耗子 e032faeae0 feat(证书管理): 不返回关联的模型信息 2023-11-07 02:09:29 +08:00
耗子 70601dfb00 feat(证书管理): 补全更新和显示接口 2023-11-07 02:05:50 +08:00
耗子 c6263ebe73 feat: 全局汉化验证器返回 2023-11-06 02:52:43 +08:00
耗子 485b7b9e8d feat(证书管理): DNS 添加备注名称 2023-11-06 01:25:39 +08:00
耗子 4c4a605684 fix: test 2023-11-05 23:26:45 +08:00
耗子 a347b5470d feat: 证书未删除禁止删除网站 2023-11-05 23:18:49 +08:00
耗子 0a3573bfb7 feat: 优化分页 2023-11-05 23:03:11 +08:00
耗子 01620ebd1d feat: 优化面板文件权限 2023-11-05 17:31:57 +08:00
耗子 57ad59093d feat: 优化数据库文件配置 2023-11-05 17:19:48 +08:00
耗子 4339ad7c50 feat: update framework 2023-11-05 16:37:24 +08:00
耗子 5916ad98bd feat: 面板入口调整 2023-11-05 16:33:12 +08:00
耗子 b8ccfbb4f5 fix: try to fix tests 2023-11-05 13:09:05 +08:00
耗子 786985341b fix: try to fix tests 2023-11-05 12:59:35 +08:00
耗子 1cb5a61ff2 fix: try to fix tests 2023-11-05 12:51:30 +08:00
耗子 f6f47785ed fix: optimize cert controller 2023-11-05 12:24:28 +08:00
耗子 3d43a2a08f refactor: migrate to fiber http driver 2023-11-04 14:27:15 +08:00
耗子 00ec8d693c chore: 统一错误输出 2023-11-02 02:56:05 +08:00
耗子 1fa96e828e feat(证书管理): 支持自动续签 2023-11-02 02:53:03 +08:00
耗子 c46656ed2d feat: 重构目录结构与添加证书管理 2023-11-02 02:10:32 +08:00
耗子 47a9e6728f feat: 添加 swagger 2023-11-01 20:58:08 +08:00
耗子 b343071ec2 feat: 优化数据库连接配置 2023-11-01 17:45:55 +08:00
耗子 5383623203 feat: 添加通用系统错误的帮助方法 2023-11-01 17:19:59 +08:00
耗子 31fff122c3 feat: 初步实现证书管理器 2023-11-01 01:41:57 +08:00
耗子 ebc0c31611 docs: 严谨化 2023-10-31 00:59:53 +08:00
耗子 52e5bc6277 docs: 更新 README 2023-10-31 00:40:30 +08:00
耗子 6892fcde43 docs: 更新 README 2023-10-31 00:38:28 +08:00
耗子 7ef2aef377 feat: 发布 v2.1.7 2023-10-30 22:08:59 +08:00
耗子 4b61cad486 feat: 优化 fail2ban 添加 2023-10-30 22:01:39 +08:00
耗子 ad9076162b feat: 优化 fail2ban 添加 2023-10-30 21:51:26 +08:00
耗子 cf82d3c1c1 feat: 同步前端修改 2023-10-30 21:45:03 +08:00
耗子 b8f5eccf9c feat: 同步前端修改 2023-10-30 21:11:02 +08:00
耗子 914e4e1b7b feat: 优化 Debian apt 卸载 2023-10-30 21:10:04 +08:00
耗子 2fd37af6ea feat: 同步前端修改 2023-10-30 19:41:37 +08:00
耗子 30b7a8f317 feat: 任务列表倒序排列 2023-10-30 19:07:08 +08:00
109 changed files with 9161 additions and 1510 deletions
+2
View File
@@ -17,6 +17,8 @@ jobs:
- name: Set up environment
run: |
cp panel-example.conf .env
echo "DB_FILE=database/panel.db" >> .env
go run . artisan key:generate
go run . artisan migrate
- name: Run tests
run: go test ./...
+2
View File
@@ -44,7 +44,9 @@ unit_test:
- apt-get update
- apt-get install -y curl jq
- cp panel-example.conf .env
- echo "DB_FILE=database/panel.db" >> .env
- go run . artisan key:generate
- go run . artisan migrate
- go test -v -coverprofile=coverage.txt -covermode=atomic ./...
build:
+1
View File
@@ -25,6 +25,7 @@ archives:
strip_parent_binary_folder: true
files:
- LICENSE
- docs/*
- public/*
- storage/*
- database/*
+1
View File
@@ -0,0 +1 @@
replace carbon.DateTime string
+20 -16
View File
@@ -1,3 +1,7 @@
<p align="right">
[简体中文] | [<a href="README_EN.md">English</a>]
</p>
<h1 align="center">耗子 Linux 面板</h1>
<p align="center">
@@ -6,25 +10,24 @@
<a href="https://goreportcard.com/report/github.com/haozi-team/panel"><img src="https://goreportcard.com/badge/github.com/haozi-team/panel"></a>
<a href="https://codecov.io/gh/haozi-team/panel"><img src="https://codecov.io/gh/haozi-team/panel/branch/main/graph/badge.svg?token=XFT5NGNSRG"></a>
<a href="https://img.shields.io/github/license/haozi-team/panel"><img src="https://img.shields.io/github/license/haozi-team/panel"></a>
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2Fhaozi-team%2Fpanel?ref=badge_shield"><img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2Fhaozi-team%2Fpanel.svg?type=shield" alt="FOSSA Status"></a>
</p>
<p align="center">
[简体中文] | [<a href="README_EN.md">English</a>]
</p>
耗子 Linux 面板是针对我们自身业务需要使用 Golang 开发的轻量 Linux 服务器运维管理面板,以 GNU Affero General Public License v3.0 开源。
免责声明:严禁使用耗子 Linux 面板从事任何非法活动,非法站点请勿向我们请求任何形式的技术支持,如果在技术支持过程中发现非法内容,我们将立即停止技术支持并留存相关证据。
耗子 Linux 面板是使用 Golang + Vue 开发的轻量 Linux 服务器运维管理面板,以 GNU Affero General Public License v3.0 开源。
交流QQ群:[12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | QQ频道:[pd.qq.com/s/fyol46wfy](https://pd.qq.com/s/fyol46wfy)
## 重要说明
## 优势
目前我们正在重构面板前端部分开发 2.1 版本,v2.0.58 将是 2.0 的最后一个版本
1. **极低占用:** 在 Debian 12 下部署面板 + LNMP 环境,内存占用不到 500 MB,遥遥领先于使用容器化的其他面板
2. **高效运维:** 面板 UI 界面简洁,操作简单,无需繁琐的配置,即可快速部署各类环境、调整应用设置。
3. **离线运行:** 面板运行可不依赖任何外部服务,你甚至可以在部署完成后停止面板进程,不会对已部署服务造成任何影响。
4. **久经考验:** 我们生产环境自 2022 年即开始使用,已稳定运行 1 年无事故。
5. **开源开放:** 面板开源,你可以自由修改、审计面板源码,安全性有保障。
新前端将使用 Vue + Naive UI 开发,预计 10 月底可以完成。
## UI 截图
存量用户请尽快更新至 v2.0.58,以便后续升级到 2.1 版本。
![UI 截图](ui.png)
## 运行环境
@@ -47,7 +50,8 @@ CentOS Stream 可使用迁移脚本迁移至支持的系统: [CentOS 8/9 迁移
## 安装面板
安装面板前,你需要了解LNMP环境的基本知识,以及如何处理常见的LNMP环境问题,不建议0基础的用户安装和使用耗子Linux面板([推荐: 宝塔 - 简单好用服务器运维面板](https://www.bt.cn/?invite_code=M190eXRpZWE=))。
> **Warning**
> 安装面板前,你需要了解LNMP环境的基本知识,以及如何处理常见的LNMP环境问题,不建议0基础的用户安装和使用耗子Linux面板。
如果你决定继续,请以`root`用户登录服务器,执行以下命令安装面板:
@@ -104,10 +108,6 @@ panel
- [盾云CDN](http://cdn.ddunyun.com/)
### 对象存储
- [又拍云](https://www.upyun.com/?utm_source=lianmeng&utm_medium=referral)
### DevOps
- [极狐 GitLab](https://www.jihulab.com/)
@@ -129,3 +129,7 @@ panel
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=haozi-team/panel&type=Date" />
</picture>
</a>
## 免责声明
严禁使用耗子 Linux 面板从事任何非法活动,非法站点请勿向我们请求任何形式的技术支持,如果在技术支持过程中发现非法内容,我们将立即停止技术支持并留存相关证据。
+20 -12
View File
@@ -1,3 +1,7 @@
<p align="right">
[<a href="README.md">简体中文</a>] | [English]
</p>
<h1 align="center">HaoZi Linux Panel</h1>
<p align="center">
@@ -6,18 +10,21 @@
<a href="https://goreportcard.com/report/github.com/haozi-team/panel"><img src="https://goreportcard.com/badge/github.com/haozi-team/panel"></a>
<a href="https://codecov.io/gh/haozi-team/panel"><img src="https://codecov.io/gh/haozi-team/panel/branch/main/graph/badge.svg?token=XFT5NGNSRG"></a>
<a href="https://img.shields.io/github/license/haozi-team/panel"><img src="https://img.shields.io/github/license/haozi-team/panel"></a>
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2Fhaozi-team%2Fpanel?ref=badge_shield"><img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2Fhaozi-team%2Fpanel.svg?type=shield" alt="FOSSA Status"></a>
</p>
<p align="center">
[<a href="README.md">简体中文</a>] | [English]
</p>
The HaoZi Linux Panel is a lightweight Linux server operation and maintenance management panel developed using Golang for our own business needs. It is open source under the GNU Affero General Public License v3.0 protocol.
Disclaimer: It is strictly prohibited to use the HaoZi Linux Panel to engage in any illegal activities. Please do not request any form of technical support from us for illegal sites. If illegal content is discovered during the technical support process, we will immediately stop technical support and retain relevant evidence.
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.
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)
## Advantages
1. **Extremely low usage:** Deploying the panel + LNMP environment under Debian 12, the memory usage is less than 500 MB, far ahead of other panels using containerization.
2. **Efficient operation and maintenance:** The panel UI interface is simple and easy to operate, and you can quickly deploy various environments and adjust application settings without complicated configuration.
3. **Offline operation:** The panel can run without relying on any external services. You can even stop the panel process after deployment is complete, and it will not affect the deployed services.
4. **Tested by time:** We have been using it in production environment since 2022, and it has been running stably for 1 year without any accidents.
5. **Open source and open:** The panel is open source, you can freely modify and audit the panel source code, and the security is guaranteed.
## UI Screenshots
![UI Screenshots](ui.png)
@@ -43,7 +50,8 @@ As system versions are constantly updated, we may also terminate support for som
## Install Panel
Before installing the panel, you need to understand the basic knowledge of the LNMP environment and how to deal with common LNMP environment problems. It is not recommended for users with zero basic knowledge to install and use HaoZi Linux Panel ([Recommended: aaPanel - easy-to-use server operation and maintenance panel](https://aapanel.com)).
> **Warning**
> Before installing the panel, you need to understand the basic knowledge of the LNMP environment and how to deal with common LNMP environment problems. It is not recommended for users with zero basic knowledge to install and use HaoZi Linux Panel.
If you decide to continue, please log in to the server as `root` user and execute the following command to install the panel:
@@ -100,10 +108,6 @@ You can find or create an Issue in [Issue List](https://github.com/haozi-team/pa
- [盾云CDN](http://cdn.ddunyun.com/)
### Object Storage
- [又拍云](https://www.upyun.com/?utm_source=lianmeng&utm_medium=referral)
### DevOps
- [JiHu GitLab](https://www.jihulab.com/)
@@ -125,3 +129,7 @@ This project owes its existence to all those who have contributed. To contribute
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=haozi-team/panel&type=Date" />
</picture>
</a>
## Disclaimer
It is strictly prohibited to use the HaoZi Linux Panel to engage in any illegal activities. Please do not request any form of technical support from us for illegal sites. If illegal content is discovered during the technical support process, we will immediately stop technical support and retain relevant evidence.
+74
View File
@@ -0,0 +1,74 @@
package commands
import (
"crypto/x509"
"encoding/pem"
"github.com/goravel/framework/contracts/console"
"github.com/goravel/framework/contracts/console/command"
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/carbon"
"panel/app/models"
"panel/app/services"
)
type CertRenew struct {
}
// Signature The name and signature of the console command.
func (receiver *CertRenew) Signature() string {
return "panel:cert-renew"
}
// Description The console command description.
func (receiver *CertRenew) Description() string {
return "[面板] 证书续签"
}
// Extend The console command extend.
func (receiver *CertRenew) Extend() command.Extend {
return command.Extend{
Category: "panel",
}
}
// Handle Execute the console command.
func (receiver *CertRenew) Handle(ctx console.Context) error {
var certs []models.Cert
err := facades.Orm().Query().With("Website").With("User").With("DNS").Find(&certs)
if err != nil {
return err
}
for _, cert := range certs {
if !cert.AutoRenew {
continue
}
block, _ := pem.Decode([]byte(cert.Cert))
if block != nil {
data, err := x509.ParseCertificate(block.Bytes)
if err != nil {
continue
}
// 结束时间大于 7 天的证书不续签
endTime := carbon.FromStdTime(data.NotAfter)
if endTime.Gt(carbon.Now().AddDays(7)) {
continue
}
}
certService := services.NewCertImpl()
_, err = certService.Renew(cert.ID)
if err != nil {
facades.Log().Tags("面板", "证书管理").With(map[string]any{
"cert_id": cert.ID,
"error": err.Error(),
}).Errorf("证书续签失败")
}
}
return nil
}
+5 -8
View File
@@ -56,7 +56,7 @@ func (receiver *Panel) Handle(ctx console.Context) error {
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.SettingKeyEntrance, Value: "/"}, {Key: models.SettingKeyVersion, Value: facades.Config().GetString("panel.version")}}
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("初始化失败")
@@ -130,21 +130,18 @@ func (receiver *Panel) Handle(ctx console.Context) error {
color.Greenln("用户名: " + user.Username)
color.Greenln("密码: " + password)
color.Greenln("面板端口: " + port)
color.Greenln("面板入口: " + services.NewSettingImpl().Get(models.SettingKeyEntrance, "/"))
color.Greenln("面板入口: " + facades.Config().GetString("http.entrance"))
case "getPort":
port := tools.Exec("cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}'")
color.Greenln("面板端口: " + port)
case "getEntrance":
color.Greenln("面板入口: " + services.NewSettingImpl().Get(models.SettingKeyEntrance, "/"))
color.Greenln("面板入口: " + facades.Config().GetString("http.entrance"))
case "deleteEntrance":
err := services.NewSettingImpl().Set(models.SettingKeyEntrance, "/")
if err != nil {
color.Redln("删除面板入口失败")
return nil
}
oldEntrance := tools.Exec(`cat /www/panel/panel.conf | grep APP_ENTRANCE | awk -F '=' '{print $2}' | tr -d '\n'`)
tools.Exec("sed -i 's!APP_ENTRANCE=" + oldEntrance + "!APP_ENTRANCE=/!g' /www/panel/panel.conf")
color.Greenln("删除面板入口成功")
+2
View File
@@ -14,6 +14,7 @@ type Kernel struct {
func (kernel *Kernel) Schedule() []schedule.Event {
return []schedule.Event{
facades.Schedule().Command("panel:monitoring").EveryMinute().SkipIfStillRunning(),
facades.Schedule().Command("panel:cert-renew").Daily().SkipIfStillRunning(),
}
}
@@ -21,5 +22,6 @@ func (kernel *Kernel) Commands() []console.Command {
return []console.Command{
&commands.Panel{},
&commands.Monitoring{},
&commands.CertRenew{},
}
}
+76
View File
@@ -0,0 +1,76 @@
package controllers
import (
"os"
"strings"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"panel/app/services"
"panel/pkg/tools"
)
type AssetController struct {
setting services.Setting
}
func NewAssetController() *AssetController {
return &AssetController{
setting: services.NewSettingImpl(),
}
}
func (r *AssetController) Index(ctx http.Context) http.Response {
entrance := facades.Config().GetString("http.entrance")
if entrance == "/" {
entrance = ""
}
// 自动纠正 URL 格式
if ctx.Request().Path() == entrance && ctx.Request().Path() != "/" {
return ctx.Response().Redirect(http.StatusMovedPermanently, ctx.Request().Path()+"/")
}
// 拒绝访问非入口文件
if !strings.HasPrefix(ctx.Request().Path(), entrance) {
return ctx.Response().Status(http.StatusNotFound).String(http.StatusText(http.StatusNotFound))
}
path := strings.TrimPrefix(ctx.Request().Path(), entrance)
// 设置默认首页
if path == "/" || path == "" {
path = "/index.html"
}
if !tools.Exists("public" + path) {
return ctx.Response().Status(http.StatusNotFound).String(http.StatusText(http.StatusNotFound))
}
file, err := os.Open("public" + path)
if err != nil {
return ctx.Response().Status(http.StatusInternalServerError).String(http.StatusText(http.StatusInternalServerError))
}
stat, err := file.Stat()
if err != nil {
return ctx.Response().Status(http.StatusInternalServerError).String(http.StatusText(http.StatusInternalServerError))
}
if stat.IsDir() {
return ctx.Response().Status(http.StatusForbidden).String(http.StatusText(http.StatusForbidden))
}
return ctx.Response().Header("Cache-Control", "no-cache").File("public" + path)
}
func (r *AssetController) Favicon(ctx http.Context) http.Response {
return ctx.Response().File("public/favicon.png")
}
func (r *AssetController) Robots(ctx http.Context) http.Response {
return ctx.Response().File("public/robots.txt")
}
func (r *AssetController) NotFound(ctx http.Context) http.Response {
return ctx.Response().Status(http.StatusNotFound).String(http.StatusText(http.StatusNotFound))
}
+686
View File
@@ -0,0 +1,686 @@
package controllers
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
requests "panel/app/http/requests/cert"
commonrequests "panel/app/http/requests/common"
responses "panel/app/http/responses/cert"
"panel/app/models"
"panel/app/services"
"panel/pkg/acme"
)
type CertController struct {
cron services.Cron
cert services.Cert
}
func NewCertController() *CertController {
return &CertController{
cron: services.NewCronImpl(),
cert: services.NewCertImpl(),
}
}
// CAProviders
// @Summary 获取 CA 提供商
// @Description 获取面板证书管理支持的 CA 提供商
// @Tags 证书管理
// @Produce json
// @Security BearerToken
// @Success 200 {object} SuccessResponse
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Router /panel/cert/caProviders [get]
func (r *CertController) CAProviders(ctx http.Context) http.Response {
return Success(ctx, []map[string]string{
{
"name": "Let's Encrypt",
"ca": "letsencrypt",
},
{
"name": "ZeroSSL",
"ca": "zerossl",
},
{
"name": "SSL.com",
"ca": "sslcom",
},
{
"name": "Google",
"ca": "google",
},
{
"name": "Buypass",
"ca": "buypass",
},
})
}
// DNSProviders
// @Summary 获取 DNS 提供商
// @Description 获取面板证书管理支持的 DNS 提供商
// @Tags 证书管理
// @Produce json
// @Security BearerToken
// @Success 200 {object} SuccessResponse
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Router /panel/cert/dnsProviders [get]
func (r *CertController) DNSProviders(ctx http.Context) http.Response {
return Success(ctx, []map[string]any{
{
"name": "DNSPod",
"dns": acme.DnsPod,
},
{
"name": "阿里云",
"dns": acme.AliYun,
},
{
"name": "CloudFlare",
"dns": acme.CloudFlare,
},
})
}
// Algorithms
// @Summary 获取算法列表
// @Description 获取面板证书管理支持的算法列表
// @Tags 证书管理
// @Produce json
// @Security BearerToken
// @Success 200 {object} SuccessResponse
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Router /panel/cert/algorithms [get]
func (r *CertController) Algorithms(ctx http.Context) http.Response {
return Success(ctx, []map[string]any{
{
"name": "EC256",
"key": acme.KeyEC256,
},
{
"name": "EC384",
"key": acme.KeyEC384,
},
{
"name": "RSA2048",
"key": acme.KeyRSA2048,
},
{
"name": "RSA4096",
"key": acme.KeyRSA4096,
},
})
}
// UserList
// @Summary 获取用户列表
// @Description 获取面板证书管理的 ACME 用户列表
// @Tags 证书管理
// @Produce json
// @Security BearerToken
// @Success 200 {object} SuccessResponse{data=responses.CertList}
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/cert/users [get]
func (r *CertController) UserList(ctx http.Context) http.Response {
var paginateRequest commonrequests.Paginate
sanitize := Sanitize(ctx, &paginateRequest)
if sanitize != nil {
return sanitize
}
var users []models.CertUser
var total int64
err := facades.Orm().Query().Paginate(paginateRequest.Page, paginateRequest.Limit, &users, &total)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"error": err.Error(),
}).Error("获取ACME用户列表失败")
return ErrorSystem(ctx)
}
return Success(ctx, &responses.UserList{
Total: total,
Items: users,
})
}
// UserStore
// @Summary 添加 ACME 用户
// @Description 添加 ACME 用户到面板证书管理
// @Tags 证书管理
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.UserStore true "用户信息"
// @Success 200 {object} SuccessResponse
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/cert/users [post]
func (r *CertController) UserStore(ctx http.Context) http.Response {
var storeRequest requests.UserStore
sanitize := Sanitize(ctx, &storeRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.UserStore(storeRequest)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"error": err.Error(),
}).Error("添加ACME用户失败")
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// UserUpdate
// @Summary 更新 ACME 用户
// @Description 更新面板证书管理的 ACME 用户
// @Tags 证书管理
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "用户 ID"
// @Param data body requests.UserUpdate true "用户信息"
// @Success 200 {object} SuccessResponse
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/cert/users/{id} [put]
func (r *CertController) UserUpdate(ctx http.Context) http.Response {
var updateRequest requests.UserUpdate
sanitize := Sanitize(ctx, &updateRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.UserUpdate(updateRequest)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"userID": updateRequest.ID,
"error": err.Error(),
}).Error("更新ACME用户失败")
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// UserShow
// @Summary 获取 ACME 用户
// @Description 获取面板证书管理的 ACME 用户
// @Tags 证书管理
// @Produce json
// @Security BearerToken
// @Param id path int true "用户 ID"
// @Success 200 {object} SuccessResponse{data=models.CertUser}
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/cert/users/{id} [get]
func (r *CertController) UserShow(ctx http.Context) http.Response {
var showAndDestroyRequest requests.UserShowAndDestroy
sanitize := Sanitize(ctx, &showAndDestroyRequest)
if sanitize != nil {
return sanitize
}
user, err := r.cert.UserShow(showAndDestroyRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"userID": showAndDestroyRequest.ID,
"error": err.Error(),
}).Error("获取ACME用户失败")
return ErrorSystem(ctx)
}
return Success(ctx, user)
}
// UserDestroy
// @Summary 删除 ACME 用户
// @Description 删除面板证书管理的 ACME 用户
// @Tags 证书管理
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "用户 ID"
// @Success 200 {object} SuccessResponse
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/cert/users/{id} [delete]
func (r *CertController) UserDestroy(ctx http.Context) http.Response {
var showAndDestroyRequest requests.UserShowAndDestroy
sanitize := Sanitize(ctx, &showAndDestroyRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.UserDestroy(showAndDestroyRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"userID": showAndDestroyRequest.ID,
"error": err.Error(),
}).Error("删除ACME用户失败")
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// DNSList
// @Summary 获取 DNS 接口列表
// @Description 获取面板证书管理的 DNS 接口列表
// @Tags 证书管理
// @Produce json
// @Security BearerToken
// @Success 200 {object} SuccessResponse{data=responses.DNSList}
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/cert/dns [get]
func (r *CertController) DNSList(ctx http.Context) http.Response {
var paginateRequest commonrequests.Paginate
sanitize := Sanitize(ctx, &paginateRequest)
if sanitize != nil {
return sanitize
}
var dns []models.CertDNS
var total int64
err := facades.Orm().Query().Paginate(paginateRequest.Page, paginateRequest.Limit, &dns, &total)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"error": err.Error(),
}).Error("获取DNS接口列表失败")
return ErrorSystem(ctx)
}
return Success(ctx, &responses.DNSList{
Total: total,
Items: dns,
})
}
// DNSStore
// @Summary 添加 DNS 接口
// @Description 添加 DNS 接口到面板证书管理
// @Tags 证书管理
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.DNSStore true "DNS 接口信息"
// @Success 200 {object} SuccessResponse
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/cert/dns [post]
func (r *CertController) DNSStore(ctx http.Context) http.Response {
var storeRequest requests.DNSStore
sanitize := Sanitize(ctx, &storeRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.DNSStore(storeRequest)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"error": err.Error(),
}).Error("添加DNS接口失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// DNSShow
// @Summary 获取 DNS 接口
// @Description 获取面板证书管理的 DNS 接口
// @Tags 证书管理
// @Produce json
// @Security BearerToken
// @Param id path int true "DNS 接口 ID"
// @Success 200 {object} SuccessResponse{data=models.CertDNS}
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/cert/dns/{id} [get]
func (r *CertController) DNSShow(ctx http.Context) http.Response {
var showAndDestroyRequest requests.DNSShowAndDestroy
sanitize := Sanitize(ctx, &showAndDestroyRequest)
if sanitize != nil {
return sanitize
}
dns, err := r.cert.DNSShow(showAndDestroyRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"dnsID": showAndDestroyRequest.ID,
"error": err.Error(),
}).Error("获取DNS接口失败")
return ErrorSystem(ctx)
}
return Success(ctx, dns)
}
// DNSUpdate
// @Summary 更新 DNS 接口
// @Description 更新面板证书管理的 DNS 接口
// @Tags 证书管理
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "DNS 接口 ID"
// @Param data body requests.DNSUpdate true "DNS 接口信息"
// @Success 200 {object} SuccessResponse
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/cert/dns/{id} [put]
func (r *CertController) DNSUpdate(ctx http.Context) http.Response {
var updateRequest requests.DNSUpdate
sanitize := Sanitize(ctx, &updateRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.DNSUpdate(updateRequest)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"dnsID": updateRequest.ID,
"error": err.Error(),
}).Error("更新DNS接口失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// DNSDestroy
// @Summary 删除 DNS 接口
// @Description 删除面板证书管理的 DNS 接口
// @Tags 证书管理
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "DNS 接口 ID"
// @Success 200 {object} SuccessResponse
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/cert/dns/{id} [delete]
func (r *CertController) DNSDestroy(ctx http.Context) http.Response {
var showAndDestroyRequest requests.DNSShowAndDestroy
sanitize := Sanitize(ctx, &showAndDestroyRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.DNSDestroy(showAndDestroyRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"dnsID": showAndDestroyRequest.ID,
"error": err.Error(),
}).Error("删除DNS接口失败")
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// CertList
// @Summary 获取证书列表
// @Description 获取面板证书管理的证书列表
// @Tags 证书管理
// @Produce json
// @Security BearerToken
// @Success 200 {object} SuccessResponse{data=responses.CertList}
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/cert/certs [get]
func (r *CertController) CertList(ctx http.Context) http.Response {
var paginateRequest commonrequests.Paginate
sanitize := Sanitize(ctx, &paginateRequest)
if sanitize != nil {
return sanitize
}
var certs []models.Cert
var total int64
err := facades.Orm().Query().With("Website").With("User").With("DNS").Paginate(paginateRequest.Page, paginateRequest.Limit, &certs, &total)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"error": err.Error(),
}).Error("获取证书列表失败")
return ErrorSystem(ctx)
}
return Success(ctx, &responses.CertList{
Total: total,
Items: certs,
})
}
// CertStore
// @Summary 添加证书
// @Description 添加证书到面板证书管理
// @Tags 证书管理
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.CertStore true "证书信息"
// @Success 200 {object} SuccessResponse
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/cert/certs [post]
func (r *CertController) CertStore(ctx http.Context) http.Response {
var storeRequest requests.CertStore
sanitize := Sanitize(ctx, &storeRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.CertStore(storeRequest)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"error": err.Error(),
}).Error("添加证书失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// CertUpdate
// @Summary 更新证书
// @Description 更新面板证书管理的证书
// @Tags 证书管理
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "证书 ID"
// @Param data body requests.CertUpdate true "证书信息"
// @Success 200 {object} SuccessResponse
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/cert/certs/{id} [put]
func (r *CertController) CertUpdate(ctx http.Context) http.Response {
var updateRequest requests.CertUpdate
sanitize := Sanitize(ctx, &updateRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.CertUpdate(updateRequest)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"certID": updateRequest.ID,
"error": err.Error(),
}).Error("更新证书失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// CertShow
// @Summary 获取证书
// @Description 获取面板证书管理的证书
// @Tags 证书管理
// @Produce json
// @Security BearerToken
// @Param id path int true "证书 ID"
// @Success 200 {object} SuccessResponse{data=models.Cert}
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/cert/certs/{id} [get]
func (r *CertController) CertShow(ctx http.Context) http.Response {
var showAndDestroyRequest requests.CertShowAndDestroy
sanitize := Sanitize(ctx, &showAndDestroyRequest)
if sanitize != nil {
return sanitize
}
cert, err := r.cert.CertShow(showAndDestroyRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"certID": showAndDestroyRequest.ID,
"error": err.Error(),
}).Error("获取证书失败")
return ErrorSystem(ctx)
}
return Success(ctx, cert)
}
// CertDestroy
// @Summary 删除证书
// @Description 删除面板证书管理的证书
// @Tags 证书管理
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "证书 ID"
// @Success 200 {object} SuccessResponse
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/cert/certs/{id} [delete]
func (r *CertController) CertDestroy(ctx http.Context) http.Response {
var showAndDestroyRequest requests.CertShowAndDestroy
sanitize := Sanitize(ctx, &showAndDestroyRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.CertDestroy(showAndDestroyRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"certID": showAndDestroyRequest.ID,
"error": err.Error(),
}).Error("删除证书失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// Obtain
// @Summary 签发证书
// @Description 签发面板证书管理的证书
// @Tags 证书管理
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.Obtain true "证书信息"
// @Success 200 {object} SuccessResponse
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/cert/obtain [post]
func (r *CertController) Obtain(ctx http.Context) http.Response {
var obtainRequest requests.Obtain
sanitize := Sanitize(ctx, &obtainRequest)
if sanitize != nil {
return sanitize
}
cert, err := r.cert.CertShow(obtainRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"certID": obtainRequest.ID,
"error": err.Error(),
}).Error("获取证书失败")
return ErrorSystem(ctx)
}
if cert.DNS != nil || cert.Website != nil {
_, err = r.cert.ObtainAuto(obtainRequest.ID)
} else {
_, err = r.cert.ObtainManual(obtainRequest.ID)
}
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"error": err.Error(),
}).Error("签发证书失败")
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// Renew
// @Summary 续签证书
// @Description 续签面板证书管理的证书
// @Tags 证书管理
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.Renew true "证书信息"
// @Success 200 {object} SuccessResponse
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/cert/renew [post]
func (r *CertController) Renew(ctx http.Context) http.Response {
var renewRequest requests.Renew
sanitize := Sanitize(ctx, &renewRequest)
if sanitize != nil {
return sanitize
}
_, err := r.cert.Renew(renewRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"error": err.Error(),
}).Error("续签证书失败")
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// ManualDNS
// @Summary 获取手动 DNS 记录
// @Description 获取签发证书所需的 DNS 记录
// @Tags 证书管理
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.Obtain true "证书信息"
// @Success 200 {object} SuccessResponse{data=map[string]acme.Resolve}
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/cert/manualDNS [post]
func (r *CertController) ManualDNS(ctx http.Context) http.Response {
var obtainRequest requests.Obtain
sanitize := Sanitize(ctx, &obtainRequest)
if sanitize != nil {
return sanitize
}
resolves, err := r.cert.ManualDNS(obtainRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"error": err.Error(),
}).Error("获取手动DNS记录失败")
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, resolves)
}
@@ -9,21 +9,57 @@ import (
"panel/app/services"
)
// SuccessResponse 通用成功响应
type SuccessResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data any `json:"data"`
}
// ErrorResponse 通用错误响应
type ErrorResponse struct {
Code int `json:"code"`
Message string `json:"message"`
}
// Success 响应成功
func Success(ctx http.Context, data any) http.Response {
return ctx.Response().Success().Json(http.Json{
"code": 0,
"message": "success",
"data": data,
return ctx.Response().Success().Json(&SuccessResponse{
Code: 0,
Message: "success",
Data: data,
})
}
func Error(ctx http.Context, code int, message any) http.Response {
return ctx.Response().Json(http.StatusOK, http.Json{
"code": code,
"message": message,
// Error 响应错误
func Error(ctx http.Context, code int, message string) http.Response {
return ctx.Response().Json(http.StatusOK, &ErrorResponse{
Code: code,
Message: message,
})
}
// ErrorSystem 响应系统错误
func ErrorSystem(ctx http.Context) http.Response {
return ctx.Response().Json(http.StatusOK, &ErrorResponse{
Code: http.StatusInternalServerError,
Message: "系统内部错误",
})
}
// Sanitize 消毒请求参数
func Sanitize(ctx http.Context, request http.FormRequest) http.Response {
errors, err := ctx.Request().ValidateRequest(request)
if err != nil {
return Error(ctx, http.StatusUnprocessableEntity, err.Error())
}
if errors != nil {
return Error(ctx, http.StatusUnprocessableEntity, errors.One())
}
return nil
}
// Check 检查插件是否可用
func Check(ctx http.Context, slug string) http.Response {
plugin := services.NewPluginImpl().GetBySlug(slug)
@@ -31,7 +67,7 @@ func Check(ctx http.Context, slug string) http.Response {
installedPlugins, err := services.NewPluginImpl().AllInstalled()
if err != nil {
facades.Log().Error("[面板][插件] 获取已安装插件失败")
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
if installedPlugin.Version != plugin.Version || installedPlugin.Slug != plugin.Slug {
+7 -7
View File
@@ -35,7 +35,7 @@ func (c *CronController) List(ctx http.Context) http.Response {
err := facades.Orm().Query().Paginate(page, limit, &crons, &total)
if err != nil {
facades.Log().Error("[面板][CronController] 查询计划任务列表失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
return Success(ctx, http.Json{
@@ -121,7 +121,7 @@ panel cutoff ${name} ${save} 2>&1
shellFile := strconv.Itoa(int(carbon.Now().Timestamp())) + tools.RandomString(16)
if !tools.Write(shellDir+shellFile+".sh", shell, 0700) {
facades.Log().Error("[面板][CronController] 创建计划任务脚本失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
tools.Exec("dos2unix " + shellDir + shellFile + ".sh")
@@ -136,7 +136,7 @@ panel cutoff ${name} ${save} 2>&1
err = facades.Orm().Query().Create(&cron)
if err != nil {
facades.Log().Error("[面板][CronController] 创建计划任务失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
c.cron.AddToSystem(cron)
@@ -191,12 +191,12 @@ func (c *CronController) Update(ctx http.Context) http.Response {
err = facades.Orm().Query().Save(&cron)
if err != nil {
facades.Log().Error("[面板][CronController] 更新计划任务失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
if !tools.Write(cron.Shell, ctx.Request().Input("script"), 0644) {
facades.Log().Error("[面板][CronController] 更新计划任务脚本失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
tools.Exec("dos2unix " + cron.Shell)
@@ -222,7 +222,7 @@ func (c *CronController) Delete(ctx http.Context) http.Response {
_, err = facades.Orm().Query().Delete(&cron)
if err != nil {
facades.Log().Error("[面板][CronController] 删除计划任务失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
@@ -250,7 +250,7 @@ func (c *CronController) Status(ctx http.Context) http.Response {
err = facades.Orm().Query().Save(&cron)
if err != nil {
facades.Log().Error("[面板][CronController] 更新计划任务状态失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
c.cron.DeleteFromSystem(cron)
+7 -7
View File
@@ -39,7 +39,7 @@ func (c *InfoController) Name(ctx http.Context) http.Response {
err := facades.Orm().Query().Where("key", "name").First(&setting)
if err != nil {
facades.Log().Error("[面板][InfoController] 查询面板名称失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
return Success(ctx, http.Json{
@@ -53,7 +53,7 @@ func (c *InfoController) HomePlugins(ctx http.Context) http.Response {
err := facades.Orm().Query().Where("show", 1).Find(&plugins)
if err != nil {
facades.Log().Error("[面板][InfoController] 查询首页插件失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
type pluginsData struct {
@@ -119,7 +119,7 @@ func (c *InfoController) CountInfo(ctx http.Context) http.Response {
db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/")
if err != nil {
facades.Log().With(map[string]any{
facades.Log().Request(ctx.Request()).With(map[string]any{
"error": err.Error(),
}).Error("[面板][InfoController] 获取数据库列表失败")
databaseCount = -1
@@ -127,7 +127,7 @@ func (c *InfoController) CountInfo(ctx http.Context) http.Response {
defer db.Close()
rows, err := db.Query("SHOW DATABASES")
if err != nil {
facades.Log().With(map[string]any{
facades.Log().Request(ctx.Request()).With(map[string]any{
"error": err.Error(),
}).Error("[面板][InfoController] 获取数据库列表失败")
databaseCount = -1
@@ -199,7 +199,7 @@ func (c *InfoController) InstalledDbAndPhp(ctx http.Context) http.Response {
var php []models.Plugin
err := facades.Orm().Query().Where("slug like ?", "php%").Find(&php)
if err != nil {
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
var mysql models.Plugin
@@ -305,7 +305,7 @@ func (c *InfoController) Update(ctx http.Context) http.Response {
panel, err := tools.GetLatestPanelVersion()
if err != nil {
facades.Log().With(map[string]any{
facades.Log().Request(ctx.Request()).With(map[string]any{
"error": err.Error(),
}).Error("[面板][InfoController] 获取最新版本失败")
return Error(ctx, http.StatusInternalServerError, "获取最新版本失败")
@@ -313,7 +313,7 @@ func (c *InfoController) Update(ctx http.Context) http.Response {
err = tools.UpdatePanel(panel)
if err != nil {
facades.Log().With(map[string]any{
facades.Log().Request(ctx.Request()).With(map[string]any{
"error": err.Error(),
}).Error("[面板][InfoController] 更新面板失败")
return Error(ctx, http.StatusInternalServerError, "更新失败: "+err.Error())
+4 -4
View File
@@ -27,7 +27,7 @@ func (r *MonitorController) Switch(ctx http.Context) http.Response {
err := r.setting.Set(models.SettingKeyMonitor, cast.ToString(value))
if err != nil {
facades.Log().Error("[面板][MonitorController] 更新监控开关失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
@@ -39,7 +39,7 @@ func (r *MonitorController) SaveDays(ctx http.Context) http.Response {
err := r.setting.Set(models.SettingKeyMonitorDays, days)
if err != nil {
facades.Log().Error("[面板][MonitorController] 更新监控天数失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
@@ -61,7 +61,7 @@ func (r *MonitorController) Clear(ctx http.Context) http.Response {
_, err := facades.Orm().Query().Where("1 = 1").Delete(&models.Monitor{})
if err != nil {
facades.Log().Error("[面板][MonitorController] 清空监控数据失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
@@ -78,7 +78,7 @@ func (r *MonitorController) List(ctx http.Context) http.Response {
err := facades.Orm().Query().Where("created_at >= ?", startTime.ToDateTimeString()).Where("created_at <= ?", endTime.ToDateTimeString()).Get(&monitors)
if err != nil {
facades.Log().Error("[面板][MonitorController] 查询监控数据失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
if len(monitors) == 0 {
+9 -9
View File
@@ -27,7 +27,7 @@ func (r *PluginController) List(ctx http.Context) http.Response {
plugins := r.plugin.All()
installedPlugins, err := r.plugin.AllInstalled()
if err != nil {
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
var lock sync.RWMutex
@@ -101,7 +101,7 @@ func (r *PluginController) Install(ctx http.Context) http.Response {
installedPlugins, err := r.plugin.AllInstalled()
if err != nil {
facades.Log().Error("[面板][PluginController] 获取已安装插件失败")
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
if installedPlugin.ID != 0 {
@@ -142,7 +142,7 @@ func (r *PluginController) Install(ctx http.Context) http.Response {
task.Log = "/tmp/" + plugin.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[面板][PluginController] 创建任务失败: " + err.Error())
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
r.task.Process(task.ID)
@@ -157,7 +157,7 @@ func (r *PluginController) Uninstall(ctx http.Context) http.Response {
installedPlugins, err := r.plugin.AllInstalled()
if err != nil {
facades.Log().Error("[面板][PluginController] 获取已安装插件失败")
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
if installedPlugin.ID == 0 {
@@ -198,7 +198,7 @@ func (r *PluginController) Uninstall(ctx http.Context) http.Response {
task.Log = "/tmp/" + plugin.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[面板][PluginController] 创建任务失败: " + err.Error())
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
r.task.Process(task.ID)
@@ -213,7 +213,7 @@ func (r *PluginController) Update(ctx http.Context) http.Response {
installedPlugins, err := r.plugin.AllInstalled()
if err != nil {
facades.Log().Error("[面板][PluginController] 获取已安装插件失败")
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
if installedPlugin.ID == 0 {
@@ -254,7 +254,7 @@ func (r *PluginController) Update(ctx http.Context) http.Response {
task.Log = "/tmp/" + plugin.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[面板][PluginController] 创建任务失败: " + err.Error())
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
r.task.Process(task.ID)
@@ -269,7 +269,7 @@ func (r *PluginController) UpdateShow(ctx http.Context) http.Response {
var plugin models.Plugin
if err := facades.Orm().Query().Where("slug", slug).First(&plugin); err != nil {
facades.Log().Error("[面板][PluginController] 查询插件失败: " + err.Error())
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
if plugin.ID == 0 {
return Error(ctx, http.StatusUnprocessableEntity, "插件未安装")
@@ -278,7 +278,7 @@ func (r *PluginController) UpdateShow(ctx http.Context) http.Response {
plugin.Show = show
if err := facades.Orm().Query().Save(&plugin); err != nil {
facades.Log().Error("[面板][PluginController] 更新插件失败: " + err.Error())
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
return Success(ctx, "操作成功")
+29
View File
@@ -0,0 +1,29 @@
package plugins
type PHPExtension struct {
Name string `json:"name"`
Slug string `json:"slug"`
Description string `json:"description"`
Installed bool `json:"installed"`
}
type LoadInfo struct {
Name string `json:"name"`
Value string `json:"value"`
}
type Fail2banJail struct {
Name string `json:"name"`
Enabled bool `json:"enabled"`
LogPath string `json:"log_path"`
MaxRetry int `json:"max_retry"`
FindTime int `json:"find_time"`
BanTime int `json:"ban_time"`
}
type S3fsMount struct {
ID int64 `json:"id"`
Path string `json:"path"`
Bucket string `json:"bucket"`
Url string `json:"url"`
}
@@ -1,4 +1,4 @@
package fail2ban
package plugins
import (
"regexp"
@@ -24,17 +24,8 @@ func NewFail2banController() *Fail2banController {
}
}
type Jail struct {
Name string `json:"name"`
Enabled bool `json:"enabled"`
LogPath string `json:"log_path"`
MaxRetry int `json:"max_retry"`
FindTime int `json:"find_time"`
BanTime int `json:"ban_time"`
}
// Status 获取运行状态
func (c *Fail2banController) Status(ctx http.Context) http.Response {
func (r *Fail2banController) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
@@ -53,7 +44,7 @@ func (c *Fail2banController) Status(ctx http.Context) http.Response {
}
// Reload 重载配置
func (c *Fail2banController) Reload(ctx http.Context) http.Response {
func (r *Fail2banController) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
@@ -73,7 +64,7 @@ func (c *Fail2banController) Reload(ctx http.Context) http.Response {
}
// Restart 重启服务
func (c *Fail2banController) Restart(ctx http.Context) http.Response {
func (r *Fail2banController) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
@@ -93,7 +84,7 @@ func (c *Fail2banController) Restart(ctx http.Context) http.Response {
}
// Start 启动服务
func (c *Fail2banController) Start(ctx http.Context) http.Response {
func (r *Fail2banController) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
@@ -113,7 +104,7 @@ func (c *Fail2banController) Start(ctx http.Context) http.Response {
}
// Stop 停止服务
func (c *Fail2banController) Stop(ctx http.Context) http.Response {
func (r *Fail2banController) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
@@ -133,7 +124,7 @@ func (c *Fail2banController) Stop(ctx http.Context) http.Response {
}
// List 所有 Fail2ban 规则
func (c *Fail2banController) List(ctx http.Context) http.Response {
func (r *Fail2banController) List(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
@@ -151,7 +142,7 @@ func (c *Fail2banController) List(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "Fail2ban 规则为空")
}
var jails []Jail
var jails []Fail2banJail
for i, jail := range jailList {
if i == 0 {
continue
@@ -168,7 +159,7 @@ func (c *Fail2banController) List(ctx http.Context) http.Response {
jailFindTime := regexp.MustCompile(`findtime = (.*)`).FindStringSubmatch(jailRaw)
jailBanTime := regexp.MustCompile(`bantime = (.*)`).FindStringSubmatch(jailRaw)
jails = append(jails, Jail{
jails = append(jails, Fail2banJail{
Name: jailName,
Enabled: jailEnabled,
LogPath: jailLogPath[1],
@@ -183,13 +174,16 @@ func (c *Fail2banController) List(ctx http.Context) http.Response {
if startIndex > len(jails) {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []Jail{},
"items": []Fail2banJail{},
})
}
if endIndex > len(jails) {
endIndex = len(jails)
}
pagedJails := jails[startIndex:endIndex]
if pagedJails == nil {
pagedJails = []Fail2banJail{}
}
return controllers.Success(ctx, http.Json{
"total": len(jails),
@@ -198,7 +192,7 @@ func (c *Fail2banController) List(ctx http.Context) http.Response {
}
// Add 添加 Fail2ban 规则
func (c *Fail2banController) Add(ctx http.Context) http.Response {
func (r *Fail2banController) Add(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
@@ -210,8 +204,9 @@ func (c *Fail2banController) Add(ctx http.Context) http.Response {
"maxretry": "required",
"findtime": "required",
"bantime": "required",
"website_name": "required_if:type,website",
"website_mode": "required_if:type,website",
"website_path": "required_if:type,website",
"website_path": "required_if:website_mode,path",
})
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
@@ -225,22 +220,23 @@ func (c *Fail2banController) Add(ctx http.Context) http.Response {
jailMaxRetry := ctx.Request().Input("maxretry")
jailFindTime := ctx.Request().Input("findtime")
jailBanTime := ctx.Request().Input("bantime")
jailWebsiteName := ctx.Request().Input("website_name")
jailWebsiteMode := ctx.Request().Input("website_mode")
jailWebsitePath := ctx.Request().Input("website_path")
raw := tools.Read("/etc/fail2ban/jail.local")
if strings.Contains(raw, "["+jailName+"]") || (strings.Contains(raw, "["+jailName+"]"+"-cc") && jailWebsiteMode == "cc") || (strings.Contains(raw, "["+jailName+"]"+"-path") && jailWebsiteMode == "path") {
if (strings.Contains(raw, "["+jailName+"]") && jailType == "service") || (strings.Contains(raw, "["+jailWebsiteName+"]"+"-cc") && jailType == "website" && jailWebsiteMode == "cc") || (strings.Contains(raw, "["+jailWebsiteName+"]"+"-path") && jailType == "website" && jailWebsiteMode == "path") {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "规则已存在")
}
switch jailType {
case "website":
var website models.Website
err := facades.Orm().Query().Where("name", jailName).FirstOrFail(&website)
err := facades.Orm().Query().Where("name", jailWebsiteName).FirstOrFail(&website)
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "网站不存在")
}
config, err := c.website.GetConfig(int(website.ID))
config, err := r.website.GetConfig(int(website.ID))
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取网站配置失败")
}
@@ -254,17 +250,17 @@ func (c *Fail2banController) Add(ctx http.Context) http.Response {
}
rule := `
# ` + jailName + `-` + jailWebsiteMode + `-START
[` + jailName + `-` + jailWebsiteMode + `]
# ` + jailWebsiteName + `-` + jailWebsiteMode + `-START
[` + jailWebsiteName + `-` + jailWebsiteMode + `]
enabled = true
filter = haozi-` + jailName + `-` + jailWebsiteMode + `
filter = haozi-` + jailWebsiteName + `-` + jailWebsiteMode + `
port = ` + ports + `
maxretry = ` + jailMaxRetry + `
findtime = ` + jailFindTime + `
bantime = ` + jailBanTime + `
action = %(action_mwl)s
logpath = /www/wwwlogs/` + website.Name + `.log
# ` + jailName + `-` + jailWebsiteMode + `-END
# ` + jailWebsiteName + `-` + jailWebsiteMode + `-END
`
raw += rule
tools.Write("/etc/fail2ban/jail.local", raw, 0644)
@@ -283,7 +279,7 @@ failregex = ^<HOST>\s-.*\s` + jailWebsitePath + `.*HTTP/.*$
ignoreregex =
`
}
tools.Write("/etc/fail2ban/filter.d/haozi-"+jailName+"-"+jailWebsiteMode+".conf", filter, 0644)
tools.Write("/etc/fail2ban/filter.d/haozi-"+jailWebsiteName+"-"+jailWebsiteMode+".conf", filter, 0644)
case "service":
var logPath string
@@ -335,7 +331,7 @@ logpath = ` + logPath + `
}
// Delete 删除规则
func (c *Fail2banController) Delete(ctx http.Context) http.Response {
func (r *Fail2banController) Delete(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
@@ -357,13 +353,13 @@ func (c *Fail2banController) Delete(ctx http.Context) http.Response {
}
// BanList 获取封禁列表
func (c *Fail2banController) BanList(ctx http.Context) http.Response {
func (r *Fail2banController) BanList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
}
name := ctx.Request().Query("name")
name := ctx.Request().Input("name")
if len(name) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "缺少参数")
}
@@ -382,16 +378,19 @@ func (c *Fail2banController) BanList(ctx http.Context) http.Response {
})
}
}
if list == nil {
list = []map[string]string{}
}
return controllers.Success(ctx, http.Json{
"currentlyBan": currentlyBan,
"totalBan": totalBan,
"bannedIpList": list,
"currently_ban": currentlyBan,
"total_ban": totalBan,
"baned_list": list,
})
}
// Unban 解封
func (c *Fail2banController) Unban(ctx http.Context) http.Response {
func (r *Fail2banController) Unban(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
@@ -408,7 +407,7 @@ func (c *Fail2banController) Unban(ctx http.Context) http.Response {
}
// SetWhiteList 设置白名单
func (c *Fail2banController) SetWhiteList(ctx http.Context) http.Response {
func (r *Fail2banController) SetWhiteList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
@@ -434,7 +433,7 @@ func (c *Fail2banController) SetWhiteList(ctx http.Context) http.Response {
}
// GetWhiteList 获取白名单
func (c *Fail2banController) GetWhiteList(ctx http.Context) http.Response {
func (r *Fail2banController) GetWhiteList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
@@ -1,4 +1,4 @@
package mysql57
package plugins
import (
"database/sql"
@@ -29,7 +29,7 @@ func NewMysql57Controller() *Mysql57Controller {
}
// Status 获取运行状态
func (c *Mysql57Controller) Status(ctx http.Context) http.Response {
func (r *Mysql57Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -48,7 +48,7 @@ func (c *Mysql57Controller) Status(ctx http.Context) http.Response {
}
// Reload 重载配置
func (c *Mysql57Controller) Reload(ctx http.Context) http.Response {
func (r *Mysql57Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -68,7 +68,7 @@ func (c *Mysql57Controller) Reload(ctx http.Context) http.Response {
}
// Restart 重启服务
func (c *Mysql57Controller) Restart(ctx http.Context) http.Response {
func (r *Mysql57Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -88,7 +88,7 @@ func (c *Mysql57Controller) Restart(ctx http.Context) http.Response {
}
// Start 启动服务
func (c *Mysql57Controller) Start(ctx http.Context) http.Response {
func (r *Mysql57Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -108,7 +108,7 @@ func (c *Mysql57Controller) Start(ctx http.Context) http.Response {
}
// Stop 停止服务
func (c *Mysql57Controller) Stop(ctx http.Context) http.Response {
func (r *Mysql57Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -128,7 +128,7 @@ func (c *Mysql57Controller) Stop(ctx http.Context) http.Response {
}
// GetConfig 获取配置
func (c *Mysql57Controller) GetConfig(ctx http.Context) http.Response {
func (r *Mysql57Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -144,7 +144,7 @@ func (c *Mysql57Controller) GetConfig(ctx http.Context) http.Response {
}
// SaveConfig 保存配置
func (c *Mysql57Controller) SaveConfig(ctx http.Context) http.Response {
func (r *Mysql57Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -159,17 +159,17 @@ func (c *Mysql57Controller) SaveConfig(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusInternalServerError, "写入MySQL配置失败")
}
return c.Restart(ctx)
return r.Restart(ctx)
}
// Load 获取负载
func (c *Mysql57Controller) Load(ctx http.Context) http.Response {
func (r *Mysql57Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
if len(rootPassword) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码为空")
}
@@ -238,7 +238,7 @@ func (c *Mysql57Controller) Load(ctx http.Context) http.Response {
}
// ErrorLog 获取错误日志
func (c *Mysql57Controller) ErrorLog(ctx http.Context) http.Response {
func (r *Mysql57Controller) ErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -249,7 +249,7 @@ func (c *Mysql57Controller) ErrorLog(ctx http.Context) http.Response {
}
// ClearErrorLog 清空错误日志
func (c *Mysql57Controller) ClearErrorLog(ctx http.Context) http.Response {
func (r *Mysql57Controller) ClearErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -260,7 +260,7 @@ func (c *Mysql57Controller) ClearErrorLog(ctx http.Context) http.Response {
}
// SlowLog 获取慢查询日志
func (c *Mysql57Controller) SlowLog(ctx http.Context) http.Response {
func (r *Mysql57Controller) SlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -271,7 +271,7 @@ func (c *Mysql57Controller) SlowLog(ctx http.Context) http.Response {
}
// ClearSlowLog 清空慢查询日志
func (c *Mysql57Controller) ClearSlowLog(ctx http.Context) http.Response {
func (r *Mysql57Controller) ClearSlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -282,13 +282,13 @@ func (c *Mysql57Controller) ClearSlowLog(ctx http.Context) http.Response {
}
// GetRootPassword 获取root密码
func (c *Mysql57Controller) GetRootPassword(ctx http.Context) http.Response {
func (r *Mysql57Controller) GetRootPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
if len(rootPassword) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码为空")
}
@@ -297,7 +297,7 @@ func (c *Mysql57Controller) GetRootPassword(ctx http.Context) http.Response {
}
// SetRootPassword 设置root密码
func (c *Mysql57Controller) SetRootPassword(ctx http.Context) http.Response {
func (r *Mysql57Controller) SetRootPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -316,11 +316,11 @@ func (c *Mysql57Controller) SetRootPassword(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码不能为空")
}
oldRootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
oldRootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
if oldRootPassword != rootPassword {
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + oldRootPassword + " -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '" + rootPassword + "';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + oldRootPassword + " -e \"FLUSH PRIVILEGES;\"")
err := c.setting.Set(models.SettingKeyMysqlRootPassword, rootPassword)
err := r.setting.Set(models.SettingKeyMysqlRootPassword, rootPassword)
if err != nil {
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '" + oldRootPassword + "';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
@@ -332,13 +332,13 @@ func (c *Mysql57Controller) SetRootPassword(ctx http.Context) http.Response {
}
// DatabaseList 获取数据库列表
func (c *Mysql57Controller) DatabaseList(ctx http.Context) http.Response {
func (r *Mysql57Controller) DatabaseList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
type database struct {
Name string `json:"name"`
}
@@ -395,7 +395,7 @@ func (c *Mysql57Controller) DatabaseList(ctx http.Context) http.Response {
}
// AddDatabase 添加数据库
func (c *Mysql57Controller) AddDatabase(ctx http.Context) http.Response {
func (r *Mysql57Controller) AddDatabase(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -413,7 +413,7 @@ func (c *Mysql57Controller) AddDatabase(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
database := ctx.Request().Input("database")
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
@@ -427,7 +427,7 @@ func (c *Mysql57Controller) AddDatabase(ctx http.Context) http.Response {
}
// DeleteDatabase 删除数据库
func (c *Mysql57Controller) DeleteDatabase(ctx http.Context) http.Response {
func (r *Mysql57Controller) DeleteDatabase(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -443,7 +443,7 @@ func (c *Mysql57Controller) DeleteDatabase(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
database := ctx.Request().Input("database")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP DATABASE IF EXISTS " + database + ";\"")
@@ -451,13 +451,13 @@ func (c *Mysql57Controller) DeleteDatabase(ctx http.Context) http.Response {
}
// BackupList 获取备份列表
func (c *Mysql57Controller) BackupList(ctx http.Context) http.Response {
func (r *Mysql57Controller) BackupList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
backupList, err := c.backup.MysqlList()
backupList, err := r.backup.MysqlList()
if err != nil {
facades.Log().Error("[MySQL57] 获取备份列表失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "获取备份列表失败")
@@ -488,7 +488,7 @@ func (c *Mysql57Controller) BackupList(ctx http.Context) http.Response {
}
// UploadBackup 上传备份
func (c *Mysql57Controller) UploadBackup(ctx http.Context) http.Response {
func (r *Mysql57Controller) UploadBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -499,7 +499,7 @@ func (c *Mysql57Controller) UploadBackup(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "上传文件失败")
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/mysql"
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/mysql"
if !tools.Exists(backupPath) {
tools.Mkdir(backupPath, 0644)
}
@@ -514,7 +514,7 @@ func (c *Mysql57Controller) UploadBackup(ctx http.Context) http.Response {
}
// CreateBackup 创建备份
func (c *Mysql57Controller) CreateBackup(ctx http.Context) http.Response {
func (r *Mysql57Controller) CreateBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -531,7 +531,7 @@ func (c *Mysql57Controller) CreateBackup(ctx http.Context) http.Response {
}
database := ctx.Request().Input("database")
err = c.backup.MysqlBackup(database)
err = r.backup.MysqlBackup(database)
if err != nil {
facades.Log().Error("[MYSQL57] 创建备份失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "创建备份失败")
@@ -541,7 +541,7 @@ func (c *Mysql57Controller) CreateBackup(ctx http.Context) http.Response {
}
// DeleteBackup 删除备份
func (c *Mysql57Controller) DeleteBackup(ctx http.Context) http.Response {
func (r *Mysql57Controller) DeleteBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -557,7 +557,7 @@ func (c *Mysql57Controller) DeleteBackup(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/mysql"
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/mysql"
fileName := ctx.Request().Input("name")
tools.Remove(backupPath + "/" + fileName)
@@ -565,7 +565,7 @@ func (c *Mysql57Controller) DeleteBackup(ctx http.Context) http.Response {
}
// RestoreBackup 还原备份
func (c *Mysql57Controller) RestoreBackup(ctx http.Context) http.Response {
func (r *Mysql57Controller) RestoreBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -582,7 +582,7 @@ func (c *Mysql57Controller) RestoreBackup(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
err = c.backup.MysqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup"))
err = r.backup.MysqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup"))
if err != nil {
facades.Log().Error("[MYSQL57] 还原失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "还原失败: "+err.Error())
@@ -592,7 +592,7 @@ func (c *Mysql57Controller) RestoreBackup(ctx http.Context) http.Response {
}
// UserList 用户列表
func (c *Mysql57Controller) UserList(ctx http.Context) http.Response {
func (r *Mysql57Controller) UserList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -604,7 +604,7 @@ func (c *Mysql57Controller) UserList(ctx http.Context) http.Response {
Grants []string `json:"grants"`
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/")
if err != nil {
facades.Log().Error("[MYSQL57] 连接数据库失败:" + err.Error())
@@ -678,7 +678,7 @@ func (c *Mysql57Controller) UserList(ctx http.Context) http.Response {
}
// AddUser 添加用户
func (c *Mysql57Controller) AddUser(ctx http.Context) http.Response {
func (r *Mysql57Controller) AddUser(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -696,7 +696,7 @@ func (c *Mysql57Controller) AddUser(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
database := ctx.Request().Input("database")
@@ -708,7 +708,7 @@ func (c *Mysql57Controller) AddUser(ctx http.Context) http.Response {
}
// DeleteUser 删除用户
func (c *Mysql57Controller) DeleteUser(ctx http.Context) http.Response {
func (r *Mysql57Controller) DeleteUser(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -724,7 +724,7 @@ func (c *Mysql57Controller) DeleteUser(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP USER '" + user + "'@'localhost';\"")
@@ -732,7 +732,7 @@ func (c *Mysql57Controller) DeleteUser(ctx http.Context) http.Response {
}
// SetUserPassword 设置用户密码
func (c *Mysql57Controller) SetUserPassword(ctx http.Context) http.Response {
func (r *Mysql57Controller) SetUserPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -749,7 +749,7 @@ func (c *Mysql57Controller) SetUserPassword(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\"")
@@ -759,7 +759,7 @@ func (c *Mysql57Controller) SetUserPassword(ctx http.Context) http.Response {
}
// SetUserPrivileges 设置用户权限
func (c *Mysql57Controller) SetUserPrivileges(ctx http.Context) http.Response {
func (r *Mysql57Controller) SetUserPrivileges(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
@@ -776,7 +776,7 @@ func (c *Mysql57Controller) SetUserPrivileges(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
database := ctx.Request().Input("database")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"REVOKE ALL PRIVILEGES ON *.* FROM '" + user + "'@'localhost';\"")
@@ -1,4 +1,4 @@
package mysql80
package plugins
import (
"database/sql"
@@ -29,7 +29,7 @@ func NewMysql80Controller() *Mysql80Controller {
}
// Status 获取运行状态
func (c *Mysql80Controller) Status(ctx http.Context) http.Response {
func (r *Mysql80Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -48,7 +48,7 @@ func (c *Mysql80Controller) Status(ctx http.Context) http.Response {
}
// Reload 重载配置
func (c *Mysql80Controller) Reload(ctx http.Context) http.Response {
func (r *Mysql80Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -68,7 +68,7 @@ func (c *Mysql80Controller) Reload(ctx http.Context) http.Response {
}
// Restart 重启服务
func (c *Mysql80Controller) Restart(ctx http.Context) http.Response {
func (r *Mysql80Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -88,7 +88,7 @@ func (c *Mysql80Controller) Restart(ctx http.Context) http.Response {
}
// Start 启动服务
func (c *Mysql80Controller) Start(ctx http.Context) http.Response {
func (r *Mysql80Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -108,7 +108,7 @@ func (c *Mysql80Controller) Start(ctx http.Context) http.Response {
}
// Stop 停止服务
func (c *Mysql80Controller) Stop(ctx http.Context) http.Response {
func (r *Mysql80Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -128,7 +128,7 @@ func (c *Mysql80Controller) Stop(ctx http.Context) http.Response {
}
// GetConfig 获取配置
func (c *Mysql80Controller) GetConfig(ctx http.Context) http.Response {
func (r *Mysql80Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -144,7 +144,7 @@ func (c *Mysql80Controller) GetConfig(ctx http.Context) http.Response {
}
// SaveConfig 保存配置
func (c *Mysql80Controller) SaveConfig(ctx http.Context) http.Response {
func (r *Mysql80Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -159,17 +159,17 @@ func (c *Mysql80Controller) SaveConfig(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusInternalServerError, "写入MySQL配置失败")
}
return c.Restart(ctx)
return r.Restart(ctx)
}
// Load 获取负载
func (c *Mysql80Controller) Load(ctx http.Context) http.Response {
func (r *Mysql80Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
if len(rootPassword) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码为空")
}
@@ -238,7 +238,7 @@ func (c *Mysql80Controller) Load(ctx http.Context) http.Response {
}
// ErrorLog 获取错误日志
func (c *Mysql80Controller) ErrorLog(ctx http.Context) http.Response {
func (r *Mysql80Controller) ErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -249,7 +249,7 @@ func (c *Mysql80Controller) ErrorLog(ctx http.Context) http.Response {
}
// ClearErrorLog 清空错误日志
func (c *Mysql80Controller) ClearErrorLog(ctx http.Context) http.Response {
func (r *Mysql80Controller) ClearErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -260,7 +260,7 @@ func (c *Mysql80Controller) ClearErrorLog(ctx http.Context) http.Response {
}
// SlowLog 获取慢查询日志
func (c *Mysql80Controller) SlowLog(ctx http.Context) http.Response {
func (r *Mysql80Controller) SlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -271,7 +271,7 @@ func (c *Mysql80Controller) SlowLog(ctx http.Context) http.Response {
}
// ClearSlowLog 清空慢查询日志
func (c *Mysql80Controller) ClearSlowLog(ctx http.Context) http.Response {
func (r *Mysql80Controller) ClearSlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -282,13 +282,13 @@ func (c *Mysql80Controller) ClearSlowLog(ctx http.Context) http.Response {
}
// GetRootPassword 获取root密码
func (c *Mysql80Controller) GetRootPassword(ctx http.Context) http.Response {
func (r *Mysql80Controller) GetRootPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
if len(rootPassword) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码为空")
}
@@ -297,7 +297,7 @@ func (c *Mysql80Controller) GetRootPassword(ctx http.Context) http.Response {
}
// SetRootPassword 设置root密码
func (c *Mysql80Controller) SetRootPassword(ctx http.Context) http.Response {
func (r *Mysql80Controller) SetRootPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -316,11 +316,11 @@ func (c *Mysql80Controller) SetRootPassword(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码不能为空")
}
oldRootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
oldRootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
if oldRootPassword != rootPassword {
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + oldRootPassword + " -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '" + rootPassword + "';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + oldRootPassword + " -e \"FLUSH PRIVILEGES;\"")
err := c.setting.Set(models.SettingKeyMysqlRootPassword, rootPassword)
err := r.setting.Set(models.SettingKeyMysqlRootPassword, rootPassword)
if err != nil {
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '" + oldRootPassword + "';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
@@ -332,13 +332,13 @@ func (c *Mysql80Controller) SetRootPassword(ctx http.Context) http.Response {
}
// DatabaseList 获取数据库列表
func (c *Mysql80Controller) DatabaseList(ctx http.Context) http.Response {
func (r *Mysql80Controller) DatabaseList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
type database struct {
Name string `json:"name"`
}
@@ -395,7 +395,7 @@ func (c *Mysql80Controller) DatabaseList(ctx http.Context) http.Response {
}
// AddDatabase 添加数据库
func (c *Mysql80Controller) AddDatabase(ctx http.Context) http.Response {
func (r *Mysql80Controller) AddDatabase(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -413,7 +413,7 @@ func (c *Mysql80Controller) AddDatabase(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
database := ctx.Request().Input("database")
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
@@ -427,7 +427,7 @@ func (c *Mysql80Controller) AddDatabase(ctx http.Context) http.Response {
}
// DeleteDatabase 删除数据库
func (c *Mysql80Controller) DeleteDatabase(ctx http.Context) http.Response {
func (r *Mysql80Controller) DeleteDatabase(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -443,7 +443,7 @@ func (c *Mysql80Controller) DeleteDatabase(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
database := ctx.Request().Input("database")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP DATABASE IF EXISTS " + database + ";\"")
@@ -451,13 +451,13 @@ func (c *Mysql80Controller) DeleteDatabase(ctx http.Context) http.Response {
}
// BackupList 获取备份列表
func (c *Mysql80Controller) BackupList(ctx http.Context) http.Response {
func (r *Mysql80Controller) BackupList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
backupList, err := c.backup.MysqlList()
backupList, err := r.backup.MysqlList()
if err != nil {
facades.Log().Error("[MySQL80] 获取备份列表失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "获取备份列表失败")
@@ -488,7 +488,7 @@ func (c *Mysql80Controller) BackupList(ctx http.Context) http.Response {
}
// UploadBackup 上传备份
func (c *Mysql80Controller) UploadBackup(ctx http.Context) http.Response {
func (r *Mysql80Controller) UploadBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -499,7 +499,7 @@ func (c *Mysql80Controller) UploadBackup(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "上传文件失败")
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/mysql"
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/mysql"
if !tools.Exists(backupPath) {
tools.Mkdir(backupPath, 0644)
}
@@ -514,7 +514,7 @@ func (c *Mysql80Controller) UploadBackup(ctx http.Context) http.Response {
}
// CreateBackup 创建备份
func (c *Mysql80Controller) CreateBackup(ctx http.Context) http.Response {
func (r *Mysql80Controller) CreateBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -531,7 +531,7 @@ func (c *Mysql80Controller) CreateBackup(ctx http.Context) http.Response {
}
database := ctx.Request().Input("database")
err = c.backup.MysqlBackup(database)
err = r.backup.MysqlBackup(database)
if err != nil {
facades.Log().Error("[MYSQL80] 创建备份失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "创建备份失败")
@@ -541,7 +541,7 @@ func (c *Mysql80Controller) CreateBackup(ctx http.Context) http.Response {
}
// DeleteBackup 删除备份
func (c *Mysql80Controller) DeleteBackup(ctx http.Context) http.Response {
func (r *Mysql80Controller) DeleteBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -557,7 +557,7 @@ func (c *Mysql80Controller) DeleteBackup(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/mysql"
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/mysql"
fileName := ctx.Request().Input("name")
tools.Remove(backupPath + "/" + fileName)
@@ -565,7 +565,7 @@ func (c *Mysql80Controller) DeleteBackup(ctx http.Context) http.Response {
}
// RestoreBackup 还原备份
func (c *Mysql80Controller) RestoreBackup(ctx http.Context) http.Response {
func (r *Mysql80Controller) RestoreBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -582,7 +582,7 @@ func (c *Mysql80Controller) RestoreBackup(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
err = c.backup.MysqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup"))
err = r.backup.MysqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup"))
if err != nil {
facades.Log().Error("[MYSQL80] 还原失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "还原失败: "+err.Error())
@@ -592,7 +592,7 @@ func (c *Mysql80Controller) RestoreBackup(ctx http.Context) http.Response {
}
// UserList 用户列表
func (c *Mysql80Controller) UserList(ctx http.Context) http.Response {
func (r *Mysql80Controller) UserList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -604,7 +604,7 @@ func (c *Mysql80Controller) UserList(ctx http.Context) http.Response {
Grants []string `json:"grants"`
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/")
if err != nil {
facades.Log().Error("[MYSQL80] 连接数据库失败:" + err.Error())
@@ -678,7 +678,7 @@ func (c *Mysql80Controller) UserList(ctx http.Context) http.Response {
}
// AddUser 添加用户
func (c *Mysql80Controller) AddUser(ctx http.Context) http.Response {
func (r *Mysql80Controller) AddUser(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -696,7 +696,7 @@ func (c *Mysql80Controller) AddUser(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
database := ctx.Request().Input("database")
@@ -708,7 +708,7 @@ func (c *Mysql80Controller) AddUser(ctx http.Context) http.Response {
}
// DeleteUser 删除用户
func (c *Mysql80Controller) DeleteUser(ctx http.Context) http.Response {
func (r *Mysql80Controller) DeleteUser(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -724,7 +724,7 @@ func (c *Mysql80Controller) DeleteUser(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP USER '" + user + "'@'localhost';\"")
@@ -732,7 +732,7 @@ func (c *Mysql80Controller) DeleteUser(ctx http.Context) http.Response {
}
// SetUserPassword 设置用户密码
func (c *Mysql80Controller) SetUserPassword(ctx http.Context) http.Response {
func (r *Mysql80Controller) SetUserPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -749,7 +749,7 @@ func (c *Mysql80Controller) SetUserPassword(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\"")
@@ -759,7 +759,7 @@ func (c *Mysql80Controller) SetUserPassword(ctx http.Context) http.Response {
}
// SetUserPrivileges 设置用户权限
func (c *Mysql80Controller) SetUserPrivileges(ctx http.Context) http.Response {
func (r *Mysql80Controller) SetUserPrivileges(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
@@ -776,7 +776,7 @@ func (c *Mysql80Controller) SetUserPrivileges(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
database := ctx.Request().Input("database")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"REVOKE ALL PRIVILEGES ON *.* FROM '" + user + "'@'localhost';\"")
@@ -1,4 +1,4 @@
package openresty
package plugins
import (
"regexp"
@@ -24,7 +24,7 @@ func NewOpenrestyController() *OpenRestyController {
}
// Status 获取运行状态
func (c *OpenRestyController) Status(ctx http.Context) http.Response {
func (r *OpenRestyController) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
@@ -43,7 +43,7 @@ func (c *OpenRestyController) Status(ctx http.Context) http.Response {
}
// Reload 重载配置
func (c *OpenRestyController) Reload(ctx http.Context) http.Response {
func (r *OpenRestyController) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
@@ -63,7 +63,7 @@ func (c *OpenRestyController) Reload(ctx http.Context) http.Response {
}
// Start 启动OpenResty
func (c *OpenRestyController) Start(ctx http.Context) http.Response {
func (r *OpenRestyController) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
@@ -83,7 +83,7 @@ func (c *OpenRestyController) Start(ctx http.Context) http.Response {
}
// Stop 停止OpenResty
func (c *OpenRestyController) Stop(ctx http.Context) http.Response {
func (r *OpenRestyController) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
@@ -103,7 +103,7 @@ func (c *OpenRestyController) Stop(ctx http.Context) http.Response {
}
// Restart 重启OpenResty
func (c *OpenRestyController) Restart(ctx http.Context) http.Response {
func (r *OpenRestyController) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
@@ -123,7 +123,7 @@ func (c *OpenRestyController) Restart(ctx http.Context) http.Response {
}
// GetConfig 获取配置
func (c *OpenRestyController) GetConfig(ctx http.Context) http.Response {
func (r *OpenRestyController) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
@@ -138,7 +138,7 @@ func (c *OpenRestyController) GetConfig(ctx http.Context) http.Response {
}
// SaveConfig 保存配置
func (c *OpenRestyController) SaveConfig(ctx http.Context) http.Response {
func (r *OpenRestyController) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
@@ -153,11 +153,11 @@ func (c *OpenRestyController) SaveConfig(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusInternalServerError, "保存OpenResty配置失败")
}
return c.Reload(ctx)
return r.Reload(ctx)
}
// ErrorLog 获取错误日志
func (c *OpenRestyController) ErrorLog(ctx http.Context) http.Response {
func (r *OpenRestyController) ErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
@@ -172,7 +172,7 @@ func (c *OpenRestyController) ErrorLog(ctx http.Context) http.Response {
}
// ClearErrorLog 清空错误日志
func (c *OpenRestyController) ClearErrorLog(ctx http.Context) http.Response {
func (r *OpenRestyController) ClearErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
@@ -183,7 +183,7 @@ func (c *OpenRestyController) ClearErrorLog(ctx http.Context) http.Response {
}
// Load 获取负载
func (c *OpenRestyController) Load(ctx http.Context) http.Response {
func (r *OpenRestyController) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
@@ -1,4 +1,4 @@
package php74
package plugins
import (
"fmt"
@@ -30,15 +30,15 @@ func NewPhp74Controller() *Php74Controller {
}
}
func (c *Php74Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php74Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
status := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status == "active" {
@@ -48,17 +48,17 @@ func (c *Php74Controller) Status(ctx http.Context) http.Response {
}
}
func (c *Php74Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php74Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("systemctl reload php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl reload php-fpm-" + r.version)
out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status == "active" {
@@ -68,17 +68,17 @@ func (c *Php74Controller) Reload(ctx http.Context) http.Response {
}
}
func (c *Php74Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php74Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("systemctl start php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl start php-fpm-" + r.version)
out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status == "active" {
@@ -88,17 +88,17 @@ func (c *Php74Controller) Start(ctx http.Context) http.Response {
}
}
func (c *Php74Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php74Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("systemctl stop php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl stop php-fpm-" + r.version)
out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status != "active" {
@@ -108,17 +108,17 @@ func (c *Php74Controller) Stop(ctx http.Context) http.Response {
}
}
func (c *Php74Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php74Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("systemctl restart php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl restart php-fpm-" + r.version)
out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status == "active" {
@@ -128,38 +128,38 @@ func (c *Php74Controller) Restart(ctx http.Context) http.Response {
}
}
func (c *Php74Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php74Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
config := tools.Read("/www/server/php/" + c.version + "/etc/php.ini")
config := tools.Read("/www/server/php/" + r.version + "/etc/php.ini")
return controllers.Success(ctx, config)
}
func (c *Php74Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php74Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
config := ctx.Request().Input("config")
tools.Write("/www/server/php/"+c.version+"/etc/php.ini", config, 0644)
return c.Reload(ctx)
tools.Write("/www/server/php/"+r.version+"/etc/php.ini", config, 0644)
return r.Reload(ctx)
}
func (c *Php74Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php74Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
client := req.C().SetTimeout(10 * time.Second)
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + c.version)
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + r.version)
if err != nil || !resp.IsSuccessState() {
facades.Log().Error("获取PHP-" + c.version + "运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+c.version+"] 获取运行状态失败")
facades.Log().Error("获取PHP-" + r.version + "运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+r.version+"] 获取运行状态失败")
}
raw := resp.String()
@@ -185,65 +185,58 @@ func (c *Php74Controller) Load(ctx http.Context) http.Response {
return controllers.Success(ctx, data)
}
func (c *Php74Controller) ErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php74Controller) ErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/php-fpm.log"))
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/php-fpm.log"))
return controllers.Success(ctx, log)
}
func (c *Php74Controller) SlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php74Controller) SlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/slow.log"))
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/slow.log"))
return controllers.Success(ctx, log)
}
func (c *Php74Controller) ClearErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php74Controller) ClearErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/php-fpm.log")
tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/php-fpm.log")
return controllers.Success(ctx, true)
}
func (c *Php74Controller) ClearSlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php74Controller) ClearSlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/slow.log")
tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/slow.log")
return controllers.Success(ctx, true)
}
type Extension struct {
Name string `json:"name"`
Slug string `json:"slug"`
Description string `json:"description"`
Installed bool `json:"installed"`
}
func (c *Php74Controller) GetExtensionList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php74Controller) GetExtensionList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
extensions := c.GetExtensions()
extensions := r.GetExtensions()
return controllers.Success(ctx, extensions)
}
func (c *Php74Controller) InstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php74Controller) InstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
@@ -253,7 +246,7 @@ func (c *Php74Controller) InstallExtension(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
}
extensions := c.GetExtensions()
extensions := r.GetExtensions()
for _, item := range extensions {
if item.Slug == slug {
if item.Installed {
@@ -261,16 +254,16 @@ func (c *Php74Controller) InstallExtension(ctx http.Context) http.Response {
}
var task models.Task
task.Name = "安装PHP-" + c.version + "扩展-" + item.Name
task.Name = "安装PHP-" + r.version + "扩展-" + item.Name
task.Status = models.TaskStatusWaiting
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + r.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
task.Log = "/tmp/" + item.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[PHP-" + c.version + "] 创建安装拓展任务失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
facades.Log().Error("[PHP-" + r.version + "] 创建安装拓展任务失败:" + err.Error())
return controllers.ErrorSystem(ctx)
}
c.task.Process(task.ID)
r.task.Process(task.ID)
return controllers.Success(ctx, true)
}
@@ -279,8 +272,8 @@ func (c *Php74Controller) InstallExtension(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在")
}
func (c *Php74Controller) UninstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php74Controller) UninstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
@@ -290,7 +283,7 @@ func (c *Php74Controller) UninstallExtension(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
}
extensions := c.GetExtensions()
extensions := r.GetExtensions()
for _, item := range extensions {
if item.Slug == slug {
if !item.Installed {
@@ -298,16 +291,16 @@ func (c *Php74Controller) UninstallExtension(ctx http.Context) http.Response {
}
var task models.Task
task.Name = "卸载PHP-" + c.version + "扩展-" + item.Name
task.Name = "卸载PHP-" + r.version + "扩展-" + item.Name
task.Status = models.TaskStatusWaiting
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + r.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
task.Log = "/tmp/" + item.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[PHP-" + c.version + "] 创建卸载拓展任务失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
facades.Log().Error("[PHP-" + r.version + "] 创建卸载拓展任务失败:" + err.Error())
return controllers.ErrorSystem(ctx)
}
c.task.Process(task.ID)
r.task.Process(task.ID)
return controllers.Success(ctx, true)
}
@@ -316,46 +309,46 @@ func (c *Php74Controller) UninstallExtension(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在")
}
func (c *Php74Controller) GetExtensions() []Extension {
var extensions []Extension
extensions = append(extensions, Extension{
func (r *Php74Controller) GetExtensions() []PHPExtension {
var extensions []PHPExtension
extensions = append(extensions, PHPExtension{
Name: "OPcache",
Slug: "Zend OPcache",
Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "PhpRedis",
Slug: "redis",
Description: "PhpRedis 是一个用C语言编写的PHP模块,用来连接并操作 Redis 数据库上的数据。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "ImageMagick",
Slug: "imagick",
Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "Exif",
Slug: "exif",
Description: "通过 exif 扩展,你可以操作图像元数据。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "pdo_pgsql",
Slug: "pdo_pgsql",
Description: "(需先安装PostgreSQLpdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象(PDO)接口以启用从 PHP 到 PostgreSQL 数据库的访问。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "ionCube",
Slug: "ionCube Loader",
Description: "ionCube 是一个专业级的PHP加密解密工具。",
Installed: false,
})
raw := tools.Exec("/www/server/php/" + c.version + "/bin/php -m")
raw := tools.Exec("/www/server/php/" + r.version + "/bin/php -m")
rawExtensionList := strings.Split(raw, "\n")
for _, item := range rawExtensionList {
@@ -1,4 +1,4 @@
package php80
package plugins
import (
"fmt"
@@ -30,15 +30,15 @@ func NewPhp80Controller() *Php80Controller {
}
}
func (c *Php80Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php80Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
status := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status == "active" {
@@ -48,17 +48,17 @@ func (c *Php80Controller) Status(ctx http.Context) http.Response {
}
}
func (c *Php80Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php80Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("systemctl reload php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl reload php-fpm-" + r.version)
out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status == "active" {
@@ -68,17 +68,17 @@ func (c *Php80Controller) Reload(ctx http.Context) http.Response {
}
}
func (c *Php80Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php80Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("systemctl start php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl start php-fpm-" + r.version)
out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status == "active" {
@@ -88,17 +88,17 @@ func (c *Php80Controller) Start(ctx http.Context) http.Response {
}
}
func (c *Php80Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php80Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("systemctl stop php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl stop php-fpm-" + r.version)
out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status != "active" {
@@ -108,17 +108,17 @@ func (c *Php80Controller) Stop(ctx http.Context) http.Response {
}
}
func (c *Php80Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php80Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("systemctl restart php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl restart php-fpm-" + r.version)
out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status == "active" {
@@ -128,38 +128,38 @@ func (c *Php80Controller) Restart(ctx http.Context) http.Response {
}
}
func (c *Php80Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php80Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
config := tools.Read("/www/server/php/" + c.version + "/etc/php.ini")
config := tools.Read("/www/server/php/" + r.version + "/etc/php.ini")
return controllers.Success(ctx, config)
}
func (c *Php80Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php80Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
config := ctx.Request().Input("config")
tools.Write("/www/server/php/"+c.version+"/etc/php.ini", config, 0644)
return c.Reload(ctx)
tools.Write("/www/server/php/"+r.version+"/etc/php.ini", config, 0644)
return r.Reload(ctx)
}
func (c *Php80Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php80Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
client := req.C().SetTimeout(10 * time.Second)
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + c.version)
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + r.version)
if err != nil || !resp.IsSuccessState() {
facades.Log().Error("获取PHP-" + c.version + "运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+c.version+"] 获取运行状态失败")
facades.Log().Error("获取PHP-" + r.version + "运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+r.version+"] 获取运行状态失败")
}
raw := resp.String()
@@ -185,65 +185,58 @@ func (c *Php80Controller) Load(ctx http.Context) http.Response {
return controllers.Success(ctx, data)
}
func (c *Php80Controller) ErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php80Controller) ErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/php-fpm.log"))
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/php-fpm.log"))
return controllers.Success(ctx, log)
}
func (c *Php80Controller) SlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php80Controller) SlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/slow.log"))
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/slow.log"))
return controllers.Success(ctx, log)
}
func (c *Php80Controller) ClearErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php80Controller) ClearErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/php-fpm.log")
tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/php-fpm.log")
return controllers.Success(ctx, true)
}
func (c *Php80Controller) ClearSlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php80Controller) ClearSlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/slow.log")
tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/slow.log")
return controllers.Success(ctx, true)
}
type Extension struct {
Name string `json:"name"`
Slug string `json:"slug"`
Description string `json:"description"`
Installed bool `json:"installed"`
}
func (c *Php80Controller) GetExtensionList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php80Controller) GetExtensionList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
extensions := c.GetExtensions()
extensions := r.GetExtensions()
return controllers.Success(ctx, extensions)
}
func (c *Php80Controller) InstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php80Controller) InstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
@@ -253,7 +246,7 @@ func (c *Php80Controller) InstallExtension(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
}
extensions := c.GetExtensions()
extensions := r.GetExtensions()
for _, item := range extensions {
if item.Slug == slug {
if item.Installed {
@@ -261,16 +254,16 @@ func (c *Php80Controller) InstallExtension(ctx http.Context) http.Response {
}
var task models.Task
task.Name = "安装PHP-" + c.version + "扩展-" + item.Name
task.Name = "安装PHP-" + r.version + "扩展-" + item.Name
task.Status = models.TaskStatusWaiting
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + r.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
task.Log = "/tmp/" + item.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[PHP-" + c.version + "] 创建安装拓展任务失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
facades.Log().Error("[PHP-" + r.version + "] 创建安装拓展任务失败:" + err.Error())
return controllers.ErrorSystem(ctx)
}
c.task.Process(task.ID)
r.task.Process(task.ID)
return controllers.Success(ctx, true)
}
@@ -279,8 +272,8 @@ func (c *Php80Controller) InstallExtension(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在")
}
func (c *Php80Controller) UninstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php80Controller) UninstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
@@ -290,7 +283,7 @@ func (c *Php80Controller) UninstallExtension(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
}
extensions := c.GetExtensions()
extensions := r.GetExtensions()
for _, item := range extensions {
if item.Slug == slug {
if !item.Installed {
@@ -298,16 +291,16 @@ func (c *Php80Controller) UninstallExtension(ctx http.Context) http.Response {
}
var task models.Task
task.Name = "卸载PHP-" + c.version + "扩展-" + item.Name
task.Name = "卸载PHP-" + r.version + "扩展-" + item.Name
task.Status = models.TaskStatusWaiting
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + r.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
task.Log = "/tmp/" + item.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[PHP-" + c.version + "] 创建卸载拓展任务失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
facades.Log().Error("[PHP-" + r.version + "] 创建卸载拓展任务失败:" + err.Error())
return controllers.ErrorSystem(ctx)
}
c.task.Process(task.ID)
r.task.Process(task.ID)
return controllers.Success(ctx, true)
}
@@ -316,46 +309,46 @@ func (c *Php80Controller) UninstallExtension(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在")
}
func (c *Php80Controller) GetExtensions() []Extension {
var extensions []Extension
extensions = append(extensions, Extension{
func (r *Php80Controller) GetExtensions() []PHPExtension {
var extensions []PHPExtension
extensions = append(extensions, PHPExtension{
Name: "OPcache",
Slug: "Zend OPcache",
Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "PhpRedis",
Slug: "redis",
Description: "PhpRedis 是一个用C语言编写的PHP模块,用来连接并操作 Redis 数据库上的数据。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "ImageMagick",
Slug: "imagick",
Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "Exif",
Slug: "exif",
Description: "通过 exif 扩展,你可以操作图像元数据。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "pdo_pgsql",
Slug: "pdo_pgsql",
Description: "(需先安装PostgreSQLpdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象(PDO)接口以启用从 PHP 到 PostgreSQL 数据库的访问。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "ionCube",
Slug: "ionCube Loader",
Description: "ionCube 是一个专业级的PHP加密解密工具。",
Installed: false,
})
raw := tools.Exec("/www/server/php/" + c.version + "/bin/php -m")
raw := tools.Exec("/www/server/php/" + r.version + "/bin/php -m")
rawExtensionList := strings.Split(raw, "\n")
for _, item := range rawExtensionList {
@@ -1,4 +1,4 @@
package php81
package plugins
import (
"fmt"
@@ -30,15 +30,15 @@ func NewPhp81Controller() *Php81Controller {
}
}
func (c *Php81Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php81Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
status := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status == "active" {
@@ -48,17 +48,17 @@ func (c *Php81Controller) Status(ctx http.Context) http.Response {
}
}
func (c *Php81Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php81Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("systemctl reload php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl reload php-fpm-" + r.version)
out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status == "active" {
@@ -68,17 +68,17 @@ func (c *Php81Controller) Reload(ctx http.Context) http.Response {
}
}
func (c *Php81Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php81Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("systemctl start php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl start php-fpm-" + r.version)
out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status == "active" {
@@ -88,17 +88,17 @@ func (c *Php81Controller) Start(ctx http.Context) http.Response {
}
}
func (c *Php81Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php81Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("systemctl stop php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl stop php-fpm-" + r.version)
out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status != "active" {
@@ -108,17 +108,17 @@ func (c *Php81Controller) Stop(ctx http.Context) http.Response {
}
}
func (c *Php81Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php81Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("systemctl restart php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl restart php-fpm-" + r.version)
out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status == "active" {
@@ -128,38 +128,38 @@ func (c *Php81Controller) Restart(ctx http.Context) http.Response {
}
}
func (c *Php81Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php81Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
config := tools.Read("/www/server/php/" + c.version + "/etc/php.ini")
config := tools.Read("/www/server/php/" + r.version + "/etc/php.ini")
return controllers.Success(ctx, config)
}
func (c *Php81Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php81Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
config := ctx.Request().Input("config")
tools.Write("/www/server/php/"+c.version+"/etc/php.ini", config, 0644)
return c.Reload(ctx)
tools.Write("/www/server/php/"+r.version+"/etc/php.ini", config, 0644)
return r.Reload(ctx)
}
func (c *Php81Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php81Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
client := req.C().SetTimeout(10 * time.Second)
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + c.version)
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + r.version)
if err != nil || !resp.IsSuccessState() {
facades.Log().Error("获取PHP-" + c.version + "运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+c.version+"] 获取运行状态失败")
facades.Log().Error("获取PHP-" + r.version + "运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+r.version+"] 获取运行状态失败")
}
raw := resp.String()
@@ -185,65 +185,58 @@ func (c *Php81Controller) Load(ctx http.Context) http.Response {
return controllers.Success(ctx, data)
}
func (c *Php81Controller) ErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php81Controller) ErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/php-fpm.log"))
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/php-fpm.log"))
return controllers.Success(ctx, log)
}
func (c *Php81Controller) SlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php81Controller) SlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/slow.log"))
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/slow.log"))
return controllers.Success(ctx, log)
}
func (c *Php81Controller) ClearErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php81Controller) ClearErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/php-fpm.log")
tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/php-fpm.log")
return controllers.Success(ctx, true)
}
func (c *Php81Controller) ClearSlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php81Controller) ClearSlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/slow.log")
tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/slow.log")
return controllers.Success(ctx, true)
}
type Extension struct {
Name string `json:"name"`
Slug string `json:"slug"`
Description string `json:"description"`
Installed bool `json:"installed"`
}
func (c *Php81Controller) GetExtensionList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php81Controller) GetExtensionList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
extensions := c.GetExtensions()
extensions := r.GetExtensions()
return controllers.Success(ctx, extensions)
}
func (c *Php81Controller) InstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php81Controller) InstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
@@ -253,7 +246,7 @@ func (c *Php81Controller) InstallExtension(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
}
extensions := c.GetExtensions()
extensions := r.GetExtensions()
for _, item := range extensions {
if item.Slug == slug {
if item.Installed {
@@ -261,16 +254,16 @@ func (c *Php81Controller) InstallExtension(ctx http.Context) http.Response {
}
var task models.Task
task.Name = "安装PHP-" + c.version + "扩展-" + item.Name
task.Name = "安装PHP-" + r.version + "扩展-" + item.Name
task.Status = models.TaskStatusWaiting
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + r.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
task.Log = "/tmp/" + item.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[PHP-" + c.version + "] 创建安装拓展任务失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
facades.Log().Error("[PHP-" + r.version + "] 创建安装拓展任务失败:" + err.Error())
return controllers.ErrorSystem(ctx)
}
c.task.Process(task.ID)
r.task.Process(task.ID)
return controllers.Success(ctx, true)
}
@@ -279,8 +272,8 @@ func (c *Php81Controller) InstallExtension(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在")
}
func (c *Php81Controller) UninstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php81Controller) UninstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
@@ -290,7 +283,7 @@ func (c *Php81Controller) UninstallExtension(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
}
extensions := c.GetExtensions()
extensions := r.GetExtensions()
for _, item := range extensions {
if item.Slug == slug {
if !item.Installed {
@@ -298,16 +291,16 @@ func (c *Php81Controller) UninstallExtension(ctx http.Context) http.Response {
}
var task models.Task
task.Name = "卸载PHP-" + c.version + "扩展-" + item.Name
task.Name = "卸载PHP-" + r.version + "扩展-" + item.Name
task.Status = models.TaskStatusWaiting
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + r.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
task.Log = "/tmp/" + item.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[PHP-" + c.version + "] 创建卸载拓展任务失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
facades.Log().Error("[PHP-" + r.version + "] 创建卸载拓展任务失败:" + err.Error())
return controllers.ErrorSystem(ctx)
}
c.task.Process(task.ID)
r.task.Process(task.ID)
return controllers.Success(ctx, true)
}
@@ -316,46 +309,46 @@ func (c *Php81Controller) UninstallExtension(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在")
}
func (c *Php81Controller) GetExtensions() []Extension {
var extensions []Extension
extensions = append(extensions, Extension{
func (r *Php81Controller) GetExtensions() []PHPExtension {
var extensions []PHPExtension
extensions = append(extensions, PHPExtension{
Name: "OPcache",
Slug: "Zend OPcache",
Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "PhpRedis",
Slug: "redis",
Description: "PhpRedis 是一个用C语言编写的PHP模块,用来连接并操作 Redis 数据库上的数据。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "ImageMagick",
Slug: "imagick",
Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "Exif",
Slug: "exif",
Description: "通过 exif 扩展,你可以操作图像元数据。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "pdo_pgsql",
Slug: "pdo_pgsql",
Description: "(需先安装PostgreSQLpdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象(PDO)接口以启用从 PHP 到 PostgreSQL 数据库的访问。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "ionCube",
Slug: "ionCube Loader",
Description: "ionCube 是一个专业级的PHP加密解密工具。",
Installed: false,
})
raw := tools.Exec("/www/server/php/" + c.version + "/bin/php -m")
raw := tools.Exec("/www/server/php/" + r.version + "/bin/php -m")
rawExtensionList := strings.Split(raw, "\n")
for _, item := range rawExtensionList {
@@ -1,4 +1,4 @@
package php82
package plugins
import (
"fmt"
@@ -30,15 +30,15 @@ func NewPhp82Controller() *Php82Controller {
}
}
func (c *Php82Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php82Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
status := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status == "active" {
@@ -48,17 +48,17 @@ func (c *Php82Controller) Status(ctx http.Context) http.Response {
}
}
func (c *Php82Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php82Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("systemctl reload php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl reload php-fpm-" + r.version)
out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status == "active" {
@@ -68,17 +68,17 @@ func (c *Php82Controller) Reload(ctx http.Context) http.Response {
}
}
func (c *Php82Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php82Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("systemctl start php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl start php-fpm-" + r.version)
out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status == "active" {
@@ -88,17 +88,17 @@ func (c *Php82Controller) Start(ctx http.Context) http.Response {
}
}
func (c *Php82Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php82Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("systemctl stop php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl stop php-fpm-" + r.version)
out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status != "active" {
@@ -108,17 +108,17 @@ func (c *Php82Controller) Stop(ctx http.Context) http.Response {
}
}
func (c *Php82Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php82Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("systemctl restart php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl restart php-fpm-" + r.version)
out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败")
}
if status == "active" {
@@ -128,38 +128,38 @@ func (c *Php82Controller) Restart(ctx http.Context) http.Response {
}
}
func (c *Php82Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php82Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
config := tools.Read("/www/server/php/" + c.version + "/etc/php.ini")
config := tools.Read("/www/server/php/" + r.version + "/etc/php.ini")
return controllers.Success(ctx, config)
}
func (c *Php82Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php82Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
config := ctx.Request().Input("config")
tools.Write("/www/server/php/"+c.version+"/etc/php.ini", config, 0644)
return c.Reload(ctx)
tools.Write("/www/server/php/"+r.version+"/etc/php.ini", config, 0644)
return r.Reload(ctx)
}
func (c *Php82Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php82Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
client := req.C().SetTimeout(10 * time.Second)
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + c.version)
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + r.version)
if err != nil || !resp.IsSuccessState() {
facades.Log().Error("获取PHP-" + c.version + "运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+c.version+"] 获取运行状态失败")
facades.Log().Error("获取PHP-" + r.version + "运行状态失败")
return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+r.version+"] 获取运行状态失败")
}
raw := resp.String()
@@ -185,65 +185,58 @@ func (c *Php82Controller) Load(ctx http.Context) http.Response {
return controllers.Success(ctx, data)
}
func (c *Php82Controller) ErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php82Controller) ErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/php-fpm.log"))
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/php-fpm.log"))
return controllers.Success(ctx, log)
}
func (c *Php82Controller) SlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php82Controller) SlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/slow.log"))
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/slow.log"))
return controllers.Success(ctx, log)
}
func (c *Php82Controller) ClearErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php82Controller) ClearErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/php-fpm.log")
tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/php-fpm.log")
return controllers.Success(ctx, true)
}
func (c *Php82Controller) ClearSlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php82Controller) ClearSlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/slow.log")
tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/slow.log")
return controllers.Success(ctx, true)
}
type Extension struct {
Name string `json:"name"`
Slug string `json:"slug"`
Description string `json:"description"`
Installed bool `json:"installed"`
}
func (c *Php82Controller) GetExtensionList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php82Controller) GetExtensionList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
extensions := c.GetExtensions()
extensions := r.GetExtensions()
return controllers.Success(ctx, extensions)
}
func (c *Php82Controller) InstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php82Controller) InstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
@@ -253,7 +246,7 @@ func (c *Php82Controller) InstallExtension(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
}
extensions := c.GetExtensions()
extensions := r.GetExtensions()
for _, item := range extensions {
if item.Slug == slug {
if item.Installed {
@@ -261,16 +254,16 @@ func (c *Php82Controller) InstallExtension(ctx http.Context) http.Response {
}
var task models.Task
task.Name = "安装PHP-" + c.version + "扩展-" + item.Name
task.Name = "安装PHP-" + r.version + "扩展-" + item.Name
task.Status = models.TaskStatusWaiting
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + r.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
task.Log = "/tmp/" + item.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[PHP-" + c.version + "] 创建安装拓展任务失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
facades.Log().Error("[PHP-" + r.version + "] 创建安装拓展任务失败:" + err.Error())
return controllers.ErrorSystem(ctx)
}
c.task.Process(task.ID)
r.task.Process(task.ID)
return controllers.Success(ctx, true)
}
@@ -279,8 +272,8 @@ func (c *Php82Controller) InstallExtension(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在")
}
func (c *Php82Controller) UninstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
func (r *Php82Controller) UninstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+r.version)
if check != nil {
return check
}
@@ -290,7 +283,7 @@ func (c *Php82Controller) UninstallExtension(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
}
extensions := c.GetExtensions()
extensions := r.GetExtensions()
for _, item := range extensions {
if item.Slug == slug {
if !item.Installed {
@@ -298,16 +291,16 @@ func (c *Php82Controller) UninstallExtension(ctx http.Context) http.Response {
}
var task models.Task
task.Name = "卸载PHP-" + c.version + "扩展-" + item.Name
task.Name = "卸载PHP-" + r.version + "扩展-" + item.Name
task.Status = models.TaskStatusWaiting
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + r.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
task.Log = "/tmp/" + item.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[PHP-" + c.version + "] 创建卸载拓展任务失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
facades.Log().Error("[PHP-" + r.version + "] 创建卸载拓展任务失败:" + err.Error())
return controllers.ErrorSystem(ctx)
}
c.task.Process(task.ID)
r.task.Process(task.ID)
return controllers.Success(ctx, true)
}
@@ -316,40 +309,40 @@ func (c *Php82Controller) UninstallExtension(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在")
}
func (c *Php82Controller) GetExtensions() []Extension {
var extensions []Extension
extensions = append(extensions, Extension{
func (r *Php82Controller) GetExtensions() []PHPExtension {
var extensions []PHPExtension
extensions = append(extensions, PHPExtension{
Name: "OPcache",
Slug: "Zend OPcache",
Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "PhpRedis",
Slug: "redis",
Description: "PhpRedis 是一个用C语言编写的PHP模块,用来连接并操作 Redis 数据库上的数据。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "ImageMagick",
Slug: "imagick",
Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "Exif",
Slug: "exif",
Description: "通过 exif 扩展,你可以操作图像元数据。",
Installed: false,
})
extensions = append(extensions, Extension{
extensions = append(extensions, PHPExtension{
Name: "pdo_pgsql",
Slug: "pdo_pgsql",
Description: "(需先安装PostgreSQLpdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象(PDO)接口以启用从 PHP 到 PostgreSQL 数据库的访问。",
Installed: false,
})
raw := tools.Exec("/www/server/php/" + c.version + "/bin/php -m")
raw := tools.Exec("/www/server/php/" + r.version + "/bin/php -m")
rawExtensionList := strings.Split(raw, "\n")
for _, item := range rawExtensionList {
@@ -1,4 +1,4 @@
package phpmyadmin
package plugins
import (
"os"
@@ -19,7 +19,7 @@ func NewPhpMyAdminController() *PhpMyAdminController {
return &PhpMyAdminController{}
}
func (c *PhpMyAdminController) Info(ctx http.Context) http.Response {
func (r *PhpMyAdminController) Info(ctx http.Context) http.Response {
check := controllers.Check(ctx, "phpmyadmin")
if check != nil {
return check
@@ -52,7 +52,7 @@ func (c *PhpMyAdminController) Info(ctx http.Context) http.Response {
})
}
func (c *PhpMyAdminController) SetPort(ctx http.Context) http.Response {
func (r *PhpMyAdminController) SetPort(ctx http.Context) http.Response {
check := controllers.Check(ctx, "phpmyadmin")
if check != nil {
return check
@@ -1,4 +1,4 @@
package postgresql15
package plugins
import (
"strings"
@@ -18,11 +18,6 @@ type Postgresql15Controller struct {
backup services.Backup
}
type Info struct {
Name string `json:"name"`
Value string `json:"value"`
}
func NewPostgresql15Controller() *Postgresql15Controller {
return &Postgresql15Controller{
setting: services.NewSettingImpl(),
@@ -31,7 +26,7 @@ func NewPostgresql15Controller() *Postgresql15Controller {
}
// Status 获取运行状态
func (c *Postgresql15Controller) Status(ctx http.Context) http.Response {
func (r *Postgresql15Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -50,7 +45,7 @@ func (c *Postgresql15Controller) Status(ctx http.Context) http.Response {
}
// Reload 重载配置
func (c *Postgresql15Controller) Reload(ctx http.Context) http.Response {
func (r *Postgresql15Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -70,7 +65,7 @@ func (c *Postgresql15Controller) Reload(ctx http.Context) http.Response {
}
// Restart 重启服务
func (c *Postgresql15Controller) Restart(ctx http.Context) http.Response {
func (r *Postgresql15Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -90,7 +85,7 @@ func (c *Postgresql15Controller) Restart(ctx http.Context) http.Response {
}
// Start 启动服务
func (c *Postgresql15Controller) Start(ctx http.Context) http.Response {
func (r *Postgresql15Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -110,7 +105,7 @@ func (c *Postgresql15Controller) Start(ctx http.Context) http.Response {
}
// Stop 停止服务
func (c *Postgresql15Controller) Stop(ctx http.Context) http.Response {
func (r *Postgresql15Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -130,7 +125,7 @@ func (c *Postgresql15Controller) Stop(ctx http.Context) http.Response {
}
// GetConfig 获取配置
func (c *Postgresql15Controller) GetConfig(ctx http.Context) http.Response {
func (r *Postgresql15Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -146,7 +141,7 @@ func (c *Postgresql15Controller) GetConfig(ctx http.Context) http.Response {
}
// GetUserConfig 获取用户配置
func (c *Postgresql15Controller) GetUserConfig(ctx http.Context) http.Response {
func (r *Postgresql15Controller) GetUserConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -162,7 +157,7 @@ func (c *Postgresql15Controller) GetUserConfig(ctx http.Context) http.Response {
}
// SaveConfig 保存配置
func (c *Postgresql15Controller) SaveConfig(ctx http.Context) http.Response {
func (r *Postgresql15Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -177,11 +172,11 @@ func (c *Postgresql15Controller) SaveConfig(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败")
}
return c.Restart(ctx)
return r.Restart(ctx)
}
// SaveUserConfig 保存用户配置
func (c *Postgresql15Controller) SaveUserConfig(ctx http.Context) http.Response {
func (r *Postgresql15Controller) SaveUserConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -196,11 +191,11 @@ func (c *Postgresql15Controller) SaveUserConfig(ctx http.Context) http.Response
return controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败")
}
return c.Restart(ctx)
return r.Restart(ctx)
}
// Load 获取负载
func (c *Postgresql15Controller) Load(ctx http.Context) http.Response {
func (r *Postgresql15Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -211,7 +206,7 @@ func (c *Postgresql15Controller) Load(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusInternalServerError, "PostgreSQL 已停止运行")
}
data := []Info{
data := []LoadInfo{
{"启动时间", carbon.Parse(tools.Exec(`echo "select pg_postmaster_start_time();" | su - postgres -c "psql" | sed -n 3p | cut -d'.' -f1`)).ToDateTimeString()},
{"进程 PID", tools.Exec(`echo "select pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)},
{"进程数", tools.Exec(`ps aux | grep postgres | grep -v grep | wc -l`)},
@@ -223,7 +218,7 @@ func (c *Postgresql15Controller) Load(ctx http.Context) http.Response {
}
// Log 获取日志
func (c *Postgresql15Controller) Log(ctx http.Context) http.Response {
func (r *Postgresql15Controller) Log(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -234,7 +229,7 @@ func (c *Postgresql15Controller) Log(ctx http.Context) http.Response {
}
// ClearLog 清空日志
func (c *Postgresql15Controller) ClearLog(ctx http.Context) http.Response {
func (r *Postgresql15Controller) ClearLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -245,7 +240,7 @@ func (c *Postgresql15Controller) ClearLog(ctx http.Context) http.Response {
}
// DatabaseList 获取数据库列表
func (c *Postgresql15Controller) DatabaseList(ctx http.Context) http.Response {
func (r *Postgresql15Controller) DatabaseList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -309,7 +304,7 @@ func (c *Postgresql15Controller) DatabaseList(ctx http.Context) http.Response {
}
// AddDatabase 添加数据库
func (c *Postgresql15Controller) AddDatabase(ctx http.Context) http.Response {
func (r *Postgresql15Controller) AddDatabase(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -339,11 +334,11 @@ func (c *Postgresql15Controller) AddDatabase(ctx http.Context) http.Response {
userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256"
tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`)
return c.Reload(ctx)
return r.Reload(ctx)
}
// DeleteDatabase 删除数据库
func (c *Postgresql15Controller) DeleteDatabase(ctx http.Context) http.Response {
func (r *Postgresql15Controller) DeleteDatabase(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -366,13 +361,13 @@ func (c *Postgresql15Controller) DeleteDatabase(ctx http.Context) http.Response
}
// BackupList 获取备份列表
func (c *Postgresql15Controller) BackupList(ctx http.Context) http.Response {
func (r *Postgresql15Controller) BackupList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
backupList, err := c.backup.PostgresqlList()
backupList, err := r.backup.PostgresqlList()
if err != nil {
facades.Log().Error("[PostgreSQL] 获取备份列表失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "获取备份列表失败")
@@ -403,7 +398,7 @@ func (c *Postgresql15Controller) BackupList(ctx http.Context) http.Response {
}
// UploadBackup 上传备份
func (c *Postgresql15Controller) UploadBackup(ctx http.Context) http.Response {
func (r *Postgresql15Controller) UploadBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -414,7 +409,7 @@ func (c *Postgresql15Controller) UploadBackup(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "上传文件失败")
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
if !tools.Exists(backupPath) {
tools.Mkdir(backupPath, 0644)
}
@@ -429,7 +424,7 @@ func (c *Postgresql15Controller) UploadBackup(ctx http.Context) http.Response {
}
// CreateBackup 创建备份
func (c *Postgresql15Controller) CreateBackup(ctx http.Context) http.Response {
func (r *Postgresql15Controller) CreateBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -446,7 +441,7 @@ func (c *Postgresql15Controller) CreateBackup(ctx http.Context) http.Response {
}
database := ctx.Request().Input("database")
err = c.backup.PostgresqlBackup(database)
err = r.backup.PostgresqlBackup(database)
if err != nil {
facades.Log().Error("[PostgreSQL] 创建备份失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "创建备份失败")
@@ -456,7 +451,7 @@ func (c *Postgresql15Controller) CreateBackup(ctx http.Context) http.Response {
}
// DeleteBackup 删除备份
func (c *Postgresql15Controller) DeleteBackup(ctx http.Context) http.Response {
func (r *Postgresql15Controller) DeleteBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -472,7 +467,7 @@ func (c *Postgresql15Controller) DeleteBackup(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
fileName := ctx.Request().Input("name")
tools.Remove(backupPath + "/" + fileName)
@@ -480,7 +475,7 @@ func (c *Postgresql15Controller) DeleteBackup(ctx http.Context) http.Response {
}
// RestoreBackup 还原备份
func (c *Postgresql15Controller) RestoreBackup(ctx http.Context) http.Response {
func (r *Postgresql15Controller) RestoreBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -497,7 +492,7 @@ func (c *Postgresql15Controller) RestoreBackup(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
err = c.backup.PostgresqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup"))
err = r.backup.PostgresqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup"))
if err != nil {
facades.Log().Error("[PostgreSQL] 还原失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "还原失败: "+err.Error())
@@ -507,7 +502,7 @@ func (c *Postgresql15Controller) RestoreBackup(ctx http.Context) http.Response {
}
// UserList 用户列表
func (c *Postgresql15Controller) UserList(ctx http.Context) http.Response {
func (r *Postgresql15Controller) UserList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -560,7 +555,7 @@ func (c *Postgresql15Controller) UserList(ctx http.Context) http.Response {
}
// AddUser 添加用户
func (c *Postgresql15Controller) AddUser(ctx http.Context) http.Response {
func (r *Postgresql15Controller) AddUser(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -587,11 +582,11 @@ func (c *Postgresql15Controller) AddUser(ctx http.Context) http.Response {
userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256"
tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`)
return c.Reload(ctx)
return r.Reload(ctx)
}
// DeleteUser 删除用户
func (c *Postgresql15Controller) DeleteUser(ctx http.Context) http.Response {
func (r *Postgresql15Controller) DeleteUser(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -611,11 +606,11 @@ func (c *Postgresql15Controller) DeleteUser(ctx http.Context) http.Response {
tools.Exec(`echo "DROP USER ` + user + `;" | su - postgres -c "psql"`)
tools.Exec(`sed -i '/` + user + `/d' /www/server/postgresql/data/pg_hba.conf`)
return c.Reload(ctx)
return r.Reload(ctx)
}
// SetUserPassword 设置用户密码
func (c *Postgresql15Controller) SetUserPassword(ctx http.Context) http.Response {
func (r *Postgresql15Controller) SetUserPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
@@ -1,4 +1,4 @@
package postgresql16
package plugins
import (
"strings"
@@ -18,11 +18,6 @@ type Postgresql16Controller struct {
backup services.Backup
}
type Info struct {
Name string `json:"name"`
Value string `json:"value"`
}
func NewPostgresql16Controller() *Postgresql16Controller {
return &Postgresql16Controller{
setting: services.NewSettingImpl(),
@@ -31,7 +26,7 @@ func NewPostgresql16Controller() *Postgresql16Controller {
}
// Status 获取运行状态
func (c *Postgresql16Controller) Status(ctx http.Context) http.Response {
func (r *Postgresql16Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -50,7 +45,7 @@ func (c *Postgresql16Controller) Status(ctx http.Context) http.Response {
}
// Reload 重载配置
func (c *Postgresql16Controller) Reload(ctx http.Context) http.Response {
func (r *Postgresql16Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -70,7 +65,7 @@ func (c *Postgresql16Controller) Reload(ctx http.Context) http.Response {
}
// Restart 重启服务
func (c *Postgresql16Controller) Restart(ctx http.Context) http.Response {
func (r *Postgresql16Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -90,7 +85,7 @@ func (c *Postgresql16Controller) Restart(ctx http.Context) http.Response {
}
// Start 启动服务
func (c *Postgresql16Controller) Start(ctx http.Context) http.Response {
func (r *Postgresql16Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -110,7 +105,7 @@ func (c *Postgresql16Controller) Start(ctx http.Context) http.Response {
}
// Stop 停止服务
func (c *Postgresql16Controller) Stop(ctx http.Context) http.Response {
func (r *Postgresql16Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -130,7 +125,7 @@ func (c *Postgresql16Controller) Stop(ctx http.Context) http.Response {
}
// GetConfig 获取配置
func (c *Postgresql16Controller) GetConfig(ctx http.Context) http.Response {
func (r *Postgresql16Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -146,7 +141,7 @@ func (c *Postgresql16Controller) GetConfig(ctx http.Context) http.Response {
}
// GetUserConfig 获取用户配置
func (c *Postgresql16Controller) GetUserConfig(ctx http.Context) http.Response {
func (r *Postgresql16Controller) GetUserConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -162,7 +157,7 @@ func (c *Postgresql16Controller) GetUserConfig(ctx http.Context) http.Response {
}
// SaveConfig 保存配置
func (c *Postgresql16Controller) SaveConfig(ctx http.Context) http.Response {
func (r *Postgresql16Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -177,11 +172,11 @@ func (c *Postgresql16Controller) SaveConfig(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败")
}
return c.Restart(ctx)
return r.Restart(ctx)
}
// SaveUserConfig 保存用户配置
func (c *Postgresql16Controller) SaveUserConfig(ctx http.Context) http.Response {
func (r *Postgresql16Controller) SaveUserConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -196,11 +191,11 @@ func (c *Postgresql16Controller) SaveUserConfig(ctx http.Context) http.Response
return controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败")
}
return c.Restart(ctx)
return r.Restart(ctx)
}
// Load 获取负载
func (c *Postgresql16Controller) Load(ctx http.Context) http.Response {
func (r *Postgresql16Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -211,7 +206,7 @@ func (c *Postgresql16Controller) Load(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusInternalServerError, "PostgreSQL 已停止运行")
}
data := []Info{
data := []LoadInfo{
{"启动时间", carbon.Parse(tools.Exec(`echo "select pg_postmaster_start_time();" | su - postgres -c "psql" | sed -n 3p | cut -d'.' -f1`)).ToDateTimeString()},
{"进程 PID", tools.Exec(`echo "select pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)},
{"进程数", tools.Exec(`ps aux | grep postgres | grep -v grep | wc -l`)},
@@ -223,7 +218,7 @@ func (c *Postgresql16Controller) Load(ctx http.Context) http.Response {
}
// Log 获取日志
func (c *Postgresql16Controller) Log(ctx http.Context) http.Response {
func (r *Postgresql16Controller) Log(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -234,7 +229,7 @@ func (c *Postgresql16Controller) Log(ctx http.Context) http.Response {
}
// ClearLog 清空日志
func (c *Postgresql16Controller) ClearLog(ctx http.Context) http.Response {
func (r *Postgresql16Controller) ClearLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -245,7 +240,7 @@ func (c *Postgresql16Controller) ClearLog(ctx http.Context) http.Response {
}
// DatabaseList 获取数据库列表
func (c *Postgresql16Controller) DatabaseList(ctx http.Context) http.Response {
func (r *Postgresql16Controller) DatabaseList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -309,7 +304,7 @@ func (c *Postgresql16Controller) DatabaseList(ctx http.Context) http.Response {
}
// AddDatabase 添加数据库
func (c *Postgresql16Controller) AddDatabase(ctx http.Context) http.Response {
func (r *Postgresql16Controller) AddDatabase(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -339,11 +334,11 @@ func (c *Postgresql16Controller) AddDatabase(ctx http.Context) http.Response {
userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256"
tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`)
return c.Reload(ctx)
return r.Reload(ctx)
}
// DeleteDatabase 删除数据库
func (c *Postgresql16Controller) DeleteDatabase(ctx http.Context) http.Response {
func (r *Postgresql16Controller) DeleteDatabase(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -366,13 +361,13 @@ func (c *Postgresql16Controller) DeleteDatabase(ctx http.Context) http.Response
}
// BackupList 获取备份列表
func (c *Postgresql16Controller) BackupList(ctx http.Context) http.Response {
func (r *Postgresql16Controller) BackupList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
backupList, err := c.backup.PostgresqlList()
backupList, err := r.backup.PostgresqlList()
if err != nil {
facades.Log().Error("[PostgreSQL] 获取备份列表失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "获取备份列表失败")
@@ -403,7 +398,7 @@ func (c *Postgresql16Controller) BackupList(ctx http.Context) http.Response {
}
// UploadBackup 上传备份
func (c *Postgresql16Controller) UploadBackup(ctx http.Context) http.Response {
func (r *Postgresql16Controller) UploadBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -414,7 +409,7 @@ func (c *Postgresql16Controller) UploadBackup(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "上传文件失败")
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
if !tools.Exists(backupPath) {
tools.Mkdir(backupPath, 0644)
}
@@ -429,7 +424,7 @@ func (c *Postgresql16Controller) UploadBackup(ctx http.Context) http.Response {
}
// CreateBackup 创建备份
func (c *Postgresql16Controller) CreateBackup(ctx http.Context) http.Response {
func (r *Postgresql16Controller) CreateBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -446,7 +441,7 @@ func (c *Postgresql16Controller) CreateBackup(ctx http.Context) http.Response {
}
database := ctx.Request().Input("database")
err = c.backup.PostgresqlBackup(database)
err = r.backup.PostgresqlBackup(database)
if err != nil {
facades.Log().Error("[PostgreSQL] 创建备份失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "创建备份失败")
@@ -456,7 +451,7 @@ func (c *Postgresql16Controller) CreateBackup(ctx http.Context) http.Response {
}
// DeleteBackup 删除备份
func (c *Postgresql16Controller) DeleteBackup(ctx http.Context) http.Response {
func (r *Postgresql16Controller) DeleteBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -472,7 +467,7 @@ func (c *Postgresql16Controller) DeleteBackup(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
fileName := ctx.Request().Input("name")
tools.Remove(backupPath + "/" + fileName)
@@ -480,7 +475,7 @@ func (c *Postgresql16Controller) DeleteBackup(ctx http.Context) http.Response {
}
// RestoreBackup 还原备份
func (c *Postgresql16Controller) RestoreBackup(ctx http.Context) http.Response {
func (r *Postgresql16Controller) RestoreBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -497,7 +492,7 @@ func (c *Postgresql16Controller) RestoreBackup(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
err = c.backup.PostgresqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup"))
err = r.backup.PostgresqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup"))
if err != nil {
facades.Log().Error("[PostgreSQL] 还原失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "还原失败: "+err.Error())
@@ -507,7 +502,7 @@ func (c *Postgresql16Controller) RestoreBackup(ctx http.Context) http.Response {
}
// UserList 用户列表
func (c *Postgresql16Controller) UserList(ctx http.Context) http.Response {
func (r *Postgresql16Controller) UserList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -560,7 +555,7 @@ func (c *Postgresql16Controller) UserList(ctx http.Context) http.Response {
}
// AddUser 添加用户
func (c *Postgresql16Controller) AddUser(ctx http.Context) http.Response {
func (r *Postgresql16Controller) AddUser(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -587,11 +582,11 @@ func (c *Postgresql16Controller) AddUser(ctx http.Context) http.Response {
userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256"
tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`)
return c.Reload(ctx)
return r.Reload(ctx)
}
// DeleteUser 删除用户
func (c *Postgresql16Controller) DeleteUser(ctx http.Context) http.Response {
func (r *Postgresql16Controller) DeleteUser(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -611,11 +606,11 @@ func (c *Postgresql16Controller) DeleteUser(ctx http.Context) http.Response {
tools.Exec(`echo "DROP USER ` + user + `;" | su - postgres -c "psql"`)
tools.Exec(`sed -i '/` + user + `/d' /www/server/postgresql/data/pg_hba.conf`)
return c.Reload(ctx)
return r.Reload(ctx)
}
// SetUserPassword 设置用户密码
func (c *Postgresql16Controller) SetUserPassword(ctx http.Context) http.Response {
func (r *Postgresql16Controller) SetUserPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
@@ -1,4 +1,4 @@
package pureftpd
package plugins
import (
"regexp"
@@ -24,7 +24,7 @@ func NewPureFtpdController() *PureFtpdController {
}
// Status 获取运行状态
func (c *PureFtpdController) Status(ctx http.Context) http.Response {
func (r *PureFtpdController) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
@@ -43,7 +43,7 @@ func (c *PureFtpdController) Status(ctx http.Context) http.Response {
}
// Restart 重启服务
func (c *PureFtpdController) Restart(ctx http.Context) http.Response {
func (r *PureFtpdController) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
@@ -63,7 +63,7 @@ func (c *PureFtpdController) Restart(ctx http.Context) http.Response {
}
// Start 启动服务
func (c *PureFtpdController) Start(ctx http.Context) http.Response {
func (r *PureFtpdController) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
@@ -83,7 +83,7 @@ func (c *PureFtpdController) Start(ctx http.Context) http.Response {
}
// Stop 停止服务
func (c *PureFtpdController) Stop(ctx http.Context) http.Response {
func (r *PureFtpdController) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
@@ -103,7 +103,7 @@ func (c *PureFtpdController) Stop(ctx http.Context) http.Response {
}
// List 获取用户列表
func (c *PureFtpdController) List(ctx http.Context) http.Response {
func (r *PureFtpdController) List(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
@@ -153,7 +153,7 @@ func (c *PureFtpdController) List(ctx http.Context) http.Response {
}
// Add 添加用户
func (c *PureFtpdController) Add(ctx http.Context) http.Response {
func (r *PureFtpdController) Add(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
@@ -191,7 +191,7 @@ func (c *PureFtpdController) Add(ctx http.Context) http.Response {
}
// Delete 删除用户
func (c *PureFtpdController) Delete(ctx http.Context) http.Response {
func (r *PureFtpdController) Delete(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
@@ -216,7 +216,7 @@ func (c *PureFtpdController) Delete(ctx http.Context) http.Response {
}
// ChangePassword 修改密码
func (c *PureFtpdController) ChangePassword(ctx http.Context) http.Response {
func (r *PureFtpdController) ChangePassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
@@ -243,7 +243,7 @@ func (c *PureFtpdController) ChangePassword(ctx http.Context) http.Response {
}
// GetPort 获取端口
func (c *PureFtpdController) GetPort(ctx http.Context) http.Response {
func (r *PureFtpdController) GetPort(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
@@ -258,7 +258,7 @@ func (c *PureFtpdController) GetPort(ctx http.Context) http.Response {
}
// SetPort 设置端口
func (c *PureFtpdController) SetPort(ctx http.Context) http.Response {
func (r *PureFtpdController) SetPort(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
@@ -1,4 +1,4 @@
package redis
package plugins
import (
"strings"
@@ -12,17 +12,12 @@ import (
type RedisController struct {
}
type Info struct {
Name string `json:"name"`
Value string `json:"value"`
}
func NewRedisController() *RedisController {
return &RedisController{}
}
// Status 获取运行状态
func (c *RedisController) Status(ctx http.Context) http.Response {
func (r *RedisController) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "redis")
if check != nil {
return check
@@ -41,7 +36,7 @@ func (c *RedisController) Status(ctx http.Context) http.Response {
}
// Restart 重启服务
func (c *RedisController) Restart(ctx http.Context) http.Response {
func (r *RedisController) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "redis")
if check != nil {
return check
@@ -61,7 +56,7 @@ func (c *RedisController) Restart(ctx http.Context) http.Response {
}
// Start 启动服务
func (c *RedisController) Start(ctx http.Context) http.Response {
func (r *RedisController) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "redis")
if check != nil {
return check
@@ -81,7 +76,7 @@ func (c *RedisController) Start(ctx http.Context) http.Response {
}
// Stop 停止服务
func (c *RedisController) Stop(ctx http.Context) http.Response {
func (r *RedisController) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "redis")
if check != nil {
return check
@@ -101,7 +96,7 @@ func (c *RedisController) Stop(ctx http.Context) http.Response {
}
// GetConfig 获取配置
func (c *RedisController) GetConfig(ctx http.Context) http.Response {
func (r *RedisController) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "redis")
if check != nil {
return check
@@ -117,7 +112,7 @@ func (c *RedisController) GetConfig(ctx http.Context) http.Response {
}
// SaveConfig 保存配置
func (c *RedisController) SaveConfig(ctx http.Context) http.Response {
func (r *RedisController) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "redis")
if check != nil {
return check
@@ -132,11 +127,11 @@ func (c *RedisController) SaveConfig(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusInternalServerError, "写入Redis配置失败")
}
return c.Restart(ctx)
return r.Restart(ctx)
}
// Load 获取负载
func (c *RedisController) Load(ctx http.Context) http.Response {
func (r *RedisController) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "redis")
if check != nil {
return check
@@ -162,7 +157,7 @@ func (c *RedisController) Load(ctx http.Context) http.Response {
}
}
data := []Info{
data := []LoadInfo{
{"TCP 端口", dataRaw["tcp_port"]},
{"已运行天数", dataRaw["uptime_in_days"]},
{"连接的客户端数", dataRaw["connected_clients"]},
@@ -1,11 +1,11 @@
package s3fs
package plugins
import (
"strings"
"github.com/bytedance/sonic"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/support/carbon"
"github.com/goravel/framework/support/json"
"github.com/spf13/cast"
"panel/app/http/controllers"
@@ -17,13 +17,6 @@ type S3fsController struct {
setting services.Setting
}
type s3fs struct {
ID int64 `json:"id"`
Path string `json:"path"`
Bucket string `json:"bucket"`
Url string `json:"url"`
}
func NewS3fsController() *S3fsController {
return &S3fsController{
setting: services.NewSettingImpl(),
@@ -31,7 +24,7 @@ func NewS3fsController() *S3fsController {
}
// List 所有 S3fs 挂载
func (c *S3fsController) List(ctx http.Context) http.Response {
func (r *S3fsController) List(ctx http.Context) http.Response {
check := controllers.Check(ctx, "s3fs")
if check != nil {
return check
@@ -40,8 +33,8 @@ func (c *S3fsController) List(ctx http.Context) http.Response {
page := ctx.Request().QueryInt("page", 1)
limit := ctx.Request().QueryInt("limit", 10)
var s3fsList []s3fs
err := sonic.UnmarshalString(c.setting.Get("s3fs", "[]"), &s3fsList)
var s3fsList []S3fsMount
err := json.UnmarshalString(r.setting.Get("s3fs", "[]"), &s3fsList)
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取 S3fs 挂载失败")
}
@@ -51,7 +44,7 @@ func (c *S3fsController) List(ctx http.Context) http.Response {
if startIndex > len(s3fsList) {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []s3fs{},
"items": []S3fsMount{},
})
}
if endIndex > len(s3fsList) {
@@ -59,7 +52,7 @@ func (c *S3fsController) List(ctx http.Context) http.Response {
}
pagedS3fsList := s3fsList[startIndex:endIndex]
if pagedS3fsList == nil {
pagedS3fsList = []s3fs{}
pagedS3fsList = []S3fsMount{}
}
return controllers.Success(ctx, http.Json{
@@ -69,7 +62,7 @@ func (c *S3fsController) List(ctx http.Context) http.Response {
}
// Add 添加 S3fs 挂载
func (c *S3fsController) Add(ctx http.Context) http.Response {
func (r *S3fsController) Add(ctx http.Context) http.Response {
check := controllers.Check(ctx, "s3fs")
if check != nil {
return check
@@ -108,8 +101,8 @@ func (c *S3fsController) Add(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载目录必须为空")
}
var s3fsList []s3fs
err = sonic.UnmarshalString(c.setting.Get("s3fs", "[]"), &s3fsList)
var s3fsList []S3fsMount
err = json.UnmarshalString(r.setting.Get("s3fs", "[]"), &s3fsList)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取 S3fs 挂载失败")
}
@@ -135,17 +128,17 @@ func (c *S3fsController) Add(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusInternalServerError, "挂载失败,请检查配置是否正确")
}
s3fsList = append(s3fsList, s3fs{
s3fsList = append(s3fsList, S3fsMount{
ID: id,
Path: path,
Bucket: bucket,
Url: url,
})
json, err := sonic.MarshalString(s3fsList)
encoded, err := json.MarshalString(s3fsList)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "添加 S3fs 挂载失败")
}
err = c.setting.Set("s3fs", json)
err = r.setting.Set("s3fs", encoded)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "添加 S3fs 挂载失败")
}
@@ -154,7 +147,7 @@ func (c *S3fsController) Add(ctx http.Context) http.Response {
}
// Delete 删除 S3fs 挂载
func (c *S3fsController) Delete(ctx http.Context) http.Response {
func (r *S3fsController) Delete(ctx http.Context) http.Response {
check := controllers.Check(ctx, "s3fs")
if check != nil {
return check
@@ -165,13 +158,13 @@ func (c *S3fsController) Delete(ctx http.Context) http.Response {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载ID不能为空")
}
var s3fsList []s3fs
err := sonic.UnmarshalString(c.setting.Get("s3fs", "[]"), &s3fsList)
var s3fsList []S3fsMount
err := json.UnmarshalString(r.setting.Get("s3fs", "[]"), &s3fsList)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取 S3fs 挂载失败")
}
var mount s3fs
var mount S3fsMount
for _, s := range s3fsList {
if cast.ToString(s.ID) == id {
mount = s
@@ -191,17 +184,17 @@ func (c *S3fsController) Delete(ctx http.Context) http.Response {
}
tools.Remove("/etc/passwd-s3fs-" + cast.ToString(mount.ID))
var newS3fsList []s3fs
var newS3fsList []S3fsMount
for _, s := range s3fsList {
if s.ID != mount.ID {
newS3fsList = append(newS3fsList, s)
}
}
json, err := sonic.MarshalString(newS3fsList)
encoded, err := json.MarshalString(newS3fsList)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "删除 S3fs 挂载失败")
}
err = c.setting.Set("s3fs", json)
err = r.setting.Set("s3fs", encoded)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "删除 S3fs 挂载失败")
}
@@ -1,4 +1,4 @@
package supervisor
package plugins
import (
"strconv"
@@ -28,13 +28,13 @@ func NewSupervisorController() *SupervisorController {
}
// Status 状态
func (c *SupervisorController) Status(ctx http.Context) http.Response {
func (r *SupervisorController) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
status := tools.Exec(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
status := tools.Exec(`systemctl status ` + r.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
if status == "active" {
return controllers.Success(ctx, true)
} else {
@@ -43,14 +43,14 @@ func (c *SupervisorController) Status(ctx http.Context) http.Response {
}
// Start 启动
func (c *SupervisorController) Start(ctx http.Context) http.Response {
func (r *SupervisorController) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
tools.Exec(`systemctl start ` + c.ServiceName)
status := tools.Exec(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
tools.Exec(`systemctl start ` + r.ServiceName)
status := tools.Exec(`systemctl status ` + r.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
if status == "active" {
return controllers.Success(ctx, true)
} else {
@@ -59,14 +59,14 @@ func (c *SupervisorController) Start(ctx http.Context) http.Response {
}
// Stop 停止
func (c *SupervisorController) Stop(ctx http.Context) http.Response {
func (r *SupervisorController) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
tools.Exec(`systemctl stop ` + c.ServiceName)
status := tools.Exec(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
tools.Exec(`systemctl stop ` + r.ServiceName)
status := tools.Exec(`systemctl status ` + r.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
if status != "active" {
return controllers.Success(ctx, true)
} else {
@@ -75,14 +75,14 @@ func (c *SupervisorController) Stop(ctx http.Context) http.Response {
}
// Restart 重启
func (c *SupervisorController) Restart(ctx http.Context) http.Response {
func (r *SupervisorController) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
tools.Exec(`systemctl restart ` + c.ServiceName)
status := tools.Exec(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
tools.Exec(`systemctl restart ` + r.ServiceName)
status := tools.Exec(`systemctl status ` + r.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
if status == "active" {
return controllers.Success(ctx, true)
} else {
@@ -91,14 +91,14 @@ func (c *SupervisorController) Restart(ctx http.Context) http.Response {
}
// Reload 重载
func (c *SupervisorController) Reload(ctx http.Context) http.Response {
func (r *SupervisorController) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
tools.Exec(`systemctl reload ` + c.ServiceName)
status := tools.Exec(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
tools.Exec(`systemctl reload ` + r.ServiceName)
status := tools.Exec(`systemctl status ` + r.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
if status == "active" {
return controllers.Success(ctx, true)
} else {
@@ -107,7 +107,7 @@ func (c *SupervisorController) Reload(ctx http.Context) http.Response {
}
// Log 日志
func (c *SupervisorController) Log(ctx http.Context) http.Response {
func (r *SupervisorController) Log(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
@@ -118,7 +118,7 @@ func (c *SupervisorController) Log(ctx http.Context) http.Response {
}
// ClearLog 清空日志
func (c *SupervisorController) ClearLog(ctx http.Context) http.Response {
func (r *SupervisorController) ClearLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
@@ -129,7 +129,7 @@ func (c *SupervisorController) ClearLog(ctx http.Context) http.Response {
}
// Config 获取配置
func (c *SupervisorController) Config(ctx http.Context) http.Response {
func (r *SupervisorController) Config(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
@@ -145,7 +145,7 @@ func (c *SupervisorController) Config(ctx http.Context) http.Response {
}
// SaveConfig 保存配置
func (c *SupervisorController) SaveConfig(ctx http.Context) http.Response {
func (r *SupervisorController) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
@@ -158,11 +158,11 @@ func (c *SupervisorController) SaveConfig(ctx http.Context) http.Response {
tools.Write(`/etc/supervisor/supervisord.conf`, config, 0644)
}
return c.Restart(ctx)
return r.Restart(ctx)
}
// Processes 进程列表
func (c *SupervisorController) Processes(ctx http.Context) http.Response {
func (r *SupervisorController) Processes(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
@@ -210,6 +210,9 @@ func (c *SupervisorController) Processes(ctx http.Context) http.Response {
endIndex = len(processList)
}
pagedProcessList := processList[startIndex:endIndex]
if pagedProcessList == nil {
pagedProcessList = []process{}
}
return controllers.Success(ctx, http.Json{
"total": len(processList),
@@ -218,7 +221,7 @@ func (c *SupervisorController) Processes(ctx http.Context) http.Response {
}
// StartProcess 启动进程
func (c *SupervisorController) StartProcess(ctx http.Context) http.Response {
func (r *SupervisorController) StartProcess(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
@@ -230,7 +233,7 @@ func (c *SupervisorController) StartProcess(ctx http.Context) http.Response {
}
// StopProcess 停止进程
func (c *SupervisorController) StopProcess(ctx http.Context) http.Response {
func (r *SupervisorController) StopProcess(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
@@ -242,7 +245,7 @@ func (c *SupervisorController) StopProcess(ctx http.Context) http.Response {
}
// RestartProcess 重启进程
func (c *SupervisorController) RestartProcess(ctx http.Context) http.Response {
func (r *SupervisorController) RestartProcess(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
@@ -254,7 +257,7 @@ func (c *SupervisorController) RestartProcess(ctx http.Context) http.Response {
}
// ProcessLog 进程日志
func (c *SupervisorController) ProcessLog(ctx http.Context) http.Response {
func (r *SupervisorController) ProcessLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
@@ -273,7 +276,7 @@ func (c *SupervisorController) ProcessLog(ctx http.Context) http.Response {
}
// ClearProcessLog 清空进程日志
func (c *SupervisorController) ClearProcessLog(ctx http.Context) http.Response {
func (r *SupervisorController) ClearProcessLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
@@ -292,7 +295,7 @@ func (c *SupervisorController) ClearProcessLog(ctx http.Context) http.Response {
}
// ProcessConfig 获取进程配置
func (c *SupervisorController) ProcessConfig(ctx http.Context) http.Response {
func (r *SupervisorController) ProcessConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
@@ -310,7 +313,7 @@ func (c *SupervisorController) ProcessConfig(ctx http.Context) http.Response {
}
// SaveProcessConfig 保存进程配置
func (c *SupervisorController) SaveProcessConfig(ctx http.Context) http.Response {
func (r *SupervisorController) SaveProcessConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
@@ -331,7 +334,7 @@ func (c *SupervisorController) SaveProcessConfig(ctx http.Context) http.Response
}
// AddProcess 添加进程
func (c *SupervisorController) AddProcess(ctx http.Context) http.Response {
func (r *SupervisorController) AddProcess(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
@@ -381,7 +384,7 @@ stdout_logfile_maxbytes=2MB
}
// DeleteProcess 删除进程
func (c *SupervisorController) DeleteProcess(ctx http.Context) http.Response {
func (r *SupervisorController) DeleteProcess(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
@@ -1,4 +1,4 @@
package toolbox
package plugins
import (
"regexp"
@@ -19,7 +19,7 @@ func NewToolBoxController() *ToolBoxController {
}
// GetDNS 获取 DNS 信息
func (c *ToolBoxController) GetDNS(ctx http.Context) http.Response {
func (r *ToolBoxController) GetDNS(ctx http.Context) http.Response {
check := controllers.Check(ctx, "toolbox")
if check != nil {
return check
@@ -40,7 +40,7 @@ func (c *ToolBoxController) GetDNS(ctx http.Context) http.Response {
}
// SetDNS 设置 DNS 信息
func (c *ToolBoxController) SetDNS(ctx http.Context) http.Response {
func (r *ToolBoxController) SetDNS(ctx http.Context) http.Response {
check := controllers.Check(ctx, "toolbox")
if check != nil {
return check
@@ -62,7 +62,7 @@ func (c *ToolBoxController) SetDNS(ctx http.Context) http.Response {
}
// GetSWAP 获取 SWAP 信息
func (c *ToolBoxController) GetSWAP(ctx http.Context) http.Response {
func (r *ToolBoxController) GetSWAP(ctx http.Context) http.Response {
check := controllers.Check(ctx, "toolbox")
if check != nil {
return check
@@ -95,7 +95,7 @@ func (c *ToolBoxController) GetSWAP(ctx http.Context) http.Response {
}
// SetSWAP 设置 SWAP 信息
func (c *ToolBoxController) SetSWAP(ctx http.Context) http.Response {
func (r *ToolBoxController) SetSWAP(ctx http.Context) http.Response {
check := controllers.Check(ctx, "toolbox")
if check != nil {
return check
@@ -130,7 +130,7 @@ func (c *ToolBoxController) SetSWAP(ctx http.Context) http.Response {
}
// GetTimezone 获取时区
func (c *ToolBoxController) GetTimezone(ctx http.Context) http.Response {
func (r *ToolBoxController) GetTimezone(ctx http.Context) http.Response {
check := controllers.Check(ctx, "toolbox")
if check != nil {
return check
@@ -165,7 +165,7 @@ func (c *ToolBoxController) GetTimezone(ctx http.Context) http.Response {
}
// SetTimezone 设置时区
func (c *ToolBoxController) SetTimezone(ctx http.Context) http.Response {
func (r *ToolBoxController) SetTimezone(ctx http.Context) http.Response {
check := controllers.Check(ctx, "toolbox")
if check != nil {
return check
@@ -182,7 +182,7 @@ func (c *ToolBoxController) SetTimezone(ctx http.Context) http.Response {
}
// GetHosts 获取 hosts 信息
func (c *ToolBoxController) GetHosts(ctx http.Context) http.Response {
func (r *ToolBoxController) GetHosts(ctx http.Context) http.Response {
check := controllers.Check(ctx, "toolbox")
if check != nil {
return check
@@ -192,7 +192,7 @@ func (c *ToolBoxController) GetHosts(ctx http.Context) http.Response {
}
// SetHosts 设置 hosts 信息
func (c *ToolBoxController) SetHosts(ctx http.Context) http.Response {
func (r *ToolBoxController) SetHosts(ctx http.Context) http.Response {
check := controllers.Check(ctx, "toolbox")
if check != nil {
return check
@@ -209,7 +209,7 @@ func (c *ToolBoxController) SetHosts(ctx http.Context) http.Response {
}
// SetRootPassword 设置 root 密码
func (c *ToolBoxController) SetRootPassword(ctx http.Context) http.Response {
func (r *ToolBoxController) SetRootPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "toolbox")
if check != nil {
return check
+81 -71
View File
@@ -1,14 +1,15 @@
package controllers
import (
"regexp"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"panel/pkg/tools"
"github.com/spf13/cast"
requests "panel/app/http/requests/setting"
responses "panel/app/http/responses/setting"
"panel/app/models"
"panel/app/services"
"panel/pkg/tools"
)
type SettingController struct {
@@ -21,29 +22,27 @@ func NewSettingController() *SettingController {
}
}
// List 获取设置列表
// List
// @Summary 设置列表
// @Description 获取面板设置列表
// @Tags 面板设置
// @Produce json
// @Security BearerToken
// @Success 200 {object} SuccessResponse{data=responses.Settings}
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/setting/list [get]
func (r *SettingController) List(ctx http.Context) http.Response {
var settings []models.Setting
err := facades.Orm().Query().Get(&settings)
if err != nil {
facades.Log().Error("[面板][SettingController] 查询设置列表失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
type data struct {
Name string `json:"name"`
Username string `json:"username"`
Password string `json:"password"`
Email string `json:"email"`
Port string `json:"port"`
Entrance string `json:"entrance"`
WebsitePath string `json:"website_path"`
BackupPath string `json:"backup_path"`
}
var result data
var result responses.Settings
result.Name = r.setting.Get(models.SettingKeyName)
result.Entrance = r.setting.Get(models.SettingKeyEntrance)
result.Entrance = facades.Config().GetString("http.entrance")
result.WebsitePath = r.setting.Get(models.SettingKeyWebsitePath)
result.BackupPath = r.setting.Get(models.SettingKeyBackupPath)
@@ -51,7 +50,7 @@ func (r *SettingController) List(ctx http.Context) http.Response {
err = facades.Auth().User(ctx, &user)
if err != nil {
facades.Log().Error("[面板][SettingController] 获取用户失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
result.Username = user.Username
result.Email = user.Email
@@ -61,78 +60,89 @@ func (r *SettingController) List(ctx http.Context) http.Response {
return Success(ctx, result)
}
// Save 保存设置
func (r *SettingController) Save(ctx http.Context) http.Response {
name := ctx.Request().Input("name")
port := ctx.Request().Input("port")
backupPath := ctx.Request().Input("backup_path")
websitePath := ctx.Request().Input("website_path")
entrance := ctx.Request().Input("entrance")
username := ctx.Request().Input("username")
email := ctx.Request().Input("email")
password := ctx.Request().Input("password")
if !regexp.MustCompile(`^/[^/]*[^/]$`).MatchString(entrance) || entrance == "/api" {
return Error(ctx, http.StatusUnprocessableEntity, "入口格式错误")
// Update
// @Summary 更新设置
// @Description 更新面板设置
// @Tags 面板设置
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.Update true "更新设置"
// @Success 200 {object} SuccessResponse
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/setting/update [post]
func (r *SettingController) Update(ctx http.Context) http.Response {
var updateRequest requests.Update
sanitize := Sanitize(ctx, &updateRequest)
if sanitize != nil {
return sanitize
}
err := r.setting.Set(models.SettingKeyName, name)
err := r.setting.Set(models.SettingKeyName, updateRequest.Name)
if err != nil {
facades.Log().Error("[面板][SettingController] 保存设置失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
facades.Log().Request(ctx.Request()).With(map[string]any{
"error": err.Error(),
}).Tags("面板", "面板设置").Error("保存面板名称失败")
return ErrorSystem(ctx)
}
oldPort := tools.Exec(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
if oldPort != port {
tools.Exec("sed -i 's/APP_PORT=" + oldPort + "/APP_PORT=" + port + "/g' /www/panel/panel.conf")
if !tools.Exists(updateRequest.BackupPath) {
tools.Mkdir(updateRequest.BackupPath, 0644)
}
if !tools.Exists(backupPath) {
tools.Mkdir(backupPath, 0644)
}
err = r.setting.Set(models.SettingKeyBackupPath, backupPath)
err = r.setting.Set(models.SettingKeyBackupPath, updateRequest.BackupPath)
if err != nil {
facades.Log().Error("[面板][SettingController] 保存设置失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
facades.Log().Request(ctx.Request()).With(map[string]any{
"error": err.Error(),
}).Tags("面板", "面板设置").Error("保存备份目录失败")
return ErrorSystem(ctx)
}
if !tools.Exists(websitePath) {
tools.Mkdir(websitePath, 0755)
tools.Chown(websitePath, "www", "www")
if !tools.Exists(updateRequest.WebsitePath) {
tools.Mkdir(updateRequest.WebsitePath, 0755)
tools.Chown(updateRequest.WebsitePath, "www", "www")
}
err = r.setting.Set(models.SettingKeyWebsitePath, websitePath)
err = r.setting.Set(models.SettingKeyWebsitePath, updateRequest.WebsitePath)
if err != nil {
facades.Log().Error("[面板][SettingController] 保存设置失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
err = r.setting.Set(models.SettingKeyEntrance, entrance)
if err != nil {
facades.Log().Error("[面板][SettingController] 保存设置失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
facades.Log().Request(ctx.Request()).With(map[string]any{
"error": err.Error(),
}).Tags("面板", "面板设置").Error("保存建站目录失败")
return ErrorSystem(ctx)
}
var user models.User
err = facades.Auth().User(ctx, &user)
if err != nil {
facades.Log().Error("[面板][SettingController] 获取用户失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
if len(username) > 0 {
user.Username = username
}
if len(email) > 0 {
user.Email = email
}
if len(password) > 0 {
hash, err := facades.Hash().Make(password)
user.Username = updateRequest.UserName
user.Email = updateRequest.Email
if len(updateRequest.Password) > 0 {
hash, err := facades.Hash().Make(updateRequest.Password)
if err != nil {
facades.Log().Error("[面板][SettingController] 保存设置失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
user.Password = hash
}
if err = facades.Orm().Query().Save(&user); err != nil {
facades.Log().Error("[面板][SettingController] 保存设置失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
facades.Log().Request(ctx.Request()).With(map[string]any{
"error": err.Error(),
}).Tags("面板", "面板设置").Error("保存用户信息失败")
return ErrorSystem(ctx)
}
oldPort := tools.Exec(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
port := cast.ToString(updateRequest.Port)
if oldPort != port {
tools.Exec("sed -i 's/APP_PORT=" + oldPort + "/APP_PORT=" + port + "/g' /www/panel/panel.conf")
}
oldEntrance := tools.Exec(`cat /www/panel/panel.conf | grep APP_ENTRANCE | awk -F '=' '{print $2}' | tr -d '\n'`)
entrance := cast.ToString(updateRequest.Entrance)
if oldEntrance != entrance {
tools.Exec("sed -i 's/APP_ENTRANCE=" + oldEntrance + "/APP_ENTRANCE=" + entrance + "/g' /www/panel/panel.conf")
}
if oldPort != port || oldEntrance != entrance {
tools.RestartPanel()
}
return Success(ctx, nil)
+58 -56
View File
@@ -3,14 +3,15 @@ package controllers
import (
"bytes"
"context"
nethttp "net/http"
"sync"
"time"
"github.com/fasthttp/websocket"
"github.com/goravel/fiber"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/gorilla/websocket"
"github.com/spf13/cast"
"github.com/valyala/fasthttp"
"panel/app/models"
"panel/app/services"
@@ -69,22 +70,22 @@ func (r *SshController) UpdateInfo(ctx http.Context) http.Response {
err = r.setting.Set(models.SettingKeySshHost, host)
if err != nil {
facades.Log().Error("[面板][SSH] 更新配置失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
err = r.setting.Set(models.SettingKeySshPort, port)
if err != nil {
facades.Log().Error("[面板][SSH] 更新配置失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
err = r.setting.Set(models.SettingKeySshUser, user)
if err != nil {
facades.Log().Error("[面板][SSH] 更新配置失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
err = r.setting.Set(models.SettingKeySshPassword, password)
if err != nil {
facades.Log().Error("[面板][SSH] 更新配置失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
@@ -92,71 +93,72 @@ func (r *SshController) UpdateInfo(ctx http.Context) http.Response {
// Session SSH 会话
func (r *SshController) Session(ctx http.Context) http.Response {
upGrader := websocket.Upgrader{
upGrader := websocket.FastHTTPUpgrader{
ReadBufferSize: 4096,
WriteBufferSize: 4096,
CheckOrigin: func(r *nethttp.Request) bool {
CheckOrigin: func(ctx *fasthttp.RequestCtx) bool {
return true
},
Subprotocols: []string{ctx.Request().Header("Sec-WebSocket-Protocol")},
}
ws, err := upGrader.Upgrade(ctx.Response().Writer(), ctx.Request().Origin(), nil)
if err != nil {
facades.Log().Error("[面板][SSH] 建立连接失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
defer ws.Close()
config := ssh.SSHClientConfigPassword(
r.setting.Get(models.SettingKeySshHost)+":"+r.setting.Get(models.SettingKeySshPort),
r.setting.Get(models.SettingKeySshUser),
r.setting.Get(models.SettingKeySshPassword),
)
client, err := ssh.NewSSHClient(config)
if err != nil {
_ = ws.WriteControl(websocket.CloseMessage,
[]byte(err.Error()), time.Now().Add(time.Second))
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
defer client.Close()
turn, err := ssh.NewTurn(ws, client)
if err != nil {
_ = ws.WriteControl(websocket.CloseMessage,
[]byte(err.Error()), time.Now().Add(time.Second))
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
defer turn.Close()
var bufPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
var logBuff = bufPool.Get().(*bytes.Buffer)
logBuff.Reset()
defer bufPool.Put(logBuff)
ctx2, cancel := context.WithCancel(context.Background())
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
err := turn.LoopRead(logBuff, ctx2)
err = upGrader.Upgrade(ctx.(*fiber.Context).Instance().Context(), func(conn *websocket.Conn) {
if err != nil {
facades.Log().Error("[面板][SSH] 读取数据失败 ", err.Error())
_ = conn.WriteControl(websocket.CloseMessage,
[]byte(err.Error()), time.Now().Add(time.Second))
return
}
}()
go func() {
defer wg.Done()
err := turn.SessionWait()
if err != nil {
facades.Log().Error("[面板][SSH] 会话失败 ", err.Error())
}
cancel()
}()
wg.Wait()
return Success(ctx, logBuff.String())
defer client.Close()
turn, err := ssh.NewTurn(conn, client)
if err != nil {
_ = conn.WriteControl(websocket.CloseMessage,
[]byte(err.Error()), time.Now().Add(time.Second))
return
}
defer turn.Close()
var bufPool = sync.Pool{
New: func() any {
return new(bytes.Buffer)
},
}
var logBuff = bufPool.Get().(*bytes.Buffer)
logBuff.Reset()
defer bufPool.Put(logBuff)
ctx2, cancel := context.WithCancel(context.Background())
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
err := turn.LoopRead(logBuff, ctx2)
if err != nil {
facades.Log().Error("[面板][SSH] 读取数据失败 ", err.Error())
}
}()
go func() {
defer wg.Done()
err := turn.SessionWait()
if err != nil {
facades.Log().Error("[面板][SSH] 会话失败 ", err.Error())
}
cancel()
}()
wg.Wait()
})
if err != nil {
facades.Log().Error("[面板][SSH] 建立连接失败 ", err)
return ErrorSystem(ctx)
}
return nil
}
+188
View File
@@ -0,0 +1,188 @@
package controllers
import (
"html/template"
"io"
"path/filepath"
"regexp"
"strings"
"github.com/goravel/framework/contracts/http"
swaggerFiles "github.com/swaggo/files/v2"
"github.com/swaggo/swag"
)
type SwaggerController struct {
// Dependent services
}
// Config stores fiberSwagger configuration variables.
type Config struct {
URL string
InstanceName string
DocExpansion string
DomID string
DeepLinking bool
PersistAuthorization bool
}
func NewSwaggerController() *SwaggerController {
return &SwaggerController{
// Inject services
}
}
// Index
// @Summary Swagger UI
// @Description Swagger UI
// @Tags Swagger
// @Success 200
// @Failure 500
// @Router /swagger [get]
func (r *SwaggerController) Index(ctx http.Context) http.Response {
config := Config{
URL: "swagger.json",
DocExpansion: "list",
DomID: "swagger-ui",
InstanceName: swag.Name,
DeepLinking: true,
}
// create a template with name
index, _ := template.New("swagger_index.html").Parse(indexTemplate)
var re = regexp.MustCompile(`^(.*/)([^?].*)?[?|.]*$`)
matches := re.FindStringSubmatch(ctx.Request().Path())
path := matches[2]
fileExt := filepath.Ext(path)
switch path {
case "":
return ctx.Response().Redirect(http.StatusMovedPermanently, filepath.Join(matches[1], "index.html"))
case "index.html":
var builder strings.Builder
err := index.Execute(&builder, config)
if err != nil {
return ctx.Response().Status(http.StatusInternalServerError).String(http.StatusText(http.StatusInternalServerError))
}
return ctx.Response().Success().String("html", builder.String())
case "swagger.json":
return ctx.Response().File("docs/swagger.json")
}
file, err := swaggerFiles.FS.Open(path)
if err != nil {
return ctx.Response().Status(http.StatusInternalServerError).String(http.StatusText(http.StatusInternalServerError))
}
defer file.Close()
content, err := io.ReadAll(file)
if err != nil {
return ctx.Response().Status(http.StatusInternalServerError).String(http.StatusText(http.StatusInternalServerError))
}
return ctx.Response().Success().String(fileExt[0:], string(content))
}
const indexTemplate = `<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link href="https://gfont.cdn.haozi.net/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
<style>
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
}
body {
margin:0;
background: #fafafa;
}
</style>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
<defs>
<symbol viewBox="0 0 20 20" id="unlocked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
</symbol>
<symbol viewBox="0 0 20 20" id="locked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="close">
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow">
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow-down">
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="jump-to">
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="expand">
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
</symbol>
</defs>
</svg>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js"> </script>
<script src="./swagger-ui-standalone-preset.js"> </script>
<script>
window.onload = function() {
// Build a system
const ui = SwaggerUIBundle({
url: "{{.URL}}",
deepLinking: {{.DeepLinking}},
docExpansion: "{{.DocExpansion}}",
dom_id: "#{{.DomID}}",
persistAuthorization: {{.PersistAuthorization}},
validatorUrl: null,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
window.ui = ui
}
</script>
</body>
</html>
`
+6 -6
View File
@@ -36,12 +36,12 @@ func (r *TaskController) Status(ctx http.Context) http.Response {
func (r *TaskController) List(ctx http.Context) http.Response {
var tasks []models.Task
var total int64
err := facades.Orm().Query().Paginate(ctx.Request().QueryInt("page", 1), ctx.Request().QueryInt("limit", 10), &tasks, &total)
err := facades.Orm().Query().Order("id desc").Paginate(ctx.Request().QueryInt("page", 1), ctx.Request().QueryInt("limit", 10), &tasks, &total)
if err != nil {
facades.Log().With(map[string]any{
facades.Log().Request(ctx.Request()).With(map[string]any{
"error": err.Error(),
}).Error("[面板][TaskController] 查询任务列表失败")
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
return Success(ctx, http.Json{
@@ -55,11 +55,11 @@ func (r *TaskController) Log(ctx http.Context) http.Response {
var task models.Task
err := facades.Orm().Query().Where("id", ctx.Request().QueryInt("id")).FirstOrFail(&task)
if err != nil {
facades.Log().With(map[string]any{
facades.Log().Request(ctx.Request()).With(map[string]any{
"id": ctx.Request().QueryInt("id"),
"error": err.Error(),
}).Error("[面板][TaskController] 查询任务失败")
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
log := tools.Exec("tail -n 1000 " + task.Log)
@@ -76,7 +76,7 @@ func (r *TaskController) Delete(ctx http.Context) http.Response {
"id": ctx.Request().QueryInt("id"),
"error": err.Error(),
}).Error("[面板][TaskController] 删除任务失败")
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
+48 -25
View File
@@ -4,7 +4,8 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"panel/app/http/requests"
"panel/app/http/requests/user"
responses "panel/app/http/responses/user"
"panel/app/models"
)
@@ -18,22 +19,31 @@ func NewUserController() *UserController {
}
}
// Login 用户登录
// Login
// @Summary 登录
// @Description 通过用户名和密码获取访问令牌
// @Tags 用户鉴权
// @Accept json
// @Produce json
// @Param data body requests.Login true "登录信息"
// @Success 200 {object} SuccessResponse
// @Failure 403 {object} ErrorResponse "用户名或密码错误"
// @Failure 500 {object} ErrorResponse "系统内部错误
// @Router /panel/user/login [post]
func (r *UserController) Login(ctx http.Context) http.Response {
var loginRequest requests.LoginRequest
errors, err := ctx.Request().ValidateRequest(&loginRequest)
if err != nil {
return Error(ctx, http.StatusUnprocessableEntity, err.Error())
}
if errors != nil {
return Error(ctx, http.StatusUnprocessableEntity, errors.One())
var loginRequest requests.Login
sanitize := Sanitize(ctx, &loginRequest)
if sanitize != nil {
return sanitize
}
var user models.User
err = facades.Orm().Query().Where("username", loginRequest.Username).First(&user)
err := facades.Orm().Query().Where("username", loginRequest.Username).First(&user)
if err != nil {
facades.Log().Error("[面板][UserController] 查询用户失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
facades.Log().Request(ctx.Request()).With(map[string]any{
"error": err.Error(),
}).Tags("面板", "用户").Error("查询用户失败")
return ErrorSystem(ctx)
}
if user.ID == 0 || !facades.Hash().Check(loginRequest.Password, user.Password) {
@@ -43,15 +53,19 @@ func (r *UserController) Login(ctx http.Context) http.Response {
if facades.Hash().NeedsRehash(user.Password) {
user.Password, err = facades.Hash().Make(loginRequest.Password)
if err != nil {
facades.Log().Error("[面板][UserController] 更新密码失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
facades.Log().Request(ctx.Request()).With(map[string]any{
"error": err.Error(),
}).Tags("面板", "用户").Error("更新密码失败")
return ErrorSystem(ctx)
}
}
token, loginErr := facades.Auth().LoginUsingID(ctx, user.ID)
if loginErr != nil {
facades.Log().Error("[面板][UserController] 登录失败 ", loginErr)
return Error(ctx, http.StatusInternalServerError, loginErr.Error())
facades.Log().Request(ctx.Request()).With(map[string]any{
"error": err.Error(),
}).Tags("面板", "用户").Error("登录失败")
return ErrorSystem(ctx)
}
return Success(ctx, http.Json{
@@ -59,21 +73,30 @@ func (r *UserController) Login(ctx http.Context) http.Response {
})
}
// Info 用户信息
// Info
// @Summary 用户信息
// @Description 获取当前登录用户信息
// @Tags 用户鉴权
// @Produce json
// @Security BearerToken
// @Success 200 {object} SuccessResponse{data=responses.Info}
// @Failure 401 {object} ErrorResponse "登录已过期"
// @Failure 500 {object} ErrorResponse "系统内部错误"
// @Router /panel/user/info [get]
func (r *UserController) Info(ctx http.Context) http.Response {
var user models.User
err := facades.Auth().User(ctx, &user)
if err != nil {
facades.Log().With(map[string]any{
facades.Log().Request(ctx.Request()).With(map[string]any{
"error": err.Error(),
}).Error("[面板][UserController] 查询用户信息失败")
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}).Tags("面板", "用户").Error("获取用户信息失败")
return ErrorSystem(ctx)
}
return Success(ctx, http.Json{
"id": user.ID,
"role": []string{"admin"},
"username": user.Username,
"email": user.Email,
return Success(ctx, responses.Info{
ID: user.ID,
Role: []string{"admin"},
Username: user.Username,
Email: user.Email,
})
}
+14 -14
View File
@@ -36,7 +36,7 @@ func (c *WebsiteController) List(ctx http.Context) http.Response {
total, websites, err := c.website.List(page, limit)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 获取网站列表失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
return Success(ctx, http.Json{
@@ -83,7 +83,7 @@ func (c *WebsiteController) Add(ctx http.Context) http.Response {
newSite, err := c.website.Add(website)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 添加网站失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
return Success(ctx, newSite)
@@ -131,12 +131,12 @@ func (c *WebsiteController) SaveDefaultConfig(ctx http.Context) http.Response {
if !tools.Write("/www/server/openresty/html/index.html", index, 0644) {
facades.Log().Error("[面板][WebsiteController] 保存默认配置失败")
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
if !tools.Write("/www/server/openresty/html/stop.html", stop, 0644) {
facades.Log().Error("[面板][WebsiteController] 保存默认配置失败")
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
@@ -156,7 +156,7 @@ func (c *WebsiteController) GetConfig(ctx http.Context) http.Response {
config, err := c.website.GetConfig(id)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 获取网站配置失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
return Success(ctx, config)
@@ -378,7 +378,7 @@ func (c *WebsiteController) SaveConfig(ctx http.Context) http.Response {
err = facades.Orm().Query().Save(&website)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 保存网站配置失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
tools.Write("/www/server/vhost/"+website.Name+".conf", raw, 0644)
@@ -403,7 +403,7 @@ func (c *WebsiteController) ClearLog(ctx http.Context) http.Response {
err := facades.Orm().Query().Where("id", id).Get(&website)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 获取网站信息失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
tools.Remove("/www/wwwlogs/" + website.Name + ".log")
@@ -422,14 +422,14 @@ func (c *WebsiteController) UpdateRemark(ctx http.Context) http.Response {
err := facades.Orm().Query().Where("id", id).Get(&website)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 获取网站信息失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
website.Remark = ctx.Request().Input("remark")
err = facades.Orm().Query().Save(&website)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 保存网站备注失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
@@ -440,7 +440,7 @@ func (c *WebsiteController) BackupList(ctx http.Context) http.Response {
backupList, err := c.backup.WebsiteList()
if err != nil {
facades.Log().Error("[面板][WebsiteController] 获取网站备份列表失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
return Success(ctx, backupList)
@@ -550,14 +550,14 @@ func (c *WebsiteController) ResetConfig(ctx http.Context) http.Response {
website := models.Website{}
if err := facades.Orm().Query().Where("id", id).Get(&website); err != nil {
facades.Log().Error("[面板][WebsiteController] 获取网站信息失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
website.Status = true
website.Ssl = false
if err := facades.Orm().Query().Save(&website); err != nil {
facades.Log().Error("[面板][WebsiteController] 保存网站配置失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
raw := fmt.Sprintf(`
@@ -640,13 +640,13 @@ func (c *WebsiteController) Status(ctx http.Context) http.Response {
website := models.Website{}
if err := facades.Orm().Query().Where("id", id).Get(&website); err != nil {
facades.Log().Error("[面板][WebsiteController] 获取网站信息失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
website.Status = ctx.Request().InputBool("status")
if err := facades.Orm().Query().Save(&website); err != nil {
facades.Log().Error("[面板][WebsiteController] 保存网站配置失败 ", err)
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
return ErrorSystem(ctx)
}
raw := tools.Read("/www/server/vhost/" + website.Name + ".conf")
+1 -5
View File
@@ -2,8 +2,6 @@ package http
import (
"github.com/goravel/framework/contracts/http"
"panel/app/http/middleware"
)
type Kernel struct {
@@ -12,7 +10,5 @@ type Kernel struct {
// The application's global HTTP middleware stack.
// These middleware are run during every request to your application.
func (kernel Kernel) Middleware() []http.Middleware {
return []http.Middleware{
middleware.Static(),
}
return []http.Middleware{}
}
-30
View File
@@ -1,30 +0,0 @@
package middleware
import (
"fmt"
"github.com/gin-contrib/static"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/gin"
"panel/app/services"
)
func Static() http.Middleware {
return func(ctx http.Context) {
// 自动纠正 URL 格式
if ctx.Request().Path() == services.NewSettingImpl().Get("entrance", "/") && ctx.Request().Path() != "/" {
// ctx.Response().Redirect(http.StatusFound, ctx.Request().Path()+"/")
ctx.Response().Writer().WriteHeader(http.StatusFound)
_, err := ctx.Response().Writer().Write([]byte(fmt.Sprintf(`<html><head><meta http-equiv="refresh" content="0;url=%s/"></head></html>`, ctx.Request().Path())))
if err != nil {
return
}
ctx.Response().Flush()
return
}
static.Serve(services.NewSettingImpl().Get("entrance", "/"), static.LocalFile("public", false))(ctx.(*gin.Context).Instance())
ctx.Request().Next()
}
}
+34
View File
@@ -0,0 +1,34 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type CertDeploy struct {
ID uint `form:"id" json:"id"`
WebsiteID uint `form:"website_id" json:"website_id"`
}
func (r *CertDeploy) Authorize(ctx http.Context) error {
return nil
}
func (r *CertDeploy) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|uint|min:1|exists:certs,id",
"website_id": "required|uint|min:1|exists:websites,id",
}
}
func (r *CertDeploy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertDeploy) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertDeploy) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
@@ -0,0 +1,32 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type CertShowAndDestroy struct {
ID uint `form:"id" json:"id"`
}
func (r *CertShowAndDestroy) Authorize(ctx http.Context) error {
return nil
}
func (r *CertShowAndDestroy) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|uint|min:1|exists:certs,id",
}
}
func (r *CertShowAndDestroy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertShowAndDestroy) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertShowAndDestroy) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
+67
View File
@@ -0,0 +1,67 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
"github.com/spf13/cast"
)
type CertStore struct {
Type string `form:"type" json:"type"`
Domains []string `form:"domains" json:"domains"`
AutoRenew bool `form:"auto_renew" json:"auto_renew"`
UserID uint `form:"user_id" json:"user_id"`
DNSID *uint `form:"dns_id" json:"dns_id"`
WebsiteID *uint `form:"website_id" json:"website_id"`
}
func (r *CertStore) Authorize(ctx http.Context) error {
return nil
}
func (r *CertStore) Rules(ctx http.Context) map[string]string {
return map[string]string{
"type": "required|in:P256,P384,2048,4096",
"domains": "required|slice",
"auto_renew": "required|bool",
"user_id": "required|uint|exists:cert_users,id",
"dns_id": "uint",
"website_id": "uint",
}
}
func (r *CertStore) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertStore) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertStore) PrepareForValidation(ctx http.Context, data validation.Data) error {
// TODO 由于验证器 filter 标签的问题,暂时这里这样处理
userID, exist := data.Get("user_id")
if exist {
err := data.Set("user_id", cast.ToUint(userID))
if err != nil {
return err
}
}
dnsID, exist := data.Get("dns_id")
if exist {
err := data.Set("dns_id", cast.ToUint(dnsID))
if err != nil {
return err
}
}
websiteID, exist := data.Get("website_id")
if exist {
err := data.Set("website_id", cast.ToUint(websiteID))
if err != nil {
return err
}
}
return nil
}
+69
View File
@@ -0,0 +1,69 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
"github.com/spf13/cast"
)
type CertUpdate struct {
ID uint `form:"id" json:"id"`
Type string `form:"type" json:"type"`
Domains []string `form:"domains" json:"domains"`
AutoRenew bool `form:"auto_renew" json:"auto_renew"`
UserID uint `form:"user_id" json:"user_id"`
DNSID *uint `form:"dns_id" json:"dns_id"`
WebsiteID *uint `form:"website_id" json:"website_id"`
}
func (r *CertUpdate) Authorize(ctx http.Context) error {
return nil
}
func (r *CertUpdate) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|uint|min:1|exists:certs,id",
"type": "required|in:P256,P384,2048,4096",
"domains": "required|slice",
"auto_renew": "required|bool",
"user_id": "required|uint|exists:cert_users,id",
"dns_id": "uint",
"website_id": "uint",
}
}
func (r *CertUpdate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertUpdate) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertUpdate) PrepareForValidation(ctx http.Context, data validation.Data) error {
// TODO 由于验证器 filter 标签的问题,暂时这里这样处理
userID, exist := data.Get("user_id")
if exist {
err := data.Set("user_id", cast.ToUint(userID))
if err != nil {
return err
}
}
dnsID, exist := data.Get("dns_id")
if exist {
err := data.Set("dns_id", cast.ToUint(dnsID))
if err != nil {
return err
}
}
websiteID, exist := data.Get("website_id")
if exist {
err := data.Set("website_id", cast.ToUint(websiteID))
if err != nil {
return err
}
}
return nil
}
@@ -0,0 +1,32 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type DNSShowAndDestroy struct {
ID uint `form:"id" json:"id"`
}
func (r *DNSShowAndDestroy) Authorize(ctx http.Context) error {
return nil
}
func (r *DNSShowAndDestroy) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|uint|min:1|exists:cert_dns,id",
}
}
func (r *DNSShowAndDestroy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DNSShowAndDestroy) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DNSShowAndDestroy) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
+44
View File
@@ -0,0 +1,44 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
"panel/pkg/acme"
)
type DNSStore struct {
Type string `form:"type" json:"type"`
Name string `form:"name" json:"name"`
Data acme.DNSParam `form:"data" json:"data"`
}
func (r *DNSStore) Authorize(ctx http.Context) error {
return nil
}
func (r *DNSStore) Rules(ctx http.Context) map[string]string {
return map[string]string{
"type": "required|in:dnspod,aliyun,cloudflare",
"name": "required",
"data": "required",
"data.id": "required_if:type,dnspod",
"data.token": "required_if:type,dnspod",
"data.access_key": "required_if:type,aliyun",
"data.secret_key": "required_if:type,aliyun",
"data.email": "required_if:type,cloudflare",
"data.api_key": "required_if:type,cloudflare",
}
}
func (r *DNSStore) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DNSStore) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DNSStore) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
+46
View File
@@ -0,0 +1,46 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
"panel/pkg/acme"
)
type DNSUpdate struct {
ID uint `form:"id" json:"id"`
Type string `form:"type" json:"type"`
Name string `form:"name" json:"name"`
Data acme.DNSParam `form:"data" json:"data"`
}
func (r *DNSUpdate) Authorize(ctx http.Context) error {
return nil
}
func (r *DNSUpdate) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|uint|min:1|exists:cert_dns,id",
"type": "required|in:dnspod,aliyun,cloudflare",
"name": "required",
"data": "required",
"data.id": "required_if:type,dnspod",
"data.token": "required_if:type,dnspod",
"data.access_key": "required_if:type,aliyun",
"data.secret_key": "required_if:type,aliyun",
"data.email": "required_if:type,cloudflare",
"data.api_key": "required_if:type,cloudflare",
}
}
func (r *DNSUpdate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DNSUpdate) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DNSUpdate) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
+32
View File
@@ -0,0 +1,32 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type Obtain struct {
ID uint `form:"id" json:"id"`
}
func (r *Obtain) Authorize(ctx http.Context) error {
return nil
}
func (r *Obtain) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|exists:certs,id",
}
}
func (r *Obtain) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Obtain) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Obtain) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
+32
View File
@@ -0,0 +1,32 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type Renew struct {
ID uint `form:"id" json:"id"`
}
func (r *Renew) Authorize(ctx http.Context) error {
return nil
}
func (r *Renew) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|exists:certs,id",
}
}
func (r *Renew) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Renew) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Renew) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
@@ -0,0 +1,32 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type UserShowAndDestroy struct {
ID uint `form:"id" json:"id"`
}
func (r *UserShowAndDestroy) Authorize(ctx http.Context) error {
return nil
}
func (r *UserShowAndDestroy) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|uint|min:1|exists:cert_users,id",
}
}
func (r *UserShowAndDestroy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UserShowAndDestroy) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UserShowAndDestroy) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
+40
View File
@@ -0,0 +1,40 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type UserStore struct {
CA string `form:"ca" json:"ca"`
Email string `form:"email" json:"email"`
Kid string `form:"kid" json:"kid"`
HmacEncoded string `form:"hmac_encoded" json:"hmac_encoded"`
KeyType string `form:"key_type" json:"key_type"`
}
func (r *UserStore) Authorize(ctx http.Context) error {
return nil
}
func (r *UserStore) Rules(ctx http.Context) map[string]string {
return map[string]string{
"ca": "required|in:letsencrypt,zerossl,sslcom,google,buypass",
"email": "required|email",
"kid": "required_unless:ca,letsencrypt,buypass",
"hmac_encoded": "required_unless:ca,letsencrypt,buypass",
"key_type": "required|in:P256,P384,2048,4096",
}
}
func (r *UserStore) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UserStore) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UserStore) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
+42
View File
@@ -0,0 +1,42 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type UserUpdate struct {
ID uint `form:"id" json:"id"`
CA string `form:"ca" json:"ca"`
Email string `form:"email" json:"email"`
Kid string `form:"kid" json:"kid"`
HmacEncoded string `form:"hmac_encoded" json:"hmac_encoded"`
KeyType string `form:"key_type" json:"key_type"`
}
func (r *UserUpdate) Authorize(ctx http.Context) error {
return nil
}
func (r *UserUpdate) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|uint|min:1|exists:cert_users,id",
"ca": "required|in:letsencrypt,zerossl,sslcom,google,buypass",
"email": "required|email",
"kid": "required_unless:ca,letsencrypt,buypass",
"hmac_encoded": "required_unless:ca,letsencrypt,buypass",
"key_type": "required|in:P256,P384,2048,4096",
}
}
func (r *UserUpdate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UserUpdate) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UserUpdate) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
+41
View File
@@ -0,0 +1,41 @@
package commonrequests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type Paginate struct {
Page int `form:"page" json:"page"`
Limit int `form:"limit" json:"limit"`
}
func (r *Paginate) Authorize(ctx http.Context) error {
return nil
}
func (r *Paginate) Rules(ctx http.Context) map[string]string {
return map[string]string{
"page": "required|uint|min:1",
"limit": "required|uint|min:1",
}
}
func (r *Paginate) Messages(ctx http.Context) map[string]string {
return map[string]string{
"page.required": "分页参数 page 不能为空",
"page.uint": "分页参数 page 必须是一个整数",
"page.min": "分页参数 page 必须大于等于 1",
"limit.required": "分页参数 limit 不能为空",
"limit.uint": "分页参数 limit 必须是一个整数",
"limit.min": "分页参数 limit 必须大于等于 1",
}
}
func (r *Paginate) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Paginate) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
+49
View File
@@ -0,0 +1,49 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type Update struct {
Name string `form:"name" json:"name"`
Port uint `form:"port" json:"port" filter:"uint"`
BackupPath string `form:"backup_path" json:"backup_path"`
WebsitePath string `form:"website_path" json:"website_path"`
Entrance string `form:"entrance" json:"entrance"`
UserName string `form:"username" json:"username"`
Email string `form:"email" json:"email"`
Password string `form:"password" json:"password"`
}
func (r *Update) Authorize(ctx http.Context) error {
return nil
}
func (r *Update) Rules(ctx http.Context) map[string]string {
return map[string]string{
"name": "required|string:2,20",
"port": "required|int:1000,65535",
"backup_path": "required|string:2,255",
"website_path": "required|string:2,255",
"entrance": `required|regex:^/(\w+)?$|not_in:/api`,
"username": "required|string:2,20",
"email": "required|email",
"password": "string:8,255",
}
}
func (r *Update) Messages(ctx http.Context) map[string]string {
return map[string]string{
"port.int": "port 值必须是一个整数且在 1000 - 65535 之间",
"password.string": "password 必须是一个字符串且长度在 8 - 255 之间",
}
}
func (r *Update) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Update) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
@@ -5,39 +5,34 @@ import (
"github.com/goravel/framework/contracts/validation"
)
type LoginRequest struct {
type Login struct {
Username string `json:"username" form:"username"`
Password string `json:"password" form:"password"`
CaptchaID string `json:"captcha_id" form:"captcha_id"`
Captcha string `json:"captcha" form:"captcha"`
}
func (r *LoginRequest) Authorize(ctx http.Context) error {
func (r *Login) Authorize(ctx http.Context) error {
return nil
}
func (r *LoginRequest) Rules(ctx http.Context) map[string]string {
func (r *Login) Rules(ctx http.Context) map[string]string {
return map[string]string{
"username": "required",
"password": "required|min_len:8",
"captcha": "captcha:true",
}
}
func (r *LoginRequest) Messages(ctx http.Context) map[string]string {
func (r *Login) Messages(ctx http.Context) map[string]string {
return map[string]string{
"username.required": "登录名不能为空",
"password.required": "密码不能为空",
"password.min_len": "密码长度不能小于 8 位",
"captcha.captcha": "验证码错误",
}
}
func (r *LoginRequest) Attributes(ctx http.Context) map[string]string {
func (r *Login) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *LoginRequest) PrepareForValidation(ctx http.Context, data validation.Data) error {
func (r *Login) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
+8
View File
@@ -0,0 +1,8 @@
package responses
import "panel/app/models"
type CertList struct {
Total int64 `json:"total"`
Items []models.Cert `json:"items"`
}
+8
View File
@@ -0,0 +1,8 @@
package responses
import "panel/app/models"
type DNSList struct {
Total int64 `json:"total"`
Items []models.CertDNS `json:"items"`
}
+8
View File
@@ -0,0 +1,8 @@
package responses
import "panel/app/models"
type UserList struct {
Total int64 `json:"total"`
Items []models.CertUser `json:"items"`
}
+12
View File
@@ -0,0 +1,12 @@
package responses
type Settings struct {
Name string `json:"name"`
Username string `json:"username"`
Password string `json:"password"`
Email string `json:"email"`
Port string `json:"port"`
Entrance string `json:"entrance"`
WebsitePath string `json:"website_path"`
BackupPath string `json:"backup_path"`
}
+8
View File
@@ -0,0 +1,8 @@
package responses
type Info struct {
ID uint `json:"id"`
Role []string `json:"role"`
Username string `json:"username"`
Email string `json:"email"`
}
+24
View File
@@ -0,0 +1,24 @@
package models
import (
"github.com/goravel/framework/support/carbon"
)
type Cert struct {
ID uint `gorm:"primaryKey" json:"id"`
UserID uint `gorm:"default:null" json:"user_id"` // 关联的 ACME 用户 ID
WebsiteID *uint `gorm:"default:null" json:"website_id"` // 关联的网站 ID
DNSID *uint `gorm:"column:dns_id;default:null" json:"dns_id"` // 关联的 DNS ID
Type string `gorm:"not null" json:"type"` // 证书类型 (P256, P384, 2048, 4096)
Domains []string `gorm:"type:json;serializer:json" json:"domains"`
AutoRenew bool `gorm:"default:true" json:"auto_renew"` // 自动续签
CertURL *string `gorm:"default:null" json:"cert_url"` // 证书 URL (续签时使用)
Cert string `gorm:"default:null" json:"cert"` // 证书内容
Key string `gorm:"default:null" json:"key"` // 私钥内容
CreatedAt carbon.DateTime `gorm:"autoCreateTime;column:created_at" json:"created_at"`
UpdatedAt carbon.DateTime `gorm:"autoUpdateTime;column:updated_at" json:"updated_at"`
Website *Website `gorm:"foreignKey:WebsiteID" json:"website"`
User *CertUser `gorm:"foreignKey:UserID" json:"user"`
DNS *CertDNS `gorm:"foreignKey:DNSID" json:"dns"`
}
+22
View File
@@ -0,0 +1,22 @@
package models
import (
"github.com/goravel/framework/support/carbon"
"panel/pkg/acme"
)
type CertDNS struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"not null" json:"name"` // 备注名称
Type string `gorm:"not null" json:"type"` // DNS 提供商 (dnspod, aliyun, cloudflare)
Data acme.DNSParam `gorm:"type:json;serializer:json" json:"dns_param"`
CreatedAt carbon.DateTime `gorm:"autoCreateTime;column:created_at" json:"created_at"`
UpdatedAt carbon.DateTime `gorm:"autoUpdateTime;column:updated_at" json:"updated_at"`
Certs []*Cert `gorm:"foreignKey:DNSID" json:"-"`
}
func (CertDNS) TableName() string {
return "cert_dns"
}
+19
View File
@@ -0,0 +1,19 @@
package models
import (
"github.com/goravel/framework/support/carbon"
)
type CertUser struct {
ID uint `gorm:"primaryKey" json:"id"`
Email string `gorm:"not null" json:"email"`
CA string `gorm:"not null" json:"ca"` // CA 提供商 (letsencrypt, zerossl, sslcom, google, buypass)
Kid *string `gorm:"default:null" json:"kid"`
HmacEncoded *string `gorm:"default:null" json:"hmac_encoded"`
PrivateKey string `gorm:"not null" json:"private_key"`
KeyType string `gorm:"not null" json:"key_type"`
CreatedAt carbon.DateTime `gorm:"autoCreateTime;column:created_at" json:"created_at"`
UpdatedAt carbon.DateTime `gorm:"autoUpdateTime;column:updated_at" json:"updated_at"`
Certs []*Cert `gorm:"foreignKey:UserID" json:"-"`
}
-1
View File
@@ -9,7 +9,6 @@ const (
SettingKeyMonitorDays = "monitor_days"
SettingKeyBackupPath = "backup_path"
SettingKeyWebsitePath = "website_path"
SettingKeyEntrance = "entrance"
SettingKeyMysqlRootPassword = "mysql_root_password"
SettingKeySshHost = "ssh_host"
SettingKeySshPort = "ssh_port"
+2
View File
@@ -14,4 +14,6 @@ type Website struct {
Remark string `gorm:"default:''" json:"remark"`
CreatedAt carbon.DateTime `gorm:"autoCreateTime;column:created_at" json:"created_at"`
UpdatedAt carbon.DateTime `gorm:"autoUpdateTime;column:updated_at" json:"updated_at"`
Cert *Cert `gorm:"foreignKey:WebsiteID" json:"cert"`
}
+1 -1
View File
@@ -20,8 +20,8 @@ func (receiver *RouteServiceProvider) Boot(app foundation.Application) {
receiver.configureRateLimiting()
routes.Api()
routes.Plugin()
routes.Api()
}
func (receiver *RouteServiceProvider) configureRateLimiting() {
+4 -3
View File
@@ -3,6 +3,7 @@ package rules
import (
"github.com/goravel/framework/contracts/validation"
"github.com/goravel/framework/facades"
"github.com/spf13/cast"
)
type Exists struct {
@@ -21,8 +22,8 @@ func (receiver *Exists) Passes(_ validation.Data, val any, options ...any) bool
// 第二个参数,字段名称,如 id
fieldName := options[1].(string)
// 用户请求过来的数据
requestValue, ok := val.(string)
if !ok {
requestValue, err := cast.ToStringE(val)
if err != nil {
return false
}
@@ -40,7 +41,7 @@ func (receiver *Exists) Passes(_ validation.Data, val any, options ...any) bool
query = query.OrWhere(options[i].(string), requestValue)
}
}
err := query.Count(&count)
err = query.Count(&count)
if err != nil {
return false
}
+4 -3
View File
@@ -3,6 +3,7 @@ package rules
import (
"github.com/goravel/framework/contracts/validation"
"github.com/goravel/framework/facades"
"github.com/spf13/cast"
)
type NotExists struct {
@@ -21,8 +22,8 @@ func (receiver *NotExists) Passes(_ validation.Data, val any, options ...any) bo
// 第二个参数,字段名称,如 id
fieldName := options[1].(string)
// 用户请求过来的数据
requestValue, ok := val.(string)
if !ok {
requestValue, err := cast.ToStringE(val)
if err != nil {
return false
}
@@ -40,7 +41,7 @@ func (receiver *NotExists) Passes(_ validation.Data, val any, options ...any) bo
query = query.OrWhere(options[i].(string), requestValue)
}
}
err := query.Count(&count)
err = query.Count(&count)
if err != nil {
return false
}
+474
View File
@@ -0,0 +1,474 @@
// Package services 证书服务
package services
import (
"errors"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate"
"github.com/goravel/framework/facades"
requests "panel/app/http/requests/cert"
"panel/app/models"
"panel/pkg/acme"
"panel/pkg/tools"
)
type Cert interface {
UserStore(request requests.UserStore) error
UserUpdate(request requests.UserUpdate) error
UserShow(ID uint) (models.CertUser, error)
UserDestroy(ID uint) error
DNSStore(request requests.DNSStore) error
DNSUpdate(request requests.DNSUpdate) error
DNSShow(ID uint) (models.CertDNS, error)
DNSDestroy(ID uint) error
CertStore(request requests.CertStore) error
CertUpdate(request requests.CertUpdate) error
CertShow(ID uint) (models.Cert, error)
CertDestroy(ID uint) error
ObtainAuto(ID uint) (certificate.Resource, error)
ObtainManual(ID uint) (certificate.Resource, error)
ManualDNS(ID uint) (map[string]acme.Resolve, error)
Renew(ID uint) (certificate.Resource, error)
}
type CertImpl struct {
}
func NewCertImpl() *CertImpl {
return &CertImpl{}
}
// UserStore 添加用户
func (s *CertImpl) UserStore(request requests.UserStore) error {
var user models.CertUser
user.CA = request.CA
user.Email = request.Email
user.Kid = &request.Kid
user.HmacEncoded = &request.HmacEncoded
user.KeyType = request.KeyType
var err error
var client *acme.Client
switch user.CA {
case "letsencrypt":
client, err = acme.NewRegisterClient(user.Email, acme.CALetEncrypt, certcrypto.KeyType(user.KeyType))
case "buypass":
client, err = acme.NewRegisterClient(user.Email, acme.CABuypass, certcrypto.KeyType(user.KeyType))
case "zerossl":
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CAZeroSSL, certcrypto.KeyType(user.KeyType))
case "sslcom":
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CASSLcom, certcrypto.KeyType(user.KeyType))
case "google":
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CAGoogle, certcrypto.KeyType(user.KeyType))
default:
return errors.New("CA 提供商不支持")
}
if err != nil {
return errors.New("向 CA 注册账号失败,请检查参数是否正确")
}
privateKey, err := acme.GetPrivateKey(client.User.GetPrivateKey(), acme.KeyType(user.KeyType))
if err != nil {
return errors.New("获取私钥失败")
}
user.PrivateKey = string(privateKey)
return facades.Orm().Query().Create(&user)
}
// UserUpdate 更新用户
func (s *CertImpl) UserUpdate(request requests.UserUpdate) error {
var user models.CertUser
err := facades.Orm().Query().Where("id = ?", request.ID).First(&user)
if err != nil {
return err
}
user.CA = request.CA
user.Email = request.Email
user.Kid = &request.Kid
user.HmacEncoded = &request.HmacEncoded
user.KeyType = request.KeyType
var client *acme.Client
switch user.CA {
case "letsencrypt":
client, err = acme.NewRegisterClient(user.Email, acme.CALetEncrypt, certcrypto.KeyType(user.KeyType))
case "buypass":
client, err = acme.NewRegisterClient(user.Email, acme.CABuypass, certcrypto.KeyType(user.KeyType))
case "zerossl":
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CAZeroSSL, certcrypto.KeyType(user.KeyType))
case "sslcom":
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CASSLcom, certcrypto.KeyType(user.KeyType))
case "google":
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CAGoogle, certcrypto.KeyType(user.KeyType))
default:
return errors.New("CA 提供商不支持")
}
if err != nil {
return errors.New("向 CA 注册账号失败,请检查参数是否正确")
}
privateKey, err := acme.GetPrivateKey(client.User.GetPrivateKey(), acme.KeyType(user.KeyType))
if err != nil {
return errors.New("获取私钥失败")
}
user.PrivateKey = string(privateKey)
return facades.Orm().Query().Save(&user)
}
// UserShow 根据 ID 获取用户
func (s *CertImpl) UserShow(ID uint) (models.CertUser, error) {
var user models.CertUser
err := facades.Orm().Query().With("Certs").Where("id = ?", ID).First(&user)
return user, err
}
// UserDestroy 删除用户
func (s *CertImpl) UserDestroy(ID uint) error {
var cert models.Cert
err := facades.Orm().Query().Where("user_id = ?", ID).First(&cert)
if err != nil {
return err
}
if cert.ID != 0 {
return errors.New("该用户下存在证书,无法删除")
}
_, err = facades.Orm().Query().Delete(&models.CertUser{}, ID)
return err
}
// DNSStore 添加 DNS
func (s *CertImpl) DNSStore(request requests.DNSStore) error {
var dns models.CertDNS
dns.Type = request.Type
dns.Name = request.Name
dns.Data = request.Data
return facades.Orm().Query().Create(&dns)
}
// DNSUpdate 更新 DNS
func (s *CertImpl) DNSUpdate(request requests.DNSUpdate) error {
var dns models.CertDNS
err := facades.Orm().Query().Where("id = ?", request.ID).First(&dns)
if err != nil {
return err
}
dns.Type = request.Type
dns.Name = request.Name
dns.Data = request.Data
return facades.Orm().Query().Save(&dns)
}
// DNSShow 根据 ID 获取 DNS
func (s *CertImpl) DNSShow(ID uint) (models.CertDNS, error) {
var dns models.CertDNS
err := facades.Orm().Query().With("Certs").Where("id = ?", ID).First(&dns)
return dns, err
}
// DNSDestroy 删除 DNS
func (s *CertImpl) DNSDestroy(ID uint) error {
var cert models.Cert
err := facades.Orm().Query().Where("dns_id = ?", ID).First(&cert)
if err != nil {
return err
}
if cert.ID != 0 {
return errors.New("该 DNS 接口下存在证书,无法删除")
}
_, err = facades.Orm().Query().Delete(&models.CertDNS{}, ID)
return err
}
// CertStore 添加证书
func (s *CertImpl) CertStore(request requests.CertStore) error {
var cert models.Cert
cert.Type = request.Type
cert.Domains = request.Domains
cert.AutoRenew = request.AutoRenew
cert.UserID = request.UserID
if request.DNSID != nil {
cert.DNSID = request.DNSID
}
if request.WebsiteID != nil {
cert.WebsiteID = request.WebsiteID
}
return facades.Orm().Query().Create(&cert)
}
// CertUpdate 更新证书
func (s *CertImpl) CertUpdate(request requests.CertUpdate) error {
var cert models.Cert
err := facades.Orm().Query().Where("id = ?", request.ID).First(&cert)
if err != nil {
return err
}
cert.Type = request.Type
cert.Domains = request.Domains
cert.AutoRenew = request.AutoRenew
cert.UserID = request.UserID
if request.DNSID != nil {
cert.DNSID = request.DNSID
}
if request.WebsiteID != nil {
cert.WebsiteID = request.WebsiteID
}
return facades.Orm().Query().Save(&cert)
}
// CertShow 根据 ID 获取证书
func (s *CertImpl) CertShow(ID uint) (models.Cert, error) {
var cert models.Cert
err := facades.Orm().Query().With("User").With("DNS").With("Website").Where("id = ?", ID).First(&cert)
return cert, err
}
// CertDestroy 删除证书
func (s *CertImpl) CertDestroy(ID uint) error {
var cert models.Cert
err := facades.Orm().Query().Where("id = ?", ID).First(&cert)
if err != nil {
return err
}
_, err = facades.Orm().Query().Delete(&models.Cert{}, ID)
return err
}
// ObtainAuto 自动签发证书
func (s *CertImpl) ObtainAuto(ID uint) (certificate.Resource, error) {
var cert models.Cert
err := facades.Orm().Query().With("Website").With("User").With("DNS").Where("id = ?", ID).First(&cert)
if err != nil {
return certificate.Resource{}, err
}
var ca string
switch cert.User.CA {
case "letsencrypt":
ca = acme.CALetEncrypt
case "buypass":
ca = acme.CABuypass
case "zerossl":
ca = acme.CAZeroSSL
case "sslcom":
ca = acme.CASSLcom
case "google":
ca = acme.CAGoogle
}
client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType))
if err != nil {
return certificate.Resource{}, err
}
if cert.DNS != nil {
err = client.UseDns(acme.DnsType(cert.DNS.Type), cert.DNS.Data)
} else {
if cert.Website == nil {
return certificate.Resource{}, errors.New("该证书没有关联网站,无法自动签发")
} else {
err = client.UseHTTP(cert.Website.Path)
}
}
if err != nil {
return certificate.Resource{}, err
}
ssl, err := client.ObtainSSL(cert.Domains)
if err != nil {
return certificate.Resource{}, err
}
cert.CertURL = &ssl.CertURL
cert.Cert = string(ssl.Certificate)
cert.Key = string(ssl.PrivateKey)
err = facades.Orm().Query().Save(&cert)
if err != nil {
return certificate.Resource{}, err
}
if cert.Website != nil {
tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", string(ssl.Certificate), 0644)
tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", string(ssl.PrivateKey), 0644)
tools.Exec("systemctl reload openresty")
}
return ssl, nil
}
// ObtainManual 手动签发证书
func (s *CertImpl) ObtainManual(ID uint) (certificate.Resource, error) {
var cert models.Cert
err := facades.Orm().Query().With("User").Where("id = ?", ID).First(&cert)
if err != nil {
return certificate.Resource{}, err
}
var ca string
switch cert.User.CA {
case "letsencrypt":
ca = acme.CALetEncrypt
case "buypass":
ca = acme.CABuypass
case "zerossl":
ca = acme.CAZeroSSL
case "sslcom":
ca = acme.CASSLcom
case "google":
ca = acme.CAGoogle
}
client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType))
if err != nil {
return certificate.Resource{}, err
}
err = client.UseManualDns()
if err != nil {
return certificate.Resource{}, err
}
ssl, err := client.ObtainSSL(cert.Domains)
if err != nil {
return certificate.Resource{}, err
}
cert.CertURL = &ssl.CertURL
cert.Cert = string(ssl.Certificate)
cert.Key = string(ssl.PrivateKey)
err = facades.Orm().Query().Save(&cert)
if err != nil {
return certificate.Resource{}, err
}
if cert.Website != nil {
tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", string(ssl.Certificate), 0644)
tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", string(ssl.PrivateKey), 0644)
tools.Exec("systemctl reload openresty")
}
return ssl, nil
}
// ManualDNS 获取手动 DNS 解析信息
func (s *CertImpl) ManualDNS(ID uint) (map[string]acme.Resolve, error) {
var cert models.Cert
err := facades.Orm().Query().With("User").Where("id = ?", ID).First(&cert)
if err != nil {
return nil, err
}
var ca string
switch cert.User.CA {
case "letsencrypt":
ca = acme.CALetEncrypt
case "buypass":
ca = acme.CABuypass
case "zerossl":
ca = acme.CAZeroSSL
case "sslcom":
ca = acme.CASSLcom
case "google":
ca = acme.CAGoogle
}
client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType))
if err != nil {
return nil, err
}
err = client.UseManualDns()
if err != nil {
return nil, err
}
return client.GetDNSResolve(cert.Domains)
}
// Renew 续签证书
func (s *CertImpl) Renew(ID uint) (certificate.Resource, error) {
var cert models.Cert
err := facades.Orm().Query().With("Website").With("User").With("DNS").Where("id = ?", ID).First(&cert)
if err != nil {
return certificate.Resource{}, err
}
var ca string
switch cert.User.CA {
case "letsencrypt":
ca = acme.CALetEncrypt
case "buypass":
ca = acme.CABuypass
case "zerossl":
ca = acme.CAZeroSSL
case "sslcom":
ca = acme.CASSLcom
case "google":
ca = acme.CAGoogle
}
client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType))
if err != nil {
return certificate.Resource{}, err
}
if cert.CertURL == nil {
return certificate.Resource{}, errors.New("该证书没有签发成功,无法续签")
}
if cert.DNS != nil {
err = client.UseDns(acme.DnsType(cert.DNS.Type), cert.DNS.Data)
} else {
if cert.Website == nil {
return certificate.Resource{}, errors.New("该证书没有关联网站,无法续签,可以尝试手动签发")
} else {
err = client.UseHTTP(cert.Website.Path)
}
}
if err != nil {
return certificate.Resource{}, err
}
ssl, err := client.RenewSSL(*cert.CertURL)
if err != nil {
return certificate.Resource{}, err
}
cert.CertURL = &ssl.CertURL
cert.Cert = string(ssl.Certificate)
cert.Key = string(ssl.PrivateKey)
err = facades.Orm().Query().Save(&cert)
if err != nil {
return certificate.Resource{}, err
}
if cert.Website != nil {
tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", string(ssl.Certificate), 0644)
tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", string(ssl.PrivateKey), 0644)
tools.Exec("systemctl reload openresty")
}
return ssl, nil
}
+5 -1
View File
@@ -295,10 +295,14 @@ server
// Delete 删除网站
func (r *WebsiteImpl) Delete(id int) error {
var website models.Website
if err := facades.Orm().Query().Where("id", id).FirstOrFail(&website); err != nil {
if err := facades.Orm().Query().With("Cert").Where("id", id).FirstOrFail(&website); err != nil {
return err
}
if website.Cert != nil {
return errors.New("网站" + website.Name + "已绑定SSL证书,请先删除证书")
}
if _, err := facades.Orm().Query().Delete(&website); err != nil {
return err
}
+3
View File
@@ -1,12 +1,15 @@
package bootstrap
import (
"github.com/gookit/validate/locales/zhcn"
"github.com/goravel/framework/foundation"
"panel/config"
)
func Boot() {
zhcn.RegisterGlobal()
app := foundation.NewApplication()
// Bootstrap the application
+2 -2
View File
@@ -1,6 +1,7 @@
package config
import (
"github.com/goravel/fiber"
"github.com/goravel/framework/auth"
"github.com/goravel/framework/cache"
"github.com/goravel/framework/console"
@@ -20,7 +21,6 @@ import (
"github.com/goravel/framework/support/carbon"
"github.com/goravel/framework/testing"
"github.com/goravel/framework/validation"
"github.com/goravel/gin"
"panel/app/providers"
)
@@ -91,7 +91,7 @@ func init() {
&providers.EventServiceProvider{},
&providers.ValidationServiceProvider{},
&providers.DatabaseServiceProvider{},
&gin.ServiceProvider{},
&fiber.ServiceProvider{},
},
})
}
+1 -1
View File
@@ -19,7 +19,7 @@ func init() {
// Here you may define all the cache "stores" for your application as
// well as their drivers. You may even define multiple stores for the
// same cache driver to group types of items stored in your caches.
// Available Drivers: "memory", "redis", "custom"
// Available Drivers: "memory", "custom"
"stores": map[string]any{
"memory": map[string]any{
"driver": "memory",
+1 -1
View File
@@ -14,7 +14,7 @@ func init() {
"connections": map[string]any{
"panel": map[string]any{
"driver": "sqlite",
"database": "/www/panel/database/panel.db",
"database": config.Env("DB_FILE", "/www/panel/database/panel.db"),
"prefix": "",
"singular": false, // Table name is singular
},
+6
View File
@@ -2,6 +2,7 @@ package config
import (
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/path"
)
func init() {
@@ -27,6 +28,11 @@ func init() {
"root": "/",
"url": "http://localhost/",
},
"public": map[string]any{
"driver": "local",
"root": path.Storage("app/public"),
"url": config.Env("APP_URL", "").(string) + "/storage",
},
},
})
}
+1 -1
View File
@@ -23,7 +23,7 @@ func init() {
// factor by 1 doubles the necessary time. After a certain point, the returns on
// hashing time versus attacker time are diminishing, so choose your cost factor wisely.
"bcrypt": map[string]any{
"rounds": 10,
"rounds": 12,
},
// Argon2id Hashing Options
+8 -4
View File
@@ -1,21 +1,23 @@
package config
import (
fiberfacades "github.com/goravel/fiber/facades"
"github.com/goravel/framework/contracts/route"
"github.com/goravel/framework/facades"
ginfacades "github.com/goravel/gin/facades"
)
func init() {
config := facades.Config()
config.Add("http", map[string]any{
// HTTP Driver
"default": "gin",
"default": "fiber",
// HTTP Drivers
"drivers": map[string]any{
"gin": map[string]any{
"fiber": map[string]any{
// prefork mode, see https://docs.gofiber.io/api/fiber/#config
"prefork": false,
"route": func() (route.Route, error) {
return ginfacades.Route("gin"), nil
return fiberfacades.Route("fiber"), nil
},
},
},
@@ -25,6 +27,8 @@ func init() {
"host": config.Env("APP_HOST", "0.0.0.0"),
// HTTP Port
"port": config.Env("APP_PORT", "8888"),
// HTTP Entrance
"entrance": config.Env("APP_ENTRANCE", "/"),
// HTTPS Configuration
"tls": map[string]any{
// HTTPS Host
+1 -1
View File
@@ -8,6 +8,6 @@ func init() {
config := facades.Config()
config.Add("panel", map[string]any{
"name": "耗子Linux面板",
"version": "v2.1.3",
"version": "v2.1.10",
})
}
@@ -0,0 +1 @@
DROP TABLE IF EXISTS cert_users;
@@ -0,0 +1,14 @@
CREATE TABLE cert_users
(
id integer PRIMARY KEY AUTOINCREMENT NOT NULL,
email varchar(255) NOT NULL,
ca varchar(255) NOT NULL,
kid varchar(255) DEFAULT NULL,
hmac_encoded varchar(255) DEFAULT NULL,
private_key text NOT NULL,
key_type varchar(255) NOT NULL,
created_at datetime NOT NULL,
updated_at datetime NOT NULL
);
CREATE INDEX idx_cert_users_email ON cert_users (email);
@@ -0,0 +1 @@
DROP TABLE IF EXISTS cert_dns;
@@ -0,0 +1,9 @@
CREATE TABLE cert_dns
(
id integer PRIMARY KEY AUTOINCREMENT NOT NULL,
name varchar(255) NOT NULL,
type varchar(255) NOT NULL,
data text NOT NULL,
created_at datetime NOT NULL,
updated_at datetime NOT NULL
);
@@ -0,0 +1 @@
DROP TABLE IF EXISTS certs;
@@ -0,0 +1,15 @@
CREATE TABLE certs
(
id integer PRIMARY KEY AUTOINCREMENT NOT NULL,
user_id integer NOT NULL,
website_id integer DEFAULT NULL,
dns_id integer DEFAULT NULL,
type varchar(255) NOT NULL,
domains text NOT NULL,
auto_renew integer DEFAULT 1,
cert_url varchar(255) DEFAULT NULL,
cert text DEFAULT NULL,
key text DEFAULT NULL,
created_at datetime NOT NULL,
updated_at datetime NOT NULL
);
+1775
View File
File diff suppressed because it is too large Load Diff
+1750
View File
File diff suppressed because it is too large Load Diff
+1096
View File
File diff suppressed because it is too large Load Diff
+81 -59
View File
@@ -1,79 +1,89 @@
module panel
go 1.18
go 1.20
require (
github.com/bytedance/sonic v1.10.2
github.com/fasthttp/websocket v1.5.6
github.com/gertd/go-pluralize v0.2.1
github.com/gin-contrib/static v0.0.1
github.com/go-acme/lego/v4 v4.14.2
github.com/gookit/color v1.5.4
github.com/goravel/framework v1.13.2
github.com/goravel/gin v1.1.5
github.com/gorilla/websocket v1.5.0
github.com/gookit/validate v1.5.1
github.com/goravel/fiber v1.1.11-0.20231108081345-36e967f101d0
github.com/goravel/framework v1.13.1-0.20231109083801-f2c86a7c0796
github.com/iancoleman/strcase v0.3.0
github.com/imroc/req/v3 v3.42.1
github.com/mojocn/base64Captcha v1.3.5
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/spf13/cast v1.5.1
github.com/stretchr/testify v1.8.4
github.com/swaggo/files/v2 v2.0.0
github.com/swaggo/swag v1.16.2
github.com/valyala/fasthttp v1.50.0
golang.org/x/crypto v0.14.0
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
)
require (
cloud.google.com/go v0.110.6 // indirect
cloud.google.com/go/compute v1.23.0 // indirect
cloud.google.com/go v0.110.9 // indirect
cloud.google.com/go/compute v1.23.2 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.1.1 // indirect
cloud.google.com/go/iam v1.1.4 // indirect
cloud.google.com/go/pubsub v1.33.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.16 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
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/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae // indirect
github.com/RichardKnop/machinery/v2 v2.0.11 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 // indirect
github.com/andybalholm/brotli v1.0.6 // indirect
github.com/aws/aws-sdk-go v1.37.16 // indirect
github.com/bytedance/sonic v1.10.2 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.0 // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/containerd/continuity v0.4.2 // indirect
github.com/cloudflare/cloudflare-go v0.70.0 // indirect
github.com/containerd/continuity v0.4.3 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // 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/docker/cli v24.0.6+incompatible // indirect
github.com/docker/docker v24.0.6+incompatible // indirect
github.com/docker/cli v24.0.7+incompatible // indirect
github.com/docker/docker v24.0.7+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gaukas/godicttls v0.0.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.21.2 // indirect
github.com/glebarez/sqlite v1.9.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
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.3 // indirect
github.com/go-openapi/jsonpointer v0.20.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/spec v0.20.9 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/go-redsync/redsync/v4 v4.0.4 // indirect
github.com/go-sql-driver/mysql v1.7.1 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gofiber/fiber/v2 v2.50.0 // indirect
github.com/gofiber/template v1.8.2 // indirect
github.com/gofiber/template/html/v2 v2.0.5 // indirect
github.com/gofiber/utils v1.1.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
github.com/golang-migrate/migrate/v4 v4.16.2 // indirect
github.com/golang-module/carbon/v2 v2.2.8 // indirect
github.com/golang-module/carbon/v2 v2.2.12 // indirect
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
@@ -82,22 +92,23 @@ require (
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/gomodule/redigo v2.0.0+incompatible // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/pprof v0.0.0-20230907193218-d3ddc7976beb // indirect
github.com/google/s2a-go v0.1.4 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/google/wire v0.5.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
github.com/googleapis/gax-go/v2 v2.11.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/gookit/filter v1.2.0 // indirect
github.com/gookit/goutil v0.6.12 // indirect
github.com/gookit/validate v1.5.1 // indirect
github.com/gookit/goutil v0.6.14 // indirect
github.com/goravel/file-rotatelogs v0.0.0-20211215053220-2ab31dd9575c // indirect
github.com/goravel/file-rotatelogs/v2 v2.4.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
@@ -107,54 +118,63 @@ require (
github.com/jinzhu/now v1.1.5 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible // 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.16.7 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/klauspost/compress v1.17.2 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/lestrrat-go/strftime v1.0.5 // indirect
github.com/lib/pq v1.10.2 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/microsoft/go-mssqldb v1.1.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/microsoft/go-mssqldb v1.6.0 // indirect
github.com/miekg/dns v1.1.55 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nrdcg/dnspod-go v0.4.0 // indirect
github.com/onsi/ginkgo/v2 v2.12.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/opencontainers/runc v1.1.9 // indirect
github.com/opencontainers/runc v1.1.10 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/ory/dockertest/v3 v3.10.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.3.4 // indirect
github.com/quic-go/quic-go v0.38.1 // indirect
github.com/refraction-networking/utls v1.5.3 // 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.4 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rotisserie/eris v0.5.4 // indirect
github.com/rs/cors v1.10.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.3.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/savioxavier/termlink v1.3.0 // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.10.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.16.0 // indirect
github.com/spf13/viper v1.17.0 // indirect
github.com/streadway/amqp v1.0.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tklauser/go-sysconf v0.3.11 // indirect
github.com/tklauser/numcpus v0.6.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/unrolled/secure v1.13.0 // indirect
github.com/urfave/cli/v2 v2.25.7 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/tcplisten v1.0.0 // 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
@@ -168,29 +188,31 @@ require (
go.mongodb.org/mongo-driver v1.7.5 // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
golang.org/x/arch v0.5.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/arch v0.6.0 // indirect
golang.org/x/image v0.11.0 // indirect
golang.org/x/mod v0.13.0 // indirect
golang.org/x/net v0.16.0 // indirect
golang.org/x/oauth2 v0.10.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.12.0 // indirect
golang.org/x/sync v0.4.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.14.0 // indirect
google.golang.org/api v0.126.0 // indirect
google.golang.org/api v0.143.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/grpc v1.58.2 // indirect
google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.31.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.1 // indirect
gorm.io/driver/mysql v1.5.2 // indirect
gorm.io/driver/postgres v1.5.2 // indirect
gorm.io/driver/sqlserver v1.5.1 // indirect
gorm.io/gorm v1.25.4 // indirect
gorm.io/driver/sqlserver v1.5.2 // indirect
gorm.io/gorm v1.25.5 // indirect
gorm.io/plugin/dbresolver v1.4.7 // indirect
modernc.org/libc v1.22.5 // indirect
modernc.org/mathutil v1.5.0 // indirect
+212 -145
View File
@@ -17,23 +17,23 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q=
cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
cloud.google.com/go v0.110.9 h1:e7ITSqGFFk4rbz/JFIqZh3G4VEHguhAL4BQcFlWtU68=
cloud.google.com/go v0.110.9/go.mod h1:rpxevX/0Lqvlbc88b7Sc1SPNdyK1riNBTUU6JXhYNpM=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
cloud.google.com/go/compute v1.23.2 h1:nWEMDhgbBkBJjfpVySqU4jgWdc22PLR0o4vEexZHers=
cloud.google.com/go/compute v1.23.2/go.mod h1:JJ0atRC0J/oWYiiVBmsSsrRnh92DhZPG4hFDcR04Rns=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y=
cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU=
cloud.google.com/go/kms v1.15.0 h1:xYl5WEaSekKYN5gGRyhjvZKM22GVBBCzegGNVPy+aIs=
cloud.google.com/go/iam v1.1.4 h1:K6n/GZHFTtEoKT5aUG3l9diPi0VduZNQ1PfdnpkkIFk=
cloud.google.com/go/iam v1.1.4/go.mod h1:l/rg8l1AaA+VFMho/HYx2Vv6xinPSLMF8qfhRPIZ0L8=
cloud.google.com/go/kms v1.15.4 h1:gEZzC54ZBI+aeW8/jg9tgz9KR4Aa+WEDPbdGIV3iJ7A=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
@@ -48,15 +48,29 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 h1:/iHxaJhsFr0+xVFfbMr5vxz848jyiWuIEDhYq3y5odY=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0 h1:yfJe15aSwEQ6Oo6J+gdfdulPNoZ3TEhmbhLIoxZcA+U=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0/go.mod h1:Q28U+75mpCaSCDowNEmhIo/rmgdkqmkmzI7N6TGR4UY=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0 h1:T028gtTPiYt/RMUfs8nVsAL7FDQrfLlrm/NnRG/zcC4=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0/go.mod h1:cw4zVQgBby0Z5f2v0itn6se2dDP17nTjbZFXW5uPyHA=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest/adal v0.9.16 h1:P8An8Z9rH1ldbOLdFpxYorgOt2sywL9V24dAwWHPuGc=
github.com/Azure/go-autorest/autorest/adal v0.9.16/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A=
github.com/Azure/go-autorest/autorest v0.11.24 h1:1fIGgHKqVm54KIPT+q8Zmd1QlVsmHqeUGso5qm2BqqE=
github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
@@ -66,8 +80,12 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0 h1:HCc0+LpPfpCKs6LGGLAhwBARt9632unrVcI6i8s/8os=
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
@@ -76,15 +94,16 @@ github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae h1:DcFpTQBYQ9C
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae/go.mod h1:rJJ84PyA/Wlmw1hO+xTzV2wsSUon6J5ktg0g8BF2PuU=
github.com/RichardKnop/machinery/v2 v2.0.11 h1:BTfLGOmOju3W/OtlZmLX26OjYNZsU4PJo04pQReycdc=
github.com/RichardKnop/machinery/v2 v2.0.11/go.mod h1:b5Q6cT/w7YLlIl4Vi+jpdEoyYiqhTgx+0USoKb1wzqU=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 h1:J45/QHgrzUdqe/Vco/Vxk0wRvdS2nKUxmf/zLgvfass=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
github.com/aws/aws-sdk-go v1.37.16 h1:Q4YOP2s00NpB9wfmTDZArdcLRuG9ijbnoAwTW3ivleI=
github.com/aws/aws-sdk-go v1.37.16/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/brianvoe/gofakeit/v6 v6.23.2 h1:lVde18uhad5wII/f5RMVFLtdQNE0HaGFuBUXmYKk8i8=
github.com/brianvoe/gofakeit/v6 v6.24.0 h1:74yq7RRz/noddscZHRS2T84oHZisW9muwbb8sRnU52A=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
@@ -99,40 +118,41 @@ github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo=
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/cloudflare-go v0.70.0 h1:4opGbUygM8DjirUuaz23jn3akuAcnOCEx+0nQtQEcFo=
github.com/cloudflare/cloudflare-go v0.70.0/go.mod h1:VW6GuazkaZ4xEDkFt24lkXQUsE8q7BiGqDniC2s8WEM=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM=
github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8=
github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dhui/dktest v0.3.16 h1:i6gq2YQEtcrjKbeJpBkWjE8MmLZPYllcjOFbTZuPDnw=
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY=
github.com/docker/cli v24.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v24.0.7+incompatible h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg=
github.com/docker/cli v24.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE=
github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@@ -144,51 +164,53 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fasthttp/websocket v1.5.6 h1:4WtWgRJ0Gzj1Ou+xGKy66Ji+a0mUfgAj9ZdPqHiUwQE=
github.com/fasthttp/websocket v1.5.6/go.mod h1:yiKhNx2zFOv65YYtCJNhtl5VjdCFew3W+gt8U/9aFkI=
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw=
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
github.com/gertd/go-pluralize v0.2.1 h1:M3uASbVjMnTsPb0PNqg+E/24Vwigyo/tvyMTtAlLgiA=
github.com/gertd/go-pluralize v0.2.1/go.mod h1:rbYaKDbsXxmRfr8uygAEKhOWsjyrrqrkHVpZvoOp8zk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-contrib/static v0.0.1 h1:JVxuvHPuUfkoul12N7dtQw7KRn/pSMq7Ue1Va9Swm1U=
github.com/gin-contrib/static v0.0.1/go.mod h1:CSxeF+wep05e0kCOsqWdAWbSszmc31zTIbD8TvWl7Hs=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
github.com/glebarez/sqlite v1.9.0 h1:Aj6bPA12ZEx5GbSF6XADmCkYXlljPNUY+Zf1EQxynXs=
github.com/glebarez/sqlite v1.9.0/go.mod h1:YBYCoyupOao60lzp1MVBLEjZfgkq0tdB1voAQ09K9zw=
github.com/go-acme/lego/v4 v4.14.2 h1:/D/jqRgLi8Cbk33sLGtu2pX2jEg3bGJWHyV8kFuUHGM=
github.com/go-acme/lego/v4 v4.14.2/go.mod h1:kBXxbeTg0x9AgaOYjPSwIeJy3Y33zTz+tMD16O4MO6c=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-playground/validator/v10 v10.15.3 h1:S+sSpunYjNPDuXkWbK+x+bA7iXiW296KG4dL3X7xUZo=
github.com/go-playground/validator/v10 v10.15.3/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ=
github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA=
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8=
github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
@@ -232,10 +254,17 @@ github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWe
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw=
github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw=
github.com/gofiber/template v1.8.2 h1:PIv9s/7Uq6m+Fm2MDNd20pAFFKt5wWs7ZBd8iV9pWwk=
github.com/gofiber/template v1.8.2/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
github.com/gofiber/template/html/v2 v2.0.5 h1:BKLJ6Qr940NjntbGmpO3zVa4nFNGDCi/IfUiDB9OC20=
github.com/gofiber/template/html/v2 v2.0.5/go.mod h1:RCF14eLeQDCSUPp0IGc2wbSSDv6yt+V54XB/+Unz+LM=
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
@@ -244,8 +273,8 @@ github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJ
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-migrate/migrate/v4 v4.16.2 h1:8coYbMKUyInrFk1lfGfRovTLAW7PhWp8qQDT2iKfuoA=
github.com/golang-migrate/migrate/v4 v4.16.2/go.mod h1:pfcJX4nPHaVdc5nmdCikFBWtm+UBpiZjRNNsyBbp0/o=
github.com/golang-module/carbon/v2 v2.2.8 h1:a1VxHHKAR7fc1ho7sYXhS1s5S4x7+oqAf2EY5p8C46A=
github.com/golang-module/carbon/v2 v2.2.8/go.mod h1:XDALX7KgqmHk95xyLeaqX9/LJGbfLATyruTziq68SZ8=
github.com/golang-module/carbon/v2 v2.2.12 h1:qydcu64b5+29dOnMNVS27V3JaphyA2eLrUvKv8CNPMI=
github.com/golang-module/carbon/v2 v2.2.12/go.mod h1:XDALX7KgqmHk95xyLeaqX9/LJGbfLATyruTziq68SZ8=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
@@ -282,7 +311,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -305,7 +333,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -323,54 +352,57 @@ github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20230907193218-d3ddc7976beb h1:LCMfzVg3sflxTs4UvuP4D8CkoZnfHLe2qzqgDn/4OHs=
github.com/google/pprof v0.0.0-20230907193218-d3ddc7976beb/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ=
github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4=
github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
github.com/gookit/filter v1.2.0 h1:r7E01dHVkysb5WgzooiGsfblHGShEZCeGcyYM+5IpYU=
github.com/gookit/filter v1.2.0/go.mod h1:bXs9RcB4Blxwny970opiwABeIEqQ/gzOMmHBhKwBdms=
github.com/gookit/goutil v0.6.12 h1:73vPUcTtVGXbhSzBOFcnSB1aJl7Jq9np3RAE50yIDZc=
github.com/gookit/goutil v0.6.12/go.mod h1:g6krlFib8xSe3G1h02IETowOtrUGpAmetT8IevDpvpM=
github.com/gookit/goutil v0.6.14 h1:96elyOG4BvVoDaiT7vx1vHPrVyEtFfYlPPBODR0/FGQ=
github.com/gookit/goutil v0.6.14/go.mod h1:YyDBddefmjS+mU2PDPgCcjVzTDM5WgExiDv5ZA/b8I8=
github.com/gookit/validate v1.5.1 h1:rPp64QZQJM+fysGFAhKpvekQAav4Ok6sjfTs9ZtxcpA=
github.com/gookit/validate v1.5.1/go.mod h1:SskOHUQokzMNt6T3r7N+N/4me/6fxDx+tmoXf/3ZQog=
github.com/goravel/fiber v1.1.11-0.20231108081345-36e967f101d0 h1:mEuJdUWmNXIrg5njcbOS+GucmOM2QrqAHL+ODTgDHPo=
github.com/goravel/fiber v1.1.11-0.20231108081345-36e967f101d0/go.mod h1:IOvZ0G1bojUU/Kv9DZl+/8cwGhcNr0YRhwElz0hpjTg=
github.com/goravel/file-rotatelogs v0.0.0-20211215053220-2ab31dd9575c h1:obhFK91JAhcf7s6h5sggZishm1VyGW/gBCreo+7/SwQ=
github.com/goravel/file-rotatelogs v0.0.0-20211215053220-2ab31dd9575c/go.mod h1:YSWsLXlG16u5CWFaXNZHhEQD10+NwF3xfgDV816OwLE=
github.com/goravel/file-rotatelogs/v2 v2.4.1 h1:ogkeIFcTHSBRUBpZYiyJbpul8hkVXxHPuDbOaP78O1M=
github.com/goravel/file-rotatelogs/v2 v2.4.1/go.mod h1:euk9qr52WrzM8ICs1hecFcR4CZ/ZZOPdacHfvHgbOf0=
github.com/goravel/framework v1.13.2 h1:7BRNihQ/ZNeA384941yg8zRytXRXkkStksXNyyUEgQI=
github.com/goravel/framework v1.13.2/go.mod h1:BOUTIwzhOMyKNXaxSYbVB74NkmbS2xv1W1fPRv9eDws=
github.com/goravel/gin v1.1.5 h1:HWVBxSDHsfpq2lWvuagQVMJlCwznf6ifc71IYEo3sCE=
github.com/goravel/gin v1.1.5/go.mod h1:FxnhnqcMJk/Sk6jA/xcXn6tHtFzayNXeFuw7GnLORMY=
github.com/goravel/framework v1.13.1-0.20231109083801-f2c86a7c0796 h1:4ZqUXMfPP8M10joipSdP9PGgDoVD51vZIcCPds/EyGk=
github.com/goravel/framework v1.13.1-0.20231109083801-f2c86a7c0796/go.mod h1:5jKFbJzfqhaQTP3HCgbyrpnrCyoJjxN5JWSXO96H0iQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA=
github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -405,6 +437,7 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
@@ -414,7 +447,9 @@ github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
@@ -427,24 +462,24 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
github.com/lestrrat-go/strftime v0.0.0-20180821113735-8b31f9c59b0f/go.mod h1:RMlXygAD3c48Psmr06d2G75L4E4xxzxkIe/+ppX9eAU=
@@ -454,13 +489,24 @@ github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/microsoft/go-mssqldb v1.1.0 h1:jsV+tpvcPTbNNKW0o3kiCD69kOHICsfjZ2VcVu2lKYc=
github.com/microsoft/go-mssqldb v1.1.0/go.mod h1:LzkFdl4z2Ck+Hi+ycGOTbL56VEfgoyA2DvYejrNGbRk=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/microsoft/go-mssqldb v1.6.0 h1:mM3gYdVwEPFrlg/Dvr2DNVEgYFG7L42l+dGc67NNNpc=
github.com/microsoft/go-mssqldb v1.6.0/go.mod h1:00mDtPbeQCRGC1HwOOR5K/gr30P1NcEG0vx6Kbv2aJU=
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
@@ -468,7 +514,7 @@ github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
@@ -477,6 +523,9 @@ github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nrdcg/dnspod-go v0.4.0 h1:c/jn1mLZNKF3/osJ6mz3QPxTudvPArXTjpkmYj0uK6U=
github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -497,8 +546,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v1.1.9 h1:XR0VIHTGce5eWPkaPesqTBrhW2yAcaraWfsEalNwQLM=
github.com/opencontainers/runc v1.1.9/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50=
github.com/opencontainers/runc v1.1.10 h1:EaL5WeO9lv9wmS6SASjszOeQdSctvpbu0DdBQBizE40=
github.com/opencontainers/runc v1.1.10/go.mod h1:+/R6+KmDlh+hOO8NkjmgkG9Qzvypzk0yXxAPYYR65+M=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
@@ -509,14 +558,16 @@ github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTK
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
@@ -531,20 +582,28 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo=
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rotisserie/eris v0.5.4 h1:Il6IvLdAapsMhvuOahHWiBnl1G++Q0/L5UIkI5mARSk=
github.com/rotisserie/eris v0.5.4/go.mod h1:Z/kgYTJiJtocxCbFfvRmO+QejApzG6zpyky9G1A4g9s=
github.com/rs/cors v1.10.0 h1:62NOS1h+r8p1mW6FM0FSB0exioXLhd/sh15KpjWBZ+8=
github.com/rs/cors v1.10.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ=
github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/savioxavier/termlink v1.3.0 h1:3Gl4FzQjUyiHzmoEDfmWEhgIwDiJY4poOQHP+k8ReA4=
github.com/savioxavier/termlink v1.3.0/go.mod h1:5T5ePUlWbxCHIwyF8/Ez1qufOoGM89RCg9NvG+3G3gc=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@@ -553,18 +612,18 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY=
github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=
github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg=
github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI=
github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI=
github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo=
github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -586,8 +645,12 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM=
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw=
github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM=
github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04=
github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E=
github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
@@ -597,15 +660,15 @@ github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYm
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/unrolled/secure v1.13.0 h1:sdr3Phw2+f8Px8HE5sd1EHdj1aV3yUwed/uZXChLFsk=
github.com/unrolled/secure v1.13.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
@@ -653,33 +716,34 @@ go.opentelemetry.io/otel v0.17.0/go.mod h1:Oqtdxmf7UtEvL037ohlgnaYa1h7GtMh0NcSd9
go.opentelemetry.io/otel/metric v0.17.0/go.mod h1:hUz9lH1rNXyEwWAhIWCMFWKhYtpASgSnObJFnU26dJ0=
go.opentelemetry.io/otel/oteltest v0.17.0/go.mod h1:JT/LGFxPwpN+nlsTiinSYjdIx3hZIGqHCpChcIZmdoE=
go.opentelemetry.io/otel/trace v0.17.0/go.mod h1:bIujpqg6ZL6xUTubIUgziI1jSaUPthmabA/ygf/6Cfg=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y=
golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc=
golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -723,8 +787,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -754,6 +818,7 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
@@ -768,8 +833,9 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos=
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -780,8 +846,8 @@ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210113205817-d3ed898aa8a3/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4=
golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -822,7 +888,6 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -857,18 +922,21 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -879,17 +947,18 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -975,8 +1044,8 @@ google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.39.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o=
google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw=
google.golang.org/api v0.143.0 h1:o8cekTkqhywkbZT6p1UHJPZ9+9uuCAJs/KYomxZB8fA=
google.golang.org/api v0.143.0/go.mod h1:FoX9DO9hT7DLNn97OuoZAGSDuNAXdJRuGK98rSUgurk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1009,7 +1078,6 @@ google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfG
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
@@ -1024,12 +1092,12 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g=
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8=
google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e h1:z3vDksarJxsAKM5dmEGv0GHwE2hKJ096wZra71Vs4sw=
google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 h1:I6WNifs6pF9tNdSob2W24JtyxIYjzFB9qDlpUC76q+U=
google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJk36UQhjQ89emUzb1mdaHcPeeAh4SCBKznB4=
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k=
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1043,14 +1111,11 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I=
google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1068,39 +1133,41 @@ google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw=
gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o=
gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs=
gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8=
gorm.io/driver/postgres v1.5.2 h1:ytTDxxEv+MplXOfFe3Lzm7SjG09fcdb3Z/c056DTBx0=
gorm.io/driver/postgres v1.5.2/go.mod h1:fmpX0m2I1PKuR7mKZiEluwrP3hbs+ps7JIGMUBpCgl8=
gorm.io/driver/sqlserver v1.5.1 h1:wpyW/pR26U94uaujltiFGXY7fd2Jw5hC9PB1ZF/Y5s4=
gorm.io/driver/sqlserver v1.5.1/go.mod h1:AYHzzte2msKTmYBYsSIq8ZUsznLJwBdkB2wpI+kt0nM=
gorm.io/driver/sqlserver v1.5.2 h1:+o4RQ8w1ohPbADhFqDxeeZnSWjwOcBnxBckjTbcP4wk=
gorm.io/driver/sqlserver v1.5.2/go.mod h1:gaKF0MO0cfTq9Q3/XhkowSw4g6nIwHPGAs4hzKCmvBo=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.2-0.20230610234218-206613868439/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw=
gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/plugin/dbresolver v1.4.7 h1:ZwtwmJQxTx9us7o6zEHFvH1q4OeEo1pooU7efmnunJA=
gorm.io/plugin/dbresolver v1.4.7/go.mod h1:l4Cn87EHLEYuqUncpEeTC2tTJQkjngPSD+lo8hIvcT0=
gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
+12
View File
@@ -22,6 +22,18 @@ import (
"panel/bootstrap"
)
// @title 耗子 Linux 面板 API
// @version 2
// @description 耗子 Linux 面板的 API 信息
// @contact.name 耗子科技
// @contact.email i@haozi.net
// @securityDefinitions.apikey BearerToken
// @in header
// @name Authorization
// @BasePath /api
func main() {
// 启动框架
bootstrap.Boot()
+1
View File
@@ -3,5 +3,6 @@ APP_KEY=
APP_DEBUG=false
APP_HOST=0.0.0.0
APP_PORT=8888
APP_ENTRANCE=/
JWT_SECRET=
+177
View File
@@ -0,0 +1,177 @@
package acme
import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/registration"
)
const (
CALetEncrypt = "https://acme-v02.api.letsencrypt.org/directory"
CAZeroSSL = "https://acme.zerossl.com/v2/DV90"
CAGoogle = "https://dv.acme-v02.api.pki.goog/directory"
CABuypass = "https://api.buypass.com/acme/directory"
CASSLcom = "https://acme.ssl.com/sslcom-dv-rsa"
)
type KeyType = certcrypto.KeyType
const (
KeyEC256 = certcrypto.EC256
KeyEC384 = certcrypto.EC384
KeyRSA2048 = certcrypto.RSA2048
KeyRSA3072 = certcrypto.RSA3072
KeyRSA4096 = certcrypto.RSA4096
)
type domainError struct {
Domain string
Error error
}
type User struct {
Email string
Registration *registration.Resource
Key crypto.PrivateKey
}
func (u *User) GetEmail() string {
return u.Email
}
func (u *User) GetRegistration() *registration.Resource {
return u.Registration
}
func (u *User) GetPrivateKey() crypto.PrivateKey {
return u.Key
}
func GetPrivateKey(priKey crypto.PrivateKey, keyType KeyType) ([]byte, error) {
var marshal []byte
var block *pem.Block
var err error
switch keyType {
case KeyEC256, KeyEC384:
key := priKey.(*ecdsa.PrivateKey)
marshal, err = x509.MarshalECPrivateKey(key)
if err != nil {
return nil, err
}
block = &pem.Block{
Type: "PRIVATE KEY",
Bytes: marshal,
}
case KeyRSA2048, KeyRSA3072, KeyRSA4096:
key := priKey.(*rsa.PrivateKey)
marshal = x509.MarshalPKCS1PrivateKey(key)
block = &pem.Block{
Type: "privateKey",
Bytes: marshal,
}
}
return pem.EncodeToMemory(block), nil
}
func NewRegisterClient(email string, CA string, keyType certcrypto.KeyType) (*Client, error) {
privateKey, err := certcrypto.GeneratePrivateKey(keyType)
if err != nil {
return nil, err
}
user := &User{
Email: email,
Key: privateKey,
}
config := lego.NewConfig(user)
config.CADirURL = CA
config.Certificate.KeyType = keyType
client, err := lego.NewClient(config)
if err != nil {
return nil, err
}
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
if err != nil {
return nil, err
}
user.Registration = reg
acmeClient := &Client{
User: user,
Client: client,
Config: config,
}
return acmeClient, nil
}
func NewRegisterWithExternalAccountBindingClient(email, kid, hmac, CA string, keyType certcrypto.KeyType) (*Client, error) {
privateKey, err := certcrypto.GeneratePrivateKey(keyType)
if err != nil {
return nil, err
}
user := &User{
Email: email,
Key: privateKey,
}
config := lego.NewConfig(user)
config.CADirURL = CA
config.Certificate.KeyType = keyType
client, err := lego.NewClient(config)
if err != nil {
return nil, err
}
reg, err := client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{TermsOfServiceAgreed: true, Kid: kid, HmacEncoded: hmac})
if err != nil {
return nil, err
}
user.Registration = reg
acmeClient := &Client{
User: user,
Client: client,
Config: config,
}
return acmeClient, nil
}
func NewPrivateKeyClient(email string, privateKey string, CA string, keyType certcrypto.KeyType) (*Client, error) {
key, err := certcrypto.ParsePEMPrivateKey([]byte(privateKey))
if err != nil {
return nil, err
}
user := &User{
Email: email,
Key: key,
}
config := lego.NewConfig(user)
config.CADirURL = CA
config.Certificate.KeyType = keyType
client, err := lego.NewClient(config)
if err != nil {
return nil, err
}
reg, err := client.Registration.ResolveAccountByKey()
if err != nil {
return nil, err
}
user.Registration = reg
acmeClient := &Client{
User: user,
Client: client,
Config: config,
}
return acmeClient, nil
}
+191
View File
@@ -0,0 +1,191 @@
package acme
import (
"time"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/providers/dns/alidns"
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
"github.com/go-acme/lego/v4/providers/dns/dnspod"
"github.com/go-acme/lego/v4/providers/http/webroot"
)
type Client struct {
Config *lego.Config
Client *lego.Client
User *User
}
type DnsType string
const (
DnsPod DnsType = "dnspod"
AliYun DnsType = "aliyun"
CloudFlare DnsType = "cloudflare"
)
type DNSParam struct {
ID string `form:"id" json:"id"`
Token string `form:"token" json:"token"`
AccessKey string `form:"access_key" json:"access_key"`
SecretKey string `form:"secret_key" json:"secret_key"`
Email string `form:"email" json:"email"`
APIkey string `form:"api_key" json:"api_key"`
}
// UseDns 使用 DNS 接口验证
func (c *Client) UseDns(dnsType DnsType, param DNSParam) error {
var p challenge.Provider
var err error
if dnsType == DnsPod {
dnsPodConfig := dnspod.NewDefaultConfig()
dnsPodConfig.LoginToken = param.ID + "," + param.Token
p, err = dnspod.NewDNSProviderConfig(dnsPodConfig)
if err != nil {
return err
}
}
if dnsType == AliYun {
aliyunConfig := alidns.NewDefaultConfig()
aliyunConfig.SecretKey = param.SecretKey
aliyunConfig.APIKey = param.AccessKey
p, err = alidns.NewDNSProviderConfig(aliyunConfig)
if err != nil {
return err
}
}
if dnsType == CloudFlare {
cloudflareConfig := cloudflare.NewDefaultConfig()
cloudflareConfig.AuthEmail = param.Email
cloudflareConfig.AuthKey = param.APIkey
p, err = cloudflare.NewDNSProviderConfig(cloudflareConfig)
if err != nil {
return err
}
}
return c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(3*time.Minute))
}
// UseManualDns 使用手动 DNS 验证
func (c *Client) UseManualDns(checkDns ...bool) error {
p := &manualDnsProvider{}
var err error
if len(checkDns) > 0 && !checkDns[0] {
err = c.Client.Challenge.SetDNS01Provider(p, dns01.DisableCompletePropagationRequirement())
} else {
err = c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(3*time.Minute))
}
return err
}
// UseHTTP 使用 HTTP 验证
func (c *Client) UseHTTP(path string) error {
httpProvider, err := webroot.NewHTTPProvider(path)
if err != nil {
return err
}
err = c.Client.Challenge.SetHTTP01Provider(httpProvider)
if err != nil {
return err
}
return nil
}
// ObtainSSL 签发 SSL 证书
func (c *Client) ObtainSSL(domains []string) (certificate.Resource, error) {
request := certificate.ObtainRequest{
Domains: domains,
Bundle: true,
}
certificates, err := c.Client.Certificate.Obtain(request)
if err != nil {
return certificate.Resource{}, err
}
return *certificates, nil
}
// RenewSSL 续签 SSL 证书
func (c *Client) RenewSSL(certUrl string) (certificate.Resource, error) {
certificates, err := c.Client.Certificate.Get(certUrl, true)
if err != nil {
return certificate.Resource{}, err
}
certificates, err = c.Client.Certificate.RenewWithOptions(*certificates, &certificate.RenewOptions{
Bundle: true,
MustStaple: true,
})
if err != nil {
return certificate.Resource{}, err
}
return *certificates, nil
}
// GetDNSResolve 获取 DNS 解析(手动设置)
func (c *Client) GetDNSResolve(domains []string) (map[string]Resolve, error) {
core, err := api.New(c.Config.HTTPClient, c.Config.UserAgent, c.Config.CADirURL, c.User.Registration.URI, c.User.Key)
if err != nil {
return nil, err
}
order, err := core.Orders.New(domains)
if err != nil {
return nil, err
}
resolves := make(map[string]Resolve)
resChan, errChan := make(chan acme.Authorization), make(chan domainError)
for _, authzURL := range order.Authorizations {
go func(authzURL string) {
authz, err := core.Authorizations.Get(authzURL)
if err != nil {
errChan <- domainError{Domain: authz.Identifier.Value, Error: err}
return
}
resChan <- authz
}(authzURL)
}
var responses []acme.Authorization
for i := 0; i < len(order.Authorizations); i++ {
select {
case res := <-resChan:
responses = append(responses, res)
case err := <-errChan:
resolves[err.Domain] = Resolve{Err: err.Error.Error()}
}
}
close(resChan)
close(errChan)
for _, auth := range responses {
domain := challenge.GetTargetedDomain(auth)
acmeChallenge, err := challenge.FindChallenge(challenge.DNS01, auth)
if err != nil {
resolves[domain] = Resolve{Err: err.Error()}
continue
}
keyAuth, err := core.GetKeyAuthorization(acmeChallenge.Token)
if err != nil {
resolves[domain] = Resolve{Err: err.Error()}
continue
}
challengeInfo := dns01.GetChallengeInfo(domain, keyAuth)
resolves[domain] = Resolve{
Key: challengeInfo.FQDN,
Value: challengeInfo.Value,
}
}
return resolves, nil
}
+39
View File
@@ -0,0 +1,39 @@
package acme
import (
"fmt"
"testing"
"github.com/stretchr/testify/suite"
)
type ClientTestSuite struct {
suite.Suite
}
func TestClientTestSuite(t *testing.T) {
suite.Run(t, &ClientTestSuite{})
}
func (s *ClientTestSuite) TestObtainSSL() {
client, err := NewRegisterClient("ci@haozi.net", "https://acme-staging-v02.api.letsencrypt.org/directory", KeyEC256)
s.Nil(err)
err = client.UseDns(DnsPod, DNSParam{
ID: "xxx",
Token: "xxx",
})
s.Nil(err)
err = client.UseManualDns(false)
s.Nil(err)
resolves, err := client.GetDNSResolve([]string{"haozi.dev"})
s.Nil(err)
s.NotNil(resolves)
ssl, err := client.ObtainSSL([]string{"haozi.dev"})
fmt.Println(err.Error())
s.Error(err)
s.NotNil(ssl)
}
+19
View File
@@ -0,0 +1,19 @@
package acme
type Resolve struct {
Key string `json:"key"`
Value string `json:"value"`
Err string `json:"err"`
}
type manualDnsProvider struct {
Resolve *Resolve
}
func (p *manualDnsProvider) Present(domain, token, keyAuth string) error {
return nil
}
func (p *manualDnsProvider) CleanUp(domain, token, keyAuth string) error {
return nil
}
+98
View File
@@ -0,0 +1,98 @@
package acme
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"net"
"time"
)
// GenerateSelfSignedSSL 生成自签名证书
func GenerateSelfSignedSSL(domains []string) ([]byte, []byte, error) {
rootPrivateKey, _ := rsa.GenerateKey(rand.Reader, 4096)
var ip []net.IP
isIP := false
for _, item := range domains {
ipItem := net.ParseIP(item)
if len(ipItem) != 0 {
isIP = true
ip = append(ip, ipItem)
}
}
rootTemplate := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: "HaoZi Panel Root CA"},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(20, 0, 0),
BasicConstraintsValid: true,
IsCA: true,
KeyUsage: x509.KeyUsageCertSign,
}
if isIP {
rootTemplate.IPAddresses = ip
} else {
rootTemplate.DNSNames = domains
}
rootCertBytes, _ := x509.CreateCertificate(rand.Reader, &rootTemplate, &rootTemplate, &rootPrivateKey.PublicKey, rootPrivateKey)
rootCertBlock := &pem.Block{
Type: "CERTIFICATE",
Bytes: rootCertBytes,
}
interPrivateKey, _ := rsa.GenerateKey(rand.Reader, 4096)
interTemplate := x509.Certificate{
SerialNumber: big.NewInt(2),
Subject: pkix.Name{CommonName: "HaoZi Panel Intermediate CA"},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(20, 0, 0),
BasicConstraintsValid: true,
IsCA: true,
KeyUsage: x509.KeyUsageCertSign,
}
if isIP {
interTemplate.IPAddresses = ip
} else {
interTemplate.DNSNames = domains
}
interCertBytes, _ := x509.CreateCertificate(rand.Reader, &interTemplate, &rootTemplate, &interPrivateKey.PublicKey, rootPrivateKey)
interCertBlock := &pem.Block{
Type: "CERTIFICATE",
Bytes: interCertBytes,
}
clientPrivateKey, _ := rsa.GenerateKey(rand.Reader, 4096)
clientTemplate := x509.Certificate{
SerialNumber: big.NewInt(3),
Subject: pkix.Name{CommonName: "HaoZi Panel Client"},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(20, 0, 0),
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
if isIP {
clientTemplate.IPAddresses = ip
} else {
clientTemplate.DNSNames = domains
}
clientCertBytes, _ := x509.CreateCertificate(rand.Reader, &clientTemplate, &interTemplate, &clientPrivateKey.PublicKey, interPrivateKey)
clientCertBlock := &pem.Block{
Type: "CERTIFICATE",
Bytes: clientCertBytes,
}
pemBytes := []byte{}
pemBytes = append(pemBytes, pem.EncodeToMemory(clientCertBlock)...)
pemBytes = append(pemBytes, pem.EncodeToMemory(interCertBlock)...)
pemBytes = append(pemBytes, pem.EncodeToMemory(rootCertBlock)...)
keyBytes := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(clientPrivateKey)})
return pemBytes, keyBytes, nil
}
+22
View File
@@ -0,0 +1,22 @@
package acme
import (
"testing"
"github.com/stretchr/testify/suite"
)
type SSLTestSuite struct {
suite.Suite
}
func TestSSLTestSuite(t *testing.T) {
suite.Run(t, &SSLTestSuite{})
}
func (s *SSLTestSuite) TestGenerateSelfSignedSSL() {
pem, key, err := GenerateSelfSignedSSL([]string{"haozi.dev"})
s.Nil(err)
s.NotNil(pem)
s.NotNil(key)
}
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"fmt"
"io"
"github.com/gorilla/websocket"
"github.com/fasthttp/websocket"
"golang.org/x/crypto/ssh"
)
+51 -24
View File
@@ -14,13 +14,21 @@ import (
// Write 写入文件
func Write(path string, data string, permission os.FileMode) bool {
if err := os.MkdirAll(filepath.Dir(path), permission); err != nil {
facades.Log().Errorf("[面板][Helpers] 创建目录失败: %s", err.Error())
facades.Log().With(map[string]any{
"path": filepath.Dir(path),
"permission": permission,
"error": err.Error(),
}).Tags("面板", "工具函数").Error("创建目录失败")
return false
}
err := os.WriteFile(path, []byte(data), permission)
if err != nil {
facades.Log().Errorf("[面板][Helpers] 写入文件 %s 失败: %s", path, err.Error())
facades.Log().With(map[string]any{
"path": path,
"permission": permission,
"error": err.Error(),
}).Tags("面板", "工具函数").Error("写入文件失败")
return false
}
@@ -31,17 +39,23 @@ func Write(path string, data string, permission os.FileMode) bool {
func Read(path string) string {
data, err := os.ReadFile(path)
if err != nil {
facades.Log().Errorf("[面板][Helpers] 读取文件 %s 失败: %s", path, err.Error())
facades.Log().With(map[string]any{
"path": path,
"error": err.Error(),
}).Tags("面板", "工具函数").Error("读取文件失败")
return ""
}
return string(data)
}
// Remove 删除文件
// Remove 删除文件/目录
func Remove(path string) bool {
if err := os.Remove(path); err != nil {
facades.Log().Errorf("[面板][Helpers] 删除文件 %s 失败: %s", path, err.Error())
if err := os.RemoveAll(path); err != nil {
facades.Log().With(map[string]any{
"path": path,
"error": err.Error(),
}).Tags("面板", "工具函数").Error("删除文件/目录失败")
return false
}
@@ -59,7 +73,10 @@ func Exec(shell string) string {
fmt.Println(err.Error())
panic(err)
} else {
facades.Log().Errorf("[面板][Helpers] 执行命令 %s 失败: %s", shell, err.Error())
facades.Log().With(map[string]any{
"shell": shell,
"error": err.Error(),
}).Tags("面板", "工具函数").Error("执行命令失败")
}
return ""
}
@@ -68,17 +85,11 @@ func Exec(shell string) string {
}
// ExecAsync 异步执行 shell 命令
func ExecAsync(shell string) {
func ExecAsync(shell string) error {
cmd := exec.Command("bash", "-c", shell)
err := cmd.Start()
if err != nil {
if support.Env == support.EnvTest {
fmt.Println(err.Error())
panic(err)
} else {
facades.Log().Errorf("[面板][Helpers] 执行命令 %s 失败: %s", shell, err.Error())
}
return err
}
go func() {
@@ -88,39 +99,56 @@ func ExecAsync(shell string) {
fmt.Println(err.Error())
panic(err)
} else {
facades.Log().Errorf("[面板][Helpers] 执行命令 %s 失败: %s", shell, err.Error())
facades.Log().With(map[string]any{
"shell": shell,
"error": err.Error(),
}).Tags("面板", "工具函数").Error("异步执行命令失败")
}
}
}()
return nil
}
// Mkdir 创建目录
func Mkdir(path string, permission os.FileMode) bool {
if err := os.MkdirAll(path, permission); err != nil {
facades.Log().Errorf("[面板][Helpers] 创建目录 %s 失败: %s", path, err.Error())
facades.Log().With(map[string]any{
"path": path,
"permission": permission,
"error": err.Error(),
}).Tags("面板", "工具函数").Error("创建目录失败")
return false
}
return true
}
// Chmod 修改文件权限
// Chmod 修改文件/目录权限
func Chmod(path string, permission os.FileMode) bool {
if err := os.Chmod(path, permission); err != nil {
facades.Log().Errorf("[面板][Helpers] 修改文件 %s 权限失败: %s", path, err.Error())
facades.Log().With(map[string]any{
"path": path,
"permission": permission,
}).Tags("面板", "工具函数").Error("修改文件/目录权限失败")
return false
}
return true
}
// Chown 修改路径所有者
// Chown 修改文件/目录所有者
func Chown(path, user, group string) bool {
cmd := exec.Command("chown", "-R", user+":"+group, path)
err := cmd.Run()
if err != nil {
facades.Log().Errorf("[面板][Helpers] 修改路径 %s 所有者失败: %s", path, err.Error())
facades.Log().With(map[string]any{
"path": path,
"user": user,
"group": group,
"error": err.Error(),
}).Tags("面板", "工具函数").Error("修改文件/目录所有者失败")
return false
}
@@ -143,20 +171,19 @@ func Empty(path string) bool {
return len(files) == 0
}
// Mv 移动路径
// Mv 移动文件/目录
func Mv(src, dst string) (bool, error) {
cmd := exec.Command("mv", src, dst)
err := cmd.Run()
if err != nil {
facades.Log().Errorf("[面板][Helpers] 移动 %s 到 %s 失败: %s", src, dst, err.Error())
return false, err
}
return true, nil
}
// Cp 复制路径
// Cp 复制文件/目录
func Cp(src, dst string) (bool, error) {
cmd := exec.Command("cp", "-r", src, dst)

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