Compare commits

...

57 Commits

Author SHA1 Message Date
耗子 00815979d2 feat: 发布v2.2.20 2024-07-11 02:59:33 +08:00
耗子 c4e01b37b7 refactor: 使用session鉴权 2024-07-11 02:51:49 +08:00
耗子 7907345521 feat: 更新依赖 2024-07-11 01:08:37 +08:00
耗子 8f32d6cdb0 feat: 优化版本号处理 2024-07-08 04:56:00 +08:00
耗子 c3582903bf fix: CVE-2024-0406 2024-07-08 04:35:52 +08:00
renovate[bot] 7d25d2f5aa chore(deps): Update module golang.org/x/net to v0.27.0 2024-07-05 20:03:32 +00:00
renovate[bot] 46fc7b1c6a chore(deps): Update module golang.org/x/crypto to v0.25.0 2024-07-05 15:11:03 +00:00
耗子 34ac62d1ae fix: lint 2024-07-03 02:03:58 +08:00
耗子 30e78ccf46 feat: 发布v2.2.19 2024-07-03 02:02:38 +08:00
耗子 22f4cc7fcb feat: 支持腾讯云 2024-07-03 01:45:25 +08:00
耗子 1771ae682d Merge remote-tracking branch 'origin/main' 2024-07-02 21:07:19 +08:00
耗子 d1beb60ffd fix: service文件错误 2024-07-02 21:06:57 +08:00
renovate[bot] cba5f86667 chore(deps): Update module github.com/docker/docker to v27.0.3+incompatible 2024-07-02 10:03:44 +00:00
耗子 e1d9832ce0 fix: lint 2024-07-01 15:49:24 +08:00
耗子 6278535902 fix: lint 2024-06-28 23:40:59 +08:00
耗子 32fb87243b feat: 部分软件增加安装校验 2024-06-28 16:41:57 +08:00
耗子 5f85e7fbe0 feat: 添加PostgreSQL的操作方法 2024-06-28 04:01:46 +08:00
耗子 c36104a075 feat: 去掉mysql命令的路径 2024-06-28 03:25:59 +08:00
耗子 583f5d13c7 feat: 优化安装脚本 2024-06-28 03:14:26 +08:00
耗子 b453738daf fix: docker的弃用警告 2024-06-28 03:05:46 +08:00
耗子 98431fb4b2 feat: 发布v2.2.18 2024-06-28 02:53:45 +08:00
耗子 4d76e9286d feat: 删除网站支持删除数据库 2024-06-28 02:52:31 +08:00
耗子 c70758339f fix: build 2024-06-28 01:58:43 +08:00
耗子 ff524cdb0b feat: update dependencies 2024-06-28 01:32:32 +08:00
耗子 73c95d880a Merge remote-tracking branch 'refs/remotes/origin/renovate/github.com-docker-docker-27.x' 2024-06-28 01:28:31 +08:00
耗子 8a1d8c0b05 fix: 优化数据库正则 2024-06-28 01:20:37 +08:00
耗子 2251175906 docs: update readme 2024-06-27 19:13:45 +08:00
机器人 69edde98c8 chore(deps): Update module github.com/docker/docker to v27 2024-06-27 04:07:08 +00:00
耗子 29a3db640d fix: 去掉错误的参数 2024-06-25 18:43:55 +08:00
耗子 ee87bf65b4 feat: 调整部分参数 2024-06-25 18:42:00 +08:00
耗子 aa080dab15 feat: 发布v2.2.17 2024-06-25 03:37:37 +08:00
耗子 b4e5498b48 feat: OpenResty支持返回具体错误 2024-06-25 03:35:35 +08:00
耗子 63838bffa1 feat: phpMyAdmin支持编辑配置 2024-06-25 03:07:38 +08:00
耗子 fa41b18ced fix: 优化查询 2024-06-25 02:51:34 +08:00
耗子 31bd15217b feat: 添加日志记录中间件 2024-06-25 02:38:13 +08:00
耗子 e6b6f06706 feat: 尝试优化内存 2024-06-25 02:08:13 +08:00
耗子 1b836297bf feat: 尝试优化内存 2024-06-25 01:52:41 +08:00
耗子 96bea2cf90 fix: 去掉无用的检查 2024-06-25 01:23:01 +08:00
耗子 8aff513284 fix: 禁止80端口使用HTTPS 2024-06-25 01:13:24 +08:00
耗子 368e41a512 feat: 发布v2.2.16 2024-06-25 01:01:07 +08:00
耗子 924fdee7c5 feat: 支持设置HTTPS端口 2024-06-25 01:00:08 +08:00
耗子 438ba1db1e fix: lint 2024-06-24 23:50:53 +08:00
耗子 d4e52a2d87 feat: ZeroSSL支持自动获取EAB 2024-06-24 23:46:13 +08:00
耗子 4432a12c7a feat: 临时性手段阻止任务重复添加 2024-06-24 23:28:35 +08:00
耗子 a75e36e447 feat: 优化任务 2024-06-24 23:10:39 +08:00
耗子 05674ee04e feat: 优化迁移 2024-06-24 22:53:43 +08:00
耗子 09703761eb fix(文件): 权限设置错误 2024-06-24 22:20:41 +08:00
耗子 dfa14c3fd4 refactor: MySQL改密逻辑 2024-06-24 21:52:46 +08:00
耗子 7b80f034ea feat: 安装前先清理文件 2024-06-24 19:46:43 +08:00
耗子 71923093fd feat(文件): 解压不再判断路径存在 2024-06-24 19:11:01 +08:00
耗子 aae3cf0518 fix: 统一格式 2024-06-24 03:46:21 +08:00
耗子 ed22e79552 feat: 防止密码爆破攻击 2024-06-24 03:45:38 +08:00
耗子 15dd5bb718 feat: 发布v2.2.15 2024-06-23 05:09:30 +08:00
耗子 0e7af2914e feat: 发布v2.2.14 2024-06-23 04:52:18 +08:00
耗子 7ec378829b feat: 优化https证书签发 2024-06-23 04:44:14 +08:00
耗子 f2e8ff86c6 feat: 发布v2.2.13 2024-06-23 03:19:39 +08:00
耗子 eed03d644f fix: test 2024-06-23 03:19:13 +08:00
135 changed files with 1909 additions and 1003 deletions
+5 -1
View File
@@ -2,7 +2,11 @@
[简体中文] | [<a href="README_EN.md">English</a>]
</p>
<h1 align="center">耗子面板</h1>
<h1 align="center" style="font-size: 40px">耗子面板</h1>
<p align="center">
<a href="https://trendshift.io/repositories/10950" target="_blank"><img src="https://trendshift.io/api/badge/repositories/10950" alt="TheTNB%2Fpanel | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
</p>
<p align="center">
<a href="https://github.com/TheTNB/panel/releases"><img src="https://img.shields.io/github/release/TheTNB/panel.svg"></a>
+5 -1
View File
@@ -2,7 +2,11 @@
[<a href="README.md">简体中文</a>] | [English]
</p>
<h1 align="center">Rat Panel</h1>
<h1 align="center" style="font-size: 40px">Rat Panel</h1>
<p align="center">
<a href="https://trendshift.io/repositories/10950" target="_blank"><img src="https://trendshift.io/api/badge/repositories/10950" alt="TheTNB%2Fpanel | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
</p>
<p align="center">
<a href="https://github.com/TheTNB/panel/releases"><img src="https://img.shields.io/github/release/TheTNB/panel.svg"></a>
+2 -2
View File
@@ -46,8 +46,8 @@ func (receiver *Monitoring) Handle(console.Context) error {
// 将等待中的任务分发
// TODO 有bug,需要设计一个锁机制防止重复分发
//task := services.NewTaskImpl()
//_ = task.DispatchWaiting()
task := services.NewTaskImpl()
_ = task.DispatchWaiting()
setting := services.NewSettingImpl()
monitor := setting.Get(models.SettingKeyMonitor)
+3 -2
View File
@@ -15,6 +15,7 @@ import (
"github.com/goravel/framework/support/color"
"github.com/spf13/cast"
requests "github.com/TheTNB/panel/app/http/requests/website"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/io"
@@ -614,7 +615,7 @@ func (receiver *Panel) Handle(ctx console.Context) error {
return nil
}
_, err = website.Add(types.Website{
_, err = website.Add(types.WebsiteAdd{
Name: name,
Status: true,
Domains: domains,
@@ -649,7 +650,7 @@ func (receiver *Panel) Handle(ctx console.Context) error {
return nil
}
if err = website.Delete(id); err != nil {
if err = website.Delete(requests.Delete{ID: id}); err != nil {
color.Red().Printfln(err.Error())
return nil
}
+6
View File
@@ -2,6 +2,8 @@ package commands
import (
"context"
"runtime"
"runtime/debug"
"github.com/goravel/framework/contracts/console"
"github.com/goravel/framework/contracts/console/command"
@@ -68,6 +70,10 @@ func (receiver *PanelTask) Handle(console.Context) error {
return err
}
// 回收内存
runtime.GC()
debug.FreeOSMemory()
types.Status = types.StatusNormal
return nil
}
+4
View File
@@ -73,6 +73,10 @@ func (r *CertController) DNSProviders(ctx http.Context) http.Response {
"name": "DNSPod",
"dns": acme.DnsPod,
},
{
"name": "腾讯云",
"dns": acme.Tencent,
},
{
"name": "阿里云",
"dns": acme.AliYun,
+1 -1
View File
@@ -29,7 +29,7 @@ func Success(ctx http.Context, data any) http.Response {
// Error 响应错误
func Error(ctx http.Context, code int, message string) http.Response {
return ctx.Response().Json(code, &ErrorResponse{
Message: facades.Lang(ctx).Get("messages.mistake") + ": " + message,
Message: message,
})
}
+11 -4
View File
@@ -5,6 +5,7 @@ import (
stdio "io"
stdos "os"
"path/filepath"
"strconv"
"strings"
"syscall"
@@ -354,10 +355,16 @@ func (r *FileController) Permission(ctx http.Context) http.Response {
return sanitize
}
if err := io.Chmod(request.Path, stdos.FileMode(request.Mode)); err != nil {
// 解析成8进制
mode, err := strconv.ParseUint(request.Mode, 8, 64)
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if err := io.Chown(request.Path, request.Owner, request.Group); err != nil {
if err = io.Chmod(request.Path, stdos.FileMode(mode)); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if err = io.Chown(request.Path, request.Owner, request.Group); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
@@ -505,7 +512,7 @@ func (r *FileController) List(ctx http.Context) http.Response {
}
// setPermission
func (r *FileController) setPermission(path string, mode uint, owner, group string) {
_ = io.Chmod(path, stdos.FileMode(mode))
func (r *FileController) setPermission(path string, mode stdos.FileMode, owner, group string) {
_ = io.Chmod(path, mode)
_ = io.Chown(path, owner, group)
}
+24 -7
View File
@@ -8,6 +8,7 @@ import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/hashicorp/go-version"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
@@ -250,13 +251,21 @@ func (r *InfoController) InstalledDbAndPhp(ctx http.Context) http.Response {
// CheckUpdate 检查面板更新
func (r *InfoController) CheckUpdate(ctx http.Context) http.Response {
version := facades.Config().GetString("panel.version")
remote, err := tools.GetLatestPanelVersion()
current := facades.Config().GetString("panel.version")
latest, err := tools.GetLatestPanelVersion()
if err != nil {
return Error(ctx, http.StatusInternalServerError, "获取最新版本失败")
}
if tools.VersionCompare(version, remote.Version, ">=") {
v1, err := version.NewVersion(current)
if err != nil {
return Error(ctx, http.StatusInternalServerError, "版本号解析失败")
}
v2, err := version.NewVersion(latest.Version)
if err != nil {
return Error(ctx, http.StatusInternalServerError, "版本号解析失败")
}
if v1.GreaterThanOrEqual(v2) {
return Success(ctx, http.Json{
"update": false,
})
@@ -269,17 +278,25 @@ func (r *InfoController) CheckUpdate(ctx http.Context) http.Response {
// UpdateInfo 获取更新信息
func (r *InfoController) UpdateInfo(ctx http.Context) http.Response {
version := facades.Config().GetString("panel.version")
current, err := tools.GetLatestPanelVersion()
current := facades.Config().GetString("panel.version")
latest, err := tools.GetLatestPanelVersion()
if err != nil {
return Error(ctx, http.StatusInternalServerError, "获取最新版本失败")
}
if tools.VersionCompare(version, current.Version, ">=") {
v1, err := version.NewVersion(current)
if err != nil {
return Error(ctx, http.StatusInternalServerError, "版本号解析失败")
}
v2, err := version.NewVersion(latest.Version)
if err != nil {
return Error(ctx, http.StatusInternalServerError, "版本号解析失败")
}
if v1.GreaterThanOrEqual(v2) {
return Error(ctx, http.StatusInternalServerError, "当前版本已是最新版本")
}
versions, err := tools.GenerateVersions(version, current.Version)
versions, err := tools.GenerateVersions(current, latest.Version)
if err != nil {
return Error(ctx, http.StatusInternalServerError, "获取更新信息失败")
}
+99 -151
View File
@@ -1,7 +1,6 @@
package plugins
import (
"database/sql"
"fmt"
"regexp"
@@ -12,6 +11,7 @@ import (
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/db"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/str"
@@ -71,7 +71,7 @@ func (r *MySQLController) Load(ctx http.Context) http.Response {
return controllers.Success(ctx, []types.NV{})
}
raw, err := shell.Execf("/www/server/mysql/bin/mysqladmin -uroot -p" + rootPassword + " extended-status 2>&1")
raw, err := shell.Execf("mysqladmin -uroot -p" + rootPassword + " extended-status 2>&1")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL负载失败")
}
@@ -175,80 +175,45 @@ func (r *MySQLController) GetRootPassword(ctx http.Context) http.Response {
// SetRootPassword 设置root密码
func (r *MySQLController) SetRootPassword(ctx http.Context) http.Response {
status, err := systemctl.Status("mysqld")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
}
if !status {
return controllers.Error(ctx, http.StatusInternalServerError, "MySQL 未运行")
}
rootPassword := ctx.Request().Input("password")
if len(rootPassword) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码不能为空")
}
oldRootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
if oldRootPassword != rootPassword {
if _, err = shell.Execf(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '%s';"`, oldRootPassword, rootPassword)); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("设置root密码失败: %v", err))
mysql, err := db.NewMySQL("root", oldRootPassword, r.getSock(), "unix")
if err != nil {
// 尝试安全模式直接改密
if err = db.MySQLResetRootPassword(rootPassword); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if _, err = shell.Execf(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "FLUSH PRIVILEGES;"`, rootPassword)); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "设置root密码失败")
}
if err = r.setting.Set(models.SettingKeyMysqlRootPassword, rootPassword); err != nil {
_, _ = shell.Execf(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '%s';"`, rootPassword, oldRootPassword))
_, _ = shell.Execf(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "FLUSH PRIVILEGES;"`, oldRootPassword))
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("设置保存失败: %v", err))
} else {
if err = mysql.UserPassword("root", rootPassword); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
}
if err = r.setting.Set(models.SettingKeyMysqlRootPassword, rootPassword); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("设置保存失败: %v", err))
}
return controllers.Success(ctx, nil)
}
// DatabaseList 获取数据库列表
func (r *MySQLController) DatabaseList(ctx http.Context) http.Response {
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
type database struct {
Name string `json:"name"`
}
db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/")
password := r.setting.Get(models.SettingKeyMysqlRootPassword)
mysql, err := db.NewMySQL("root", password, r.getSock(), "unix")
if err != nil {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []database{},
})
}
defer db.Close()
if err = db.Ping(); err != nil {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []database{},
"items": []types.MySQLDatabase{},
})
}
rows, err := db.Query("SHOW DATABASES")
databases, err := mysql.Databases()
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
defer rows.Close()
var databases []database
for rows.Next() {
var d database
if err = rows.Scan(&d.Name); err != nil {
continue
}
databases = append(databases, d)
}
if err = rows.Err(); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取数据库列表失败")
}
paged, total := controllers.Paginate(ctx, databases)
return controllers.Success(ctx, http.Json{
@@ -260,9 +225,9 @@ func (r *MySQLController) DatabaseList(ctx http.Context) http.Response {
// AddDatabase 添加数据库
func (r *MySQLController) AddDatabase(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:255",
"database": "required|min_len:1|max_len:64|regex:^[a-zA-Z0-9_]+$",
"user": "required|min_len:1|max_len:32|regex:^[a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:32",
}); sanitize != nil {
return sanitize
}
@@ -272,17 +237,18 @@ func (r *MySQLController) AddDatabase(ctx http.Context) http.Response {
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE DATABASE IF NOT EXISTS " + database + " DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;\""); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
mysql, err := db.NewMySQL("root", rootPassword, r.getSock(), "unix")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\""); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
if err = mysql.DatabaseCreate(database); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\""); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
if err = mysql.UserCreate(user, password); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
if err = mysql.PrivilegesGrant(user, database); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
@@ -291,15 +257,19 @@ func (r *MySQLController) AddDatabase(ctx http.Context) http.Response {
// DeleteDatabase 删除数据库
func (r *MySQLController) DeleteDatabase(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$|not_in:information_schema,mysql,performance_schema,sys",
"database": "required|min_len:1|max_len:64|regex:^[a-zA-Z0-9_]+$|not_in:information_schema,mysql,performance_schema,sys",
}); sanitize != nil {
return sanitize
}
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
database := ctx.Request().Input("database")
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP DATABASE IF EXISTS " + database + ";\""); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
mysql, err := db.NewMySQL("root", rootPassword, r.getSock(), "unix")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err = mysql.DatabaseDrop(database); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
@@ -346,7 +316,7 @@ func (r *MySQLController) UploadBackup(ctx http.Context) http.Response {
// CreateBackup 创建备份
func (r *MySQLController) CreateBackup(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$|not_in:information_schema,mysql,performance_schema,sys",
"database": "required|min_len:1|max_len:64|regex:^[a-zA-Z0-9_]+$|not_in:information_schema,mysql,performance_schema,sys",
}); sanitize != nil {
return sanitize
}
@@ -380,7 +350,7 @@ func (r *MySQLController) DeleteBackup(ctx http.Context) http.Response {
func (r *MySQLController) RestoreBackup(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"backup": "required|min_len:1|max_len:255",
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$|not_in:information_schema,mysql,performance_schema,sys",
"database": "required|min_len:1|max_len:64|regex:^[a-zA-Z0-9_]+$|not_in:information_schema,mysql,performance_schema,sys",
}); sanitize != nil {
return sanitize
}
@@ -394,71 +364,20 @@ func (r *MySQLController) RestoreBackup(ctx http.Context) http.Response {
// UserList 用户列表
func (r *MySQLController) UserList(ctx http.Context) http.Response {
type user struct {
User string `json:"user"`
Host string `json:"host"`
Grants []string `json:"grants"`
}
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/")
password := r.setting.Get(models.SettingKeyMysqlRootPassword)
mysql, err := db.NewMySQL("root", password, r.getSock(), "unix")
if err != nil {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []user{},
})
}
defer db.Close()
if err = db.Ping(); err != nil {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []user{},
"items": []types.MySQLUser{},
})
}
rows, err := db.Query("SELECT user, host FROM mysql.user")
users, err := mysql.Users()
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
defer rows.Close()
var userGrants []user
for rows.Next() {
var u user
if err = rows.Scan(&u.User, &u.Host); err != nil {
continue
}
// 查询用户权限
grantsRows, err := db.Query(fmt.Sprintf("SHOW GRANTS FOR '%s'@'%s'", u.User, u.Host))
if err != nil {
continue
}
defer grantsRows.Close()
for grantsRows.Next() {
var grant string
if err = grantsRows.Scan(&grant); err != nil {
continue
}
u.Grants = append(u.Grants, grant)
}
if err = grantsRows.Err(); err != nil {
continue
}
userGrants = append(userGrants, u)
}
if err = rows.Err(); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取用户列表失败")
}
paged, total := controllers.Paginate(ctx, userGrants)
paged, total := controllers.Paginate(ctx, users)
return controllers.Success(ctx, http.Json{
"total": total,
@@ -469,9 +388,9 @@ func (r *MySQLController) UserList(ctx http.Context) http.Response {
// AddUser 添加用户
func (r *MySQLController) AddUser(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:255",
"database": "required|min_len:1|max_len:64|regex:^[a-zA-Z0-9_]+$",
"user": "required|min_len:1|max_len:32|regex:^[a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:32",
}); sanitize != nil {
return sanitize
}
@@ -480,14 +399,15 @@ func (r *MySQLController) AddUser(ctx http.Context) http.Response {
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
database := ctx.Request().Input("database")
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + ";'\""); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
mysql, err := db.NewMySQL("root", rootPassword, r.getSock(), "unix")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\""); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
if err = mysql.UserCreate(user, password); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
if err = mysql.PrivilegesGrant(user, database); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
@@ -496,15 +416,19 @@ func (r *MySQLController) AddUser(ctx http.Context) http.Response {
// DeleteUser 删除用户
func (r *MySQLController) DeleteUser(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"user": "required|min_len:1|max_len:32|regex:^[a-zA-Z0-9_]+$",
}); sanitize != nil {
return sanitize
}
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP USER '" + user + "'@'localhost';\""); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
mysql, err := db.NewMySQL("root", rootPassword, r.getSock(), "unix")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err = mysql.UserDrop(user); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
@@ -513,8 +437,8 @@ func (r *MySQLController) DeleteUser(ctx http.Context) http.Response {
// SetUserPassword 设置用户密码
func (r *MySQLController) SetUserPassword(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:255",
"user": "required|min_len:1|max_len:32|regex:^[a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:32",
}); sanitize != nil {
return sanitize
}
@@ -522,11 +446,12 @@ func (r *MySQLController) SetUserPassword(ctx http.Context) http.Response {
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\""); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
mysql, err := db.NewMySQL("root", rootPassword, r.getSock(), "unix")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
if err = mysql.UserPassword(user, password); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
@@ -535,8 +460,8 @@ func (r *MySQLController) SetUserPassword(ctx http.Context) http.Response {
// SetUserPrivileges 设置用户权限
func (r *MySQLController) SetUserPrivileges(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"database": "required|min_len:1|max_len:255",
"user": "required|min_len:1|max_len:32|regex:^[a-zA-Z0-9_]+$",
"database": "required|min_len:1|max_len:64|regex:^[a-zA-Z0-9_]+$",
}); sanitize != nil {
return sanitize
}
@@ -544,15 +469,38 @@ func (r *MySQLController) SetUserPrivileges(ctx http.Context) http.Response {
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
database := ctx.Request().Input("database")
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"REVOKE ALL PRIVILEGES ON *.* FROM '" + user + "'@'localhost';\""); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
mysql, err := db.NewMySQL("root", rootPassword, r.getSock(), "unix")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\""); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
if err = mysql.PrivilegesGrant(user, database); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// getSock 获取sock文件位置
func (r *MySQLController) getSock() string {
if io.Exists("/tmp/mysql.sock") {
return "/tmp/mysql.sock"
}
if io.Exists("/www/server/mysql/config/my.cnf") {
config, _ := io.Read("/www/server/mysql/config/my.cnf")
re := regexp.MustCompile(`socket\s*=\s*(['"]?)([^'"]+)`)
matches := re.FindStringSubmatch(config)
if len(matches) > 2 {
return matches[2]
}
}
if io.Exists("/etc/my.cnf") {
config, _ := io.Read("/etc/my.cnf")
re := regexp.MustCompile(`socket\s*=\s*(['"]?)([^'"]+)`)
matches := re.FindStringSubmatch(config)
if len(matches) > 2 {
return matches[2]
}
}
return "/tmp/mysql.sock"
}
@@ -1,6 +1,7 @@
package plugins
import (
"fmt"
"regexp"
"time"
@@ -61,7 +62,8 @@ func (r *OpenRestyController) SaveConfig(ctx http.Context) http.Response {
}
if err := systemctl.Reload("openresty"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "重载服务失败")
_, err = shell.Execf("openresty -t")
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("重载服务失败: %v", err))
}
return controllers.Success(ctx, nil)
@@ -1,6 +1,7 @@
package plugins
import (
"fmt"
"regexp"
"strings"
@@ -87,8 +88,39 @@ func (r *PhpMyAdminController) SetPort(ctx http.Context) http.Response {
}
}
if err := systemctl.Reload("openresty"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "重载OpenResty失败")
if err = systemctl.Reload("openresty"); err != nil {
_, err = shell.Execf("openresty -t")
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("重载OpenResty失败: %v", err))
}
return controllers.Success(ctx, nil)
}
func (r *PhpMyAdminController) GetConfig(ctx http.Context) http.Response {
config, err := io.Read("/www/server/vhost/phpmyadmin.conf")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, config)
}
func (r *PhpMyAdminController) SaveConfig(ctx http.Context) http.Response {
config := ctx.Request().Input("config")
if len(config) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "配置不能为空")
}
if err := io.Write("/www/server/vhost/phpmyadmin.conf", config, 0644); err != nil {
facades.Log().Request(ctx.Request()).Tags("插件", "phpMyAdmin").With(map[string]any{
"error": err.Error(),
}).Info("修改 phpMyAdmin 配置失败")
return controllers.ErrorSystem(ctx)
}
if err := systemctl.Reload("openresty"); err != nil {
_, err = shell.Execf("openresty -t")
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("重载OpenResty失败: %v", err))
}
return controllers.Success(ctx, nil)
@@ -202,9 +202,9 @@ func (r *PostgreSQLController) DatabaseList(ctx http.Context) http.Response {
// AddDatabase 添加数据库
func (r *PostgreSQLController) AddDatabase(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:255",
"database": "required|min_len:1|max_len:63|regex:^[a-zA-Z0-9_]+$",
"user": "required|min_len:1|max_len:30|regex:^[a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:40",
}); sanitize != nil {
return sanitize
}
@@ -241,7 +241,7 @@ func (r *PostgreSQLController) AddDatabase(ctx http.Context) http.Response {
// DeleteDatabase 删除数据库
func (r *PostgreSQLController) DeleteDatabase(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$|not_in:postgres,template0,template1",
"database": "required|min_len:1|max_len:63|regex:^[a-zA-Z0-9_]+$|not_in:postgres,template0,template1",
}); sanitize != nil {
return sanitize
}
@@ -295,7 +295,7 @@ func (r *PostgreSQLController) UploadBackup(ctx http.Context) http.Response {
// CreateBackup 创建备份
func (r *PostgreSQLController) CreateBackup(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$|not_in:information_schema,mysql,performance_schema,sys",
"database": "required|min_len:1|max_len:63|regex:^[a-zA-Z0-9_]+$|not_in:postgres,template0,template1",
}); sanitize != nil {
return sanitize
}
@@ -329,7 +329,7 @@ func (r *PostgreSQLController) DeleteBackup(ctx http.Context) http.Response {
func (r *PostgreSQLController) RestoreBackup(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"backup": "required|min_len:1|max_len:255",
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$|not_in:information_schema,mysql,performance_schema,sys",
"database": "required|min_len:1|max_len:63|regex:^[a-zA-Z0-9_]+$|not_in:postgres,template0,template1",
}); sanitize != nil {
return sanitize
}
@@ -420,9 +420,9 @@ func (r *PostgreSQLController) RoleList(ctx http.Context) http.Response {
// AddRole 添加角色
func (r *PostgreSQLController) AddRole(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:255",
"database": "required|min_len:1|max_len:63|regex:^[a-zA-Z0-9_]+$",
"user": "required|min_len:1|max_len:30|regex:^[a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:40",
}); sanitize != nil {
return sanitize
}
@@ -452,7 +452,7 @@ func (r *PostgreSQLController) AddRole(ctx http.Context) http.Response {
// DeleteRole 删除角色
func (r *PostgreSQLController) DeleteRole(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"user": "required|min_len:1|max_len:30|regex:^[a-zA-Z0-9_]+$",
}); sanitize != nil {
return sanitize
}
@@ -475,8 +475,8 @@ func (r *PostgreSQLController) DeleteRole(ctx http.Context) http.Response {
// SetRolePassword 设置用户密码
func (r *PostgreSQLController) SetRolePassword(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:255",
"user": "required|min_len:1|max_len:30|regex:^[a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:40",
}); sanitize != nil {
return sanitize
}
+10 -11
View File
@@ -3,6 +3,7 @@ package controllers
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/path"
"github.com/spf13/cast"
requests "github.com/TheTNB/panel/app/http/requests/setting"
@@ -44,13 +45,10 @@ func (r *SettingController) List(ctx http.Context) http.Response {
return ErrorSystem(ctx)
}
userID := cast.ToUint(ctx.Value("user_id"))
var user models.User
err = facades.Auth(ctx).User(&user)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{
"error": err.Error(),
}).Info("获取用户信息失败")
return ErrorSystem(ctx)
if err = facades.Orm().Query().Where("id", userID).Get(&user); err != nil {
return Error(ctx, http.StatusInternalServerError, "获取用户信息失败")
}
port, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
@@ -128,11 +126,12 @@ func (r *SettingController) Update(ctx http.Context) http.Response {
return ErrorSystem(ctx)
}
userID := cast.ToUint(ctx.Value("user_id"))
var user models.User
err = facades.Auth(ctx).User(&user)
if err != nil {
return ErrorSystem(ctx)
if err = facades.Orm().Query().Where("id", userID).Get(&user); err != nil {
return Error(ctx, http.StatusInternalServerError, "获取用户信息失败")
}
user.Username = updateRequest.UserName
user.Email = updateRequest.Email
if len(updateRequest.Password) > 0 {
@@ -270,10 +269,10 @@ func (r *SettingController) UpdateHttps(ctx http.Context) http.Response {
if _, err := cert.ParseKey(httpsRequest.Key); err != nil {
return Error(ctx, http.StatusBadRequest, "密钥格式错误")
}
if err := io.Write(facades.App().ExecutablePath("storage/ssl.crt"), httpsRequest.Cert, 0700); err != nil {
if err := io.Write(path.Executable("storage/ssl.crt"), httpsRequest.Cert, 0700); err != nil {
return ErrorSystem(ctx)
}
if err := io.Write(facades.App().ExecutablePath("storage/ssl.key"), httpsRequest.Key, 0700); err != nil {
if err := io.Write(path.Executable("storage/ssl.key"), httpsRequest.Key, 0700); err != nil {
return ErrorSystem(ctx)
}
if out, err := shell.Execf("sed -i 's/APP_SSL=false/APP_SSL=true/g' /www/panel/panel.conf"); err != nil {
@@ -2,7 +2,6 @@ package controllers
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/swaggo/http-swagger/v2"
_ "github.com/TheTNB/panel/docs"
@@ -25,10 +24,6 @@ func NewSwaggerController() *SwaggerController {
// @Failure 500
// @Router /swagger [get]
func (r *SwaggerController) Index(ctx http.Context) http.Response {
if !facades.Config().GetBool("app.debug") {
return Error(ctx, http.StatusNotFound, http.StatusText(http.StatusNotFound))
}
handler := httpSwagger.Handler()
handler(ctx.Response().Writer(), ctx.Request().Origin())
+36 -28
View File
@@ -1,8 +1,11 @@
package controllers
import (
"fmt"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/spf13/cast"
"github.com/TheTNB/panel/app/http/requests/user"
"github.com/TheTNB/panel/app/models"
@@ -20,16 +23,15 @@ func NewUserController() *UserController {
// Login
//
// @Summary 登录
// @Description 通过用户名和密码获取访问令牌
// @Tags 用户鉴权
// @Accept json
// @Produce json
// @Param data body requests.Login true "request"
// @Success 200 {object} SuccessResponse
// @Failure 403 {object} ErrorResponse "用户名或密码错误"
// @Failure 500 {object} ErrorResponse "系统内部错误
// @Router /panel/user/login [post]
// @Summary 登录
// @Tags 用户鉴权
// @Accept json
// @Produce json
// @Param data body requests.Login true "request"
// @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.Login
sanitize := SanitizeRequest(ctx, &loginRequest)
@@ -60,32 +62,38 @@ func (r *UserController) Login(ctx http.Context) http.Response {
}
}
token, loginErr := facades.Auth(ctx).LoginUsingID(user.ID)
if loginErr != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "用户").With(map[string]any{
"error": err.Error(),
}).Info("登录失败")
return ErrorSystem(ctx)
ctx.Request().Session().Put("user_id", user.ID)
return Success(ctx, nil)
}
// Logout
//
// @Summary 登出
// @Tags 用户鉴权
// @Produce json
// @Security BearerToken
// @Success 200 {object} SuccessResponse
// @Router /panel/user/logout [post]
func (r *UserController) Logout(ctx http.Context) http.Response {
if err := ctx.Request().Session().Invalidate(); err != nil {
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("登出失败: %s", err.Error()))
}
return Success(ctx, http.Json{
"access_token": token,
})
return Success(ctx, nil)
}
// Info
//
// @Summary 用户信息
// @Description 获取当前登录用户信息
// @Tags 用户鉴权
// @Produce json
// @Security BearerToken
// @Success 200 {object} SuccessResponse
// @Router /panel/user/info [get]
// @Summary 用户信息
// @Tags 用户鉴权
// @Produce json
// @Security BearerToken
// @Success 200 {object} SuccessResponse
// @Router /panel/user/info [get]
func (r *UserController) Info(ctx http.Context) http.Response {
userID := cast.ToUint(ctx.Value("user_id"))
var user models.User
err := facades.Auth(ctx).User(&user)
if err != nil {
if err := facades.Orm().Query().Where("id", userID).Get(&user); err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "用户").With(map[string]any{
"error": err.Error(),
}).Info("获取用户信息失败")
+24 -16
View File
@@ -14,6 +14,7 @@ import (
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/str"
"github.com/TheTNB/panel/pkg/systemctl"
"github.com/TheTNB/panel/pkg/types"
@@ -84,7 +85,7 @@ func (r *WebsiteController) Add(ctx http.Context) http.Response {
addRequest.Path = r.setting.Get(models.SettingKeyWebsitePath) + "/" + addRequest.Name
}
website := types.Website{
website := types.WebsiteAdd{
Name: addRequest.Name,
Status: true,
Domains: addRequest.Domains,
@@ -117,23 +118,22 @@ func (r *WebsiteController) Add(ctx http.Context) http.Response {
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "网站 ID"
// @Success 200 {object} SuccessResponse
// @Router /panel/websites/{id} [delete]
// @Param data body requests.Delete true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/websites/delete [post]
func (r *WebsiteController) Delete(ctx http.Context) http.Response {
var idRequest requests.ID
sanitize := SanitizeRequest(ctx, &idRequest)
var deleteRequest requests.Delete
sanitize := SanitizeRequest(ctx, &deleteRequest)
if sanitize != nil {
return sanitize
}
err := r.website.Delete(idRequest.ID)
if err != nil {
if err := r.website.Delete(deleteRequest); err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "网站管理").With(map[string]any{
"id": idRequest.ID,
"id": deleteRequest.ID,
"error": err.Error(),
}).Info("删除网站失败")
return Error(ctx, http.StatusInternalServerError, "删除网站失败: "+err.Error())
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
@@ -202,7 +202,7 @@ func (r *WebsiteController) SaveDefaultConfig(ctx http.Context) http.Response {
// @Produce json
// @Security BearerToken
// @Param id path int true "网站 ID"
// @Success 200 {object} SuccessResponse{data=types.Website}
// @Success 200 {object} SuccessResponse{data=types.WebsiteAdd}
// @Router /panel/websites/{id}/config [get]
func (r *WebsiteController) GetConfig(ctx http.Context) http.Response {
var idRequest requests.ID
@@ -549,9 +549,12 @@ server
# waf标记位结束
# 错误页配置,可自行设置
#error_page 404 /404.html;
error_page 404 /404.html;
#error_page 502 /502.html;
# acme证书签发配置,不可修改
include /www/server/vhost/acme/%s.conf;
# 伪静态规则引入,修改后将导致面板设置的伪静态规则失效
include /www/server/vhost/rewrite/%s.conf;
@@ -572,15 +575,19 @@ server
error_log /www/wwwlogs/%s.log;
}
`, website.Path, website.Php, website.Name, website.Name, website.Name)
`, website.Path, website.Php, website.Name, website.Name, website.Name, website.Name)
if err := io.Write("/www/server/vhost/"+website.Name+".conf", raw, 0644); err != nil {
return nil
}
if err := io.Write("/www/server/vhost/rewrite"+website.Name+".conf", "", 0644); err != nil {
if err := io.Write("/www/server/vhost/rewrite/"+website.Name+".conf", "", 0644); err != nil {
return nil
}
if err := io.Write("/www/server/vhost/acme/"+website.Name+".conf", "", 0644); err != nil {
return nil
}
if err := systemctl.Reload("openresty"); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
_, err = shell.Execf("openresty -t")
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("重载OpenResty失败: %v", err))
}
return Success(ctx, nil)
@@ -646,7 +653,8 @@ func (r *WebsiteController) Status(ctx http.Context) http.Response {
return ErrorSystem(ctx)
}
if err = systemctl.Reload("openresty"); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
_, err = shell.Execf("openresty -t")
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("重载OpenResty失败: %v", err))
}
return Success(ctx, nil)
+3
View File
@@ -2,6 +2,7 @@ package http
import (
"github.com/goravel/framework/contracts/http"
sessionmiddleware "github.com/goravel/framework/session/middleware"
"github.com/TheTNB/panel/app/http/middleware"
)
@@ -13,6 +14,8 @@ type Kernel struct {
// These middleware are run during every request to your application.
func (kernel Kernel) Middleware() []http.Middleware {
return []http.Middleware{
sessionmiddleware.StartSession(),
middleware.Log(),
middleware.Status(),
}
}
-47
View File
@@ -1,47 +0,0 @@
package middleware
import (
"errors"
"github.com/goravel/framework/auth"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
)
// Jwt 确保通过 JWT 鉴权
func Jwt() http.Middleware {
return func(ctx http.Context) {
translate := facades.Lang(ctx)
token := ctx.Request().Header("Authorization", ctx.Request().Header("Sec-WebSocket-Protocol"))
if len(token) == 0 {
ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{
"message": translate.Get("auth.token.missing"),
})
return
}
// JWT 鉴权
if _, err := facades.Auth(ctx).Parse(token); err != nil {
if errors.Is(err, auth.ErrorTokenExpired) {
token, err = facades.Auth(ctx).Refresh()
if err != nil {
// 到达刷新时间上限
ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{
"message": translate.Get("auth.token.expired"),
})
return
}
token = "Bearer " + token
} else {
ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{
"message": translate.Get("auth.token.expired"),
})
return
}
}
ctx.Response().Header("Authorization", token)
ctx.Request().Next()
}
}
+20
View File
@@ -0,0 +1,20 @@
package middleware
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
)
// Log 记录请求日志
func Log() http.Middleware {
return func(ctx http.Context) {
facades.Log().Channel("http").With(map[string]any{
"Method": ctx.Request().Method(),
"URL": ctx.Request().FullUrl(),
"IP": ctx.Request().Ip(),
"UA": ctx.Request().Header("User-Agent"),
"Body": ctx.Request().All(),
}).Info("HTTP Request")
ctx.Request().Next()
}
}
+39
View File
@@ -0,0 +1,39 @@
package middleware
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/spf13/cast"
)
// Session 确保通过 JWT 鉴权
func Session() http.Middleware {
return func(ctx http.Context) {
translate := facades.Lang(ctx)
if !ctx.Request().HasSession() {
ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{
"message": translate.Get("auth.session.missing"),
})
return
}
if ctx.Request().Session().Missing("user_id") {
ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{
"message": translate.Get("auth.session.expired"),
})
return
}
userID := cast.ToUint(ctx.Request().Session().Get("user_id"))
if userID == 0 {
ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{
"message": translate.Get("auth.session.invalid"),
})
return
}
ctx.WithValue("user_id", userID)
ctx.Request().Next()
}
}
+4
View File
@@ -21,6 +21,10 @@ func (r *CertDeploy) Rules(ctx http.Context) map[string]string {
}
}
func (r *CertDeploy) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertDeploy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -19,6 +19,10 @@ func (r *CertShowAndDestroy) Rules(ctx http.Context) map[string]string {
}
}
func (r *CertShowAndDestroy) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertShowAndDestroy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -29,6 +29,10 @@ func (r *CertStore) Rules(ctx http.Context) map[string]string {
}
}
func (r *CertStore) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertStore) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -31,6 +31,10 @@ func (r *CertUpdate) Rules(ctx http.Context) map[string]string {
}
}
func (r *CertUpdate) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertUpdate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -19,6 +19,10 @@ func (r *DNSShowAndDestroy) Rules(ctx http.Context) map[string]string {
}
}
func (r *DNSShowAndDestroy) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DNSShowAndDestroy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+7 -3
View File
@@ -19,17 +19,21 @@ func (r *DNSStore) Authorize(ctx http.Context) error {
func (r *DNSStore) Rules(ctx http.Context) map[string]string {
return map[string]string{
"type": "required|in:dnspod,aliyun,cloudflare",
"type": "required|in:dnspod,tencent,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.access_key": "required_if:type,aliyun,tencent",
"data.secret_key": "required_if:type,aliyun,tencent",
"data.api_key": "required_if:type,cloudflare",
}
}
func (r *DNSStore) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DNSStore) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -33,6 +33,10 @@ func (r *DNSUpdate) Rules(ctx http.Context) map[string]string {
}
}
func (r *DNSUpdate) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DNSUpdate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -19,6 +19,10 @@ func (r *Obtain) Rules(ctx http.Context) map[string]string {
}
}
func (r *Obtain) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Obtain) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -19,6 +19,10 @@ func (r *Renew) Rules(ctx http.Context) map[string]string {
}
}
func (r *Renew) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Renew) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -19,6 +19,10 @@ func (r *UserShowAndDestroy) Rules(ctx http.Context) map[string]string {
}
}
func (r *UserShowAndDestroy) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UserShowAndDestroy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+6 -2
View File
@@ -21,12 +21,16 @@ 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",
"kid": "required_if:ca,sslcom,google",
"hmac_encoded": "required_if:ca,sslcom,google",
"key_type": "required|in:P256,P384,2048,4096",
}
}
func (r *UserStore) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UserStore) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+6 -2
View File
@@ -23,12 +23,16 @@ func (r *UserUpdate) Rules(ctx http.Context) 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",
"kid": "required_if:ca,sslcom,google",
"hmac_encoded": "required_if:ca,sslcom,google",
"key_type": "required|in:P256,P384,2048,4096",
}
}
func (r *UserUpdate) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UserUpdate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -21,6 +21,10 @@ func (r *Paginate) Rules(ctx http.Context) map[string]string {
}
}
func (r *Paginate) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Paginate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -64,6 +64,10 @@ func (r *ContainerCreate) Rules(ctx http.Context) map[string]string {
}
}
func (r *ContainerCreate) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ContainerCreate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -21,6 +21,10 @@ func (r *ContainerRename) Rules(ctx http.Context) map[string]string {
}
}
func (r *ContainerRename) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ContainerRename) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -57,6 +57,10 @@ func (r *ContainerUpdate) Rules(ctx http.Context) map[string]string {
}
}
func (r *ContainerUpdate) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ContainerUpdate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -19,6 +19,10 @@ func (r *ID) Rules(ctx http.Context) map[string]string {
}
}
func (r *ID) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ID) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -25,6 +25,10 @@ func (r *ImagePull) Rules(ctx http.Context) map[string]string {
}
}
func (r *ImagePull) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ImagePull) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -21,6 +21,10 @@ func (r *NetworkConnectDisConnect) Rules(ctx http.Context) map[string]string {
}
}
func (r *NetworkConnectDisConnect) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *NetworkConnectDisConnect) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -31,6 +31,10 @@ func (r *NetworkCreate) Rules(ctx http.Context) map[string]string {
}
}
func (r *NetworkCreate) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *NetworkCreate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -27,6 +27,10 @@ func (r *VolumeCreate) Rules(ctx http.Context) map[string]string {
}
}
func (r *VolumeCreate) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *VolumeCreate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -22,6 +22,10 @@ func (r *Archive) Rules(ctx http.Context) map[string]string {
}
}
func (r *Archive) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Archive) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -21,6 +21,10 @@ func (r *Copy) Rules(ctx http.Context) map[string]string {
}
}
func (r *Copy) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Copy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -19,6 +19,10 @@ func (r *Exist) Rules(ctx http.Context) map[string]string {
}
}
func (r *Exist) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Exist) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -21,6 +21,10 @@ func (r *Move) Rules(ctx http.Context) map[string]string {
}
}
func (r *Move) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Move) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -19,6 +19,10 @@ func (r *NotExist) Rules(ctx http.Context) map[string]string {
}
}
func (r *NotExist) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *NotExist) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+6 -2
View File
@@ -7,7 +7,7 @@ import (
type Permission struct {
Path string `form:"path" json:"path"`
Mode uint `form:"mode" json:"mode" filter:"uint"`
Mode string `form:"mode" json:"mode"`
Owner string `form:"owner" json:"owner"`
Group string `form:"group" json:"group"`
}
@@ -19,12 +19,16 @@ func (r *Permission) Authorize(ctx http.Context) error {
func (r *Permission) Rules(ctx http.Context) map[string]string {
return map[string]string{
"path": `regex:^/.*$|path_exists`,
"mode": "regex:^[0-7]{3}$|uint",
"mode": "regex:^0[0-7]{3}$",
"owner": "regex:^[a-zA-Z0-9_-]+$",
"group": "regex:^[a-zA-Z0-9_-]+$",
}
}
func (r *Permission) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Permission) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -21,6 +21,10 @@ func (r *Save) Rules(ctx http.Context) map[string]string {
}
}
func (r *Save) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Save) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -21,6 +21,10 @@ func (r *Search) Rules(ctx http.Context) map[string]string {
}
}
func (r *Search) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Search) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+5 -1
View File
@@ -17,10 +17,14 @@ func (r *UnArchive) Authorize(ctx http.Context) error {
func (r *UnArchive) Rules(ctx http.Context) map[string]string {
return map[string]string{
"file": `regex:^/.*$|path_exists`,
"path": `regex:^/.*$|path_not_exists`,
"path": `regex:^/.*$`,
}
}
func (r *UnArchive) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UnArchive) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -23,6 +23,10 @@ func (r *Upload) Rules(ctx http.Context) map[string]string {
}
}
func (r *Upload) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Upload) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -19,6 +19,10 @@ func (r *Service) Rules(ctx http.Context) map[string]string {
}
}
func (r *Service) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Service) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -21,6 +21,10 @@ func (r *UpdateConfig) Rules(ctx http.Context) map[string]string {
}
}
func (r *UpdateConfig) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UpdateConfig) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -19,6 +19,10 @@ func (r *UpdateConfig) Rules(ctx http.Context) map[string]string {
}
}
func (r *UpdateConfig) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UpdateConfig) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -19,6 +19,10 @@ func (r *UpdateRegistryConfig) Rules(ctx http.Context) map[string]string {
}
}
func (r *UpdateRegistryConfig) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UpdateRegistryConfig) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -19,6 +19,10 @@ func (r *UpdateStorageConfig) Rules(ctx http.Context) map[string]string {
}
}
func (r *UpdateStorageConfig) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UpdateStorageConfig) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -29,6 +29,10 @@ func (r *Create) Rules(ctx http.Context) map[string]string {
}
}
func (r *Create) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Create) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -29,6 +29,10 @@ func (r *Update) Rules(ctx http.Context) map[string]string {
}
}
func (r *Update) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Update) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -19,6 +19,10 @@ func (r *UpdateConfig) Rules(ctx http.Context) map[string]string {
}
}
func (r *UpdateConfig) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UpdateConfig) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -23,6 +23,10 @@ func (r *Https) Rules(ctx http.Context) map[string]string {
}
}
func (r *Https) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Https) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -35,6 +35,10 @@ func (r *Update) Rules(ctx http.Context) map[string]string {
}
}
func (r *Update) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Update) Messages(ctx http.Context) map[string]string {
return map[string]string{
"port.int": "port 值必须是一个整数且在 1000 - 65535 之间",
+5 -1
View File
@@ -23,12 +23,16 @@ func (r *Login) Rules(ctx http.Context) map[string]string {
func (r *Login) Messages(ctx http.Context) map[string]string {
return map[string]string{
"username.required": "登录名不能为空",
"username.required": "用户名不能为空",
"password.required": "密码不能为空",
"password.min_len": "密码长度不能小于 8 位",
}
}
func (r *Login) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Login) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -37,6 +37,10 @@ func (r *Add) Rules(ctx http.Context) map[string]string {
}
}
func (r *Add) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Add) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+40
View File
@@ -0,0 +1,40 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type Delete struct {
ID uint `form:"id" json:"id" filter:"uint"`
Path bool `form:"path" json:"path"`
DB bool `form:"db" json:"db"`
}
func (r *Delete) Authorize(ctx http.Context) error {
return nil
}
func (r *Delete) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|exists:websites,id",
"path": "bool",
"db": "bool",
}
}
func (r *Delete) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Delete) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Delete) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Delete) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
@@ -19,6 +19,10 @@ func (r *DeleteBackup) Rules(ctx http.Context) map[string]string {
}
}
func (r *DeleteBackup) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DeleteBackup) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -19,6 +19,10 @@ func (r *ID) Rules(ctx http.Context) map[string]string {
}
}
func (r *ID) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ID) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -21,6 +21,10 @@ func (r *RestoreBackup) Rules(ctx http.Context) map[string]string {
}
}
func (r *RestoreBackup) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *RestoreBackup) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+6
View File
@@ -9,6 +9,7 @@ type SaveConfig struct {
ID uint `form:"id" json:"id" filter:"uint"`
Domains []string `form:"domains" json:"domains"`
Ports []uint `form:"ports" json:"ports"`
TLSPorts []uint `form:"tls_ports" json:"tls_ports"`
Hsts bool `form:"hsts" json:"hsts"`
Ssl bool `form:"ssl" json:"ssl"`
HttpRedirect bool `form:"http_redirect" json:"http_redirect"`
@@ -36,6 +37,7 @@ func (r *SaveConfig) Rules(ctx http.Context) map[string]string {
"id": "required|exists:websites,id",
"domains": "required|slice",
"ports": "required|slice",
"tls_ports": "required_if:ssl,true|slice|not_in:80",
"hsts": "bool",
"ssl": "bool",
"http_redirect": "bool",
@@ -55,6 +57,10 @@ func (r *SaveConfig) Rules(ctx http.Context) map[string]string {
}
}
func (r *SaveConfig) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *SaveConfig) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+5 -26
View File
@@ -27,20 +27,11 @@ func (receiver *ProcessTask) Handle(args ...any) error {
}
var task models.Task
if err := facades.Orm().Query().Where("id = ?", taskID).Get(&task); err != nil {
_ = facades.Orm().Query().Where("id = ?", taskID).Get(&task)
if task.ID == 0 {
facades.Log().Tags("面板", "异步任务").With(map[string]any{
"task_id": taskID,
"error": err.Error(),
}).Infof("获取任务失败")
return nil
}
task.Status = models.TaskStatusRunning
if err := facades.Orm().Query().Save(&task); err != nil {
facades.Log().Tags("面板", "异步任务").With(map[string]any{
"task_id": taskID,
"error": err.Error(),
}).Infof("更新任务失败")
}).Infof("任务不存在")
return nil
}
@@ -50,13 +41,7 @@ func (receiver *ProcessTask) Handle(args ...any) error {
if _, err := shell.Execf(task.Shell); err != nil {
task.Status = models.TaskStatusFailed
if err := facades.Orm().Query().Save(&task); err != nil {
facades.Log().Tags("面板", "异步任务").With(map[string]any{
"task_id": taskID,
"error": err.Error(),
}).Infof("更新任务失败")
return nil
}
_ = facades.Orm().Query().Save(&task)
facades.Log().Tags("面板", "异步任务").With(map[string]any{
"task_id": taskID,
"error": err.Error(),
@@ -65,13 +50,7 @@ func (receiver *ProcessTask) Handle(args ...any) error {
}
task.Status = models.TaskStatusSuccess
if err := facades.Orm().Query().Save(&task); err != nil {
facades.Log().Tags("面板", "异步任务").With(map[string]any{
"task_id": taskID,
"error": err.Error(),
}).Infof("更新任务失败")
return nil
}
_ = facades.Orm().Query().Save(&task)
facades.Log().Tags("面板", "异步任务").With(map[string]any{
"task_id": taskID,
+1 -1
View File
@@ -9,7 +9,7 @@ import (
type CertDNS struct {
orm.Model
Name string `gorm:"not null" json:"name"` // 备注名称
Type string `gorm:"not null" json:"type"` // DNS 提供商 (dnspod, aliyun, cloudflare)
Type string `gorm:"not null" json:"type"` // DNS 提供商 (dnspod, tencent, aliyun, cloudflare)
Data acme.DNSParam `gorm:"not null;serializer:json" json:"dns_param"`
Certs []*Cert `gorm:"foreignKey:DNSID" json:"-"`
+2 -18
View File
@@ -1,14 +1,12 @@
package providers
import (
"fmt"
"github.com/goravel/framework/contracts/database/seeder"
"github.com/goravel/framework/contracts/foundation"
"github.com/goravel/framework/database/gorm"
"github.com/goravel/framework/facades"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/pkg/migrate"
)
type DatabaseServiceProvider struct {
@@ -20,19 +18,5 @@ func (receiver *DatabaseServiceProvider) Register(app foundation.Application) {
func (receiver *DatabaseServiceProvider) Boot(app foundation.Application) {
facades.Seeder().Register([]seeder.Seeder{})
if err := facades.Orm().Query().(*gorm.QueryImpl).Instance().AutoMigrate(
&models.Cert{},
&models.CertDNS{},
&models.CertUser{},
&models.Cron{},
&models.Database{},
&models.Monitor{},
&models.Plugin{},
&models.Setting{},
&models.Task{},
&models.User{},
&models.Website{},
); err != nil {
panic(fmt.Sprintf("Failed to migrate database: %v", err))
}
migrate.Migrate(facades.Orm().Query().(*gorm.QueryImpl).Instance())
}
+21 -1
View File
@@ -2,7 +2,9 @@ package providers
import (
"github.com/goravel/framework/contracts/foundation"
contractshttp "github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/goravel/framework/http/limit"
"github.com/TheTNB/panel/app/http"
"github.com/TheTNB/panel/routes"
@@ -25,5 +27,23 @@ func (receiver *RouteServiceProvider) Boot(app foundation.Application) {
}
func (receiver *RouteServiceProvider) configureRateLimiting() {
facades.RateLimiter().ForWithLimits("login", func(ctx contractshttp.Context) []contractshttp.Limit {
return []contractshttp.Limit{
limit.PerMinute(5).By(ctx.Request().Ip()).Response(func(ctx contractshttp.Context) {
ctx.Request().AbortWithStatusJson(contractshttp.StatusTooManyRequests, contractshttp.Json{
"message": "请求过于频繁,请等待一分钟后再试",
})
}),
limit.PerHour(100).By(ctx.Request().Ip()).Response(func(ctx contractshttp.Context) {
ctx.Request().AbortWithStatusJson(contractshttp.StatusTooManyRequests, contractshttp.Json{
"message": "请求过于频繁,请等待一小时后再试",
})
}),
limit.PerDay(1000).Response(func(ctx contractshttp.Context) {
ctx.Request().AbortWithStatusJson(contractshttp.StatusTooManyRequests, contractshttp.Json{
"message": "面板遭受登录爆破攻击过多,已暂时屏蔽登录,请立刻更换面板端口",
})
}),
}
})
}
@@ -25,7 +25,6 @@ func (receiver *ValidationServiceProvider) rules() []validation.Rule {
return []validation.Rule{
&rules.Exists{},
&rules.NotExists{},
&rules.Captcha{},
&rules.PathExists{},
&rules.PathNotExists{},
}
-46
View File
@@ -1,46 +0,0 @@
package rules
import (
"strconv"
"github.com/goravel/framework/contracts/validation"
"github.com/TheTNB/panel/pkg/captcha"
)
type Captcha struct {
}
// Signature The name of the rule.
func (receiver *Captcha) Signature() string {
return "captcha"
}
// Passes Determine if the validation rule passes.
func (receiver *Captcha) Passes(data validation.Data, val any, options ...any) bool {
captchaID, exist := data.Get("captcha_id")
if !exist {
return false
}
// 第一个参数(如果有),是否清除验证码,如 false
clear := true
var err error
if len(options) > 0 {
clear, err = strconv.ParseBool(options[0].(string))
if err != nil {
clear = true
}
}
if !captcha.NewCaptcha().VerifyCaptcha(captchaID.(string), val.(string), clear) {
return false
}
return true
}
// Message Get the validation error message.
func (receiver *Captcha) Message() string {
return ""
}
+5
View File
@@ -1,6 +1,8 @@
package bootstrap
import (
"runtime/debug"
"github.com/gookit/validate/locales/zhcn"
"github.com/goravel/framework/foundation"
@@ -8,6 +10,9 @@ import (
)
func Boot() {
debug.SetGCPercent(10)
debug.SetMemoryLimit(64 << 20)
zhcn.RegisterGlobal()
app := foundation.NewApplication()
+5 -2
View File
@@ -17,7 +17,9 @@ import (
"github.com/goravel/framework/queue"
"github.com/goravel/framework/route"
"github.com/goravel/framework/schedule"
"github.com/goravel/framework/session"
"github.com/goravel/framework/support/carbon"
"github.com/goravel/framework/support/path"
"github.com/goravel/framework/testing"
"github.com/goravel/framework/translation"
"github.com/goravel/framework/validation"
@@ -37,7 +39,7 @@ func init() {
// This value is the name of your application. This value is used when the
// framework needs to place the application's name in a notification or
// any other location as required by the application or its pkg.
"name": "Panel",
"name": "耗子面板",
// Application Environment
//
@@ -74,7 +76,7 @@ func init() {
//
// The path to the language files for the application. You may change
// the path to a different directory if you would like to customize it.
"lang_path": facades.App().ExecutablePath("lang"),
"lang_path": path.Executable("lang"),
// Encryption Key
//
@@ -103,6 +105,7 @@ func init() {
&crypt.ServiceProvider{},
&filesystem.ServiceProvider{},
&validation.ServiceProvider{},
&session.ServiceProvider{},
&translation.ServiceProvider{},
&testing.ServiceProvider{},
&providers.AppServiceProvider{},
-34
View File
@@ -1,34 +0,0 @@
package config
import (
"github.com/goravel/framework/facades"
)
func init() {
config := facades.Config()
config.Add("captcha", map[string]any{
// 验证码图片高度
"height": 60,
// 验证码图片宽度
"width": 120,
// 验证码的长度
"length": 6,
// 数字的最大倾斜角度
"maxskew": 0.6,
// 图片背景里的混淆点数量
"dotcount": 40,
// 过期时间,单位是分钟
"expire_time": 5,
// debug 模式下的过期时间,方便本地开发调试
"debug_expire_time": 3600,
// 非 production 环境,使用此 key 可跳过验证,方便测试
"testing_key": "captcha_skip_test",
})
}
+2 -2
View File
@@ -14,10 +14,10 @@ func init() {
// in web browsers. You are free to adjust these settings as needed.
//
// To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
"paths": []string{"*"},
"paths": []string{"1145141919810"}, // 避免框架使用CORS中间件
"allowed_methods": []string{"*"},
"allowed_origins": []string{"*"},
"allowed_headers": []string{"*"},
"allowed_headers": []string{""},
"exposed_headers": []string{""},
"max_age": 0,
"supports_credentials": false,
+2 -1
View File
@@ -2,6 +2,7 @@ package config
import (
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/path"
)
func init() {
@@ -14,7 +15,7 @@ func init() {
"connections": map[string]any{
"panel": map[string]any{
"driver": "sqlite",
"database": config.Env("DB_FILE", facades.App().ExecutablePath("storage/panel.db")),
"database": config.Env("DB_FILE", path.Executable("storage/panel.db")),
"prefix": "",
"singular": false, // Table name is singular
},
+2 -1
View File
@@ -2,6 +2,7 @@ package config
import (
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/path"
)
func init() {
@@ -24,7 +25,7 @@ func init() {
"disks": map[string]any{
"local": map[string]any{
"driver": "local",
"root": facades.App().ExecutablePath("storage"),
"root": path.Executable("storage"),
},
},
})
+3 -2
View File
@@ -3,6 +3,7 @@ package config
import (
"github.com/goravel/framework/contracts/route"
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/path"
ginfacades "github.com/goravel/gin/facades"
)
@@ -39,9 +40,9 @@ func init() {
// SSL Certificate
"ssl": map[string]any{
// ca.pem
"cert": config.Env("APP_SSL_CERT", facades.App().ExecutablePath("storage/ssl.crt")),
"cert": config.Env("APP_SSL_CERT", path.Executable("storage/ssl.crt")),
// ca.key
"key": config.Env("APP_SSL_KEY", facades.App().ExecutablePath("storage/ssl.key")),
"key": config.Env("APP_SSL_KEY", path.Executable("storage/ssl.key")),
},
},
})
+10 -2
View File
@@ -2,6 +2,7 @@ package config
import (
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/path"
)
func init() {
@@ -26,17 +27,24 @@ func init() {
},
"single": map[string]any{
"driver": "single",
"path": "storage/logs/panel.log",
"path": path.Executable("storage/logs/panel.log"),
"level": "info",
"print": true,
},
"daily": map[string]any{
"driver": "daily",
"path": "storage/logs/panel.log",
"path": path.Executable("storage/logs/panel.log"),
"level": "info",
"days": 7,
"print": true,
},
"http": map[string]any{
"driver": "daily",
"path": path.Executable("storage/logs/http.log"),
"level": "info",
"days": 7,
"print": false,
},
},
})
}
+1 -1
View File
@@ -8,7 +8,7 @@ func init() {
config := facades.Config()
config.Add("panel", map[string]any{
"name": "耗子面板",
"version": "v2.2.12",
"version": "v2.2.20",
"ssl": config.Env("APP_SSL", false),
})
}
+85
View File
@@ -0,0 +1,85 @@
package config
import (
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/path"
)
func init() {
config := facades.Config()
config.Add("session", map[string]any{
// Default Session Driver
//
// This option controls the default session "driver" that will be used on
// requests. By default, we will use the lightweight file session driver, but you
// may specify any of the other wonderful drivers provided here.
//
// Supported: "file"
"driver": config.Env("SESSION_DRIVER", "file"),
// Session Lifetime
//
// Here you may specify the number of minutes that you wish the session
// to be allowed to remain idle before it expires. If you want them
// to immediately expire when the browser is closed, then you may
// indicate that via the expire_on_close configuration option.
"lifetime": config.Env("SESSION_LIFETIME", 120),
"expire_on_close": config.Env("SESSION_EXPIRE_ON_CLOSE", false),
// Session File Location
//
// When using the file session driver, we need a location where the
// session files may be stored. A default has been set for you, but a
// different location may be specified. This is only needed for file sessions.
"files": path.Executable("storage/framework/sessions"),
// Session Sweeping Lottery
//
// Some session drivers must manually sweep their storage location to get
// rid of old sessions from storage. Here are the chances out of 100 that
// the sweeper will sweep the storage location. The default is 2 out of 100.
"lottery": []int{2, 100},
// Session Cookie Name
//
// Here you may change the name of the cookie used to identify a session
// in the application. The name specified here will get used every time
// a new session cookie is created by the framework for every driver.
"cookie": config.Env("SESSION_COOKIE", "panel_session"),
// Session Cookie Path
//
// The session cookie path determines the path for which the cookie will
// be regarded as available.Typically, this will be the root path of
// your application, but you are free to change this when necessary.
"path": config.Env("SESSION_PATH", "/"),
// Session Cookie Domain
//
// Here you may change the domain of the cookie used to identify a session
// in your application.This will determine which domains the cookie is
// available to in your application.A sensible default has been set.
"domain": config.Env("SESSION_DOMAIN", ""),
// HTTPS Only Cookies
//
// By setting this option to true, session cookies will only be sent back
// to the server if the browser has an HTTPS connection. This will keep
// the cookie from being sent to you if it cannot be done securely.
"secure": config.Env("SESSION_SECURE", false),
// HTTP Access Only
//
// Setting this to true will prevent JavaScript from accessing the value of
// the cookie, and the cookie will only be accessible through the HTTP protocol.
"http_only": config.Env("SESSION_HTTP_ONLY", true),
// Same-Site Cookies
//
// This option determines how your cookies behave when cross-site requests
// take place, and can be used to mitigate CSRF attacks.By default, we
// will set this value to "lax" since this is a secure default value.
"same_site": config.Env("SESSION_SAME_SITE", "lax"),
})
}
+57 -13
View File
@@ -3235,7 +3235,6 @@ const docTemplate = `{
"BearerToken": []
}
],
"description": "获取当前登录用户信息",
"produces": [
"application/json"
],
@@ -3255,7 +3254,6 @@ const docTemplate = `{
},
"/panel/user/login": {
"post": {
"description": "通过用户名和密码获取访问令牌",
"consumes": [
"application/json"
],
@@ -3299,6 +3297,30 @@ const docTemplate = `{
}
}
},
"/panel/user/logout": {
"post": {
"security": [
{
"BearerToken": []
}
],
"produces": [
"application/json"
],
"tags": [
"用户鉴权"
],
"summary": "登出",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
}
},
"/panel/website/backupList": {
"get": {
"security": [
@@ -3574,8 +3596,8 @@ const docTemplate = `{
}
}
},
"/panel/websites/{id}": {
"delete": {
"/panel/websites/delete": {
"post": {
"security": [
{
"BearerToken": []
@@ -3593,11 +3615,13 @@ const docTemplate = `{
"summary": "删除网站",
"parameters": [
{
"type": "integer",
"description": "网站 ID",
"name": "id",
"in": "path",
"required": true
"description": "request",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.Delete"
}
}
],
"responses": {
@@ -3648,7 +3672,7 @@ const docTemplate = `{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/types.Website"
"$ref": "#/definitions/types.WebsiteAdd"
}
}
}
@@ -5156,7 +5180,7 @@ const docTemplate = `{
"type": "string"
},
"type": {
"description": "DNS 提供商 (dnspod, aliyun, cloudflare)",
"description": "DNS 提供商 (dnspod, tencent, aliyun, cloudflare)",
"type": "string"
},
"updated_at": {
@@ -5506,6 +5530,20 @@ const docTemplate = `{
}
}
},
"requests.Delete": {
"type": "object",
"properties": {
"db": {
"type": "boolean"
},
"id": {
"type": "integer"
},
"path": {
"type": "boolean"
}
}
},
"requests.DeleteBackup": {
"type": "object",
"properties": {
@@ -5638,7 +5676,7 @@ const docTemplate = `{
"type": "string"
},
"mode": {
"type": "integer"
"type": "string"
},
"owner": {
"type": "string"
@@ -5721,6 +5759,12 @@ const docTemplate = `{
"ssl_certificate_key": {
"type": "string"
},
"tls_ports": {
"type": "array",
"items": {
"type": "integer"
}
},
"waf": {
"type": "boolean"
},
@@ -5915,7 +5959,7 @@ const docTemplate = `{
}
}
},
"types.Website": {
"types.WebsiteAdd": {
"type": "object",
"properties": {
"db": {
+57 -13
View File
@@ -3228,7 +3228,6 @@
"BearerToken": []
}
],
"description": "获取当前登录用户信息",
"produces": [
"application/json"
],
@@ -3248,7 +3247,6 @@
},
"/panel/user/login": {
"post": {
"description": "通过用户名和密码获取访问令牌",
"consumes": [
"application/json"
],
@@ -3292,6 +3290,30 @@
}
}
},
"/panel/user/logout": {
"post": {
"security": [
{
"BearerToken": []
}
],
"produces": [
"application/json"
],
"tags": [
"用户鉴权"
],
"summary": "登出",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
}
},
"/panel/website/backupList": {
"get": {
"security": [
@@ -3567,8 +3589,8 @@
}
}
},
"/panel/websites/{id}": {
"delete": {
"/panel/websites/delete": {
"post": {
"security": [
{
"BearerToken": []
@@ -3586,11 +3608,13 @@
"summary": "删除网站",
"parameters": [
{
"type": "integer",
"description": "网站 ID",
"name": "id",
"in": "path",
"required": true
"description": "request",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.Delete"
}
}
],
"responses": {
@@ -3641,7 +3665,7 @@
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/types.Website"
"$ref": "#/definitions/types.WebsiteAdd"
}
}
}
@@ -5149,7 +5173,7 @@
"type": "string"
},
"type": {
"description": "DNS 提供商 (dnspod, aliyun, cloudflare)",
"description": "DNS 提供商 (dnspod, tencent, aliyun, cloudflare)",
"type": "string"
},
"updated_at": {
@@ -5499,6 +5523,20 @@
}
}
},
"requests.Delete": {
"type": "object",
"properties": {
"db": {
"type": "boolean"
},
"id": {
"type": "integer"
},
"path": {
"type": "boolean"
}
}
},
"requests.DeleteBackup": {
"type": "object",
"properties": {
@@ -5631,7 +5669,7 @@
"type": "string"
},
"mode": {
"type": "integer"
"type": "string"
},
"owner": {
"type": "string"
@@ -5714,6 +5752,12 @@
"ssl_certificate_key": {
"type": "string"
},
"tls_ports": {
"type": "array",
"items": {
"type": "integer"
}
},
"waf": {
"type": "boolean"
},
@@ -5908,7 +5952,7 @@
}
}
},
"types.Website": {
"types.WebsiteAdd": {
"type": "object",
"properties": {
"db": {
+54 -28
View File
@@ -148,7 +148,7 @@ definitions:
description: 备注名称
type: string
type:
description: DNS 提供商 (dnspod, aliyun, cloudflare)
description: DNS 提供商 (dnspod, tencent, aliyun, cloudflare)
type: string
updated_at:
$ref: '#/definitions/github_com_goravel_framework_support_carbon.DateTime'
@@ -377,6 +377,15 @@ definitions:
type:
type: string
type: object
requests.Delete:
properties:
db:
type: boolean
id:
type: integer
path:
type: boolean
type: object
requests.DeleteBackup:
properties:
name:
@@ -462,7 +471,7 @@ definitions:
group:
type: string
mode:
type: integer
type: string
owner:
type: string
path:
@@ -516,6 +525,10 @@ definitions:
type: string
ssl_certificate_key:
type: string
tls_ports:
items:
type: integer
type: array
waf:
type: boolean
waf_cache:
@@ -641,7 +654,7 @@ definitions:
value:
type: string
type: object
types.Website:
types.WebsiteAdd:
properties:
db:
type: boolean
@@ -2632,7 +2645,6 @@ paths:
- 系统
/panel/user/info:
get:
description: 获取当前登录用户信息
produces:
- application/json
responses:
@@ -2649,7 +2661,6 @@ paths:
post:
consumes:
- application/json
description: 通过用户名和密码获取访问令牌
parameters:
- description: request
in: body
@@ -2675,6 +2686,20 @@ paths:
summary: 登录
tags:
- 用户鉴权
/panel/user/logout:
post:
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controllers.SuccessResponse'
security:
- BearerToken: []
summary: 登出
tags:
- 用户鉴权
/panel/website/backupList:
get:
parameters:
@@ -2836,28 +2861,6 @@ paths:
summary: 添加网站
tags:
- 网站
/panel/websites/{id}:
delete:
consumes:
- application/json
parameters:
- description: 网站 ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controllers.SuccessResponse'
security:
- BearerToken: []
summary: 删除网站
tags:
- 网站
/panel/websites/{id}/config:
get:
consumes:
@@ -2878,7 +2881,7 @@ paths:
- $ref: '#/definitions/controllers.SuccessResponse'
- properties:
data:
$ref: '#/definitions/types.Website'
$ref: '#/definitions/types.WebsiteAdd'
type: object
security:
- BearerToken: []
@@ -3044,6 +3047,29 @@ paths:
summary: 更新网站备注
tags:
- 网站
/panel/websites/delete:
post:
consumes:
- application/json
parameters:
- description: request
in: body
name: data
required: true
schema:
$ref: '#/definitions/requests.Delete'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controllers.SuccessResponse'
security:
- BearerToken: []
summary: 删除网站
tags:
- 网站
/plugins/frp/config:
get:
description: 获取 Frp 配置
+32 -28
View File
@@ -3,29 +3,33 @@ module github.com/TheTNB/panel
go 1.22
require (
github.com/docker/docker v26.1.4+incompatible
github.com/docker/docker v27.0.3+incompatible
github.com/docker/go-connections v0.5.0
github.com/go-gormigrate/gormigrate/v2 v2.1.2
github.com/go-resty/resty/v2 v2.13.1
github.com/go-sql-driver/mysql v1.8.1
github.com/gookit/validate v1.5.2
github.com/goravel/framework v1.14.1-0.20240618022250-731b8d9930a3
github.com/goravel/gin v1.2.1
github.com/goravel/framework v1.14.3
github.com/goravel/gin v1.2.2
github.com/gorilla/websocket v1.5.3
github.com/hashicorp/go-version v1.7.0
github.com/lib/pq v1.10.9
github.com/libdns/alidns v1.0.3
github.com/libdns/cloudflare v0.1.1
github.com/libdns/dnspod v0.0.3
github.com/libdns/libdns v0.2.2
github.com/libdns/tencentcloud v1.0.0
github.com/mholt/acmez/v2 v2.0.1
github.com/mholt/archiver/v3 v3.5.1
github.com/mojocn/base64Captcha v1.3.6
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/spf13/cast v1.6.0
github.com/stretchr/testify v1.9.0
github.com/swaggo/http-swagger/v2 v2.0.2
github.com/swaggo/swag v1.16.3
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.24.0
golang.org/x/net v0.26.0
golang.org/x/crypto v0.25.0
golang.org/x/net v0.27.0
gorm.io/gorm v1.25.10
)
require (
@@ -50,13 +54,13 @@ require (
github.com/catppuccin/go v0.2.0 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/charmbracelet/bubbles v0.18.0 // indirect
github.com/charmbracelet/bubbletea v0.26.3 // indirect
github.com/charmbracelet/huh v0.4.2 // indirect
github.com/charmbracelet/huh/spinner v0.0.0-20240508140610-13957916abf0 // indirect
github.com/charmbracelet/lipgloss v0.11.0 // indirect
github.com/charmbracelet/x/ansi v0.1.1 // indirect
github.com/charmbracelet/x/exp/strings v0.0.0-20240524151031-ff83003bf67a // indirect
github.com/charmbracelet/x/input v0.1.1 // indirect
github.com/charmbracelet/bubbletea v0.26.4 // indirect
github.com/charmbracelet/huh v0.5.1 // indirect
github.com/charmbracelet/huh/spinner v0.0.0-20240709222038-80fcf53648bd // indirect
github.com/charmbracelet/lipgloss v0.11.1 // indirect
github.com/charmbracelet/x/ansi v0.1.3 // indirect
github.com/charmbracelet/x/exp/strings v0.0.0-20240617190524-788ec55faed1 // indirect
github.com/charmbracelet/x/input v0.1.2 // indirect
github.com/charmbracelet/x/term v0.1.1 // indirect
github.com/charmbracelet/x/windows v0.1.2 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
@@ -87,7 +91,6 @@ require (
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
@@ -96,7 +99,6 @@ require (
github.com/golang-module/carbon/v2 v2.3.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
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect
@@ -106,7 +108,6 @@ require (
github.com/gookit/filter v1.2.1 // indirect
github.com/gookit/goutil v0.6.15 // indirect
github.com/goravel/file-rotatelogs/v2 v2.4.2 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
@@ -130,6 +131,7 @@ require (
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/microsoft/go-mssqldb v1.6.0 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
@@ -141,7 +143,6 @@ require (
github.com/nwaples/rardecode v1.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pierrec/lz4/v4 v4.1.16 // indirect
github.com/pkg/errors v0.9.1 // indirect
@@ -157,7 +158,7 @@ require (
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/samber/do/v2 v2.0.0-beta.7 // indirect
github.com/samber/go-type-to-string v1.7.0 // indirect
github.com/samber/go-type-to-string v1.4.0 // indirect
github.com/savioxavier/termlink v1.3.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
@@ -167,12 +168,13 @@ require (
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/swaggo/files/v2 v2.0.0 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.597 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/ulikunitz/xz v0.5.11 // indirect
github.com/unrolled/secure v1.14.0 // indirect
github.com/unrolled/secure v1.15.0 // indirect
github.com/urfave/cli/v2 v2.27.2 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
@@ -187,23 +189,21 @@ require (
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
golang.org/x/image v0.13.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 // indirect
golang.org/x/mod v0.19.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/term v0.21.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/term v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
google.golang.org/grpc v1.64.0 // indirect
golang.org/x/tools v0.23.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
google.golang.org/grpc v1.65.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/mysql v1.5.7 // indirect
gorm.io/driver/postgres v1.5.9 // indirect
gorm.io/driver/sqlserver v1.5.3 // indirect
gorm.io/gorm v1.25.10 // indirect
gorm.io/plugin/dbresolver v1.5.2 // indirect
gotest.tools/v3 v3.5.0 // indirect
modernc.org/libc v1.37.6 // indirect
@@ -211,3 +211,7 @@ require (
modernc.org/memory v1.7.2 // indirect
modernc.org/sqlite v1.28.0 // indirect
)
replace github.com/goravel/framework => github.com/goravel/framework v1.14.1-0.20240710163908-d704469a624f
replace github.com/mholt/archiver/v3 => github.com/anchore/archiver/v3 v3.5.2
+58 -79
View File
@@ -61,6 +61,8 @@ github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi
github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY=
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/anchore/archiver/v3 v3.5.2 h1:Bjemm2NzuRhmHy3m0lRe5tNoClB9A4zYyDV58PaB6aA=
github.com/anchore/archiver/v3 v3.5.2/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
@@ -70,9 +72,8 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4=
github.com/brianvoe/gofakeit/v6 v6.28.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs=
github.com/brianvoe/gofakeit/v7 v7.0.4 h1:Mkxwz9jYg8Ad8NvT9HA27pCMZGFQo08MK6jD0QTKEww=
github.com/brianvoe/gofakeit/v7 v7.0.4/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA=
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
@@ -86,22 +87,22 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
github.com/charmbracelet/bubbletea v0.26.3 h1:iXyGvI+FfOWqkB2V07m1DF3xxQijxjY2j8PqiXYqasg=
github.com/charmbracelet/bubbletea v0.26.3/go.mod h1:bpZHfDHTYJC5g+FBK+ptJRCQotRC+Dhh3AoMxa/2+3Q=
github.com/charmbracelet/huh v0.4.2 h1:5wLkwrA58XDAfEZsJzNQlfJ+K8N9+wYwvR5FOM7jXFM=
github.com/charmbracelet/huh v0.4.2/go.mod h1:g9OXBgtY3zRV4ahnVih9bZE+1yGYN+y2C9Q6L2P+WM0=
github.com/charmbracelet/huh/spinner v0.0.0-20240508140610-13957916abf0 h1:79JTuYRirtyCn9ac6rzPt5AQKtBDFc1gKxpw0wBrI+Y=
github.com/charmbracelet/huh/spinner v0.0.0-20240508140610-13957916abf0/go.mod h1:Zxt9FH6togK9kY71pRJGtmyNkJ1eIWdK1gRaXrS/FKA=
github.com/charmbracelet/lipgloss v0.11.0 h1:UoAcbQ6Qml8hDwSWs0Y1cB5TEQuZkDPH/ZqwWWYTG4g=
github.com/charmbracelet/lipgloss v0.11.0/go.mod h1:1UdRTH9gYgpcdNN5oBtjbu/IzNKtzVtb7sqN1t9LNn8=
github.com/charmbracelet/x/ansi v0.1.1 h1:CGAduulr6egay/YVbGc8Hsu8deMg1xZ/bkaXTPi1JDk=
github.com/charmbracelet/x/ansi v0.1.1/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/x/exp/strings v0.0.0-20240524151031-ff83003bf67a h1:lOpqe2UvPmlln41DGoii7wlSZ/q8qGIon5JJ8Biu46I=
github.com/charmbracelet/x/exp/strings v0.0.0-20240524151031-ff83003bf67a/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ=
github.com/charmbracelet/bubbletea v0.26.4 h1:2gDkkzLZaTjMl/dQBpNVtnvcCxsh/FCkimep7FC9c40=
github.com/charmbracelet/bubbletea v0.26.4/go.mod h1:P+r+RRA5qtI1DOHNFn0otoNwB4rn+zNAzSj/EXz6xU0=
github.com/charmbracelet/huh v0.5.1 h1:t5j6g9sMjAE2a9AQuc4lNL7pf/0X4WdHiiMGkL8v/aM=
github.com/charmbracelet/huh v0.5.1/go.mod h1:gs7b2brpzXkY0PBWUqJrlzvOowTCL0vNAR6OTItc+kA=
github.com/charmbracelet/huh/spinner v0.0.0-20240709222038-80fcf53648bd h1:ZkI3oVFQw1vZANDjUH2a886xbylVrUXDIvcAoRUkeCw=
github.com/charmbracelet/huh/spinner v0.0.0-20240709222038-80fcf53648bd/go.mod h1:CrXBZnOWs3zpyppOZZS7lu2CpLq2jx6U5chL/frRG/E=
github.com/charmbracelet/lipgloss v0.11.1 h1:a8KgVPHa7kOoP95vm2tQQrjD2AKhbWmfr4uJ2RW6kNk=
github.com/charmbracelet/lipgloss v0.11.1/go.mod h1:beLlcmkF7MWA+5UrKKIRo/VJ21xGXr7YJ9miWfdMRIU=
github.com/charmbracelet/x/ansi v0.1.3 h1:RBh/eleNWML5R524mjUF0yVRePTwqN9tPtV+DPgO5Lw=
github.com/charmbracelet/x/ansi v0.1.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/x/exp/strings v0.0.0-20240617190524-788ec55faed1 h1:VZIQzjwFE0EamzG2v8HfemeisB8X02Tl0BZBnJ0PeU8=
github.com/charmbracelet/x/exp/strings v0.0.0-20240617190524-788ec55faed1/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ=
github.com/charmbracelet/x/exp/term v0.0.0-20240524151031-ff83003bf67a h1:k/s6UoOSVynWiw7PlclyGO2VdVs5ZLbMIHiGp4shFZE=
github.com/charmbracelet/x/exp/term v0.0.0-20240524151031-ff83003bf67a/go.mod h1:YBotIGhfoWhHDlnUpJMkjebGV2pdGRCn1Y4/Nk/vVcU=
github.com/charmbracelet/x/input v0.1.1 h1:YDOJaTUKCqtGnq9PHzx3pkkl4pXDOANUHmhH3DqMtM4=
github.com/charmbracelet/x/input v0.1.1/go.mod h1:jvdTVUnNWj/RD6hjC4FsoB0SeZCJ2ZBkiuFP9zXvZI0=
github.com/charmbracelet/x/input v0.1.2 h1:QJAZr33eOhDowkkEQ24rsJy4Llxlm+fRDf/cQrmqJa0=
github.com/charmbracelet/x/input v0.1.2/go.mod h1:LGBim0maUY4Pitjn/4fHnuXb4KirU3DODsyuHuXdOyA=
github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI=
github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw=
github.com/charmbracelet/x/windows v0.1.2 h1:Iumiwq2G+BRmgoayww/qfcvof7W/3uLoelhxojXlRWg=
@@ -134,8 +135,8 @@ github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
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/docker v26.1.4+incompatible h1:vuTpXDuoga+Z38m1OZHzl7NKisKWaWlhjQk7IDPSLsU=
github.com/docker/docker v26.1.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v27.0.3+incompatible h1:aBGI9TeQ4MPlhquTQKq9XbK79rKFVwXNUAYz9aXyEBE=
github.com/docker/docker v27.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@@ -170,8 +171,8 @@ github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
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-gormigrate/gormigrate/v2 v2.1.2 h1:F/d1hpHbRAvKezziV2CC5KUE82cVe9zTgHSBoOOZ4CY=
github.com/go-gormigrate/gormigrate/v2 v2.1.2/go.mod h1:9nHVX6z3FCMCQPA7PThGcA55t22yKQfK/Dnsf5i7hUo=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -203,7 +204,6 @@ github.com/go-resty/resty/v2 v2.13.1/go.mod h1:GznXlLxkq6Nh4sU59rPmUw3VtgpO3aS96
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
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/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@@ -223,8 +223,6 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0kt
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=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -276,16 +274,14 @@ github.com/gookit/validate v1.5.2 h1:i5I2OQ7WYHFRPRATGu9QarR9snnNHydvwSuHXaRWAV0
github.com/gookit/validate v1.5.2/go.mod h1:yuPy2WwDlwGRa06fFJ5XIO8QEwhRnTC2LmxmBa5SE14=
github.com/goravel/file-rotatelogs/v2 v2.4.2 h1:g68AzbePXcm0V2CpUMc9j4qVzcDn7+7aoWSjZ51C0m4=
github.com/goravel/file-rotatelogs/v2 v2.4.2/go.mod h1:23VuSW8cBS4ax5cmbV+5AaiLpq25b8UJ96IhbAkdo8I=
github.com/goravel/framework v1.14.1-0.20240618022250-731b8d9930a3 h1:G9aMInp4s2pL/bpVXrUj+NUtnVU8i7T9t6vGUJ60NaM=
github.com/goravel/framework v1.14.1-0.20240618022250-731b8d9930a3/go.mod h1:RTwF0A7HySf5i+2jptT9oyFqmQAiQubWn1AQrJAzPWg=
github.com/goravel/gin v1.2.1 h1:lnQX3NKUEaSx8x7AAJpoeVkXgi+MVQ9FXy4QywHQElo=
github.com/goravel/gin v1.2.1/go.mod h1:Qt3NJysg/eoxXL4y/swwFUcfcIT7XG+xb0rWChweZfY=
github.com/goravel/framework v1.14.1-0.20240710163908-d704469a624f h1:TVLqQHyE7A8y1ofQQyZ8mBqq0gJblH7Rjgh9dmY3EjE=
github.com/goravel/framework v1.14.1-0.20240710163908-d704469a624f/go.mod h1:QbrOeoKX5et77/fSHEsLds0fKwOyEv7y6ZX/Cl/y274=
github.com/goravel/gin v1.2.2 h1:nja68Yt/3WxU/ZK2a+buyVGG3TqTVn4eU8caUW3lkJI=
github.com/goravel/gin v1.2.2/go.mod h1:aUx/uE2ZdUdzIjFo+HGKax+EPb2aZ79DXMTzjEzUOvQ=
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.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/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 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
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=
@@ -295,6 +291,8 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
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/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
@@ -335,7 +333,6 @@ github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQ
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -361,6 +358,8 @@ github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSO
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/libdns/tencentcloud v1.0.0 h1:u4LXnYu/lu/9P5W+MCVPeSDnwI+6w+DxYhQ1wSnQOuU=
github.com/libdns/tencentcloud v1.0.0/go.mod h1:NlCgPumzUsZWSOo1+Q/Hfh8G6TNRAaTUeWQdg6LbtUI=
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
@@ -378,10 +377,10 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mholt/acmez/v2 v2.0.1 h1:3/3N0u1pLjMK4sNEAFSI+bcvzbPhRpY383sy1kLHJ6k=
github.com/mholt/acmez/v2 v2.0.1/go.mod h1:fX4c9r5jYwMyMsC+7tkYRxHibkOTgta5DIFGoe67e1U=
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
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/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
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/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
@@ -394,8 +393,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
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=
github.com/mojocn/base64Captcha v1.3.6 h1:gZEKu1nsKpttuIAQgWHO+4Mhhls8cAKyiV2Ew03H+Tw=
github.com/mojocn/base64Captcha v1.3.6/go.mod h1:i5CtHvm+oMbj1UzEPXaA8IH/xHFZ3DGY3Wh3dBpZ28E=
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
@@ -413,9 +410,6 @@ 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.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI=
github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
@@ -423,7 +417,6 @@ github.com/pierrec/lz4/v4 v4.1.16 h1:kQPfno+wyx6C5572ABwV+Uo3pDFzQ7yhyGchSyRda0c
github.com/pierrec/lz4/v4 v4.1.16/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
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.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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -463,15 +456,14 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/samber/do/v2 v2.0.0-beta.7 h1:tmdLOVSCbTA6uGWLU5poi/nZvMRh5QxXFJ9vHytU+Jk=
github.com/samber/do/v2 v2.0.0-beta.7/go.mod h1:+LpV3vu4L81Q1JMZNSkMvSkW9lt4e5eJoXoZHkeBS4c=
github.com/samber/go-type-to-string v1.7.0 h1:FiSstaAikHMUSLt5bhVlsvCnD7bbQzC8L0UkkGS3Bj8=
github.com/samber/go-type-to-string v1.7.0/go.mod h1:jpU77vIDoIxkahknKDoEx9C8bQ1ADnh2sotZ8I4QqBU=
github.com/samber/go-type-to-string v1.4.0 h1:KXphToZgiFdnJQxryU25brhlh/CqY/cwJVeX2rfmow0=
github.com/samber/go-type-to-string v1.4.0/go.mod h1:jpU77vIDoIxkahknKDoEx9C8bQ1ADnh2sotZ8I4QqBU=
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/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
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/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/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
@@ -485,12 +477,10 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
@@ -511,6 +501,8 @@ github.com/swaggo/http-swagger/v2 v2.0.2 h1:FKCdLsl+sFCx60KFsyM0rDarwiUSZ8DqbfSy
github.com/swaggo/http-swagger/v2 v2.0.2/go.mod h1:r7/GBkAWIfK6E/OLnE8fXnviHiDeAHmgIyooa4xm3AQ=
github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.597 h1:C0GHdLTfikLVoEzfhgPfrZ7LwlG0xiCmk6iwNKE+xs0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.597/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
@@ -523,8 +515,8 @@ github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oW
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/unrolled/secure v1.14.0 h1:u9vJTU/pR4Bny0ntLUMxdfLtmIRGvQf2sEFuA0TG9AE=
github.com/unrolled/secure v1.14.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
github.com/unrolled/secure v1.15.0 h1:q7x+pdp8jAHnbzxu6UheP8fRlG/rwYTb8TPuQ3rn9Og=
github.com/unrolled/secure v1.15.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
@@ -561,16 +553,12 @@ go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.11.0 h1:cLDgIBTf4lLOlztkhzAEdQsJ4Lj+i5Wc9k6Nn0K1VyU=
go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ=
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/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
@@ -589,25 +577,22 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg=
golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk=
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w=
golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/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.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
golang.org/x/mod v0.19.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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -631,8 +616,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -650,7 +635,6 @@ golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -662,7 +646,6 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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=
@@ -676,8 +659,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.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-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -690,8 +673,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
@@ -711,7 +694,6 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@@ -719,8 +701,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -729,25 +711,23 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY=
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo=
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4=
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw=
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
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=
@@ -763,7 +743,6 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+3 -3
View File
@@ -24,16 +24,16 @@ type Container interface {
ContainerInspect(id string) (types.ContainerJSON, error)
ContainerKill(id string) error
ContainerRename(id string, newName string) error
ContainerStats(id string) (types.ContainerStats, error)
ContainerStats(id string) (container.StatsResponseReader, error)
ContainerExist(name string) (bool, error)
ContainerUpdate(id string, config container.UpdateConfig) error
ContainerLogs(id string) (string, error)
ContainerPrune() error
NetworkList() ([]types.NetworkResource, error)
NetworkList() ([]network.Inspect, error)
NetworkCreate(config requests.NetworkCreate) (string, error)
NetworkRemove(id string) error
NetworkExist(name string) (bool, error)
NetworkInspect(id string) (types.NetworkResource, error)
NetworkInspect(id string) (network.Inspect, error)
NetworkConnect(networkID string, containerID string) error
NetworkDisconnect(networkID string, containerID string) error
NetworkPrune() error
+2 -2
View File
@@ -165,7 +165,7 @@ func (s *BackupImpl) MysqlBackup(database string) error {
return err
}
if _, err := shell.Execf("/www/server/mysql/bin/mysqldump -uroot " + database + " > " + backupPath + "/" + backupFile); err != nil {
if _, err := shell.Execf("mysqldump -uroot " + database + " > " + backupPath + "/" + backupFile); err != nil {
return err
}
if _, err := shell.Execf("cd " + backupPath + " && zip -r " + backupPath + "/" + backupFile + ".zip " + backupFile); err != nil {
@@ -219,7 +219,7 @@ func (s *BackupImpl) MysqlRestore(database string, backupFile string) error {
return errors.New("无法找到备份文件")
}
if _, err = shell.Execf("/www/server/mysql/bin/mysql -uroot " + database + " < " + filepath.Join(tempDir, backupFile)); err != nil {
if _, err = shell.Execf("mysql -uroot " + database + " < " + filepath.Join(tempDir, backupFile)); err != nil {
return err
}
+46 -4
View File
@@ -4,9 +4,11 @@ package services
import (
"context"
"errors"
"fmt"
"strings"
"time"
"github.com/go-resty/resty/v2"
"github.com/goravel/framework/facades"
requests "github.com/TheTNB/panel/app/http/requests/cert"
@@ -14,6 +16,7 @@ import (
"github.com/TheTNB/panel/pkg/acme"
"github.com/TheTNB/panel/pkg/cert"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/systemctl"
)
@@ -42,7 +45,11 @@ func (s *CertImpl) UserStore(request requests.UserStore) error {
case "buypass":
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CABuypass, nil, acme.KeyType(user.KeyType))
case "zerossl":
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CAZeroSSL, &acme.EAB{KeyID: user.Kid, MACKey: user.HmacEncoded}, acme.KeyType(user.KeyType))
eab, eabErr := s.getZeroSSLEAB(user.Email)
if eabErr != nil {
return eabErr
}
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CAZeroSSL, eab, acme.KeyType(user.KeyType))
case "sslcom":
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CASSLcom, &acme.EAB{KeyID: user.Kid, MACKey: user.HmacEncoded}, acme.KeyType(user.KeyType))
case "google":
@@ -85,7 +92,11 @@ func (s *CertImpl) UserUpdate(request requests.UserUpdate) error {
case "buypass":
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CABuypass, nil, acme.KeyType(user.KeyType))
case "zerossl":
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CAZeroSSL, &acme.EAB{KeyID: user.Kid, MACKey: user.HmacEncoded}, acme.KeyType(user.KeyType))
eab, eabErr := s.getZeroSSLEAB(user.Email)
if eabErr != nil {
return eabErr
}
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CAZeroSSL, eab, acme.KeyType(user.KeyType))
case "sslcom":
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CASSLcom, &acme.EAB{KeyID: user.Kid, MACKey: user.HmacEncoded}, acme.KeyType(user.KeyType))
case "google":
@@ -107,6 +118,31 @@ func (s *CertImpl) UserUpdate(request requests.UserUpdate) error {
return facades.Orm().Query().Save(&user)
}
// getZeroSSLEAB 获取 ZeroSSL EAB
func (s *CertImpl) getZeroSSLEAB(email string) (*acme.EAB, error) {
type data struct {
Success bool `json:"success"`
EabKid string `json:"eab_kid"`
EabHmacKey string `json:"eab_hmac_key"`
}
client := resty.New()
client.SetTimeout(5 * time.Second)
client.SetRetryCount(2)
resp, err := client.R().SetFormData(map[string]string{
"email": email,
}).SetResult(&data{}).Post("https://api.zerossl.com/acme/eab-credentials-email")
if err != nil || !resp.IsSuccess() {
return &acme.EAB{}, errors.New("获取ZeroSSL EAB失败")
}
eab := resp.Result().(*data)
if !eab.Success {
return &acme.EAB{}, errors.New("获取ZeroSSL EAB失败")
}
return &acme.EAB{KeyID: eab.EabKid, MACKey: eab.EabHmacKey}, nil
}
// UserShow 根据 ID 获取用户
func (s *CertImpl) UserShow(ID uint) (models.CertUser, error) {
var user models.CertUser
@@ -255,7 +291,8 @@ func (s *CertImpl) ObtainAuto(ID uint) (acme.Certificate, error) {
return acme.Certificate{}, errors.New("通配符域名无法使用 HTTP 验证")
}
}
client.UseHTTP(cert.Website.Path)
conf := fmt.Sprintf("/www/server/vhost/acme/%s.conf", cert.Website.Name)
client.UseHTTP(conf, cert.Website.Path)
}
}
@@ -280,6 +317,7 @@ func (s *CertImpl) ObtainAuto(ID uint) (acme.Certificate, error) {
return acme.Certificate{}, err
}
if err = systemctl.Reload("openresty"); err != nil {
_, err = shell.Execf("openresty -t")
return acme.Certificate{}, err
}
}
@@ -320,6 +358,7 @@ func (s *CertImpl) ObtainManual(ID uint) (acme.Certificate, error) {
return acme.Certificate{}, err
}
if err = systemctl.Reload("openresty"); err != nil {
_, err = shell.Execf("openresty -t")
return acme.Certificate{}, err
}
}
@@ -380,7 +419,8 @@ func (s *CertImpl) Renew(ID uint) (acme.Certificate, error) {
return acme.Certificate{}, errors.New("通配符域名无法使用 HTTP 验证")
}
}
client.UseHTTP(cert.Website.Path)
conf := fmt.Sprintf("/www/server/vhost/acme/%s.conf", cert.Website.Name)
client.UseHTTP(conf, cert.Website.Path)
}
}
@@ -405,6 +445,7 @@ func (s *CertImpl) Renew(ID uint) (acme.Certificate, error) {
return acme.Certificate{}, err
}
if err = systemctl.Reload("openresty"); err != nil {
_, err = shell.Execf("openresty -t")
return acme.Certificate{}, err
}
}
@@ -437,6 +478,7 @@ func (s *CertImpl) Deploy(ID, WebsiteID uint) error {
return err
}
if err = systemctl.Reload("openresty"); err != nil {
_, err = shell.Execf("openresty -t")
return err
}
+8 -8
View File
@@ -118,7 +118,7 @@ func (r *Container) ContainerRename(id string, newName string) error {
}
// ContainerStats 查看容器状态
func (r *Container) ContainerStats(id string) (types.ContainerStats, error) {
func (r *Container) ContainerStats(id string) (container.StatsResponseReader, error) {
return r.client.ContainerStats(context.Background(), id, false)
}
@@ -167,8 +167,8 @@ func (r *Container) ContainerPrune() error {
}
// NetworkList 列出网络
func (r *Container) NetworkList() ([]types.NetworkResource, error) {
return r.client.NetworkList(context.Background(), types.NetworkListOptions{})
func (r *Container) NetworkList() ([]network.Inspect, error) {
return r.client.NetworkList(context.Background(), network.ListOptions{})
}
// NetworkCreate 创建网络
@@ -189,8 +189,8 @@ func (r *Container) NetworkCreate(config requests.NetworkCreate) (string, error)
})
}
options := types.NetworkCreate{
EnableIPv6: config.Ipv6.Enabled,
options := network.CreateOptions{
EnableIPv6: &config.Ipv6.Enabled,
Driver: config.Driver,
Options: r.KVToMap(config.Options),
Labels: r.KVToMap(config.Labels),
@@ -212,7 +212,7 @@ func (r *Container) NetworkRemove(id string) error {
// NetworkExist 判断网络是否存在
func (r *Container) NetworkExist(name string) (bool, error) {
var options types.NetworkListOptions
var options network.ListOptions
options.Filters = filters.NewArgs(filters.Arg("name", name))
networks, err := r.client.NetworkList(context.Background(), options)
if err != nil {
@@ -223,8 +223,8 @@ func (r *Container) NetworkExist(name string) (bool, error) {
}
// NetworkInspect 查看网络
func (r *Container) NetworkInspect(id string) (types.NetworkResource, error) {
return r.client.NetworkInspect(context.Background(), id, types.NetworkInspectOptions{})
func (r *Container) NetworkInspect(id string) (network.Inspect, error) {
return r.client.NetworkInspect(context.Background(), id, network.InspectOptions{})
}
// NetworkConnect 连接网络
+27
View File
@@ -113,6 +113,10 @@ func (r *PluginImpl) Install(slug string) error {
}
}
if err = r.checkTaskExists(slug); err != nil {
return err
}
var task models.Task
task.Name = "安装插件 " + plugin.Name
task.Status = models.TaskStatusWaiting
@@ -159,6 +163,10 @@ func (r *PluginImpl) Uninstall(slug string) error {
}
}
if err = r.checkTaskExists(slug); err != nil {
return err
}
var task models.Task
task.Name = "卸载插件 " + plugin.Name
task.Status = models.TaskStatusWaiting
@@ -205,6 +213,10 @@ func (r *PluginImpl) Update(slug string) error {
}
}
if err = r.checkTaskExists(slug); err != nil {
return err
}
var task models.Task
task.Name = "更新插件 " + plugin.Name
task.Status = models.TaskStatusWaiting
@@ -217,3 +229,18 @@ func (r *PluginImpl) Update(slug string) error {
_ = io.Remove(task.Log)
return r.task.Process(task.ID)
}
func (r *PluginImpl) checkTaskExists(slug string) error {
var count int64
if err := facades.Orm().Query().
Model(&models.Task{}).
Where("log LIKE ? AND (status = ? OR status = ?)", "%"+slug+"%", models.TaskStatusWaiting, models.TaskStatusRunning).
Count(&count); err != nil {
return errors.New("查询任务失败")
}
if count > 0 {
return errors.New("任务已添加,请勿重复添加")
}
return nil
}
+24
View File
@@ -1,12 +1,17 @@
package services
import (
"sync"
"github.com/goravel/framework/database/orm"
"github.com/goravel/framework/facades"
"github.com/TheTNB/panel/app/jobs"
"github.com/TheTNB/panel/app/models"
)
var taskMap sync.Map
type TaskImpl struct {
}
@@ -15,6 +20,9 @@ func NewTaskImpl() *TaskImpl {
}
func (r *TaskImpl) Process(taskID uint) error {
if err := r.markAsRunning(taskID); err != nil {
return err
}
return facades.Queue().Job(&jobs.ProcessTask{}, []any{taskID}).Dispatch()
}
@@ -25,6 +33,9 @@ func (r *TaskImpl) DispatchWaiting() error {
}
for _, task := range tasks {
if _, ok := taskMap.Load(task.ID); ok {
continue
}
if err := r.Process(task.ID); err != nil {
return err
}
@@ -32,3 +43,16 @@ func (r *TaskImpl) DispatchWaiting() error {
return nil
}
func (r *TaskImpl) markAsRunning(taskID uint) error {
task := models.Task{
Model: orm.Model{ID: taskID},
Status: models.TaskStatusRunning,
}
if _, err := facades.Orm().Query().Where("id", taskID).Update(&task); err != nil {
return err
}
taskMap.Store(taskID, true)
return nil
}
+99 -65
View File
@@ -4,7 +4,9 @@ package services
import (
"errors"
"fmt"
"path/filepath"
"regexp"
"slices"
"strconv"
"strings"
@@ -15,6 +17,7 @@ import (
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/pkg/cert"
"github.com/TheTNB/panel/pkg/db"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/str"
@@ -44,7 +47,7 @@ func (r *WebsiteImpl) List(page, limit int) (int64, []models.Website, error) {
}
// Add 添加网站
func (r *WebsiteImpl) Add(website types.Website) (models.Website, error) {
func (r *WebsiteImpl) Add(website types.WebsiteAdd) (models.Website, error) {
w := models.Website{
Name: website.Name,
Status: website.Status,
@@ -240,6 +243,9 @@ server
error_page 404 /404.html;
#error_page 502 /502.html;
# acme证书签发配置,不可修改
include /www/server/vhost/acme/%s.conf;
# 伪静态规则引入,修改后将导致面板设置的伪静态规则失效
include /www/server/vhost/rewrite/%s.conf;
@@ -259,7 +265,7 @@ server
access_log /www/wwwlogs/%s.log;
error_log /www/wwwlogs/%s.log;
}
`, portList, domainList, website.Path, website.Php, website.Name, website.Name, website.Name)
`, portList, domainList, website.Path, website.Php, website.Name, website.Name, website.Name, website.Name)
if err := io.Write("/www/server/vhost/"+website.Name+".conf", nginxConf, 0644); err != nil {
return models.Website{}, err
@@ -267,6 +273,9 @@ server
if err := io.Write("/www/server/vhost/rewrite/"+website.Name+".conf", "", 0644); err != nil {
return models.Website{}, err
}
if err := io.Write("/www/server/vhost/acme/"+website.Name+".conf", "", 0644); err != nil {
return models.Website{}, err
}
if err := io.Write("/www/server/vhost/ssl/"+website.Name+".pem", "", 0644); err != nil {
return models.Website{}, err
}
@@ -282,21 +291,31 @@ server
}
if err := systemctl.Reload("openresty"); err != nil {
_, err = shell.Execf("openresty -t")
return models.Website{}, err
}
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
if website.Db && website.DbType == "mysql" {
_, _ = shell.Execf(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "CREATE DATABASE IF NOT EXISTS ` + website.DbName + ` DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;"`)
_, _ = shell.Execf(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "CREATE USER '` + website.DbUser + `'@'localhost' IDENTIFIED BY '` + website.DbPassword + `';"`)
_, _ = shell.Execf(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "GRANT ALL PRIVILEGES ON ` + website.DbName + `.* TO '` + website.DbUser + `'@'localhost';"`)
_, _ = shell.Execf(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "FLUSH PRIVILEGES;"`)
mysql, err := db.NewMySQL("root", rootPassword, "/tmp/mysql.sock", "unix")
if err != nil {
return models.Website{}, err
}
if err = mysql.DatabaseCreate(website.DbName); err != nil {
return models.Website{}, err
}
if err = mysql.UserCreate(website.DbUser, website.DbPassword); err != nil {
return models.Website{}, err
}
if err = mysql.PrivilegesGrant(website.DbUser, website.DbName); err != nil {
return models.Website{}, err
}
}
if website.Db && website.DbType == "postgresql" {
_, _ = shell.Execf(`echo "CREATE DATABASE ` + website.DbName + `;" | su - postgres -c "psql"`)
_, _ = shell.Execf(`echo "CREATE USER ` + website.DbUser + ` WITH PASSWORD '` + website.DbPassword + `';" | su - postgres -c "psql"`)
_, _ = shell.Execf(`echo "ALTER DATABASE ` + website.DbName + ` OWNER TO ` + website.DbUser + `;" | su - postgres -c "psql"`)
_, _ = shell.Execf(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + website.DbName + ` TO ` + website.DbUser + `;" | su - postgres -c "psql"`)
_, _ = shell.Execf(`echo "CREATE DATABASE '%s';" | su - postgres -c "psql"`, website.DbName)
_, _ = shell.Execf(`echo "CREATE USER '%s' WITH PASSWORD '%s';" | su - postgres -c "psql"`, website.DbUser, website.DbPassword)
_, _ = shell.Execf(`echo "ALTER DATABASE '%s' OWNER TO '%s';" | su - postgres -c "psql"`, website.DbName, website.DbUser)
_, _ = shell.Execf(`echo "GRANT ALL PRIVILEGES ON DATABASE '%s' TO '%s';" | su - postgres -c "psql"`, website.DbName, website.DbUser)
userConfig := "host " + website.DbName + " " + website.DbUser + " 127.0.0.1/32 scram-sha-256"
_, _ = shell.Execf(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`)
_ = systemctl.Reload("postgresql")
@@ -326,6 +345,7 @@ func (r *WebsiteImpl) SaveConfig(config requests.SaveConfig) error {
return err
}
if err = systemctl.Reload("openresty"); err != nil {
_, err = shell.Execf("openresty -t")
return err
}
@@ -356,34 +376,30 @@ func (r *WebsiteImpl) SaveConfig(config requests.SaveConfig) error {
raw = strings.Replace(raw, domainConfigOld, "\n "+domain+"\n ", -1)
// 端口
var port strings.Builder
var portConf strings.Builder
ports := config.Ports
for i, v := range ports {
vStr := cast.ToString(v)
if v == 443 && config.Ssl {
vStr = ` listen 443 ssl;
listen [::]:443 ssl;
listen 443 quic;
listen [::]:443 quic;`
port.WriteString(vStr)
if i != len(ports)-1 {
port.WriteString("\n")
}
continue
for _, port := range ports {
https := ""
quic := false
if slices.Contains(config.TLSPorts, port) {
https = " ssl"
quic = true
}
if i != len(ports)-1 {
port.WriteString(" listen " + vStr + ";\n")
port.WriteString(" listen [::]:" + vStr + ";\n")
} else {
port.WriteString(" listen " + vStr + ";\n")
port.WriteString(" listen [::]:" + vStr + ";")
portConf.WriteString(fmt.Sprintf(" listen %d%s;\n", port, https))
portConf.WriteString(fmt.Sprintf(" listen [::]:%d%s;\n", port, https))
if quic {
portConf.WriteString(fmt.Sprintf(" listen %d%s;\n", port, " quic"))
portConf.WriteString(fmt.Sprintf(" listen [::]:%d%s;\n", port, " quic"))
}
}
portConfigOld := str.Cut(raw, "# port标记位开始", "# port标记位结束")
if len(strings.TrimSpace(portConfigOld)) == 0 {
portConf.WriteString(" ")
portConfNew := portConf.String()
portConfOld := str.Cut(raw, "# port标记位开始", "# port标记位结束")
if len(strings.TrimSpace(portConfOld)) == 0 {
return errors.New("配置文件中缺少port标记位")
}
raw = strings.Replace(raw, portConfigOld, "\n"+port.String()+"\n ", -1)
raw = strings.Replace(raw, portConfOld, "\n"+portConfNew, -1)
// 运行目录
root := str.Cut(raw, "# root标记位开始", "# root标记位结束")
@@ -525,13 +541,18 @@ func (r *WebsiteImpl) SaveConfig(config requests.SaveConfig) error {
return err
}
return systemctl.Reload("openresty")
err = systemctl.Reload("openresty")
if err != nil {
_, err = shell.Execf("openresty -t")
}
return err
}
// Delete 删除网站
func (r *WebsiteImpl) Delete(id uint) error {
func (r *WebsiteImpl) Delete(request requests.Delete) error {
var website models.Website
if err := facades.Orm().Query().With("Cert").Where("id", id).FirstOrFail(&website); err != nil {
if err := facades.Orm().Query().With("Cert").Where("id", request.ID).FirstOrFail(&website); err != nil {
return err
}
@@ -543,23 +564,33 @@ func (r *WebsiteImpl) Delete(id uint) error {
return err
}
if err := io.Remove("/www/server/vhost/" + website.Name + ".conf"); err != nil {
return err
_ = io.Remove("/www/server/vhost/" + website.Name + ".conf")
_ = io.Remove("/www/server/vhost/rewrite/" + website.Name + ".conf")
_ = io.Remove("/www/server/vhost/acme/" + website.Name + ".conf")
_ = io.Remove("/www/server/vhost/ssl/" + website.Name + ".pem")
_ = io.Remove("/www/server/vhost/ssl/" + website.Name + ".key")
if request.Path {
_ = io.Remove(website.Path)
}
if err := io.Remove("/www/server/vhost/rewrite/" + website.Name + ".conf"); err != nil {
return err
}
if err := io.Remove("/www/server/vhost/ssl/" + website.Name + ".pem"); err != nil {
return err
}
if err := io.Remove("/www/server/vhost/ssl/" + website.Name + ".key"); err != nil {
return err
}
if err := io.Remove(website.Path); err != nil {
return err
if request.DB {
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
mysql, err := db.NewMySQL("root", rootPassword, "/tmp/mysql.sock", "unix")
if err != nil {
return err
}
_ = mysql.DatabaseDrop(website.Name)
_ = mysql.UserDrop(website.Name)
_, _ = shell.Execf(`echo "DROP DATABASE IF EXISTS '%s';" | su - postgres -c "psql"`, website.Name)
_, _ = shell.Execf(`echo "DROP USER IF EXISTS '%s';" | su - postgres -c "psql"`, website.Name)
}
return systemctl.Reload("openresty")
err := systemctl.Reload("openresty")
if err != nil {
_, err = shell.Execf("openresty -t")
}
return err
}
// GetConfig 获取网站配置
@@ -582,7 +613,7 @@ func (r *WebsiteImpl) GetConfig(id uint) (types.WebsiteSetting, error) {
setting.Raw = config
ports := str.Cut(config, "# port标记位开始", "# port标记位结束")
matches := regexp.MustCompile(`listen\s+(.*);`).FindAllStringSubmatch(ports, -1)
matches := regexp.MustCompile(`listen\s+([^;]*);?`).FindAllStringSubmatch(ports, -1)
for _, match := range matches {
if len(match) < 2 {
continue
@@ -598,26 +629,27 @@ func (r *WebsiteImpl) GetConfig(id uint) (types.WebsiteSetting, error) {
setting.Ports = append(setting.Ports, cast.ToUint(ports[0]))
} else if len(ports) > 1 && ports[1] == "ssl" {
setting.Ports = append(setting.Ports, cast.ToUint(ports[0]))
setting.TLSPorts = append(setting.TLSPorts, cast.ToUint(ports[0]))
}
}
serverName := str.Cut(config, "# server_name标记位开始", "# server_name标记位结束")
match := regexp.MustCompile(`server_name\s+(.*);`).FindStringSubmatch(serverName)
match := regexp.MustCompile(`server_name\s+([^;]*);?`).FindStringSubmatch(serverName)
if len(match) > 1 {
setting.Domains = strings.Split(match[1], " ")
}
root := str.Cut(config, "# root标记位开始", "# root标记位结束")
match = regexp.MustCompile(`root\s+(.*);`).FindStringSubmatch(root)
match = regexp.MustCompile(`root\s+([^;]*);?`).FindStringSubmatch(root)
if len(match) > 1 {
setting.Root = match[1]
}
index := str.Cut(config, "# index标记位开始", "# index标记位结束")
match = regexp.MustCompile(`index\s+(.*);`).FindStringSubmatch(index)
match = regexp.MustCompile(`index\s+([^;]*);?`).FindStringSubmatch(index)
if len(match) > 1 {
setting.Index = match[1]
}
if io.Exists(setting.Root + "/.user.ini") {
userIni, _ := io.Read(setting.Root + "/.user.ini")
if io.Exists(filepath.Join(setting.Root, ".user.ini")) {
userIni, _ := io.Read(filepath.Join(setting.Root, ".user.ini"))
if strings.Contains(userIni, "open_basedir") {
setting.OpenBasedir = true
} else {
@@ -635,29 +667,31 @@ func (r *WebsiteImpl) GetConfig(id uint) (types.WebsiteSetting, error) {
ssl := str.Cut(config, "# ssl标记位开始", "# ssl标记位结束")
setting.HttpRedirect = strings.Contains(ssl, "# http重定向标记位")
setting.Hsts = strings.Contains(ssl, "# hsts标记位")
if decode, err := cert.ParseCert(crt); err == nil {
setting.SslNotBefore = decode.NotBefore.Format("2006-01-02 15:04:05")
setting.SslNotAfter = decode.NotAfter.Format("2006-01-02 15:04:05")
setting.SslIssuer = decode.Issuer.CommonName
setting.SslOCSPServer = decode.OCSPServer
setting.SSlDNSNames = decode.DNSNames
}
} else {
setting.HttpRedirect = false
setting.Hsts = false
}
// 解析证书信息
if decode, err := cert.ParseCert(crt); err == nil {
setting.SslNotBefore = decode.NotBefore.Format("2006-01-02 15:04:05")
setting.SslNotAfter = decode.NotAfter.Format("2006-01-02 15:04:05")
setting.SslIssuer = decode.Issuer.CommonName
setting.SslOCSPServer = decode.OCSPServer
setting.SSlDNSNames = decode.DNSNames
}
waf := str.Cut(config, "# waf标记位开始", "# waf标记位结束")
setting.Waf = strings.Contains(waf, "waf on;")
match = regexp.MustCompile(`waf_mode\s+(.+);`).FindStringSubmatch(waf)
match = regexp.MustCompile(`waf_mode\s+([^;]*);?`).FindStringSubmatch(waf)
if len(match) > 1 {
setting.WafMode = match[1]
}
match = regexp.MustCompile(`waf_cc_deny\s+(.+);`).FindStringSubmatch(waf)
match = regexp.MustCompile(`waf_cc_deny\s+([^;]*);?`).FindStringSubmatch(waf)
if len(match) > 1 {
setting.WafCcDeny = match[1]
}
match = regexp.MustCompile(`waf_cache\s+(.+);`).FindStringSubmatch(waf)
match = regexp.MustCompile(`waf_cache\s+([^;]*);?`).FindStringSubmatch(waf)
if len(match) > 1 {
setting.WafCache = match[1]
}
+2 -2
View File
@@ -8,9 +8,9 @@ import (
type Website interface {
List(page int, limit int) (int64, []models.Website, error)
Add(website types.Website) (models.Website, error)
Add(website types.WebsiteAdd) (models.Website, error)
SaveConfig(config requests.SaveConfig) error
Delete(id uint) error
Delete(id requests.Delete) error
GetConfig(id uint) (types.WebsiteSetting, error)
GetConfigByName(name string) (types.WebsiteSetting, error)
GetIDByName(name string) (uint, error)
+4 -6
View File
@@ -1,8 +1,9 @@
{
"auth": {
"token": {
"expired": "login has expired",
"missing": "not logged in"
"session": {
"expired": "session has expired",
"missing": "Please enable cookies and try again",
"invalid": "invalid session"
}
},
"commands": {
@@ -194,9 +195,6 @@
"incompatible": "plugin :slug is incompatible with :exclude plugin"
}
},
"messages": {
"mistake": "mistake"
},
"status": {
"upgrade": "Panel is currently undergoing an upgrade. Please try again later.",
"maintain": "Panel is currently undergoing maintenance. Please try again later.",
+4 -6
View File
@@ -1,8 +1,9 @@
{
"auth": {
"token": {
"expired": "登录已过期",
"missing": "未登录"
"session": {
"expired": "会话已过期",
"missing": "请启用 Cookie 后再试",
"invalid": "会话无效"
}
},
"commands": {
@@ -194,9 +195,6 @@
"incompatible": "插件 :slug 不兼容 :exclude 插件"
}
},
"messages": {
"mistake": "错误"
},
"status": {
"upgrade": "面板升级中,请稍后",
"maintain": "面板正在运行维护,请稍后",

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