Compare commits

...

22 Commits

Author SHA1 Message Date
耗子 30e78ccf46 feat: 发布v2.2.19 2024-07-03 02:02:38 +08:00
耗子 22f4cc7fcb feat: 支持腾讯云 2024-07-03 01:45:25 +08:00
耗子 1771ae682d Merge remote-tracking branch 'origin/main' 2024-07-02 21:07:19 +08:00
耗子 d1beb60ffd fix: service文件错误 2024-07-02 21:06:57 +08:00
renovate[bot] cba5f86667 chore(deps): Update module github.com/docker/docker to v27.0.3+incompatible 2024-07-02 10:03:44 +00:00
耗子 e1d9832ce0 fix: lint 2024-07-01 15:49:24 +08:00
耗子 6278535902 fix: lint 2024-06-28 23:40:59 +08:00
耗子 32fb87243b feat: 部分软件增加安装校验 2024-06-28 16:41:57 +08:00
耗子 5f85e7fbe0 feat: 添加PostgreSQL的操作方法 2024-06-28 04:01:46 +08:00
耗子 c36104a075 feat: 去掉mysql命令的路径 2024-06-28 03:25:59 +08:00
耗子 583f5d13c7 feat: 优化安装脚本 2024-06-28 03:14:26 +08:00
耗子 b453738daf fix: docker的弃用警告 2024-06-28 03:05:46 +08:00
耗子 98431fb4b2 feat: 发布v2.2.18 2024-06-28 02:53:45 +08:00
耗子 4d76e9286d feat: 删除网站支持删除数据库 2024-06-28 02:52:31 +08:00
耗子 c70758339f fix: build 2024-06-28 01:58:43 +08:00
耗子 ff524cdb0b feat: update dependencies 2024-06-28 01:32:32 +08:00
耗子 73c95d880a Merge remote-tracking branch 'refs/remotes/origin/renovate/github.com-docker-docker-27.x' 2024-06-28 01:28:31 +08:00
耗子 8a1d8c0b05 fix: 优化数据库正则 2024-06-28 01:20:37 +08:00
耗子 2251175906 docs: update readme 2024-06-27 19:13:45 +08:00
机器人 69edde98c8 chore(deps): Update module github.com/docker/docker to v27 2024-06-27 04:07:08 +00:00
耗子 29a3db640d fix: 去掉错误的参数 2024-06-25 18:43:55 +08:00
耗子 ee87bf65b4 feat: 调整部分参数 2024-06-25 18:42:00 +08:00
92 changed files with 740 additions and 393 deletions
+5 -1
View File
@@ -2,7 +2,11 @@
[简体中文] | [<a href="README_EN.md">English</a>]
</p>
<h1 align="center">耗子面板</h1>
<h1 align="center" style="font-size: 40px">耗子面板</h1>
<p align="center">
<a href="https://trendshift.io/repositories/10950" target="_blank"><img src="https://trendshift.io/api/badge/repositories/10950" alt="TheTNB%2Fpanel | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
</p>
<p align="center">
<a href="https://github.com/TheTNB/panel/releases"><img src="https://img.shields.io/github/release/TheTNB/panel.svg"></a>
+5 -1
View File
@@ -2,7 +2,11 @@
[<a href="README.md">简体中文</a>] | [English]
</p>
<h1 align="center">Rat Panel</h1>
<h1 align="center" style="font-size: 40px">Rat Panel</h1>
<p align="center">
<a href="https://trendshift.io/repositories/10950" target="_blank"><img src="https://trendshift.io/api/badge/repositories/10950" alt="TheTNB%2Fpanel | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
</p>
<p align="center">
<a href="https://github.com/TheTNB/panel/releases"><img src="https://img.shields.io/github/release/TheTNB/panel.svg"></a>
+2 -1
View File
@@ -15,6 +15,7 @@ import (
"github.com/goravel/framework/support/color"
"github.com/spf13/cast"
requests "github.com/TheTNB/panel/app/http/requests/website"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/io"
@@ -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
}
+4
View File
@@ -73,6 +73,10 @@ func (r *CertController) DNSProviders(ctx http.Context) http.Response {
"name": "DNSPod",
"dns": acme.DnsPod,
},
{
"name": "腾讯云",
"dns": acme.Tencent,
},
{
"name": "阿里云",
"dns": acme.AliYun,
@@ -225,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
}
@@ -257,7 +257,7 @@ 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
}
@@ -316,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
}
@@ -350,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
}
@@ -388,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
}
@@ -416,7 +416,7 @@ 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
}
@@ -437,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
}
@@ -460,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
}
@@ -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
}
+8 -9
View File
@@ -118,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)
+4
View File
@@ -21,6 +21,10 @@ func (r *CertDeploy) Rules(ctx http.Context) map[string]string {
}
}
func (r *CertDeploy) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertDeploy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -19,6 +19,10 @@ func (r *CertShowAndDestroy) Rules(ctx http.Context) map[string]string {
}
}
func (r *CertShowAndDestroy) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertShowAndDestroy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -29,6 +29,10 @@ func (r *CertStore) Rules(ctx http.Context) map[string]string {
}
}
func (r *CertStore) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertStore) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -31,6 +31,10 @@ func (r *CertUpdate) Rules(ctx http.Context) map[string]string {
}
}
func (r *CertUpdate) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertUpdate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -19,6 +19,10 @@ func (r *DNSShowAndDestroy) Rules(ctx http.Context) map[string]string {
}
}
func (r *DNSShowAndDestroy) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DNSShowAndDestroy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+7 -3
View File
@@ -19,17 +19,21 @@ func (r *DNSStore) Authorize(ctx http.Context) error {
func (r *DNSStore) Rules(ctx http.Context) map[string]string {
return map[string]string{
"type": "required|in:dnspod,aliyun,cloudflare",
"type": "required|in:dnspod,tencent,aliyun,cloudflare",
"name": "required",
"data": "required",
"data.id": "required_if:type,dnspod",
"data.token": "required_if:type,dnspod",
"data.access_key": "required_if:type,aliyun",
"data.secret_key": "required_if:type,aliyun",
"data.access_key": "required_if:type,aliyun,tencent",
"data.secret_key": "required_if:type,aliyun,tencent",
"data.api_key": "required_if:type,cloudflare",
}
}
func (r *DNSStore) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DNSStore) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -33,6 +33,10 @@ func (r *DNSUpdate) Rules(ctx http.Context) map[string]string {
}
}
func (r *DNSUpdate) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DNSUpdate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -19,6 +19,10 @@ func (r *Obtain) Rules(ctx http.Context) map[string]string {
}
}
func (r *Obtain) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Obtain) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -19,6 +19,10 @@ func (r *Renew) Rules(ctx http.Context) map[string]string {
}
}
func (r *Renew) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Renew) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -19,6 +19,10 @@ func (r *UserShowAndDestroy) Rules(ctx http.Context) map[string]string {
}
}
func (r *UserShowAndDestroy) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UserShowAndDestroy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -27,6 +27,10 @@ func (r *UserStore) Rules(ctx http.Context) map[string]string {
}
}
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{}
}
+4
View File
@@ -29,6 +29,10 @@ func (r *UserUpdate) Rules(ctx http.Context) map[string]string {
}
}
func (r *UserUpdate) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UserUpdate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -21,6 +21,10 @@ func (r *Paginate) Rules(ctx http.Context) map[string]string {
}
}
func (r *Paginate) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Paginate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -64,6 +64,10 @@ func (r *ContainerCreate) Rules(ctx http.Context) map[string]string {
}
}
func (r *ContainerCreate) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ContainerCreate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -21,6 +21,10 @@ func (r *ContainerRename) Rules(ctx http.Context) map[string]string {
}
}
func (r *ContainerRename) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ContainerRename) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -57,6 +57,10 @@ func (r *ContainerUpdate) Rules(ctx http.Context) map[string]string {
}
}
func (r *ContainerUpdate) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ContainerUpdate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -19,6 +19,10 @@ func (r *ID) Rules(ctx http.Context) map[string]string {
}
}
func (r *ID) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ID) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -25,6 +25,10 @@ func (r *ImagePull) Rules(ctx http.Context) map[string]string {
}
}
func (r *ImagePull) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ImagePull) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -21,6 +21,10 @@ func (r *NetworkConnectDisConnect) Rules(ctx http.Context) map[string]string {
}
}
func (r *NetworkConnectDisConnect) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *NetworkConnectDisConnect) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -31,6 +31,10 @@ func (r *NetworkCreate) Rules(ctx http.Context) map[string]string {
}
}
func (r *NetworkCreate) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *NetworkCreate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -27,6 +27,10 @@ func (r *VolumeCreate) Rules(ctx http.Context) map[string]string {
}
}
func (r *VolumeCreate) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *VolumeCreate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -22,6 +22,10 @@ func (r *Archive) Rules(ctx http.Context) map[string]string {
}
}
func (r *Archive) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Archive) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -21,6 +21,10 @@ func (r *Copy) Rules(ctx http.Context) map[string]string {
}
}
func (r *Copy) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Copy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -19,6 +19,10 @@ func (r *Exist) Rules(ctx http.Context) map[string]string {
}
}
func (r *Exist) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Exist) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -21,6 +21,10 @@ func (r *Move) Rules(ctx http.Context) map[string]string {
}
}
func (r *Move) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Move) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -19,6 +19,10 @@ func (r *NotExist) Rules(ctx http.Context) map[string]string {
}
}
func (r *NotExist) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *NotExist) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -25,6 +25,10 @@ func (r *Permission) Rules(ctx http.Context) map[string]string {
}
}
func (r *Permission) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Permission) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -21,6 +21,10 @@ func (r *Save) Rules(ctx http.Context) map[string]string {
}
}
func (r *Save) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Save) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -21,6 +21,10 @@ func (r *Search) Rules(ctx http.Context) map[string]string {
}
}
func (r *Search) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Search) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -21,6 +21,10 @@ func (r *UnArchive) Rules(ctx http.Context) map[string]string {
}
}
func (r *UnArchive) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UnArchive) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -23,6 +23,10 @@ func (r *Upload) Rules(ctx http.Context) map[string]string {
}
}
func (r *Upload) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Upload) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -19,6 +19,10 @@ func (r *Service) Rules(ctx http.Context) map[string]string {
}
}
func (r *Service) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Service) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -21,6 +21,10 @@ func (r *UpdateConfig) Rules(ctx http.Context) map[string]string {
}
}
func (r *UpdateConfig) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UpdateConfig) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -19,6 +19,10 @@ func (r *UpdateConfig) Rules(ctx http.Context) map[string]string {
}
}
func (r *UpdateConfig) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UpdateConfig) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -19,6 +19,10 @@ func (r *UpdateRegistryConfig) Rules(ctx http.Context) map[string]string {
}
}
func (r *UpdateRegistryConfig) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UpdateRegistryConfig) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -19,6 +19,10 @@ func (r *UpdateStorageConfig) Rules(ctx http.Context) map[string]string {
}
}
func (r *UpdateStorageConfig) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UpdateStorageConfig) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -29,6 +29,10 @@ func (r *Create) Rules(ctx http.Context) map[string]string {
}
}
func (r *Create) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Create) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -29,6 +29,10 @@ func (r *Update) Rules(ctx http.Context) map[string]string {
}
}
func (r *Update) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Update) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -19,6 +19,10 @@ func (r *UpdateConfig) Rules(ctx http.Context) map[string]string {
}
}
func (r *UpdateConfig) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UpdateConfig) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -23,6 +23,10 @@ func (r *Https) Rules(ctx http.Context) map[string]string {
}
}
func (r *Https) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Https) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -35,6 +35,10 @@ func (r *Update) Rules(ctx http.Context) map[string]string {
}
}
func (r *Update) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Update) Messages(ctx http.Context) map[string]string {
return map[string]string{
"port.int": "port 值必须是一个整数且在 1000 - 65535 之间",
+5 -1
View File
@@ -23,12 +23,16 @@ func (r *Login) Rules(ctx http.Context) map[string]string {
func (r *Login) Messages(ctx http.Context) map[string]string {
return map[string]string{
"username.required": "登录名不能为空",
"username.required": "用户名不能为空",
"password.required": "密码不能为空",
"password.min_len": "密码长度不能小于 8 位",
}
}
func (r *Login) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Login) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -37,6 +37,10 @@ func (r *Add) Rules(ctx http.Context) map[string]string {
}
}
func (r *Add) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Add) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+40
View File
@@ -0,0 +1,40 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type Delete struct {
ID uint `form:"id" json:"id" filter:"uint"`
Path bool `form:"path" json:"path"`
DB bool `form:"db" json:"db"`
}
func (r *Delete) Authorize(ctx http.Context) error {
return nil
}
func (r *Delete) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|exists:websites,id",
"path": "bool",
"db": "bool",
}
}
func (r *Delete) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Delete) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Delete) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Delete) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
@@ -19,6 +19,10 @@ func (r *DeleteBackup) Rules(ctx http.Context) map[string]string {
}
}
func (r *DeleteBackup) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DeleteBackup) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -19,6 +19,10 @@ func (r *ID) Rules(ctx http.Context) map[string]string {
}
}
func (r *ID) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ID) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
@@ -21,6 +21,10 @@ func (r *RestoreBackup) Rules(ctx http.Context) map[string]string {
}
}
func (r *RestoreBackup) Filters(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *RestoreBackup) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
+4
View File
@@ -57,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{}
}
+1 -1
View File
@@ -9,7 +9,7 @@ import (
type CertDNS struct {
orm.Model
Name string `gorm:"not null" json:"name"` // 备注名称
Type string `gorm:"not null" json:"type"` // DNS 提供商 (dnspod, aliyun, cloudflare)
Type string `gorm:"not null" json:"type"` // DNS 提供商 (dnspod, tencent, aliyun, cloudflare)
Data acme.DNSParam `gorm:"not null;serializer:json" json:"dns_param"`
Certs []*Cert `gorm:"foreignKey:DNSID" json:"-"`
@@ -25,7 +25,6 @@ func (receiver *ValidationServiceProvider) rules() []validation.Rule {
return []validation.Rule{
&rules.Exists{},
&rules.NotExists{},
&rules.Captcha{},
&rules.PathExists{},
&rules.PathNotExists{},
}
-46
View File
@@ -1,46 +0,0 @@
package rules
import (
"strconv"
"github.com/goravel/framework/contracts/validation"
"github.com/TheTNB/panel/pkg/captcha"
)
type Captcha struct {
}
// Signature The name of the rule.
func (receiver *Captcha) Signature() string {
return "captcha"
}
// Passes Determine if the validation rule passes.
func (receiver *Captcha) Passes(data validation.Data, val any, options ...any) bool {
captchaID, exist := data.Get("captcha_id")
if !exist {
return false
}
// 第一个参数(如果有),是否清除验证码,如 false
clear := true
var err error
if len(options) > 0 {
clear, err = strconv.ParseBool(options[0].(string))
if err != nil {
clear = true
}
}
if !captcha.NewCaptcha().VerifyCaptcha(captchaID.(string), val.(string), clear) {
return false
}
return true
}
// Message Get the validation error message.
func (receiver *Captcha) Message() string {
return ""
}
+1 -1
View File
@@ -11,7 +11,7 @@ import (
func Boot() {
debug.SetGCPercent(10)
debug.SetMemoryLimit(512 << 20)
debug.SetMemoryLimit(64 << 20)
zhcn.RegisterGlobal()
-34
View File
@@ -1,34 +0,0 @@
package config
import (
"github.com/goravel/framework/facades"
)
func init() {
config := facades.Config()
config.Add("captcha", map[string]any{
// 验证码图片高度
"height": 60,
// 验证码图片宽度
"width": 120,
// 验证码的长度
"length": 6,
// 数字的最大倾斜角度
"maxskew": 0.6,
// 图片背景里的混淆点数量
"dotcount": 40,
// 过期时间,单位是分钟
"expire_time": 5,
// debug 模式下的过期时间,方便本地开发调试
"debug_expire_time": 3600,
// 非 production 环境,使用此 key 可跳过验证,方便测试
"testing_key": "captcha_skip_test",
})
}
+1 -1
View File
@@ -8,7 +8,7 @@ func init() {
config := facades.Config()
config.Add("panel", map[string]any{
"name": "耗子面板",
"version": "v2.2.17",
"version": "v2.2.19",
"ssl": config.Env("APP_SSL", false),
})
}
+24 -8
View File
@@ -3574,8 +3574,8 @@ const docTemplate = `{
}
}
},
"/panel/websites/{id}": {
"delete": {
"/panel/websites/delete": {
"post": {
"security": [
{
"BearerToken": []
@@ -3593,11 +3593,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": {
@@ -5156,7 +5158,7 @@ const docTemplate = `{
"type": "string"
},
"type": {
"description": "DNS 提供商 (dnspod, aliyun, cloudflare)",
"description": "DNS 提供商 (dnspod, tencent, aliyun, cloudflare)",
"type": "string"
},
"updated_at": {
@@ -5506,6 +5508,20 @@ const docTemplate = `{
}
}
},
"requests.Delete": {
"type": "object",
"properties": {
"db": {
"type": "boolean"
},
"id": {
"type": "integer"
},
"path": {
"type": "boolean"
}
}
},
"requests.DeleteBackup": {
"type": "object",
"properties": {
+24 -8
View File
@@ -3567,8 +3567,8 @@
}
}
},
"/panel/websites/{id}": {
"delete": {
"/panel/websites/delete": {
"post": {
"security": [
{
"BearerToken": []
@@ -3586,11 +3586,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": {
@@ -5149,7 +5151,7 @@
"type": "string"
},
"type": {
"description": "DNS 提供商 (dnspod, aliyun, cloudflare)",
"description": "DNS 提供商 (dnspod, tencent, aliyun, cloudflare)",
"type": "string"
},
"updated_at": {
@@ -5499,6 +5501,20 @@
}
}
},
"requests.Delete": {
"type": "object",
"properties": {
"db": {
"type": "boolean"
},
"id": {
"type": "integer"
},
"path": {
"type": "boolean"
}
}
},
"requests.DeleteBackup": {
"type": "object",
"properties": {
+33 -23
View File
@@ -148,7 +148,7 @@ definitions:
description: 备注名称
type: string
type:
description: DNS 提供商 (dnspod, aliyun, cloudflare)
description: DNS 提供商 (dnspod, tencent, aliyun, cloudflare)
type: string
updated_at:
$ref: '#/definitions/github_com_goravel_framework_support_carbon.DateTime'
@@ -377,6 +377,15 @@ definitions:
type:
type: string
type: object
requests.Delete:
properties:
db:
type: boolean
id:
type: integer
path:
type: boolean
type: object
requests.DeleteBackup:
properties:
name:
@@ -2840,28 +2849,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:
@@ -3048,6 +3035,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 配置
+4 -5
View File
@@ -3,13 +3,13 @@ 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/framework v1.14.1-0.20240627172353-91c3c46cf333
github.com/goravel/gin v1.2.1
github.com/gorilla/websocket v1.5.3
github.com/lib/pq v1.10.9
@@ -17,9 +17,9 @@ require (
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
@@ -98,7 +98,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
@@ -169,6 +168,7 @@ 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
@@ -190,7 +190,6 @@ require (
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/sync v0.7.0 // indirect
golang.org/x/sys v0.21.0 // indirect
+8 -10
View File
@@ -134,8 +134,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=
@@ -225,8 +225,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=
@@ -278,8 +276,8 @@ 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/framework v1.14.1-0.20240627172353-91c3c46cf333 h1:hOj1S1TQQ/19ng0JzpbTfscB128L5E5dIcx/mU1zofc=
github.com/goravel/framework v1.14.1-0.20240627172353-91c3c46cf333/go.mod h1:8Fz0+JHyqwnOLy8rFkFWPQXv0Bkk3JEB43OV/Wx7xCA=
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/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
@@ -363,6 +361,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=
@@ -396,8 +396,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=
@@ -513,6 +511,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=
@@ -596,8 +596,6 @@ golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5D
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/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=
+3 -3
View File
@@ -24,16 +24,16 @@ type Container interface {
ContainerInspect(id string) (types.ContainerJSON, error)
ContainerKill(id string) error
ContainerRename(id string, newName string) error
ContainerStats(id string) (types.ContainerStats, error)
ContainerStats(id string) (container.StatsResponseReader, error)
ContainerExist(name string) (bool, error)
ContainerUpdate(id string, config container.UpdateConfig) error
ContainerLogs(id string) (string, error)
ContainerPrune() error
NetworkList() ([]types.NetworkResource, error)
NetworkList() ([]network.Inspect, error)
NetworkCreate(config requests.NetworkCreate) (string, error)
NetworkRemove(id string) error
NetworkExist(name string) (bool, error)
NetworkInspect(id string) (types.NetworkResource, error)
NetworkInspect(id string) (network.Inspect, error)
NetworkConnect(networkID string, containerID string) error
NetworkDisconnect(networkID string, containerID string) error
NetworkPrune() error
+2 -2
View File
@@ -165,7 +165,7 @@ func (s *BackupImpl) MysqlBackup(database string) error {
return err
}
if _, err := shell.Execf("/www/server/mysql/bin/mysqldump -uroot " + database + " > " + backupPath + "/" + backupFile); err != nil {
if _, err := shell.Execf("mysqldump -uroot " + database + " > " + backupPath + "/" + backupFile); err != nil {
return err
}
if _, err := shell.Execf("cd " + backupPath + " && zip -r " + backupPath + "/" + backupFile + ".zip " + backupFile); err != nil {
@@ -219,7 +219,7 @@ func (s *BackupImpl) MysqlRestore(database string, backupFile string) error {
return errors.New("无法找到备份文件")
}
if _, err = shell.Execf("/www/server/mysql/bin/mysql -uroot " + database + " < " + filepath.Join(tempDir, backupFile)); err != nil {
if _, err = shell.Execf("mysql -uroot " + database + " < " + filepath.Join(tempDir, backupFile)); err != nil {
return err
}
+8 -8
View File
@@ -118,7 +118,7 @@ func (r *Container) ContainerRename(id string, newName string) error {
}
// ContainerStats 查看容器状态
func (r *Container) ContainerStats(id string) (types.ContainerStats, error) {
func (r *Container) ContainerStats(id string) (container.StatsResponseReader, error) {
return r.client.ContainerStats(context.Background(), id, false)
}
@@ -167,8 +167,8 @@ func (r *Container) ContainerPrune() error {
}
// NetworkList 列出网络
func (r *Container) NetworkList() ([]types.NetworkResource, error) {
return r.client.NetworkList(context.Background(), types.NetworkListOptions{})
func (r *Container) NetworkList() ([]network.Inspect, error) {
return r.client.NetworkList(context.Background(), network.ListOptions{})
}
// NetworkCreate 创建网络
@@ -189,8 +189,8 @@ func (r *Container) NetworkCreate(config requests.NetworkCreate) (string, error)
})
}
options := types.NetworkCreate{
EnableIPv6: config.Ipv6.Enabled,
options := network.CreateOptions{
EnableIPv6: &config.Ipv6.Enabled,
Driver: config.Driver,
Options: r.KVToMap(config.Options),
Labels: r.KVToMap(config.Labels),
@@ -212,7 +212,7 @@ func (r *Container) NetworkRemove(id string) error {
// NetworkExist 判断网络是否存在
func (r *Container) NetworkExist(name string) (bool, error) {
var options types.NetworkListOptions
var options network.ListOptions
options.Filters = filters.NewArgs(filters.Arg("name", name))
networks, err := r.client.NetworkList(context.Background(), options)
if err != nil {
@@ -223,8 +223,8 @@ func (r *Container) NetworkExist(name string) (bool, error) {
}
// NetworkInspect 查看网络
func (r *Container) NetworkInspect(id string) (types.NetworkResource, error) {
return r.client.NetworkInspect(context.Background(), id, types.NetworkInspectOptions{})
func (r *Container) NetworkInspect(id string) (network.Inspect, error) {
return r.client.NetworkInspect(context.Background(), id, network.InspectOptions{})
}
// NetworkConnect 连接网络
+35 -11
View File
@@ -17,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"
@@ -296,16 +297,25 @@ server
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")
@@ -540,9 +550,9 @@ func (r *WebsiteImpl) SaveConfig(config requests.SaveConfig) error {
}
// 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
}
@@ -559,7 +569,21 @@ func (r *WebsiteImpl) Delete(id uint) error {
_ = io.Remove("/www/server/vhost/acme/" + website.Name + ".conf")
_ = io.Remove("/www/server/vhost/ssl/" + website.Name + ".pem")
_ = io.Remove("/www/server/vhost/ssl/" + website.Name + ".key")
_ = io.Remove(website.Path)
if request.Path {
_ = io.Remove(website.Path)
}
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)
}
err := systemctl.Reload("openresty")
if err != nil {
+1 -1
View File
@@ -10,7 +10,7 @@ type Website interface {
List(page int, limit int) (int64, []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)
+7
View File
@@ -3,6 +3,7 @@ package acme
import (
"context"
"fmt"
"github.com/libdns/tencentcloud"
"os"
"path/filepath"
"time"
@@ -125,6 +126,11 @@ func (s dnsSolver) getDNSProvider() (DNSProvider, error) {
dns = &dnspod.Provider{
APIToken: s.param.ID + "," + s.param.Token,
}
case Tencent:
dns = &tencentcloud.Provider{
SecretId: s.param.AccessKey,
SecretKey: s.param.SecretKey,
}
case AliYun:
dns = &alidns.Provider{
AccKeyID: s.param.AccessKey,
@@ -145,6 +151,7 @@ type DnsType string
const (
DnsPod DnsType = "dnspod"
Tencent DnsType = "tencent"
AliYun DnsType = "aliyun"
CloudFlare DnsType = "cloudflare"
)
-63
View File
@@ -1,63 +0,0 @@
// Package captcha 处理图片验证码逻辑
package captcha
import (
"sync"
"github.com/goravel/framework/facades"
"github.com/mojocn/base64Captcha"
)
type Captcha struct {
Base64Captcha *base64Captcha.Captcha
}
// once 确保 internalCaptcha 对象只初始化一次
var once sync.Once
// internalCaptcha 内部使用的 Captcha 对象
var internalCaptcha *Captcha
// NewCaptcha 单例模式获取
func NewCaptcha() *Captcha {
once.Do(func() {
// 初始化 Captcha 对象
internalCaptcha = &Captcha{}
// 使用 Cache facade 进行存储,并配置存储 Key 的前缀
store := CacheStore{
KeyPrefix: facades.Config().GetString("app.name") + ":captcha:",
}
// 配置 base64Captcha 驱动信息
driver := base64Captcha.NewDriverDigit(
facades.Config().GetInt("captcha.height"), // 宽
facades.Config().GetInt("captcha.width"), // 高
facades.Config().GetInt("captcha.length"), // 长度
facades.Config().Get("captcha.maxskew").(float64), // 数字的最大倾斜角度
facades.Config().GetInt("captcha.dotcount"), // 图片背景里的混淆点数量
)
// 实例化 base64Captcha 并赋值给内部使用的 internalCaptcha 对象
internalCaptcha.Base64Captcha = base64Captcha.NewCaptcha(driver, &store)
})
return internalCaptcha
}
// GenerateCaptcha 生成图片验证码
func (c *Captcha) GenerateCaptcha() (id string, b64s string, err error) {
id, b64s, _, err = c.Base64Captcha.Generate()
return id, b64s, err
}
// VerifyCaptcha 验证验证码是否正确
func (c *Captcha) VerifyCaptcha(id string, answer string, clear bool) (match bool) {
// 方便本地和 API 自动测试
if facades.Config().GetBool("app.debug") && id == facades.Config().GetString("captcha.testing_key") {
return true
}
// 这样方便用户多次提交,防止表单提交错误需要多次输入图片验证码
return c.Base64Captcha.Verify(id, answer, clear)
}
-45
View File
@@ -1,45 +0,0 @@
package captcha
import (
"testing"
"time"
"github.com/goravel/framework/testing/mock"
testifymock "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"
)
type CaptchaTestSuite struct {
suite.Suite
captcha *Captcha
}
func TestCaptchaTestSuite(t *testing.T) {
facade := mock.Factory()
mockConfig := facade.Config()
mockConfig.On("GetString", "app.name").Return("HaoZiPanel").Once()
mockConfig.On("GetInt", "captcha.height").Return(80).Once()
mockConfig.On("GetInt", "captcha.width").Return(240).Once()
mockConfig.On("GetInt", "captcha.length").Return(4).Once()
mockConfig.On("Get", "captcha.maxskew").Return(0.7).Once()
mockConfig.On("GetInt", "captcha.dotcount").Return(80).Once()
mockConfig.On("GetInt", "captcha.expire_time").Return(5).Once()
mockConfig.On("GetInt", "captcha.debug_expire_time").Return(10).Once()
mockConfig.On("GetBool", "app.debug").Return(true).Once()
mockCache := facade.Cache()
mockCache.On("Put", testifymock.Anything, testifymock.Anything, time.Minute*time.Duration(10)).Return(nil).Once()
suite.Run(t, &CaptchaTestSuite{
captcha: NewCaptcha(),
})
mockConfig.AssertExpectations(t)
}
func (s *CaptchaTestSuite) TestGenerateCaptcha() {
id, base64, err := s.captcha.GenerateCaptcha()
s.NotEmpty(id)
s.NotEmpty(base64)
s.Nil(err)
}
-45
View File
@@ -1,45 +0,0 @@
package captcha
import (
"time"
"github.com/goravel/framework/facades"
)
// CacheStore 实现 base64Captcha.Store interface
type CacheStore struct {
KeyPrefix string
}
// Set 实现 base64Captcha.Store interface 的 Set 方法
func (s *CacheStore) Set(key string, value string) error {
ExpireTime := time.Minute * time.Duration(facades.Config().GetInt("captcha.expire_time"))
// 方便本地开发调试
if facades.Config().GetBool("app.debug") {
ExpireTime = time.Minute * time.Duration(facades.Config().GetInt("captcha.debug_expire_time"))
}
err := facades.Cache().Put(s.KeyPrefix+key, value, ExpireTime)
if err != nil {
return err
}
return nil
}
// Get 实现 base64Captcha.Store interface 的 Get 方法
func (s *CacheStore) Get(key string, clear bool) string {
key = s.KeyPrefix + key
val := facades.Cache().Get(key, "").(string)
if clear {
facades.Cache().Forget(key)
}
return val
}
// Verify 实现 base64Captcha.Store interface 的 Verify 方法
func (s *CacheStore) Verify(key, answer string, clear bool) bool {
v := s.Get(key, clear)
return v == answer
}
+1 -1
View File
@@ -79,7 +79,7 @@ func (m *MySQL) UserCreate(user, password string) error {
}
func (m *MySQL) UserDrop(user string) error {
_, err := m.Exec(fmt.Sprintf("DROP USER IF EXISTS '%s'@'localhost'", user))
_, err := m.Exec(fmt.Sprintf("DROP USER IF EXISTS '%s'", user))
m.flushPrivileges()
return err
}
+206
View File
@@ -0,0 +1,206 @@
package db
import (
"database/sql"
"fmt"
_ "github.com/lib/pq"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/systemctl"
"github.com/TheTNB/panel/pkg/types"
)
type Postgres struct {
db *sql.DB
username string
password string
address string
port uint
}
func NewPostgres(username, password, address string, port uint) (*Postgres, error) {
dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=postgres sslmode=disable", address, port, username, password)
if password == "" {
dsn = fmt.Sprintf("host=%s port=%d user=%s dbname=postgres sslmode=disable", address, port, username)
}
db, err := sql.Open("postgres", dsn)
if err != nil {
return nil, fmt.Errorf("初始化Postgres连接失败: %w", err)
}
if db.Ping() != nil {
return nil, fmt.Errorf("连接Postgres失败: %w", err)
}
return &Postgres{
db: db,
username: username,
password: password,
address: address,
port: port,
}, nil
}
func (m *Postgres) Close() error {
return m.db.Close()
}
func (m *Postgres) Ping() error {
return m.db.Ping()
}
func (m *Postgres) Query(query string, args ...any) (*sql.Rows, error) {
return m.db.Query(query, args...)
}
func (m *Postgres) QueryRow(query string, args ...any) *sql.Row {
return m.db.QueryRow(query, args...)
}
func (m *Postgres) Exec(query string, args ...any) (sql.Result, error) {
return m.db.Exec(query, args...)
}
func (m *Postgres) Prepare(query string) (*sql.Stmt, error) {
return m.db.Prepare(query)
}
func (m *Postgres) DatabaseCreate(name string) error {
_, err := m.Exec(fmt.Sprintf("CREATE DATABASE %s", name))
return err
}
func (m *Postgres) DatabaseDrop(name string) error {
_, err := m.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", name))
return err
}
func (m *Postgres) UserCreate(user, password string) error {
_, err := m.Exec(fmt.Sprintf("CREATE USER %s WITH PASSWORD '%s'", user, password))
if err != nil {
return err
}
return nil
}
func (m *Postgres) UserDrop(user string) error {
_, err := m.Exec(fmt.Sprintf("DROP USER IF EXISTS %s", user))
if err != nil {
return err
}
_, _ = shell.Execf(`sed -i '/` + user + `/d' /www/server/postgresql/data/pg_hba.conf`)
return systemctl.Reload("postgresql")
}
func (m *Postgres) UserPassword(user, password string) error {
_, err := m.Exec(fmt.Sprintf("ALTER USER %s WITH PASSWORD '%s'", user, password))
return err
}
func (m *Postgres) PrivilegesGrant(user, database string) error {
if _, err := m.Exec(fmt.Sprintf("ALTER DATABASE %s OWNER TO %s", database, user)); err != nil {
return err
}
if _, err := m.Exec(fmt.Sprintf("GRANT ALL PRIVILEGES ON DATABASE %s TO %s", database, user)); err != nil {
return err
}
return nil
}
func (m *Postgres) PrivilegesRevoke(user, database string) error {
_, err := m.Exec(fmt.Sprintf("REVOKE ALL PRIVILEGES ON DATABASE %s FROM %s", database, user))
return err
}
func (m *Postgres) HostAdd(database, user, host string) error {
config := fmt.Sprintf("host %s %s %s scram-sha-256", database, user, host)
if err := io.WriteAppend("/www/server/postgresql/data/pg_hba.conf", config); err != nil {
return err
}
return systemctl.Reload("postgresql")
}
func (m *Postgres) HostRemove(database, user, host string) error {
regex := fmt.Sprintf(`host\s+%s\s+%s\s+%s`, database, user, host)
if _, err := shell.Execf(`sed -i '/` + regex + `/d' /www/server/postgresql/data/pg_hba.conf`); err != nil {
return err
}
return systemctl.Reload("postgresql")
}
func (m *Postgres) Users() ([]types.PostgresUser, error) {
query := `
SELECT rolname,
rolsuper,
rolcreaterole,
rolcreatedb,
rolreplication,
rolbypassrls
FROM pg_roles
WHERE rolcanlogin = true;
`
rows, err := m.Query(query)
if err != nil {
return nil, err
}
defer rows.Close()
var users []types.PostgresUser
for rows.Next() {
var user types.PostgresUser
var super, canCreateRole, canCreateDb, replication, bypassRls bool
if err = rows.Scan(&user.Role, &super, &canCreateRole, &canCreateDb, &replication, &bypassRls); err != nil {
return nil, err
}
permissions := map[string]bool{
"超级用户": super,
"创建角色": canCreateRole,
"创建数据库": canCreateDb,
"可以复制": replication,
"绕过行级安全": bypassRls,
}
for perm, enabled := range permissions {
if enabled {
user.Attributes = append(user.Attributes, perm)
}
}
if len(user.Attributes) == 0 {
user.Attributes = append(user.Attributes, "无")
}
users = append(users, user)
}
return users, nil
}
func (m *Postgres) Databases() ([]types.PostgresDatabase, error) {
query := `
SELECT d.datname, pg_catalog.pg_get_userbyid(d.datdba), pg_catalog.pg_encoding_to_char(d.encoding)
FROM pg_catalog.pg_database d
WHERE datistemplate = false;
`
rows, err := m.Query(query)
if err != nil {
return nil, err
}
defer rows.Close()
var databases []types.PostgresDatabase
for rows.Next() {
var db types.PostgresDatabase
if err := rows.Scan(&db.Name, &db.Owner, &db.Encoding); err != nil {
return nil, err
}
databases = append(databases, db)
}
return databases, nil
}
+3 -3
View File
@@ -17,7 +17,7 @@ import (
// Execf 执行 shell 命令
func Execf(shell string, args ...any) (string, error) {
if !CheckArgs(slice.ToString(args)...) {
return "", errors.New("你想干什么?")
return "", errors.New("发现危险的命令参数,中止执行")
}
var cmd *exec.Cmd
@@ -39,7 +39,7 @@ func Execf(shell string, args ...any) (string, error) {
// ExecfAsync 异步执行 shell 命令
func ExecfAsync(shell string, args ...any) error {
if !CheckArgs(slice.ToString(args)...) {
return errors.New("你想干什么?")
return errors.New("发现危险的命令参数,中止执行")
}
var cmd *exec.Cmd
@@ -67,7 +67,7 @@ func ExecfAsync(shell string, args ...any) error {
// ExecfWithTimeout 执行 shell 命令并设置超时时间
func ExecfWithTimeout(timeout time.Duration, shell string, args ...any) (string, error) {
if !CheckArgs(slice.ToString(args)...) {
return "", errors.New("你想干什么?")
return "", errors.New("发现危险的命令参数,中止执行")
}
var cmd *exec.Cmd
+8 -1
View File
@@ -482,14 +482,21 @@ func UpdatePanel(panelInfo PanelInfo) error {
_, _ = shell.Execf("chmod -R 700 /usr/bin/panel")
color.Green().Printfln("设置完成")
color.Green().Printfln("运行升级后脚本...")
if _, err = shell.Execf("bash /www/panel/scripts/update_panel.sh"); err != nil {
color.Red().Printfln("行面板升级后脚本失败")
color.Red().Printfln("行面板升级后脚本失败")
return err
}
if _, err = shell.Execf("cp -f /www/panel/scripts/panel.service /etc/systemd/system/panel.service"); err != nil {
color.Red().Printfln("写入面板服务文件失败")
return err
}
_, _ = shell.Execf("systemctl daemon-reload")
if _, err = shell.Execf("panel writeSetting version " + panelInfo.Version); err != nil {
color.Red().Printfln("写入面板版本号失败")
return err
}
color.Green().Printfln("升级完成")
_, _ = shell.Execf("rm -rf /tmp/panel-storage.zip")
_, _ = shell.Execf("rm -rf /tmp/panel.conf.bak")
+12
View File
@@ -0,0 +1,12 @@
package types
type PostgresUser struct {
Role string `json:"role"`
Attributes []string `json:"attributes"`
}
type PostgresDatabase struct {
Name string `json:"name"`
Owner string `json:"owner"`
Encoding string `json:"encoding"`
}
+4 -1
View File
@@ -13,7 +13,10 @@
"packageRules": [
{
"description": "Automerge non-major updates",
"matchUpdateTypes": ["minor", "patch"],
"matchUpdateTypes": [
"minor",
"patch"
],
"matchCurrentVersion": "!/^0/",
"automerge": true
}
+1 -1
View File
@@ -48,7 +48,7 @@ func Api() {
websiteController := controllers.NewWebsiteController()
r.Get("/", websiteController.List)
r.Post("/", websiteController.Add)
r.Delete("{id}", websiteController.Delete)
r.Post("delete", websiteController.Delete)
r.Get("{id}/config", websiteController.GetConfig)
r.Post("{id}/config", websiteController.SaveConfig)
r.Delete("{id}/log", websiteController.ClearLog)
+11 -28
View File
@@ -88,31 +88,31 @@ Prepare_System() {
[ -s /etc/selinux/config ] && sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
setenforce 0 > /dev/null 2>&1
ulimit -n 204800
echo 6553560 > /proc/sys/fs/file-max
ulimit -n 1048576
echo 2147483584 > /proc/sys/fs/file-max
checkSoftNofile=$(cat /etc/security/limits.conf | grep '^* soft nofile .*$')
checkHardNofile=$(cat /etc/security/limits.conf | grep '^* hard nofile .*$')
checkSoftNproc=$(cat /etc/security/limits.conf | grep '^* soft nproc .*$')
checkHardNproc=$(cat /etc/security/limits.conf | grep '^* hard nproc .*$')
checkFsFileMax=$(cat /etc/sysctl.conf | grep '^fs.file-max.*$')
if [ "${checkSoftNofile}" == "" ]; then
echo "* soft nofile 204800" >> /etc/security/limits.conf
echo "* soft nofile 1048576" >> /etc/security/limits.conf
fi
if [ "${checkHardNofile}" == "" ]; then
echo "* hard nofile 204800" >> /etc/security/limits.conf
echo "* hard nofile 1048576" >> /etc/security/limits.conf
fi
if [ "${checkSoftNproc}" == "" ]; then
echo "* soft nproc 204800" >> /etc/security/limits.conf
echo "* soft nproc 1048576" >> /etc/security/limits.conf
fi
if [ "${checkHardNproc}" == "" ]; then
echo "* hard nproc 204800 " >> /etc/security/limits.conf
echo "* hard nproc 1048576" >> /etc/security/limits.conf
fi
if [ "${checkFsFileMax}" == "" ]; then
echo fs.file-max = 6553560 >> /etc/sysctl.conf
echo fs.file-max = 2147483584 >> /etc/sysctl.conf
fi
# 自动开启 BBR
isBBRSupported=$(ls -l /lib/modules/*/kernel/net/ipv4/tcp_bbr.ko.xz | grep -c bbr)
isBBRSupported=$(ls -l /lib/modules/*/kernel/net/ipv4 | grep -c tcp_bbr)
if [ "${isBBRSupported}" != "0" ]; then
qdisc=$(sysctl net.core.default_qdisc | awk '{print $3}')
echo "net.core.default_qdisc=${qdisc}" >> /etc/sysctl.conf
@@ -151,14 +151,14 @@ Prepare_System() {
# Rocky Linux
/usr/bin/crb enable
dnf makecache -y
dnf install -y curl wget zip unzip tar p7zip p7zip-plugins git jq git-core dos2unix rsyslog
dnf install -y bash curl wget zip unzip tar p7zip p7zip-plugins git jq git-core dos2unix rsyslog make
elif [ "${OS}" == "debian" ]; then
if ${inChina}; then
sed -i 's/deb.debian.org/mirrors.tencent.com/g' /etc/apt/sources.list
sed -i 's/security.debian.org/mirrors.tencent.com/g' /etc/apt/sources.list
fi
apt-get update -y
apt-get install -y curl wget zip unzip tar p7zip p7zip-full git jq git dos2unix rsyslog
apt-get install -y bash curl wget zip unzip tar p7zip p7zip-full git jq git dos2unix rsyslog make
fi
if [ "$?" != "0" ]; then
echo -e $HR
@@ -309,24 +309,7 @@ Init_Panel() {
exit 1
fi
# 写入服务文件
cat > /etc/systemd/system/panel.service << EOF
[Unit]
Description=HaoZi Panel
After=syslog.target network.target
Wants=network.target
[Service]
Type=simple
WorkingDirectory=${setup_Path}/panel/
ExecStart=/www/panel/panel --env="/www/panel/panel.conf"
ExecReload=kill -s HUP \$MAINPID
ExecStop=kill -s QUIT \$MAINPID
User=root
Restart=always
[Install]
WantedBy=multi-user.target
EOF
cp -f ${setup_Path}/panel/scripts/panel.service /etc/systemd/system/panel.service
systemctl daemon-reload
systemctl enable panel.service
systemctl start panel.service
+5
View File
@@ -59,6 +59,11 @@ else
echo "错误:耗子面板不支持该系统"
exit 1
fi
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:安装依赖软件失败,请截图错误信息寻求帮助。"
exit 1
fi
mysqlUserCheck=$(cat /etc/passwd | grep mysql)
if [ "${mysqlUserCheck}" == "" ]; then
+5
View File
@@ -42,6 +42,11 @@ else
echo "错误:耗子面板不支持该系统"
exit 1
fi
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:安装依赖软件失败,请截图错误信息寻求帮助。"
exit 1
fi
# 准备目录
rm -rf ${openrestyPath}
+20
View File
@@ -0,0 +1,20 @@
[Unit]
Description=HaoZi Panel
After=syslog.target network.target
Wants=network.target
[Service]
Type=simple
WorkingDirectory=/www/panel/
ExecStart=/www/panel/panel --env="/www/panel/panel.conf"
ExecReload=kill -s HUP $MAINPID
ExecStop=kill -s QUIT $MAINPID
User=root
Restart=always
RestartSec=5
LimitNOFILE=1048576
LimitNPROC=1048576
Delegate=yes
[Install]
WantedBy=multi-user.target
+5
View File
@@ -53,6 +53,11 @@ else
echo "错误:耗子面板不支持该系统"
exit 1
fi
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:安装依赖软件失败,请截图错误信息寻求帮助。"
exit 1
fi
# 准备安装目录
rm -rf ${phpPath}
+5
View File
@@ -53,6 +53,11 @@ else
echo "错误:耗子面板不支持该系统"
exit 1
fi
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:安装依赖软件失败,请截图错误信息寻求帮助。"
exit 1
fi
postgresqlUserCheck=$(cat /etc/passwd | grep postgres)
if [ "${postgresqlUserCheck}" == "" ]; then
+5
View File
@@ -45,6 +45,11 @@ else
echo "错误:耗子面板不支持该系统"
exit 1
fi
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:安装依赖软件失败,请截图错误信息寻求帮助。"
exit 1
fi
# 准备目录
rm -rf ${redisPath}
+5
View File
@@ -30,6 +30,11 @@ else
echo "错误:不支持的操作系统"
exit 1
fi
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:安装软件失败,请截图错误信息寻求帮助。"
exit 1
fi
# 写入配置
cat > /etc/rsyncd.conf << EOF
+5
View File
@@ -30,5 +30,10 @@ else
echo "错误:不支持的操作系统"
exit 1
fi
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:安装软件失败,请截图错误信息寻求帮助。"
exit 1
fi
panel writePlugin s3fs 1.9
+5
View File
@@ -35,5 +35,10 @@ else
echo "错误:不支持的操作系统"
exit 1
fi
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:安装软件失败,请截图错误信息寻求帮助。"
exit 1
fi
panel writePlugin supervisor 4.2.5