mirror of
https://github.com/tnb-labs/panel.git
synced 2026-08-31 01:12:17 +08:00
Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 00815979d2 | |||
| c4e01b37b7 | |||
| 7907345521 | |||
| 8f32d6cdb0 | |||
| c3582903bf | |||
| 7d25d2f5aa | |||
| 46fc7b1c6a | |||
| 34ac62d1ae | |||
| 30e78ccf46 | |||
| 22f4cc7fcb | |||
| 1771ae682d | |||
| d1beb60ffd | |||
| cba5f86667 | |||
| e1d9832ce0 | |||
| 6278535902 | |||
| 32fb87243b | |||
| 5f85e7fbe0 | |||
| c36104a075 | |||
| 583f5d13c7 | |||
| b453738daf | |||
| 98431fb4b2 | |||
| 4d76e9286d | |||
| c70758339f | |||
| ff524cdb0b | |||
| 73c95d880a | |||
| 8a1d8c0b05 | |||
| 2251175906 | |||
| 69edde98c8 | |||
| 29a3db640d | |||
| ee87bf65b4 | |||
| aa080dab15 | |||
| b4e5498b48 | |||
| 63838bffa1 | |||
| fa41b18ced | |||
| 31bd15217b | |||
| e6b6f06706 | |||
| 1b836297bf | |||
| 96bea2cf90 | |||
| 8aff513284 | |||
| 368e41a512 | |||
| 924fdee7c5 | |||
| 438ba1db1e | |||
| d4e52a2d87 | |||
| 4432a12c7a | |||
| a75e36e447 | |||
| 05674ee04e | |||
| 09703761eb | |||
| dfa14c3fd4 | |||
| 7b80f034ea | |||
| 71923093fd | |||
| aae3cf0518 | |||
| ed22e79552 | |||
| 15dd5bb718 | |||
| 0e7af2914e | |||
| 7ec378829b | |||
| f2e8ff86c6 | |||
| eed03d644f |
@@ -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
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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, "获取更新信息失败")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
|
||||
@@ -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("获取用户信息失败")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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 之间",
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:"-"`
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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{},
|
||||
}
|
||||
|
||||
@@ -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 ""
|
||||
}
|
||||
@@ -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
@@ -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{},
|
||||
|
||||
@@ -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
@@ -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
@@ -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,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
@@ -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
@@ -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
@@ -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),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 配置
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 连接网络
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
Reference in New Issue
Block a user