mirror of
https://github.com/tnb-labs/panel.git
synced 2026-09-01 04:17:20 +08:00
Compare commits
103 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 367348e688 | |||
| 07dfcd778c | |||
| 789d5e7ddd | |||
| 1102aba9a3 | |||
| fddb076136 | |||
| b1ff8fff3d | |||
| 5cd68fcdc3 | |||
| 3a5ff9179a | |||
| fa7c54ada5 | |||
| 94047174d1 | |||
| 305777f65a | |||
| d56d3ec1dd | |||
| 8ac8da7914 | |||
| 5d56663b69 | |||
| 7ff574cb92 | |||
| 00b4153a28 | |||
| 9412b31279 | |||
| bbf04c91cf | |||
| 252456a1cd | |||
| 8de68b317d | |||
| 8bf273e1fd | |||
| 32111ecd7a | |||
| 82b8a32133 | |||
| b82c644d81 | |||
| 06bf1b0604 | |||
| 7b29e4546a | |||
| ae6b354a78 | |||
| d96c616c61 | |||
| 213698dd2a | |||
| 39b3c74684 | |||
| b3a1af1001 | |||
| 91b244276a | |||
| b1eefd062d | |||
| 2edf6748e7 | |||
| eac69b8dab | |||
| bb7b641193 | |||
| 3b0aee1967 | |||
| 2ae7f3e458 | |||
| ee0442d159 | |||
| 363b7a55be | |||
| 3437bb2b1f | |||
| 7e000cddc8 | |||
| a18652f134 | |||
| dcd8bb3dd9 | |||
| d62a280fe9 | |||
| c3a7710e6f | |||
| 7f1491f739 | |||
| c96b88548e | |||
| f62b112e1b | |||
| e55e3402e9 | |||
| 48b539b31a | |||
| d35245aa11 | |||
| 7f46dea052 | |||
| 48ed174dc6 | |||
| a5743f94b2 | |||
| 227c5344e3 | |||
| 4c8a88ea03 | |||
| 3503aac2f6 | |||
| 6beda2ea12 | |||
| 4579bf8fa9 | |||
| 002db34b4d | |||
| e276c4f747 | |||
| f5e6219ca4 | |||
| fd92b1d360 | |||
| 5b8289c0b0 | |||
| d8fe050607 | |||
| c0310d916f | |||
| 77ed2d77e1 | |||
| c331143e9e | |||
| 71e75312ff | |||
| 0e5bee4852 | |||
| 036ad9222a | |||
| 39cd5545c5 | |||
| 4568e60133 | |||
| d2f224eaa6 | |||
| c77713b3d9 | |||
| f1e2ba5c35 | |||
| 243a36f41c | |||
| 74f772b705 | |||
| e8f1ed5dec | |||
| 634d8c643b | |||
| 1829b46466 | |||
| 478efa5016 | |||
| 841e0e351f | |||
| 0e9761f975 | |||
| 5c067e28b7 | |||
| 0a0e0bcac1 | |||
| c3732b904d | |||
| 161370ac5a | |||
| 0cfe585ad5 | |||
| 283d9dbe26 | |||
| 45ed3f2ff9 | |||
| 5703e3354f | |||
| da2e66b7c6 | |||
| 8dca4c1cbf | |||
| a295eb9a65 | |||
| 868ee73d51 | |||
| ef7d38f2af | |||
| 801ff28854 | |||
| e0a401f688 | |||
| f50f2f7f2b | |||
| 3df1388326 | |||
| 5390ef3140 |
+1
-1
@@ -1,4 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
open_collective: haozi-panel
|
||||
open_collective: tnb
|
||||
custom: ['https://afdian.net/a/TheTNB']
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
cache: true
|
||||
go-version: 'stable'
|
||||
go-version: '1.22'
|
||||
- name: Install dependencies
|
||||
run: go mod tidy
|
||||
- name: Build ${{ matrix.goarch }}
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 'stable'
|
||||
go-version: '1.22'
|
||||
- name: Install dependencies
|
||||
run: go mod tidy
|
||||
- name: Run tests with coverage
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 'stable'
|
||||
go-version: '1.22'
|
||||
- name: Fetch Frontend
|
||||
run: |
|
||||
sudo apt-get install -y curl jq unzip zip
|
||||
|
||||
@@ -14,13 +14,11 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 'stable'
|
||||
go-version: '1.22'
|
||||
cache: false
|
||||
- name: Lint
|
||||
uses: golangci/golangci-lint-action@v4
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
skip-cache: true
|
||||
skip-pkg-cache: true
|
||||
skip-build-cache: true
|
||||
version: latest
|
||||
args: --timeout=30m ./...
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 'stable'
|
||||
go-version: '1.22'
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get install -y curl jq
|
||||
- name: Set up environment
|
||||
|
||||
+4
-2
@@ -1,4 +1,4 @@
|
||||
image: golang:bookworm
|
||||
image: golang:1.22-bookworm
|
||||
|
||||
# 在每个任务执行前运行
|
||||
before_script:
|
||||
@@ -78,7 +78,7 @@ release:
|
||||
- build
|
||||
- fetch
|
||||
image:
|
||||
name: goreleaser/goreleaser:nightly
|
||||
name: goreleaser/goreleaser
|
||||
entrypoint: [ '' ]
|
||||
only:
|
||||
- tags
|
||||
@@ -87,4 +87,6 @@ release:
|
||||
# generate a changelog.
|
||||
GIT_DEPTH: 0
|
||||
script:
|
||||
- sed -i 's/dl-cdn.alpinelinux.org/mirror.sjtu.edu.cn/g' /etc/apk/repositories
|
||||
- apk add --no-cache upx
|
||||
- goreleaser release --clean
|
||||
|
||||
+18
-8
@@ -1,7 +1,7 @@
|
||||
project_name: panel
|
||||
|
||||
builds:
|
||||
-
|
||||
id: panel
|
||||
- id: panel
|
||||
binary: panel
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
@@ -15,36 +15,46 @@ builds:
|
||||
- v2
|
||||
ldflags:
|
||||
- -s -w --extldflags "-static"
|
||||
|
||||
upx:
|
||||
- enabled: true
|
||||
# Filter by build ID.
|
||||
ids: [ panel ]
|
||||
# Compress argument.
|
||||
# Valid options are from '1' (faster) to '9' (better), and 'best'.
|
||||
compress: best
|
||||
# Whether to try LZMA (slower).
|
||||
lzma: true
|
||||
# Whether to try all methods and filters (slow).
|
||||
brute: true
|
||||
|
||||
archives:
|
||||
-
|
||||
id: panel-archive
|
||||
- id: panel-archive
|
||||
builds:
|
||||
- panel
|
||||
format: zip
|
||||
wrap_in_directory: false
|
||||
strip_parent_binary_folder: true
|
||||
strip_binary_directory: true
|
||||
files:
|
||||
- LICENSE
|
||||
- docs/*
|
||||
- public/*
|
||||
- storage/*
|
||||
- database/*
|
||||
- lang/*
|
||||
- scripts/*
|
||||
- panel-example.conf
|
||||
|
||||
gitlab_urls:
|
||||
api: https://git.haozi.net/api/v4/
|
||||
download: https://git.haozi.net
|
||||
|
||||
# set to true if you use a self-signed certificate
|
||||
skip_tls_verify: false
|
||||
|
||||
# set to true if you want to upload to the Package Registry rather than attachments
|
||||
# Only works with GitLab 13.5+
|
||||
#
|
||||
# Since: v1.3
|
||||
use_package_registry: true
|
||||
|
||||
# Set this if you set GITLAB_TOKEN to the value of CI_JOB_TOKEN.
|
||||
#
|
||||
# Since: v1.11
|
||||
|
||||
@@ -57,7 +57,7 @@ CentOS Stream 可使用迁移脚本迁移至支持的系统: [CentOS 8/9 迁移
|
||||
如果你决定继续,请以`root`用户登录服务器,执行以下命令安装面板:
|
||||
|
||||
```shell
|
||||
HAOZI_DL_URL="https://git.haozi.net/opensource/download/-/raw/main/panel"; curl -sSL -O ${HAOZI_DL_URL}/install_panel.sh && curl -sSL -O ${HAOZI_DL_URL}/install_panel.sh.checksum.txt && sha256sum -c install_panel.sh.checksum.txt && bash install_panel.sh || echo "Checksum 验证失败,文件可能被篡改,已终止操作"
|
||||
HAOZI_DL_URL="https://dl.cdn.haozi.net/panel"; curl -sSL -O ${HAOZI_DL_URL}/install_panel.sh && curl -sSL -O ${HAOZI_DL_URL}/install_panel.sh.checksum.txt && sha256sum -c install_panel.sh.checksum.txt && bash install_panel.sh || echo "Checksum 验证失败,文件可能被篡改,已终止操作"
|
||||
```
|
||||
|
||||
## 卸载面板
|
||||
@@ -67,7 +67,7 @@ HAOZI_DL_URL="https://git.haozi.net/opensource/download/-/raw/main/panel"; curl
|
||||
如果你无法重装系统,请以`root`用户登录服务器,执行以下命令卸载面板:
|
||||
|
||||
```shell
|
||||
HAOZI_DL_URL="https://git.haozi.net/opensource/download/-/raw/main/panel"; curl -sSL -O ${HAOZI_DL_URL}/uninstall_panel.sh && curl -sSL -O ${HAOZI_DL_URL}/uninstall_panel.sh.checksum.txt && sha256sum -c uninstall_panel.sh.checksum.txt && bash uninstall_panel.sh || echo "Checksum 验证失败,文件可能被篡改,已终止操作"
|
||||
HAOZI_DL_URL="https://dl.cdn.haozi.net/panel"; curl -sSL -O ${HAOZI_DL_URL}/uninstall_panel.sh && curl -sSL -O ${HAOZI_DL_URL}/uninstall_panel.sh.checksum.txt && sha256sum -c uninstall_panel.sh.checksum.txt && bash uninstall_panel.sh || echo "Checksum 验证失败,文件可能被篡改,已终止操作"
|
||||
```
|
||||
|
||||
卸载面板前请务必备份好所有数据,提前卸载面板全部插件。卸载后数据将**无法恢复**!
|
||||
@@ -84,26 +84,17 @@ panel
|
||||
|
||||
## 问题反馈
|
||||
|
||||
使用类问题,可在 [鼠片社区](https://forum.haozi.net) 提问或QQ群`@坤坤`寻求 AI 帮助,亦可在群里寻求付费支持。
|
||||
使用类问题,可在 [Moe Tom](https://tom.moe) 提问或寻求 AI 帮助,亦可在群里寻求付费支持。
|
||||
|
||||
面板自身问题,可在 GitHub 的`Issues`页面提交问题反馈,注意[提问的智慧](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md)。
|
||||
|
||||
## 贡献代码
|
||||
|
||||
### 寻找/创建 Issue
|
||||
|
||||
您可以在 [Issue 列表](https://github.com/TheTNB/panel/issues) 中寻找或创建一个 Issue,留言表达想要处理该 Issue 的意愿,得到维护者的确认后,即可开始处理。
|
||||
|
||||
### 创建 PR
|
||||
|
||||
- 在开发过程中,如果遇到问题可以随时在 Issue 中详尽描述该问题,以进一步沟通,但在此之前请确保自己已通过 Google 等方式尽可能的尝试解决问题;
|
||||
- PR 须提交至我们的极狐 GitLab 仓库[https://git.haozi.net/opensource/panel](https://git.haozi.net/opensource/panel),勿在 GitHub 上提交;
|
||||
- 当 PR 开发完毕后,请为其添加 `🚀 Review Ready` 标签,维护者将及时进行评审;
|
||||
- 我们非常欢迎您的贡献,将在下次发版时将您添加到首页贡献者中;❤️
|
||||
|
||||
## 赞助商
|
||||
|
||||
### 资金
|
||||
如果耗子 Linux 面板对你有帮助,欢迎[赞助我们](https://afdian.net/a/TheTNB),感谢以下支持者/赞助商的支持:
|
||||
|
||||
**同时接受云资源赞助,可通过QQ群咨询联系**
|
||||
|
||||
### 服务器
|
||||
|
||||
- [微晓朵](https://www.weixiaoduo.com/)
|
||||
|
||||
@@ -112,17 +103,19 @@ panel
|
||||
- [无畏云加速](https://su.sctes.com/register?code=8st689ujpmm2p)
|
||||
- [盾云CDN](http://cdn.ddunyun.com/)
|
||||
|
||||
### DevOps
|
||||
|
||||
- [JetBrains](https://www.jetbrains.com/)
|
||||
|
||||
**接受云资源和资金赞助,可通过QQ群咨询联系**
|
||||
<p align="center">
|
||||
<a target="_blank" href="https://afdian.net/a/TheTNB">
|
||||
<img alt="sponsors" src="https://github.com/TheTNB/sponsor/blob/main/sponsors.svg?raw=true"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## 贡献者
|
||||
|
||||
这个项目的存在要归功于所有做出贡献的人,参与贡献请先查看贡献代码部分。
|
||||
|
||||
<a href="https://github.com/DevHaoZi" target="_blank"><img src="https://avatars.githubusercontent.com/u/115467771?v=4" width="48" height="48"></a>
|
||||
<a href="https://github.com/TheTNB/panel/graphs/contributors">
|
||||
<img alt="contributors" src="https://contrib.rocks/image?repo=TheTNB/panel"/>
|
||||
</a>
|
||||
|
||||
## Star 历史
|
||||
|
||||
|
||||
+16
-23
@@ -57,7 +57,7 @@ As system versions are constantly updated, we may also terminate support for som
|
||||
If you decide to continue, please log in to the server as `root` user and execute the following command to install the panel:
|
||||
|
||||
```shell
|
||||
HAOZI_DL_URL="https://git.haozi.net/opensource/download/-/raw/main/panel"; curl -sSL -O ${HAOZI_DL_URL}/install_panel.sh && curl -sSL -O ${HAOZI_DL_URL}/install_panel.sh.checksum.txt && sha256sum -c install_panel.sh.checksum.txt && bash install_panel.sh || echo "Checksum Verification Failed, File May Have Been Tampered With, Operation Terminated"
|
||||
HAOZI_DL_URL="https://dl.cdn.haozi.net/panel"; curl -sSL -O ${HAOZI_DL_URL}/install_panel.sh && curl -sSL -O ${HAOZI_DL_URL}/install_panel.sh.checksum.txt && sha256sum -c install_panel.sh.checksum.txt && bash install_panel.sh || echo "Checksum Verification Failed, File May Have Been Tampered With, Operation Terminated"
|
||||
```
|
||||
|
||||
## Uninstall Panel
|
||||
@@ -67,7 +67,7 @@ Recommended to back up data and reinstall the system first, so that the system c
|
||||
If you are unable to reinstall the system, log in to the server as the `root` user and execute the following command to uninstall the panel:
|
||||
|
||||
```shell
|
||||
HAOZI_DL_URL="https://git.haozi.net/opensource/download/-/raw/main/panel"; curl -sSL -O ${HAOZI_DL_URL}/uninstall_panel.sh && curl -sSL -O ${HAOZI_DL_URL}/uninstall_panel.sh.checksum.txt && sha256sum -c uninstall_panel.sh.checksum.txt && bash uninstall_panel.sh || echo "Checksum Verification Failed, File May Have Been Tampered With, Operation Terminated"
|
||||
HAOZI_DL_URL="https://dl.cdn.haozi.net/panel"; curl -sSL -O ${HAOZI_DL_URL}/uninstall_panel.sh && curl -sSL -O ${HAOZI_DL_URL}/uninstall_panel.sh.checksum.txt && sha256sum -c uninstall_panel.sh.checksum.txt && bash uninstall_panel.sh || echo "Checksum Verification Failed, File May Have Been Tampered With, Operation Terminated"
|
||||
```
|
||||
|
||||
Before uninstalling the panel, please be sure to back up all data and uninstall all panel plugins in advance. The data will **not be recoverable** after uninstallation!
|
||||
@@ -84,26 +84,17 @@ See more usage methods and tips in [Wiki](https://github.com/TheTNB/panel/wiki).
|
||||
|
||||
## Feedback
|
||||
|
||||
For usage issues, you can ask questions in the [HaoZi Community Forum](https://forum.haozi.net) or seek AI help in the QQ group `@坤坤`. You can also seek paid support in the group.
|
||||
For usage issues, you can ask questions in the [Moe Tom](https://tom.moe) or seek AI help. You can also seek paid support in the group.
|
||||
|
||||
For issues with the panel itself, you can submit feedback on the `Issues` page of GitHub. Please note [the wisdom of asking questions](http://www.catb.org/~esr/faqs/smart-questions.html).
|
||||
|
||||
## Contribute code
|
||||
|
||||
### Find/Create Issue
|
||||
|
||||
You can find or create an Issue in [Issue List](https://github.com/TheTNB/panel/issues), leave a message to express your willingness to deal with the Issue, and get confirmation from the maintainer. Start processing.
|
||||
|
||||
### Create PR
|
||||
|
||||
- During the development process, if you encounter a problem, you can describe the problem in detail in the Issue at any time for further communication, but before doing so, please make sure that you have tried your best to solve the problem through Google and other methods;
|
||||
- PRs must be submitted to our JiHu GitLab repository [https://git.haozi.net/opensource/panel](https://git.haozi.net/opensource/panel). Do not submit on GitHub;
|
||||
- When the PR is completed, please add the `🚀 Review Ready` tag to it, and the maintainer will review it in time;
|
||||
- We very much welcome your contributions and will add you to the homepage contributors in the next release; ❤️
|
||||
|
||||
## Sponsor
|
||||
|
||||
### Funding
|
||||
If the HaoZi Linux Panel is helpful to you, welcome to [sponsor us](https://opencollective.com/tnb), thanks to the following supporters/sponsors:
|
||||
|
||||
**Accept cloud resources and financial sponsorship, you can contact us through QQ group**
|
||||
|
||||
### Server
|
||||
|
||||
- [微晓朵](https://www.weixiaoduo.com/)
|
||||
|
||||
@@ -112,17 +103,19 @@ You can find or create an Issue in [Issue List](https://github.com/TheTNB/panel/
|
||||
- [无畏云加速](https://su.sctes.com/register?code=8st689ujpmm2p)
|
||||
- [盾云CDN](http://cdn.ddunyun.com/)
|
||||
|
||||
### DevOps
|
||||
|
||||
- [JetBrains](https://www.jetbrains.com/)
|
||||
|
||||
**Accept cloud resources and financial sponsorship, you can contact us through QQ group**
|
||||
<p align="center">
|
||||
<a target="_blank" href="https://afdian.net/a/TheTNB">
|
||||
<img alt="sponsors" src="https://github.com/TheTNB/sponsor/blob/main/sponsors.svg?raw=true"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## Contributor
|
||||
|
||||
This project owes its existence to all those who have contributed. To contribute, please check the contributed code section first.
|
||||
|
||||
<a href="https://github.com/DevHaoZi" target="_blank"><img src="https://avatars.githubusercontent.com/u/115467771?v=4" width="48" height="48"></a>
|
||||
<a href="https://github.com/TheTNB/panel/graphs/contributors">
|
||||
<img alt="contributors" src="https://contrib.rocks/image?repo=TheTNB/panel"/>
|
||||
</a>
|
||||
|
||||
## Star History
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
|
||||
@@ -25,7 +26,8 @@ func (receiver *CertRenew) Signature() string {
|
||||
|
||||
// Description The console command description.
|
||||
func (receiver *CertRenew) Description() string {
|
||||
return "[面板] 证书续签"
|
||||
ctx := context.Background()
|
||||
return facades.Lang(ctx).Get("commands.panel:cert-renew.description")
|
||||
}
|
||||
|
||||
// Extend The console command extend.
|
||||
@@ -36,7 +38,7 @@ func (receiver *CertRenew) Extend() command.Extend {
|
||||
}
|
||||
|
||||
// Handle Execute the console command.
|
||||
func (receiver *CertRenew) Handle(ctx console.Context) error {
|
||||
func (receiver *CertRenew) Handle(console.Context) error {
|
||||
if internal.Status != internal.StatusNormal {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/gookit/color"
|
||||
@@ -27,7 +28,8 @@ func (receiver *Monitoring) Signature() string {
|
||||
|
||||
// Description The console command description.
|
||||
func (receiver *Monitoring) Description() string {
|
||||
return "[面板] 系统监控"
|
||||
ctx := context.Background()
|
||||
return facades.Lang(ctx).Get("commands.panel:monitoring.description")
|
||||
}
|
||||
|
||||
// Extend The console command extend.
|
||||
@@ -38,7 +40,7 @@ func (receiver *Monitoring) Extend() command.Extend {
|
||||
}
|
||||
|
||||
// Handle Execute the console command.
|
||||
func (receiver *Monitoring) Handle(ctx console.Context) error {
|
||||
func (receiver *Monitoring) Handle(console.Context) error {
|
||||
if internal.Status != internal.StatusNormal {
|
||||
return nil
|
||||
}
|
||||
@@ -51,6 +53,8 @@ func (receiver *Monitoring) Handle(ctx console.Context) error {
|
||||
|
||||
info := tools.GetMonitoringInfo()
|
||||
|
||||
translate := facades.Lang(context.Background())
|
||||
|
||||
// 去除部分数据以减少数据库存储
|
||||
info.Disk = nil
|
||||
for _, cpu := range info.Cpus {
|
||||
@@ -70,7 +74,7 @@ func (receiver *Monitoring) Handle(ctx console.Context) error {
|
||||
})
|
||||
if err != nil {
|
||||
facades.Log().Infof("[面板] 系统监控保存失败: %s", err.Error())
|
||||
color.Redf("[面板] 系统监控保存失败: %s", err.Error())
|
||||
color.Redf(translate.Get("commands.panel:monitoring.fail")+": %s", err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+254
-123
@@ -1,6 +1,7 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -31,7 +32,8 @@ func (receiver *Panel) Signature() string {
|
||||
|
||||
// Description The console command description.
|
||||
func (receiver *Panel) Description() string {
|
||||
return "[面板] 命令行"
|
||||
ctx := context.Background()
|
||||
return facades.Lang(ctx).Get("commands.panel.description")
|
||||
}
|
||||
|
||||
// Extend The console command extend.
|
||||
@@ -48,75 +50,78 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
arg2 := ctx.Argument(2)
|
||||
arg3 := ctx.Argument(3)
|
||||
arg4 := ctx.Argument(4)
|
||||
arg5 := ctx.Argument(5)
|
||||
|
||||
translate := facades.Lang(context.Background())
|
||||
|
||||
switch action {
|
||||
case "init":
|
||||
var check models.User
|
||||
err := facades.Orm().Query().FirstOrFail(&check)
|
||||
if err == nil {
|
||||
color.Redln("面板已初始化")
|
||||
color.Redln(translate.Get("commands.panel.init.exist"))
|
||||
return nil
|
||||
}
|
||||
|
||||
settings := []models.Setting{{Key: models.SettingKeyName, Value: "耗子 Linux 面板"}, {Key: models.SettingKeyMonitor, Value: "1"}, {Key: models.SettingKeyMonitorDays, Value: "30"}, {Key: models.SettingKeyBackupPath, Value: "/www/backup"}, {Key: models.SettingKeyWebsitePath, Value: "/www/wwwroot"}, {Key: models.SettingKeyVersion, Value: facades.Config().GetString("panel.version")}}
|
||||
err = facades.Orm().Query().Create(&settings)
|
||||
if err != nil {
|
||||
color.Redln("初始化失败")
|
||||
color.Redln(translate.Get("commands.panel.init.fail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
hash, err := facades.Hash().Make(tools.RandomString(32))
|
||||
if err != nil {
|
||||
color.Redln("初始化失败")
|
||||
color.Redln(translate.Get("commands.panel.init.fail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
user := services.NewUserImpl()
|
||||
_, err = user.Create("admin", hash)
|
||||
if err != nil {
|
||||
color.Redln("创建管理员失败")
|
||||
color.Redln(translate.Get("commands.panel.init.adminFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln("初始化成功")
|
||||
color.Greenln(translate.Get("commands.panel.init.success"))
|
||||
|
||||
case "update":
|
||||
var task models.Task
|
||||
err := facades.Orm().Query().Where("status", models.TaskStatusRunning).OrWhere("status", models.TaskStatusWaiting).FirstOrFail(&task)
|
||||
if err == nil {
|
||||
color.Redln("当前有任务正在执行,禁止更新")
|
||||
color.Redln(translate.Get("commands.panel.update.taskCheck"))
|
||||
return nil
|
||||
}
|
||||
|
||||
panel, err := tools.GetLatestPanelVersion()
|
||||
if err != nil {
|
||||
color.Redln("获取最新版本失败")
|
||||
color.Redln(translate.Get("commands.panel.update.versionFail"))
|
||||
return err
|
||||
}
|
||||
|
||||
internal.Status = internal.StatusUpgrade
|
||||
if err = tools.UpdatePanel(panel); err != nil {
|
||||
internal.Status = internal.StatusFailed
|
||||
color.Redln("更新失败: " + err.Error())
|
||||
color.Redln(translate.Get("commands.panel.update.fail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
internal.Status = internal.StatusNormal
|
||||
color.Greenln("更新成功")
|
||||
color.Greenln(translate.Get("commands.panel.update.success"))
|
||||
tools.RestartPanel()
|
||||
|
||||
case "getInfo":
|
||||
var user models.User
|
||||
err := facades.Orm().Query().Where("id", 1).FirstOrFail(&user)
|
||||
if err != nil {
|
||||
color.Redln("获取管理员信息失败")
|
||||
color.Redln(translate.Get("commands.panel.getInfo.adminGetFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
password := tools.RandomString(16)
|
||||
hash, err := facades.Hash().Make(password)
|
||||
if err != nil {
|
||||
color.Redln("生成密码失败")
|
||||
color.Redln(translate.Get("commands.panel.getInfo.passwordGenerationFail"))
|
||||
return nil
|
||||
}
|
||||
user.Username = tools.RandomString(8)
|
||||
@@ -127,13 +132,13 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
|
||||
err = facades.Orm().Query().Save(&user)
|
||||
if err != nil {
|
||||
color.Redln("保存管理员信息失败")
|
||||
color.Redln(translate.Get("commands.panel.getInfo.adminSaveFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
port, err := tools.Exec(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
|
||||
if err != nil {
|
||||
color.Redln("获取面板端口失败")
|
||||
color.Redln(translate.Get("commands.panel.portFail"))
|
||||
return nil
|
||||
}
|
||||
ip, err := tools.GetPublicIP()
|
||||
@@ -145,42 +150,42 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
protocol = "https"
|
||||
}
|
||||
|
||||
color.Greenln("用户名: " + user.Username)
|
||||
color.Greenln("密码: " + password)
|
||||
color.Greenln("面板端口: " + port)
|
||||
color.Greenln("面板入口: " + facades.Config().GetString("http.entrance"))
|
||||
color.Greenln("面板地址: " + protocol + "://" + ip + ":" + port + facades.Config().GetString("http.entrance"))
|
||||
color.Greenln(translate.Get("commands.panel.getInfo.username") + ": " + user.Username)
|
||||
color.Greenln(translate.Get("commands.panel.getInfo.password") + ": " + password)
|
||||
color.Greenln(translate.Get("commands.panel.port") + ": " + port)
|
||||
color.Greenln(translate.Get("commands.panel.entrance") + ": " + facades.Config().GetString("http.entrance"))
|
||||
color.Greenln(translate.Get("commands.panel.getInfo.address") + ": " + protocol + "://" + ip + ":" + port + facades.Config().GetString("http.entrance"))
|
||||
|
||||
case "getPort":
|
||||
port, err := tools.Exec(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
|
||||
if err != nil {
|
||||
color.Redln("获取面板端口失败")
|
||||
color.Redln(translate.Get("commands.panel.portFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln("面板端口: " + port)
|
||||
color.Greenln(translate.Get("commands.panel.port") + ": " + port)
|
||||
|
||||
case "getEntrance":
|
||||
color.Greenln("面板入口: " + facades.Config().GetString("http.entrance"))
|
||||
color.Greenln(translate.Get("commands.panel.entrance") + ": " + facades.Config().GetString("http.entrance"))
|
||||
|
||||
case "deleteEntrance":
|
||||
oldEntrance, err := tools.Exec(`cat /www/panel/panel.conf | grep APP_ENTRANCE | awk -F '=' '{print $2}' | tr -d '\n'`)
|
||||
if err != nil {
|
||||
color.Redln("获取面板入口失败")
|
||||
color.Redln(translate.Get("commands.panel.deleteEntrance.fail"))
|
||||
return nil
|
||||
}
|
||||
if _, err = tools.Exec("sed -i 's!APP_ENTRANCE=" + oldEntrance + "!APP_ENTRANCE=/!g' /www/panel/panel.conf"); err != nil {
|
||||
color.Redln("删除面板入口失败")
|
||||
color.Redln(translate.Get("commands.panel.deleteEntrance.fail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln("删除面板入口成功")
|
||||
color.Greenln(translate.Get("commands.panel.deleteEntrance.success"))
|
||||
|
||||
case "writePlugin":
|
||||
slug := arg1
|
||||
version := arg2
|
||||
if len(slug) == 0 || len(version) == 0 {
|
||||
color.Redln("参数错误")
|
||||
color.Redln(translate.Get("commands.panel.writePlugin.paramFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -192,31 +197,31 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
color.Redln("写入插件安装状态失败")
|
||||
color.Redln(translate.Get("commands.panel.writePlugin.fail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln("写入插件安装状态成功")
|
||||
color.Greenln(translate.Get("commands.panel.writePlugin.success"))
|
||||
|
||||
case "deletePlugin":
|
||||
slug := arg1
|
||||
if len(slug) == 0 {
|
||||
color.Redln("参数错误")
|
||||
color.Redln(translate.Get("commands.panel.deletePlugin.paramFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := facades.Orm().Query().Where("slug", slug).Delete(&models.Plugin{})
|
||||
if err != nil {
|
||||
color.Redln("移除插件安装状态失败")
|
||||
color.Redln(translate.Get("commands.panel.deletePlugin.fail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln("移除插件安装状态成功")
|
||||
color.Greenln(translate.Get("commands.panel.deletePlugin.success"))
|
||||
|
||||
case "writeMysqlPassword":
|
||||
password := arg1
|
||||
if len(password) == 0 {
|
||||
color.Redln("参数错误")
|
||||
color.Redln(translate.Get("commands.panel.writeMysqlPassword.paramFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -228,20 +233,20 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
color.Redln("写入MySQL root密码失败")
|
||||
color.Redln(translate.Get("commands.panel.writeMysqlPassword.fail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln("写入MySQL root密码成功")
|
||||
color.Greenln(translate.Get("commands.panel.writeMysqlPassword.success"))
|
||||
|
||||
case "cleanTask":
|
||||
_, err := facades.Orm().Query().Model(&models.Task{}).Where("status", models.TaskStatusRunning).OrWhere("status", models.TaskStatusWaiting).Update("status", models.TaskStatusFailed)
|
||||
if err != nil {
|
||||
color.Redln("清理任务失败")
|
||||
color.Redln(translate.Get("commands.panel.cleanTask.fail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln("清理任务成功")
|
||||
color.Greenln(translate.Get("commands.panel.cleanTask.success"))
|
||||
|
||||
case "backup":
|
||||
backupType := arg1
|
||||
@@ -250,37 +255,37 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
save := arg4
|
||||
hr := `+----------------------------------------------------`
|
||||
if len(backupType) == 0 || len(name) == 0 || len(path) == 0 || len(save) == 0 {
|
||||
color.Redln("参数错误")
|
||||
color.Redln(translate.Get("commands.panel.backup.paramFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln(hr)
|
||||
color.Greenln("★ 开始备份 [" + carbon.Now().ToDateTimeString() + "]")
|
||||
color.Greenln("★ " + translate.Get("commands.panel.backup.start") + " [" + carbon.Now().ToDateTimeString() + "]")
|
||||
color.Greenln(hr)
|
||||
|
||||
if !tools.Exists(path) {
|
||||
if err := tools.Mkdir(path, 0644); err != nil {
|
||||
color.Redln("|-创建备份目录失败: " + err.Error())
|
||||
color.Redln("|-" + translate.Get("commands.panel.backup.backupDirFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
switch backupType {
|
||||
case "website":
|
||||
color.Yellowln("|-目标网站: " + name)
|
||||
color.Yellowln("|-" + translate.Get("commands.panel.backup.targetSite") + ": " + name)
|
||||
var website models.Website
|
||||
if err := facades.Orm().Query().Where("name", name).FirstOrFail(&website); err != nil {
|
||||
color.Redln("|-网站不存在")
|
||||
color.Redln("|-" + translate.Get("commands.panel.backup.siteNotExist"))
|
||||
color.Greenln(hr)
|
||||
return nil
|
||||
}
|
||||
|
||||
backupFile := path + "/" + website.Name + "_" + carbon.Now().ToShortDateTimeString() + ".zip"
|
||||
if _, err := tools.Exec(`cd '` + website.Path + `' && zip -r '` + backupFile + `' .`); err != nil {
|
||||
color.Redln("|-备份失败: " + err.Error())
|
||||
color.Redln("|-" + translate.Get("commands.panel.backup.backupFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
color.Greenln("|-备份成功")
|
||||
color.Greenln("|-" + translate.Get("commands.panel.backup.backupSuccess"))
|
||||
|
||||
case "mysql":
|
||||
rootPassword := services.NewSettingImpl().Get(models.SettingKeyMysqlRootPassword)
|
||||
@@ -288,81 +293,81 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
|
||||
err := os.Setenv("MYSQL_PWD", rootPassword)
|
||||
if err != nil {
|
||||
color.Redln("|-备份MySQL数据库失败: " + err.Error())
|
||||
color.Redln("|-" + translate.Get("commands.panel.backup.mysqlBackupFail") + ": " + err.Error())
|
||||
color.Greenln(hr)
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln("|-目标MySQL数据库: " + name)
|
||||
color.Greenln("|-开始导出")
|
||||
color.Greenln("|-" + translate.Get("commands.panel.backup.targetMysql") + ": " + name)
|
||||
color.Greenln("|-" + translate.Get("commands.panel.backup.startExport"))
|
||||
if _, err = tools.Exec(`mysqldump -uroot ` + name + ` > /tmp/` + backupFile + ` 2>&1`); err != nil {
|
||||
color.Redln("|-导出失败: " + err.Error())
|
||||
color.Redln("|-" + translate.Get("commands.panel.backup.exportFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
color.Greenln("|-导出成功")
|
||||
color.Greenln("|-开始压缩")
|
||||
color.Greenln("|-" + translate.Get("commands.panel.backup.exportSuccess"))
|
||||
color.Greenln("|-" + translate.Get("commands.panel.backup.startCompress"))
|
||||
if _, err = tools.Exec("cd /tmp && zip -r " + backupFile + ".zip " + backupFile); err != nil {
|
||||
color.Redln("|-压缩失败: " + err.Error())
|
||||
color.Redln("|-" + translate.Get("commands.panel.backup.compressFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
if err := tools.Remove("/tmp/" + backupFile); err != nil {
|
||||
color.Redln("|-删除失败: " + err.Error())
|
||||
color.Redln("|-" + translate.Get("commands.panel.backup.deleteFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
color.Greenln("|-压缩成功")
|
||||
color.Greenln("|-开始移动")
|
||||
color.Greenln("|-" + translate.Get("commands.panel.backup.compressSuccess"))
|
||||
color.Greenln("|-" + translate.Get("commands.panel.backup.startMove"))
|
||||
if err := tools.Mv("/tmp/"+backupFile+".zip", path+"/"+backupFile+".zip"); err != nil {
|
||||
color.Redln("|-移动失败: " + err.Error())
|
||||
color.Redln("|-" + translate.Get("commands.panel.backup.moveFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
color.Greenln("|-移动成功")
|
||||
color.Greenln("|-" + translate.Get("commands.panel.backup.moveSuccess"))
|
||||
_ = os.Unsetenv("MYSQL_PWD")
|
||||
color.Greenln("|-备份成功")
|
||||
color.Greenln("|-" + translate.Get("commands.panel.backup.success"))
|
||||
|
||||
case "postgresql":
|
||||
backupFile := name + "_" + carbon.Now().ToShortDateTimeString() + ".sql"
|
||||
check, err := tools.Exec(`su - postgres -c "psql -l" 2>&1`)
|
||||
if err != nil {
|
||||
color.Redln("|-获取数据库失败: " + err.Error())
|
||||
color.Redln("|-" + translate.Get("commands.panel.backup.databaseGetFail") + ": " + err.Error())
|
||||
color.Greenln(hr)
|
||||
return nil
|
||||
}
|
||||
if !strings.Contains(check, name) {
|
||||
color.Redln("|-数据库不存在")
|
||||
color.Redln("|-" + translate.Get("commands.panel.backup.databaseNotExist"))
|
||||
color.Greenln(hr)
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln("|-目标PostgreSQL数据库: " + name)
|
||||
color.Greenln("|-开始导出")
|
||||
color.Greenln("|-" + translate.Get("commands.panel.backup.targetPostgres") + ": " + name)
|
||||
color.Greenln("|-" + translate.Get("commands.panel.backup.startExport"))
|
||||
if _, err = tools.Exec(`su - postgres -c "pg_dump '` + name + `'" > /tmp/` + backupFile + ` 2>&1`); err != nil {
|
||||
color.Redln("|-导出失败: " + err.Error())
|
||||
color.Redln("|-" + translate.Get("commands.panel.backup.exportFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
color.Greenln("|-导出成功")
|
||||
color.Greenln("|-开始压缩")
|
||||
color.Greenln("|-" + translate.Get("commands.panel.backup.exportSuccess"))
|
||||
color.Greenln("|-" + translate.Get("commands.panel.backup.startCompress"))
|
||||
if _, err = tools.Exec("cd /tmp && zip -r " + backupFile + ".zip " + backupFile); err != nil {
|
||||
color.Redln("|-压缩失败: " + err.Error())
|
||||
color.Redln("|-" + translate.Get("commands.panel.backup.compressFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
if err := tools.Remove("/tmp/" + backupFile); err != nil {
|
||||
color.Redln("|-删除失败: " + err.Error())
|
||||
color.Redln("|-" + translate.Get("commands.panel.backup.deleteFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
color.Greenln("|-压缩成功")
|
||||
color.Greenln("|-开始移动")
|
||||
color.Greenln("|-" + translate.Get("commands.panel.backup.compressSuccess"))
|
||||
color.Greenln("|-" + translate.Get("commands.panel.backup.startMove"))
|
||||
if err := tools.Mv("/tmp/"+backupFile+".zip", path+"/"+backupFile+".zip"); err != nil {
|
||||
color.Redln("|-移动失败: " + err.Error())
|
||||
color.Redln("|-" + translate.Get("commands.panel.backup.moveFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
color.Greenln("|-移动成功")
|
||||
color.Greenln("|-备份成功")
|
||||
color.Greenln("|-" + translate.Get("commands.panel.backup.moveSuccess"))
|
||||
color.Greenln("|-" + translate.Get("commands.panel.backup.success"))
|
||||
}
|
||||
|
||||
color.Greenln(hr)
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
color.Redln("|-清理失败: " + err.Error())
|
||||
color.Redln("|-" + translate.Get("commands.panel.backup.cleanupFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
var filteredFiles []os.FileInfo
|
||||
@@ -380,15 +385,15 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
})
|
||||
for i := cast.ToInt(save); i < len(filteredFiles); i++ {
|
||||
fileToDelete := filepath.Join(path, filteredFiles[i].Name())
|
||||
color.Yellowln("|-清理备份: " + fileToDelete)
|
||||
color.Yellowln("|-" + translate.Get("commands.panel.backup.cleanBackup") + ": " + fileToDelete)
|
||||
if err := tools.Remove(fileToDelete); err != nil {
|
||||
color.Redln("|-清理失败: " + err.Error())
|
||||
color.Redln("|-" + translate.Get("commands.panel.backup.cleanupFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
color.Greenln("|-清理完成")
|
||||
color.Greenln("|-" + translate.Get("commands.panel.backup.cleanupSuccess"))
|
||||
color.Greenln(hr)
|
||||
color.Greenln("☆ 备份完成 [" + carbon.Now().ToDateTimeString() + "]")
|
||||
color.Greenln("☆ " + translate.Get("commands.panel.backup.success") + " [" + carbon.Now().ToDateTimeString() + "]")
|
||||
color.Greenln(hr)
|
||||
|
||||
case "cutoff":
|
||||
@@ -396,44 +401,44 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
save := arg2
|
||||
hr := `+----------------------------------------------------`
|
||||
if len(name) == 0 || len(save) == 0 {
|
||||
color.Redln("参数错误")
|
||||
color.Redln(translate.Get("commands.panel.cutoff.paramFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln(hr)
|
||||
color.Greenln("★ 开始切割 [" + carbon.Now().ToDateTimeString() + "]")
|
||||
color.Greenln("★ " + translate.Get("commands.panel.cutoff.start") + " [" + carbon.Now().ToDateTimeString() + "]")
|
||||
color.Greenln(hr)
|
||||
|
||||
color.Yellowln("|-目标网站: " + name)
|
||||
color.Yellowln("|-" + translate.Get("commands.panel.cutoff.targetSite") + ": " + name)
|
||||
var website models.Website
|
||||
if err := facades.Orm().Query().Where("name", name).FirstOrFail(&website); err != nil {
|
||||
color.Redln("|-网站不存在")
|
||||
color.Redln("|-" + translate.Get("commands.panel.cutoff.siteNotExist"))
|
||||
color.Greenln(hr)
|
||||
return nil
|
||||
}
|
||||
|
||||
logPath := "/www/wwwlogs/" + website.Name + ".log"
|
||||
if !tools.Exists(logPath) {
|
||||
color.Redln("|-日志文件不存在")
|
||||
color.Redln("|-" + translate.Get("commands.panel.cutoff.logNotExist"))
|
||||
color.Greenln(hr)
|
||||
return nil
|
||||
}
|
||||
|
||||
backupPath := "/www/wwwlogs/" + website.Name + "_" + carbon.Now().ToShortDateTimeString() + ".log.zip"
|
||||
if _, err := tools.Exec(`cd /www/wwwlogs && zip -r ` + backupPath + ` ` + website.Name + ".log"); err != nil {
|
||||
color.Redln("|-备份失败: " + err.Error())
|
||||
color.Redln("|-" + translate.Get("commands.panel.cutoff.backupFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
if _, err := tools.Exec(`echo "" > ` + logPath); err != nil {
|
||||
color.Redln("|-清空失败: " + err.Error())
|
||||
color.Redln("|-" + translate.Get("commands.panel.cutoff.clearFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
color.Greenln("|-切割成功")
|
||||
color.Greenln("|-" + translate.Get("commands.panel.cutoff.cutSuccess"))
|
||||
|
||||
color.Greenln(hr)
|
||||
files, err := os.ReadDir("/www/wwwlogs")
|
||||
if err != nil {
|
||||
color.Redln("|-清理失败: " + err.Error())
|
||||
color.Redln("|-" + translate.Get("commands.panel.cutoff.cleanupFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
var filteredFiles []os.FileInfo
|
||||
@@ -451,15 +456,15 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
})
|
||||
for i := cast.ToInt(save); i < len(filteredFiles); i++ {
|
||||
fileToDelete := filepath.Join("/www/wwwlogs", filteredFiles[i].Name())
|
||||
color.Yellowln("|-清理日志: " + fileToDelete)
|
||||
color.Yellowln("|-" + translate.Get("commands.panel.cutoff.clearLog") + ": " + fileToDelete)
|
||||
if err := tools.Remove(fileToDelete); err != nil {
|
||||
color.Redln("|-清理失败: " + err.Error())
|
||||
color.Redln("|-" + translate.Get("commands.panel.cutoff.cleanupFail") + ": " + err.Error())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
color.Greenln("|-清理完成")
|
||||
color.Greenln("|-" + translate.Get("commands.panel.cutoff.cleanupSuccess"))
|
||||
color.Greenln(hr)
|
||||
color.Greenln("☆ 切割完成 [" + carbon.Now().ToDateTimeString() + "]")
|
||||
color.Greenln("☆ " + translate.Get("commands.panel.cutoff.end") + " [" + carbon.Now().ToDateTimeString() + "]")
|
||||
color.Greenln(hr)
|
||||
|
||||
case "writeSite":
|
||||
@@ -469,19 +474,19 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
php := cast.ToInt(arg4)
|
||||
ssl := cast.ToBool(ctx.Argument(5))
|
||||
if len(name) == 0 || len(path) == 0 {
|
||||
color.Redln("参数错误")
|
||||
color.Redln(translate.Get("commands.panel.writeSite.paramFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
var website models.Website
|
||||
if err := facades.Orm().Query().Where("name", name).FirstOrFail(&website); err == nil {
|
||||
color.Redln("网站已存在")
|
||||
color.Redln(translate.Get("commands.panel.writeSite.siteExist"))
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := os.Stat(path)
|
||||
if os.IsNotExist(err) {
|
||||
color.Redln("网站目录不存在")
|
||||
color.Redln(translate.Get("commands.panel.writeSite.pathNotExist"))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -493,32 +498,32 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
Ssl: ssl,
|
||||
})
|
||||
if err != nil {
|
||||
color.Redln("写入网站失败")
|
||||
color.Redln(translate.Get("commands.panel.writeSite.fail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln("写入网站成功")
|
||||
color.Greenln(translate.Get("commands.panel.writeSite.success"))
|
||||
|
||||
case "deleteSite":
|
||||
name := arg1
|
||||
if len(name) == 0 {
|
||||
color.Redln("参数错误")
|
||||
color.Redln(translate.Get("commands.panel.deleteSite.paramFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := facades.Orm().Query().Where("name", name).Delete(&models.Website{})
|
||||
if err != nil {
|
||||
color.Redln("删除网站失败")
|
||||
color.Redln(translate.Get("commands.panel.deleteSite.fail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln("删除网站成功")
|
||||
color.Greenln(translate.Get("commands.panel.deleteSite.success"))
|
||||
|
||||
case "writeSetting":
|
||||
key := arg1
|
||||
value := arg2
|
||||
if len(key) == 0 || len(value) == 0 {
|
||||
color.Redln("参数错误")
|
||||
color.Redln(translate.Get("commands.panel.writeSetting.paramFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -529,16 +534,16 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
Value: value,
|
||||
})
|
||||
if err != nil {
|
||||
color.Redln("写入设置失败")
|
||||
color.Redln(translate.Get("commands.panel.writeSetting.fail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln("写入设置成功")
|
||||
color.Greenln(translate.Get("commands.panel.writeSetting.success"))
|
||||
|
||||
case "getSetting":
|
||||
key := arg1
|
||||
if len(key) == 0 {
|
||||
color.Redln("参数错误")
|
||||
color.Redln(translate.Get("commands.panel.getSetting.paramFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -552,39 +557,165 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
case "deleteSetting":
|
||||
key := arg1
|
||||
if len(key) == 0 {
|
||||
color.Redln("参数错误")
|
||||
color.Redln(translate.Get("commands.panel.deleteSetting.paramFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := facades.Orm().Query().Where("key", key).Delete(&models.Setting{})
|
||||
if err != nil {
|
||||
color.Redln("删除设置失败")
|
||||
color.Redln(translate.Get("commands.panel.deleteSetting.fail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln("删除设置成功")
|
||||
color.Greenln(translate.Get("commands.panel.deleteSetting.success"))
|
||||
|
||||
case "addSite":
|
||||
name := arg1
|
||||
domain := arg2
|
||||
port := arg3
|
||||
path := arg4
|
||||
php := arg5
|
||||
if len(name) == 0 || len(domain) == 0 || len(port) == 0 || len(path) == 0 {
|
||||
color.Redln(translate.Get("commands.panel.addSite.paramFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
domains := strings.Split(domain, ",")
|
||||
ports := strings.Split(port, ",")
|
||||
if len(domains) == 0 || len(ports) == 0 {
|
||||
color.Redln(translate.Get("commands.panel.addSite.paramFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
var uintPorts []uint
|
||||
for _, p := range ports {
|
||||
uintPorts = append(uintPorts, cast.ToUint(p))
|
||||
}
|
||||
|
||||
website := services.NewWebsiteImpl()
|
||||
id, err := website.GetIDByName(name)
|
||||
if err != nil {
|
||||
color.Redln(err.Error())
|
||||
return nil
|
||||
}
|
||||
if id != 0 {
|
||||
color.Redln(translate.Get("commands.panel.addSite.siteExist"))
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = website.Add(internal.PanelWebsite{
|
||||
Name: name,
|
||||
Status: true,
|
||||
Domains: domains,
|
||||
Ports: uintPorts,
|
||||
Path: path,
|
||||
Php: php,
|
||||
Ssl: false,
|
||||
Db: false,
|
||||
})
|
||||
if err != nil {
|
||||
color.Redln(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln(translate.Get("commands.panel.addSite.success"))
|
||||
|
||||
case "removeSite":
|
||||
name := arg1
|
||||
if len(name) == 0 {
|
||||
color.Redln(translate.Get("commands.panel.removeSite.paramFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
website := services.NewWebsiteImpl()
|
||||
id, err := website.GetIDByName(name)
|
||||
if err != nil {
|
||||
color.Redln(err.Error())
|
||||
return nil
|
||||
}
|
||||
if id == 0 {
|
||||
color.Redln(translate.Get("commands.panel.removeSite.siteNotExist"))
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = website.Delete(id); err != nil {
|
||||
color.Redln(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln(translate.Get("commands.panel.removeSite.success"))
|
||||
|
||||
case "installPlugin":
|
||||
slug := arg1
|
||||
if len(slug) == 0 {
|
||||
color.Redln(translate.Get("commands.panel.installPlugin.paramFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
plugin := services.NewPluginImpl()
|
||||
if err := plugin.Install(slug); err != nil {
|
||||
color.Redln(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln(translate.Get("commands.panel.installPlugin.success"))
|
||||
|
||||
case "uninstallPlugin":
|
||||
slug := arg1
|
||||
if len(slug) == 0 {
|
||||
color.Redln(translate.Get("commands.panel.uninstallPlugin.paramFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
plugin := services.NewPluginImpl()
|
||||
if err := plugin.Uninstall(slug); err != nil {
|
||||
color.Redln(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln(translate.Get("commands.panel.uninstallPlugin.success"))
|
||||
|
||||
case "updatePlugin":
|
||||
slug := arg1
|
||||
if len(slug) == 0 {
|
||||
color.Redln(translate.Get("commands.panel.updatePlugin.paramFail"))
|
||||
return nil
|
||||
}
|
||||
|
||||
plugin := services.NewPluginImpl()
|
||||
if err := plugin.Update(slug); err != nil {
|
||||
color.Redln(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
color.Greenln(translate.Get("commands.panel.updatePlugin.success"))
|
||||
|
||||
default:
|
||||
color.Yellowln(facades.Config().GetString("panel.name") + "命令行工具 - " + facades.Config().GetString("panel.version"))
|
||||
color.Greenln("请使用以下命令:")
|
||||
color.Greenln("panel update 更新 / 修复面板到最新版本")
|
||||
color.Greenln("panel getInfo 重新初始化面板账号信息")
|
||||
color.Greenln("panel getPort 获取面板访问端口")
|
||||
color.Greenln("panel getEntrance 获取面板访问入口")
|
||||
color.Greenln("panel deleteEntrance 删除面板访问入口")
|
||||
color.Greenln("panel cleanTask 清理面板运行中和等待中的任务[任务卡住时使用]")
|
||||
color.Greenln("panel backup {website/mysql/postgresql} {name} {path} {save_copies} 备份网站 / MySQL数据库 / PostgreSQL数据库到指定目录并保留指定数量")
|
||||
color.Greenln("panel cutoff {website_name} {save_copies} 切割网站日志并保留指定数量")
|
||||
color.Redln("以下命令请在开发者指导下使用:")
|
||||
color.Yellowln("panel init 初始化面板")
|
||||
color.Yellowln("panel writePlugin {slug} {version} 写入插件安装状态")
|
||||
color.Yellowln("panel deletePlugin {slug} 移除插件安装状态")
|
||||
color.Yellowln("panel writeMysqlPassword {password} 写入MySQL root密码")
|
||||
color.Yellowln("panel writeSite {name} {status} {path} {php} {ssl} 写入网站数据到面板")
|
||||
color.Yellowln("panel deleteSite {name} 删除面板网站数据")
|
||||
color.Yellowln("panel getSetting {name} 获取面板设置数据")
|
||||
color.Yellowln("panel writeSetting {name} {value} 写入 / 更新面板设置数据")
|
||||
color.Yellowln("panel deleteSetting {name} 删除面板设置数据")
|
||||
color.Yellowln(facades.Config().GetString("panel.name") + " - " + translate.Get("commands.panel.tool") + " - " + facades.Config().GetString("panel.version"))
|
||||
color.Greenln(translate.Get("commands.panel.use") + ":")
|
||||
color.Greenln("panel update " + translate.Get("commands.panel.update.description"))
|
||||
color.Greenln("panel getInfo " + translate.Get("commands.panel.getInfo.description"))
|
||||
color.Greenln("panel getPort " + translate.Get("commands.panel.getPort.description"))
|
||||
color.Greenln("panel getEntrance " + translate.Get("commands.panel.getEntrance.description"))
|
||||
color.Greenln("panel deleteEntrance " + translate.Get("commands.panel.deleteEntrance.description"))
|
||||
color.Greenln("panel cleanTask " + translate.Get("commands.panel.cleanTask.description"))
|
||||
color.Greenln("panel backup {website/mysql/postgresql} {name} {path} {save_copies} " + translate.Get("commands.panel.backup.description"))
|
||||
color.Greenln("panel cutoff {website_name} {save_copies} " + translate.Get("commands.panel.cutoff.description"))
|
||||
color.Greenln("panel installPlugin {slug} " + translate.Get("commands.panel.installPlugin.description"))
|
||||
color.Greenln("panel uninstallPlugin {slug} " + translate.Get("commands.panel.uninstallPlugin.description"))
|
||||
color.Greenln("panel updatePlugin {slug} " + translate.Get("commands.panel.updatePlugin.description"))
|
||||
color.Greenln("panel addSite {name} {domain} {port} {path} {php} " + translate.Get("commands.panel.addSite.description"))
|
||||
color.Greenln("panel removeSite {name} " + translate.Get("commands.panel.removeSite.description"))
|
||||
color.Redln(translate.Get("commands.panel.forDeveloper") + ":")
|
||||
color.Yellowln("panel init " + translate.Get("commands.panel.init.description"))
|
||||
color.Yellowln("panel writePlugin {slug} {version} " + translate.Get("commands.panel.writePlugin.description"))
|
||||
color.Yellowln("panel deletePlugin {slug} " + translate.Get("commands.panel.deletePlugin.description"))
|
||||
color.Yellowln("panel writeMysqlPassword {password} " + translate.Get("commands.panel.writeMysqlPassword.description"))
|
||||
color.Yellowln("panel writeSite {name} {status} {path} {php} {ssl} " + translate.Get("commands.panel.writeSite.description"))
|
||||
color.Yellowln("panel deleteSite {name} " + translate.Get("commands.panel.deleteSite.description"))
|
||||
color.Yellowln("panel getSetting {name} " + translate.Get("commands.panel.getSetting.description"))
|
||||
color.Yellowln("panel writeSetting {name} {value} " + translate.Get("commands.panel.writeSetting.description"))
|
||||
color.Yellowln("panel deleteSetting {name} " + translate.Get("commands.panel.deleteSetting.description"))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/goravel/framework/contracts/console"
|
||||
"github.com/goravel/framework/contracts/console/command"
|
||||
"github.com/goravel/framework/facades"
|
||||
@@ -21,7 +23,7 @@ func (receiver *PanelTask) Signature() string {
|
||||
|
||||
// Description The console command description.
|
||||
func (receiver *PanelTask) Description() string {
|
||||
return "[面板] 每日任务"
|
||||
return facades.Lang(context.Background()).Get("commands.panel:task.description")
|
||||
}
|
||||
|
||||
// Extend The console command extend.
|
||||
@@ -32,11 +34,12 @@ func (receiver *PanelTask) Extend() command.Extend {
|
||||
}
|
||||
|
||||
// Handle Execute the console command.
|
||||
func (receiver *PanelTask) Handle(ctx console.Context) error {
|
||||
func (receiver *PanelTask) Handle(console.Context) error {
|
||||
internal.Status = internal.StatusMaintain
|
||||
|
||||
// 优化数据库
|
||||
if _, err := facades.Orm().Query().Exec("VACUUM"); err != nil {
|
||||
internal.Status = internal.StatusFailed
|
||||
facades.Log().Tags("面板", "每日任务").
|
||||
With(map[string]any{
|
||||
"error": err.Error(),
|
||||
@@ -46,6 +49,7 @@ func (receiver *PanelTask) Handle(ctx console.Context) error {
|
||||
|
||||
// 备份面板
|
||||
if err := tools.Archive([]string{"/www/panel"}, "/www/backup/panel/panel-"+carbon.Now().ToShortDateTimeString()+".zip"); err != nil {
|
||||
internal.Status = internal.StatusFailed
|
||||
facades.Log().Tags("面板", "每日任务").
|
||||
With(map[string]any{
|
||||
"error": err.Error(),
|
||||
@@ -55,6 +59,7 @@ func (receiver *PanelTask) Handle(ctx console.Context) error {
|
||||
|
||||
// 清理 7 天前的备份
|
||||
if _, err := tools.Exec(`find /www/backup/panel -mtime +7 -name "*.zip" -exec rm -rf {} \;`); err != nil {
|
||||
internal.Status = internal.StatusFailed
|
||||
facades.Log().Tags("面板", "每日任务").
|
||||
With(map[string]any{
|
||||
"error": err.Error(),
|
||||
|
||||
@@ -650,7 +650,7 @@ func (r *CertController) Renew(ctx http.Context) http.Response {
|
||||
// @Produce json
|
||||
// @Security BearerToken
|
||||
// @Param data body requests.Obtain true "request"
|
||||
// @Success 200 {object} SuccessResponse{data=map[string]acme.Resolve}
|
||||
// @Success 200 {object} SuccessResponse{data=[]acme.DNSRecord}
|
||||
// @Router /panel/cert/manualDNS [post]
|
||||
func (r *CertController) ManualDNS(ctx http.Context) http.Response {
|
||||
var obtainRequest requests.Obtain
|
||||
@@ -669,3 +669,33 @@ func (r *CertController) ManualDNS(ctx http.Context) http.Response {
|
||||
|
||||
return Success(ctx, resolves)
|
||||
}
|
||||
|
||||
// Deploy
|
||||
//
|
||||
// @Summary 部署证书
|
||||
// @Description 部署面板证书管理的证书
|
||||
// @Tags 证书管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerToken
|
||||
// @Param data body requests.CertDeploy true "request"
|
||||
// @Success 200 {object} SuccessResponse
|
||||
// @Router /panel/cert/deploy [post]
|
||||
func (r *CertController) Deploy(ctx http.Context) http.Response {
|
||||
var deployRequest requests.CertDeploy
|
||||
sanitize := Sanitize(ctx, &deployRequest)
|
||||
if sanitize != nil {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
err := r.cert.Deploy(deployRequest.ID, deployRequest.WebsiteID)
|
||||
if err != nil {
|
||||
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
|
||||
"certID": deployRequest.ID,
|
||||
"error": err.Error(),
|
||||
}).Info("部署证书失败")
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return Success(ctx, nil)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package controllers
|
||||
|
||||
import (
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/facades"
|
||||
)
|
||||
|
||||
// SuccessResponse 通用成功响应
|
||||
@@ -26,14 +27,14 @@ 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: "错误: " + message,
|
||||
Message: facades.Lang(ctx).Get("messages.mistake") + ": " + message,
|
||||
})
|
||||
}
|
||||
|
||||
// ErrorSystem 响应系统错误
|
||||
func ErrorSystem(ctx http.Context) http.Response {
|
||||
return ctx.Response().Json(http.StatusInternalServerError, &ErrorResponse{
|
||||
Message: "系统内部错误",
|
||||
Message: facades.Lang(ctx).Get("errors.internal"),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,11 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/support/carbon"
|
||||
commonrequests "panel/app/http/requests/common"
|
||||
|
||||
requests "panel/app/http/requests/file"
|
||||
"panel/pkg/tools"
|
||||
@@ -80,7 +82,7 @@ func (r *FileController) Content(ctx http.Context) http.Response {
|
||||
return Error(ctx, http.StatusInternalServerError, "目标路径不是文件")
|
||||
}
|
||||
if fileInfo.Size() > 10*1024*1024 {
|
||||
return Error(ctx, http.StatusInternalServerError, "文件大小超过 10M")
|
||||
return Error(ctx, http.StatusInternalServerError, "文件大小超过 10 M,不支持在线编辑")
|
||||
}
|
||||
|
||||
content, err := tools.Read(request.Path)
|
||||
@@ -207,15 +209,15 @@ func (r *FileController) Move(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
if tools.Exists(request.New) && !ctx.Request().InputBool("force") {
|
||||
return Error(ctx, http.StatusForbidden, "目标路径已存在,是否覆盖?")
|
||||
if tools.Exists(request.Target) && !ctx.Request().InputBool("force") {
|
||||
return Error(ctx, http.StatusForbidden, "目标路径"+request.Target+"已存在")
|
||||
}
|
||||
|
||||
if err := tools.Mv(request.Old, request.New); err != nil {
|
||||
if err := tools.Mv(request.Source, request.Target); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
r.setPermission(request.New, 0755, "www", "www")
|
||||
r.setPermission(request.Target, 0755, "www", "www")
|
||||
return Success(ctx, nil)
|
||||
}
|
||||
|
||||
@@ -237,15 +239,15 @@ func (r *FileController) Copy(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
if tools.Exists(request.New) && !ctx.Request().InputBool("force") {
|
||||
return Error(ctx, http.StatusForbidden, "目标路径已存在,是否覆盖?")
|
||||
if tools.Exists(request.Target) && !ctx.Request().InputBool("force") {
|
||||
return Error(ctx, http.StatusForbidden, "目标路径"+request.Target+"已存在")
|
||||
}
|
||||
|
||||
if err := tools.Cp(request.Old, request.New); err != nil {
|
||||
if err := tools.Cp(request.Source, request.Target); err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
r.setPermission(request.New, 0755, "www", "www")
|
||||
r.setPermission(request.Source, 0755, "www", "www")
|
||||
return Success(ctx, nil)
|
||||
}
|
||||
|
||||
@@ -454,7 +456,8 @@ func (r *FileController) Search(ctx http.Context) http.Response {
|
||||
// @Tags 文件管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data query requests.Exist true "request"
|
||||
// @Param data query requests.Exist true "request"
|
||||
// @Param data query commonrequests.Paginate true "request"
|
||||
// @Success 200 {object} SuccessResponse
|
||||
// @Router /panel/file/list [get]
|
||||
func (r *FileController) List(ctx http.Context) http.Response {
|
||||
@@ -464,6 +467,12 @@ func (r *FileController) List(ctx http.Context) http.Response {
|
||||
return sanitize
|
||||
}
|
||||
|
||||
var paginate commonrequests.Paginate
|
||||
paginateSanitize := Sanitize(ctx, &paginate)
|
||||
if paginateSanitize != nil {
|
||||
return paginateSanitize
|
||||
}
|
||||
|
||||
fileInfoList, err := os.ReadDir(request.Path)
|
||||
if err != nil {
|
||||
return Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
@@ -472,17 +481,44 @@ func (r *FileController) List(ctx http.Context) http.Response {
|
||||
var paths []any
|
||||
for _, fileInfo := range fileInfoList {
|
||||
info, _ := fileInfo.Info()
|
||||
stat := info.Sys().(*syscall.Stat_t)
|
||||
|
||||
paths = append(paths, map[string]any{
|
||||
"name": info.Name(),
|
||||
"full": filepath.Join(request.Path, info.Name()),
|
||||
"size": tools.FormatBytes(float64(info.Size())),
|
||||
"mode_str": info.Mode().String(),
|
||||
"mode": fmt.Sprintf("%04o", info.Mode().Perm()),
|
||||
"owner": tools.GetUser(stat.Uid),
|
||||
"group": tools.GetGroup(stat.Gid),
|
||||
"uid": stat.Uid,
|
||||
"gid": stat.Gid,
|
||||
"hidden": tools.IsHidden(info.Name()),
|
||||
"symlink": tools.IsSymlink(info.Mode()),
|
||||
"link": tools.GetSymlink(filepath.Join(request.Path, info.Name())),
|
||||
"dir": info.IsDir(),
|
||||
"modify": carbon.FromStdTime(info.ModTime()).ToDateTimeString(),
|
||||
})
|
||||
}
|
||||
|
||||
return Success(ctx, paths)
|
||||
start := paginate.Limit * (paginate.Page - 1)
|
||||
end := paginate.Limit * paginate.Page
|
||||
if start > len(paths) {
|
||||
start = len(paths)
|
||||
}
|
||||
if end > len(paths) {
|
||||
end = len(paths)
|
||||
}
|
||||
|
||||
paged := paths[start:end]
|
||||
if paged == nil {
|
||||
paged = []any{}
|
||||
}
|
||||
|
||||
return Success(ctx, http.Json{
|
||||
"total": len(paths),
|
||||
"items": paged,
|
||||
})
|
||||
}
|
||||
|
||||
// setPermission
|
||||
|
||||
@@ -92,162 +92,33 @@ func (r *PluginController) List(ctx http.Context) http.Response {
|
||||
// Install 安装插件
|
||||
func (r *PluginController) Install(ctx http.Context) http.Response {
|
||||
slug := ctx.Request().Input("slug")
|
||||
plugin := r.plugin.GetBySlug(slug)
|
||||
installedPlugin := r.plugin.GetInstalledBySlug(slug)
|
||||
installedPlugins, err := r.plugin.AllInstalled()
|
||||
if err != nil {
|
||||
facades.Log().Request(ctx.Request()).Tags("面板", "插件中心").With(map[string]any{
|
||||
"slug": slug,
|
||||
}).Info("检查插件安装状态失败")
|
||||
|
||||
if err := r.plugin.Install(slug); err != nil {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
if installedPlugin.ID != 0 {
|
||||
return Error(ctx, http.StatusUnprocessableEntity, "插件已安装")
|
||||
}
|
||||
|
||||
pluginsMap := make(map[string]bool)
|
||||
|
||||
for _, p := range installedPlugins {
|
||||
pluginsMap[p.Slug] = true
|
||||
}
|
||||
|
||||
for _, require := range plugin.Requires {
|
||||
_, requireFound := pluginsMap[require]
|
||||
if !requireFound {
|
||||
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 需要依赖 "+require+" 插件")
|
||||
}
|
||||
}
|
||||
|
||||
for _, exclude := range plugin.Excludes {
|
||||
_, excludeFound := pluginsMap[exclude]
|
||||
if excludeFound {
|
||||
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 不兼容 "+exclude+" 插件")
|
||||
}
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "安装插件 " + plugin.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = plugin.Install + ` >> '/tmp/` + plugin.Slug + `.log' 2>&1`
|
||||
task.Log = "/tmp/" + plugin.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Request(ctx.Request()).Tags("面板", "插件中心").With(map[string]any{
|
||||
"slug": slug,
|
||||
"err": err.Error(),
|
||||
}).Info("创建任务失败")
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
r.task.Process(task.ID)
|
||||
return Success(ctx, "任务已提交")
|
||||
}
|
||||
|
||||
// Uninstall 卸载插件
|
||||
func (r *PluginController) Uninstall(ctx http.Context) http.Response {
|
||||
slug := ctx.Request().Input("slug")
|
||||
plugin := r.plugin.GetBySlug(slug)
|
||||
installedPlugin := r.plugin.GetInstalledBySlug(slug)
|
||||
installedPlugins, err := r.plugin.AllInstalled()
|
||||
if err != nil {
|
||||
facades.Log().Request(ctx.Request()).Tags("面板", "插件中心").With(map[string]any{
|
||||
"slug": slug,
|
||||
}).Info("检查插件安装状态失败")
|
||||
|
||||
if err := r.plugin.Uninstall(slug); err != nil {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
if installedPlugin.ID == 0 {
|
||||
return Error(ctx, http.StatusUnprocessableEntity, "插件未安装")
|
||||
}
|
||||
|
||||
pluginsMap := make(map[string]bool)
|
||||
|
||||
for _, p := range installedPlugins {
|
||||
pluginsMap[p.Slug] = true
|
||||
}
|
||||
|
||||
for _, require := range plugin.Requires {
|
||||
_, requireFound := pluginsMap[require]
|
||||
if !requireFound {
|
||||
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 需要依赖 "+require+" 插件")
|
||||
}
|
||||
}
|
||||
|
||||
for _, exclude := range plugin.Excludes {
|
||||
_, excludeFound := pluginsMap[exclude]
|
||||
if excludeFound {
|
||||
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 不兼容 "+exclude+" 插件")
|
||||
}
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "卸载插件 " + plugin.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = plugin.Uninstall + " >> /tmp/" + plugin.Slug + ".log 2>&1"
|
||||
task.Log = "/tmp/" + plugin.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Request(ctx.Request()).Tags("面板", "插件中心").With(map[string]any{
|
||||
"slug": slug,
|
||||
"err": err.Error(),
|
||||
}).Info("创建任务失败")
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
r.task.Process(task.ID)
|
||||
return Success(ctx, "任务已提交")
|
||||
}
|
||||
|
||||
// Update 更新插件
|
||||
func (r *PluginController) Update(ctx http.Context) http.Response {
|
||||
slug := ctx.Request().Input("slug")
|
||||
plugin := r.plugin.GetBySlug(slug)
|
||||
installedPlugin := r.plugin.GetInstalledBySlug(slug)
|
||||
installedPlugins, err := r.plugin.AllInstalled()
|
||||
if err != nil {
|
||||
facades.Log().Request(ctx.Request()).Tags("面板", "插件中心").With(map[string]any{
|
||||
"slug": slug,
|
||||
}).Info("检查插件安装状态失败")
|
||||
|
||||
if err := r.plugin.Update(slug); err != nil {
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
if installedPlugin.ID == 0 {
|
||||
return Error(ctx, http.StatusUnprocessableEntity, "插件未安装")
|
||||
}
|
||||
|
||||
pluginsMap := make(map[string]bool)
|
||||
|
||||
for _, p := range installedPlugins {
|
||||
pluginsMap[p.Slug] = true
|
||||
}
|
||||
|
||||
for _, require := range plugin.Requires {
|
||||
_, requireFound := pluginsMap[require]
|
||||
if !requireFound {
|
||||
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 需要依赖 "+require+" 插件")
|
||||
}
|
||||
}
|
||||
|
||||
for _, exclude := range plugin.Excludes {
|
||||
_, excludeFound := pluginsMap[exclude]
|
||||
if excludeFound {
|
||||
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 不兼容 "+exclude+" 插件")
|
||||
}
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "更新插件 " + plugin.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = plugin.Update + " >> /tmp/" + plugin.Slug + ".log 2>&1"
|
||||
task.Log = "/tmp/" + plugin.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Request(ctx.Request()).Tags("面板", "插件中心").With(map[string]any{
|
||||
"slug": slug,
|
||||
"err": err.Error(),
|
||||
}).Info("创建任务失败")
|
||||
return ErrorSystem(ctx)
|
||||
}
|
||||
|
||||
r.task.Process(task.ID)
|
||||
return Success(ctx, "任务已提交")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,667 +0,0 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"panel/app/http/controllers"
|
||||
"panel/app/models"
|
||||
"panel/internal"
|
||||
"panel/internal/services"
|
||||
"panel/pkg/tools"
|
||||
)
|
||||
|
||||
type Mysql57Controller struct {
|
||||
setting internal.Setting
|
||||
backup internal.Backup
|
||||
}
|
||||
|
||||
func NewMysql57Controller() *Mysql57Controller {
|
||||
return &Mysql57Controller{
|
||||
setting: services.NewSettingImpl(),
|
||||
backup: services.NewBackupImpl(),
|
||||
}
|
||||
}
|
||||
|
||||
// Status 获取运行状态
|
||||
func (r *Mysql57Controller) Status(ctx http.Context) http.Response {
|
||||
status, err := tools.ServiceStatus("mysqld")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, status)
|
||||
}
|
||||
|
||||
// Reload 重载配置
|
||||
func (r *Mysql57Controller) Reload(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceReload("mysqld"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重载MySQL失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// Restart 重启服务
|
||||
func (r *Mysql57Controller) Restart(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceRestart("mysqld"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重启MySQL服务失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// Start 启动服务
|
||||
func (r *Mysql57Controller) Start(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceStart("mysqld"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "启动MySQL服务失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// Stop 停止服务
|
||||
func (r *Mysql57Controller) Stop(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceStop("mysqld"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "停止MySQL服务失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// GetConfig 获取配置
|
||||
func (r *Mysql57Controller) GetConfig(ctx http.Context) http.Response {
|
||||
config, err := tools.Read("/www/server/mysql/conf/my.cnf")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL配置失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, config)
|
||||
}
|
||||
|
||||
// SaveConfig 保存配置
|
||||
func (r *Mysql57Controller) SaveConfig(ctx http.Context) http.Response {
|
||||
config := ctx.Request().Input("config")
|
||||
if len(config) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "配置不能为空")
|
||||
}
|
||||
|
||||
if err := tools.Write("/www/server/mysql/conf/my.cnf", config, 0644); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "写入MySQL配置失败")
|
||||
}
|
||||
|
||||
return r.Restart(ctx)
|
||||
}
|
||||
|
||||
// Load 获取负载
|
||||
func (r *Mysql57Controller) Load(ctx http.Context) http.Response {
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
if len(rootPassword) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码为空")
|
||||
}
|
||||
|
||||
status, err := tools.ServiceStatus("mysqld")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
|
||||
}
|
||||
if !status {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "MySQL 未运行")
|
||||
}
|
||||
|
||||
raw, err := tools.Exec("/www/server/mysql/bin/mysqladmin -uroot -p" + rootPassword + " extended-status 2>&1")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL负载失败")
|
||||
}
|
||||
|
||||
var data []map[string]string
|
||||
expressions := []struct {
|
||||
regex string
|
||||
name string
|
||||
}{
|
||||
{`Uptime\s+\|\s+(\d+)\s+\|`, "运行时间"},
|
||||
{`Queries\s+\|\s+(\d+)\s+\|`, "总查询次数"},
|
||||
{`Connections\s+\|\s+(\d+)\s+\|`, "总连接次数"},
|
||||
{`Com_commit\s+\|\s+(\d+)\s+\|`, "每秒事务"},
|
||||
{`Com_rollback\s+\|\s+(\d+)\s+\|`, "每秒回滚"},
|
||||
{`Bytes_sent\s+\|\s+(\d+)\s+\|`, "发送"},
|
||||
{`Bytes_received\s+\|\s+(\d+)\s+\|`, "接收"},
|
||||
{`Threads_connected\s+\|\s+(\d+)\s+\|`, "活动连接数"},
|
||||
{`Max_used_connections\s+\|\s+(\d+)\s+\|`, "峰值连接数"},
|
||||
{`Key_read_requests\s+\|\s+(\d+)\s+\|`, "索引命中率"},
|
||||
{`Innodb_buffer_pool_reads\s+\|\s+(\d+)\s+\|`, "Innodb索引命中率"},
|
||||
{`Created_tmp_disk_tables\s+\|\s+(\d+)\s+\|`, "创建临时表到磁盘"},
|
||||
{`Open_tables\s+\|\s+(\d+)\s+\|`, "已打开的表"},
|
||||
{`Select_full_join\s+\|\s+(\d+)\s+\|`, "没有使用索引的量"},
|
||||
{`Select_full_range_join\s+\|\s+(\d+)\s+\|`, "没有索引的JOIN量"},
|
||||
{`Select_range_check\s+\|\s+(\d+)\s+\|`, "没有索引的子查询量"},
|
||||
{`Sort_merge_passes\s+\|\s+(\d+)\s+\|`, "排序后的合并次数"},
|
||||
{`Table_locks_waited\s+\|\s+(\d+)\s+\|`, "锁表次数"},
|
||||
}
|
||||
|
||||
for _, expression := range expressions {
|
||||
re := regexp.MustCompile(expression.regex)
|
||||
matches := re.FindStringSubmatch(raw)
|
||||
if len(matches) > 1 {
|
||||
d := map[string]string{"name": expression.name, "value": matches[1]}
|
||||
if expression.name == "发送" || expression.name == "接收" {
|
||||
d["value"] = tools.FormatBytes(cast.ToFloat64(matches[1]))
|
||||
}
|
||||
|
||||
data = append(data, d)
|
||||
}
|
||||
}
|
||||
|
||||
// 索引命中率
|
||||
readRequests := cast.ToFloat64(data[9]["value"])
|
||||
reads := cast.ToFloat64(data[10]["value"])
|
||||
data[9]["value"] = fmt.Sprintf("%.2f%%", readRequests/(reads+readRequests)*100)
|
||||
// Innodb 索引命中率
|
||||
bufferPoolReads := cast.ToFloat64(data[11]["value"])
|
||||
bufferPoolReadRequests := cast.ToFloat64(data[12]["value"])
|
||||
data[10]["value"] = fmt.Sprintf("%.2f%%", bufferPoolReadRequests/(bufferPoolReads+bufferPoolReadRequests)*100)
|
||||
|
||||
return controllers.Success(ctx, data)
|
||||
}
|
||||
|
||||
// ErrorLog 获取错误日志
|
||||
func (r *Mysql57Controller) ErrorLog(ctx http.Context) http.Response {
|
||||
log, err := tools.Exec("tail -n 100 /www/server/mysql/mysql-error.log")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, log)
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, log)
|
||||
}
|
||||
|
||||
// ClearErrorLog 清空错误日志
|
||||
func (r *Mysql57Controller) ClearErrorLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec("echo '' > /www/server/mysql/mysql-error.log"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// SlowLog 获取慢查询日志
|
||||
func (r *Mysql57Controller) SlowLog(ctx http.Context) http.Response {
|
||||
log, err := tools.Exec("tail -n 100 /www/server/mysql/mysql-slow.log")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, log)
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, log)
|
||||
}
|
||||
|
||||
// ClearSlowLog 清空慢查询日志
|
||||
func (r *Mysql57Controller) ClearSlowLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec("echo '' > /www/server/mysql/mysql-slow.log"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// GetRootPassword 获取root密码
|
||||
func (r *Mysql57Controller) GetRootPassword(ctx http.Context) http.Response {
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
if len(rootPassword) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码为空")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, rootPassword)
|
||||
}
|
||||
|
||||
// SetRootPassword 设置root密码
|
||||
func (r *Mysql57Controller) SetRootPassword(ctx http.Context) http.Response {
|
||||
status, err := tools.ServiceStatus("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 := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + oldRootPassword + " -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '" + rootPassword + "';\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "设置root密码失败")
|
||||
}
|
||||
if _, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + oldRootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "设置root密码失败")
|
||||
}
|
||||
err := r.setting.Set(models.SettingKeyMysqlRootPassword, rootPassword)
|
||||
if err != nil {
|
||||
if _, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '" + oldRootPassword + "';\""); err != nil {
|
||||
return nil
|
||||
}
|
||||
if _, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
|
||||
return nil
|
||||
}
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "设置root密码失败")
|
||||
}
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// DatabaseList 获取数据库列表
|
||||
func (r *Mysql57Controller) 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)/")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SHOW DATABASES")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var databases []database
|
||||
for rows.Next() {
|
||||
var d database
|
||||
err := rows.Scan(&d.Name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
databases = append(databases, d)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取数据库列表失败")
|
||||
}
|
||||
|
||||
page := ctx.Request().QueryInt("page", 1)
|
||||
limit := ctx.Request().QueryInt("limit", 10)
|
||||
startIndex := (page - 1) * limit
|
||||
endIndex := page * limit
|
||||
if startIndex > len(databases) {
|
||||
return controllers.Success(ctx, http.Json{
|
||||
"total": 0,
|
||||
"items": []database{},
|
||||
})
|
||||
}
|
||||
if endIndex > len(databases) {
|
||||
endIndex = len(databases)
|
||||
}
|
||||
pagedDatabases := databases[startIndex:endIndex]
|
||||
|
||||
return controllers.Success(ctx, http.Json{
|
||||
"total": len(databases),
|
||||
"items": pagedDatabases,
|
||||
})
|
||||
}
|
||||
|
||||
// AddDatabase 添加数据库
|
||||
func (r *Mysql57Controller) AddDatabase(ctx http.Context) http.Response {
|
||||
validator, err := ctx.Request().Validate(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",
|
||||
})
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
}
|
||||
if validator.Fails() {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
|
||||
}
|
||||
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
database := ctx.Request().Input("database")
|
||||
user := ctx.Request().Input("user")
|
||||
password := ctx.Request().Input("password")
|
||||
|
||||
if out, err := tools.Exec("/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)
|
||||
}
|
||||
if out, err := tools.Exec("/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 out, err := tools.Exec("/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 := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// DeleteDatabase 删除数据库
|
||||
func (r *Mysql57Controller) DeleteDatabase(ctx http.Context) http.Response {
|
||||
validator, err := ctx.Request().Validate(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",
|
||||
})
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
}
|
||||
if validator.Fails() {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
|
||||
}
|
||||
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
database := ctx.Request().Input("database")
|
||||
if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP DATABASE IF EXISTS " + database + ";\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// BackupList 获取备份列表
|
||||
func (r *Mysql57Controller) BackupList(ctx http.Context) http.Response {
|
||||
backupList, err := r.backup.MysqlList()
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
page := ctx.Request().QueryInt("page", 1)
|
||||
limit := ctx.Request().QueryInt("limit", 10)
|
||||
startIndex := (page - 1) * limit
|
||||
endIndex := page * limit
|
||||
if startIndex > len(backupList) {
|
||||
return controllers.Success(ctx, http.Json{
|
||||
"total": 0,
|
||||
"items": []internal.BackupFile{},
|
||||
})
|
||||
}
|
||||
if endIndex > len(backupList) {
|
||||
endIndex = len(backupList)
|
||||
}
|
||||
pagedBackupList := backupList[startIndex:endIndex]
|
||||
if pagedBackupList == nil {
|
||||
pagedBackupList = []internal.BackupFile{}
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, http.Json{
|
||||
"total": len(backupList),
|
||||
"items": pagedBackupList,
|
||||
})
|
||||
}
|
||||
|
||||
// UploadBackup 上传备份
|
||||
func (r *Mysql57Controller) UploadBackup(ctx http.Context) http.Response {
|
||||
file, err := ctx.Request().File("file")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "上传文件失败")
|
||||
}
|
||||
|
||||
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/mysql"
|
||||
if !tools.Exists(backupPath) {
|
||||
if err = tools.Mkdir(backupPath, 0644); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
name := file.GetClientOriginalName()
|
||||
_, err = file.StoreAs(backupPath, name)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "上传文件失败")
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// CreateBackup 创建备份
|
||||
func (r *Mysql57Controller) CreateBackup(ctx http.Context) http.Response {
|
||||
validator, err := ctx.Request().Validate(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",
|
||||
})
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
}
|
||||
if validator.Fails() {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
|
||||
}
|
||||
|
||||
database := ctx.Request().Input("database")
|
||||
err = r.backup.MysqlBackup(database)
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// DeleteBackup 删除备份
|
||||
func (r *Mysql57Controller) DeleteBackup(ctx http.Context) http.Response {
|
||||
validator, err := ctx.Request().Validate(map[string]string{
|
||||
"name": "required|min_len:1|max_len:255",
|
||||
})
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
}
|
||||
if validator.Fails() {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
|
||||
}
|
||||
|
||||
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/mysql"
|
||||
fileName := ctx.Request().Input("name")
|
||||
if err := tools.Remove(backupPath + "/" + fileName); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// RestoreBackup 还原备份
|
||||
func (r *Mysql57Controller) RestoreBackup(ctx http.Context) http.Response {
|
||||
validator, err := ctx.Request().Validate(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",
|
||||
})
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
}
|
||||
if validator.Fails() {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
|
||||
}
|
||||
|
||||
err = r.backup.MysqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup"))
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// UserList 用户列表
|
||||
func (r *Mysql57Controller) 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)/")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT user, host FROM mysql.user")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var userGrants []user
|
||||
|
||||
for rows.Next() {
|
||||
var u user
|
||||
err := rows.Scan(&u.User, &u.Host)
|
||||
if 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
|
||||
err := grantsRows.Scan(&grant)
|
||||
if 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, "获取用户列表失败")
|
||||
}
|
||||
|
||||
page := ctx.Request().QueryInt("page", 1)
|
||||
limit := ctx.Request().QueryInt("limit", 10)
|
||||
startIndex := (page - 1) * limit
|
||||
endIndex := page * limit
|
||||
if startIndex > len(userGrants) {
|
||||
return controllers.Success(ctx, http.Json{
|
||||
"total": 0,
|
||||
"items": []user{},
|
||||
})
|
||||
}
|
||||
if endIndex > len(userGrants) {
|
||||
endIndex = len(userGrants)
|
||||
}
|
||||
pagedUserGrants := userGrants[startIndex:endIndex]
|
||||
|
||||
return controllers.Success(ctx, http.Json{
|
||||
"total": len(userGrants),
|
||||
"items": pagedUserGrants,
|
||||
})
|
||||
}
|
||||
|
||||
// AddUser 添加用户
|
||||
func (r *Mysql57Controller) AddUser(ctx http.Context) http.Response {
|
||||
validator, err := ctx.Request().Validate(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",
|
||||
})
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
}
|
||||
if validator.Fails() {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
|
||||
}
|
||||
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
user := ctx.Request().Input("user")
|
||||
password := ctx.Request().Input("password")
|
||||
database := ctx.Request().Input("database")
|
||||
if out, err := tools.Exec("/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 out, err := tools.Exec("/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 := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// DeleteUser 删除用户
|
||||
func (r *Mysql57Controller) DeleteUser(ctx http.Context) http.Response {
|
||||
validator, err := ctx.Request().Validate(map[string]string{
|
||||
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
|
||||
})
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
}
|
||||
if validator.Fails() {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
|
||||
}
|
||||
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
user := ctx.Request().Input("user")
|
||||
if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP USER '" + user + "'@'localhost';\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// SetUserPassword 设置用户密码
|
||||
func (r *Mysql57Controller) SetUserPassword(ctx http.Context) http.Response {
|
||||
validator, err := ctx.Request().Validate(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",
|
||||
})
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
}
|
||||
if validator.Fails() {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
|
||||
}
|
||||
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
user := ctx.Request().Input("user")
|
||||
password := ctx.Request().Input("password")
|
||||
if out, err := tools.Exec("/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)
|
||||
}
|
||||
if out, err := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// SetUserPrivileges 设置用户权限
|
||||
func (r *Mysql57Controller) SetUserPrivileges(ctx http.Context) http.Response {
|
||||
validator, err := ctx.Request().Validate(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",
|
||||
})
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
}
|
||||
if validator.Fails() {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
|
||||
}
|
||||
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
user := ctx.Request().Input("user")
|
||||
database := ctx.Request().Input("database")
|
||||
if out, err := tools.Exec("/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)
|
||||
}
|
||||
if out, err := tools.Exec("/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 := tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
+30
-30
@@ -15,20 +15,20 @@ import (
|
||||
"panel/pkg/tools"
|
||||
)
|
||||
|
||||
type Mysql80Controller struct {
|
||||
type MySQLController struct {
|
||||
setting internal.Setting
|
||||
backup internal.Backup
|
||||
}
|
||||
|
||||
func NewMysql80Controller() *Mysql80Controller {
|
||||
return &Mysql80Controller{
|
||||
func NewMySQLController() *MySQLController {
|
||||
return &MySQLController{
|
||||
setting: services.NewSettingImpl(),
|
||||
backup: services.NewBackupImpl(),
|
||||
}
|
||||
}
|
||||
|
||||
// Status 获取运行状态
|
||||
func (r *Mysql80Controller) Status(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) Status(ctx http.Context) http.Response {
|
||||
status, err := tools.ServiceStatus("mysqld")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
|
||||
@@ -38,7 +38,7 @@ func (r *Mysql80Controller) Status(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// Reload 重载配置
|
||||
func (r *Mysql80Controller) Reload(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) Reload(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceReload("mysqld"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重载MySQL配置失败")
|
||||
}
|
||||
@@ -47,7 +47,7 @@ func (r *Mysql80Controller) Reload(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// Restart 重启服务
|
||||
func (r *Mysql80Controller) Restart(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) Restart(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceRestart("mysqld"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "重启MySQL服务失败")
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func (r *Mysql80Controller) Restart(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// Start 启动服务
|
||||
func (r *Mysql80Controller) Start(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) Start(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceStart("mysqld"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "启动MySQL服务失败")
|
||||
}
|
||||
@@ -65,7 +65,7 @@ func (r *Mysql80Controller) Start(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// Stop 停止服务
|
||||
func (r *Mysql80Controller) Stop(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) Stop(ctx http.Context) http.Response {
|
||||
if err := tools.ServiceStop("mysqld"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "停止MySQL服务失败")
|
||||
}
|
||||
@@ -74,7 +74,7 @@ func (r *Mysql80Controller) Stop(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// GetConfig 获取配置
|
||||
func (r *Mysql80Controller) GetConfig(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) GetConfig(ctx http.Context) http.Response {
|
||||
config, err := tools.Read("/www/server/mysql/conf/my.cnf")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL配置失败")
|
||||
@@ -84,7 +84,7 @@ func (r *Mysql80Controller) GetConfig(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// SaveConfig 保存配置
|
||||
func (r *Mysql80Controller) SaveConfig(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) SaveConfig(ctx http.Context) http.Response {
|
||||
config := ctx.Request().Input("config")
|
||||
if len(config) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "配置不能为空")
|
||||
@@ -98,7 +98,7 @@ func (r *Mysql80Controller) SaveConfig(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// Load 获取负载
|
||||
func (r *Mysql80Controller) Load(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) Load(ctx http.Context) http.Response {
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
if len(rootPassword) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码为空")
|
||||
@@ -168,7 +168,7 @@ func (r *Mysql80Controller) Load(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// ErrorLog 获取错误日志
|
||||
func (r *Mysql80Controller) ErrorLog(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) ErrorLog(ctx http.Context) http.Response {
|
||||
log, err := tools.Exec("tail -n 100 /www/server/mysql/mysql-error.log")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, log)
|
||||
@@ -178,7 +178,7 @@ func (r *Mysql80Controller) ErrorLog(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// ClearErrorLog 清空错误日志
|
||||
func (r *Mysql80Controller) ClearErrorLog(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) ClearErrorLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec("echo '' > /www/server/mysql/mysql-error.log"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
@@ -187,7 +187,7 @@ func (r *Mysql80Controller) ClearErrorLog(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// SlowLog 获取慢查询日志
|
||||
func (r *Mysql80Controller) SlowLog(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) SlowLog(ctx http.Context) http.Response {
|
||||
log, err := tools.Exec("tail -n 100 /www/server/mysql/mysql-slow.log")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, log)
|
||||
@@ -197,7 +197,7 @@ func (r *Mysql80Controller) SlowLog(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// ClearSlowLog 清空慢查询日志
|
||||
func (r *Mysql80Controller) ClearSlowLog(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) ClearSlowLog(ctx http.Context) http.Response {
|
||||
if out, err := tools.Exec("echo '' > /www/server/mysql/mysql-slow.log"); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
@@ -205,7 +205,7 @@ func (r *Mysql80Controller) ClearSlowLog(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// GetRootPassword 获取root密码
|
||||
func (r *Mysql80Controller) GetRootPassword(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) GetRootPassword(ctx http.Context) http.Response {
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
if len(rootPassword) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码为空")
|
||||
@@ -215,7 +215,7 @@ func (r *Mysql80Controller) GetRootPassword(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// SetRootPassword 设置root密码
|
||||
func (r *Mysql80Controller) SetRootPassword(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) SetRootPassword(ctx http.Context) http.Response {
|
||||
status, err := tools.ServiceStatus("mysqld")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
|
||||
@@ -253,7 +253,7 @@ func (r *Mysql80Controller) SetRootPassword(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// DatabaseList 获取数据库列表
|
||||
func (r *Mysql80Controller) DatabaseList(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) DatabaseList(ctx http.Context) http.Response {
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
type database struct {
|
||||
Name string `json:"name"`
|
||||
@@ -308,7 +308,7 @@ func (r *Mysql80Controller) DatabaseList(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// AddDatabase 添加数据库
|
||||
func (r *Mysql80Controller) AddDatabase(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) AddDatabase(ctx http.Context) http.Response {
|
||||
validator, err := ctx.Request().Validate(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_]+$",
|
||||
@@ -343,7 +343,7 @@ func (r *Mysql80Controller) AddDatabase(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// DeleteDatabase 删除数据库
|
||||
func (r *Mysql80Controller) DeleteDatabase(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) DeleteDatabase(ctx http.Context) http.Response {
|
||||
validator, err := ctx.Request().Validate(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",
|
||||
})
|
||||
@@ -364,7 +364,7 @@ func (r *Mysql80Controller) DeleteDatabase(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// BackupList 获取备份列表
|
||||
func (r *Mysql80Controller) BackupList(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) BackupList(ctx http.Context) http.Response {
|
||||
backupList, err := r.backup.MysqlList()
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
@@ -395,7 +395,7 @@ func (r *Mysql80Controller) BackupList(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// UploadBackup 上传备份
|
||||
func (r *Mysql80Controller) UploadBackup(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) UploadBackup(ctx http.Context) http.Response {
|
||||
file, err := ctx.Request().File("file")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "上传文件失败")
|
||||
@@ -418,7 +418,7 @@ func (r *Mysql80Controller) UploadBackup(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// CreateBackup 创建备份
|
||||
func (r *Mysql80Controller) CreateBackup(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) CreateBackup(ctx http.Context) http.Response {
|
||||
validator, err := ctx.Request().Validate(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",
|
||||
})
|
||||
@@ -439,7 +439,7 @@ func (r *Mysql80Controller) CreateBackup(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// DeleteBackup 删除备份
|
||||
func (r *Mysql80Controller) DeleteBackup(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) DeleteBackup(ctx http.Context) http.Response {
|
||||
validator, err := ctx.Request().Validate(map[string]string{
|
||||
"name": "required|min_len:1|max_len:255",
|
||||
})
|
||||
@@ -460,7 +460,7 @@ func (r *Mysql80Controller) DeleteBackup(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// RestoreBackup 还原备份
|
||||
func (r *Mysql80Controller) RestoreBackup(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) RestoreBackup(ctx http.Context) http.Response {
|
||||
validator, err := ctx.Request().Validate(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",
|
||||
@@ -481,7 +481,7 @@ func (r *Mysql80Controller) RestoreBackup(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// UserList 用户列表
|
||||
func (r *Mysql80Controller) UserList(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) UserList(ctx http.Context) http.Response {
|
||||
type user struct {
|
||||
User string `json:"user"`
|
||||
Host string `json:"host"`
|
||||
@@ -560,7 +560,7 @@ func (r *Mysql80Controller) UserList(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// AddUser 添加用户
|
||||
func (r *Mysql80Controller) AddUser(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) AddUser(ctx http.Context) http.Response {
|
||||
validator, err := ctx.Request().Validate(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_]+$",
|
||||
@@ -591,7 +591,7 @@ func (r *Mysql80Controller) AddUser(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// DeleteUser 删除用户
|
||||
func (r *Mysql80Controller) DeleteUser(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) DeleteUser(ctx http.Context) http.Response {
|
||||
validator, err := ctx.Request().Validate(map[string]string{
|
||||
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
|
||||
})
|
||||
@@ -612,7 +612,7 @@ func (r *Mysql80Controller) DeleteUser(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// SetUserPassword 设置用户密码
|
||||
func (r *Mysql80Controller) SetUserPassword(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) SetUserPassword(ctx http.Context) http.Response {
|
||||
validator, err := ctx.Request().Validate(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",
|
||||
@@ -638,7 +638,7 @@ func (r *Mysql80Controller) SetUserPassword(ctx http.Context) http.Response {
|
||||
}
|
||||
|
||||
// SetUserPrivileges 设置用户权限
|
||||
func (r *Mysql80Controller) SetUserPrivileges(ctx http.Context) http.Response {
|
||||
func (r *MySQLController) SetUserPrivileges(ctx http.Context) http.Response {
|
||||
validator, err := ctx.Request().Validate(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",
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/imroc/req/v3"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"panel/app/http/controllers"
|
||||
@@ -117,9 +117,9 @@ func (r *OpenRestyController) ClearErrorLog(ctx http.Context) http.Response {
|
||||
|
||||
// Load 获取负载
|
||||
func (r *OpenRestyController) Load(ctx http.Context) http.Response {
|
||||
client := req.C().SetTimeout(10 * time.Second)
|
||||
client := resty.New().SetTimeout(10 * time.Second)
|
||||
resp, err := client.R().Get("http://127.0.0.1/nginx_status")
|
||||
if err != nil || !resp.IsSuccessState() {
|
||||
if err != nil || !resp.IsSuccess() {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty负载失败")
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,11 @@ import (
|
||||
// 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": "未登录",
|
||||
"message": translate.Get("auth.token.missing"),
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -26,7 +27,7 @@ func Jwt() http.Middleware {
|
||||
if err != nil {
|
||||
// 到达刷新时间上限
|
||||
ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{
|
||||
"message": "登录已过期",
|
||||
"message": translate.Get("auth.token.expired"),
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -34,7 +35,7 @@ func Jwt() http.Middleware {
|
||||
token = "Bearer " + token
|
||||
} else {
|
||||
ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{
|
||||
"message": "登录已过期",
|
||||
"message": translate.Get("auth.token.expired"),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/contracts/translation"
|
||||
"github.com/goravel/framework/facades"
|
||||
|
||||
"panel/internal/services"
|
||||
)
|
||||
@@ -12,6 +14,7 @@ import (
|
||||
func MustInstall() http.Middleware {
|
||||
return func(ctx http.Context) {
|
||||
path := ctx.Request().Path()
|
||||
translate := facades.Lang(ctx)
|
||||
var slug string
|
||||
if strings.HasPrefix(path, "/api/panel/website") {
|
||||
slug = "openresty"
|
||||
@@ -19,7 +22,7 @@ func MustInstall() http.Middleware {
|
||||
pathArr := strings.Split(path, "/")
|
||||
if len(pathArr) < 4 {
|
||||
ctx.Request().AbortWithStatusJson(http.StatusForbidden, http.Json{
|
||||
"message": "插件不存在",
|
||||
"message": translate.Get("errors.plugin.notExist"),
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -31,14 +34,18 @@ func MustInstall() http.Middleware {
|
||||
installedPlugins, err := services.NewPluginImpl().AllInstalled()
|
||||
if err != nil {
|
||||
ctx.Request().AbortWithStatusJson(http.StatusInternalServerError, http.Json{
|
||||
"message": "系统内部错误",
|
||||
"message": translate.Get("errors.internal"),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if installedPlugin.Slug != plugin.Slug {
|
||||
ctx.Request().AbortWithStatusJson(http.StatusForbidden, http.Json{
|
||||
"message": "插件 " + slug + " 未安装",
|
||||
"message": translate.Get("errors.plugin.notInstalled", translation.Option{
|
||||
Replace: map[string]string{
|
||||
"slug": slug,
|
||||
},
|
||||
}),
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -53,7 +60,12 @@ func MustInstall() http.Middleware {
|
||||
_, requireFound := pluginsMap[require]
|
||||
if !requireFound {
|
||||
ctx.Request().AbortWithStatusJson(http.StatusForbidden, http.Json{
|
||||
"message": "插件 " + slug + " 需要依赖 " + require + " 插件",
|
||||
"message": translate.Get("errors.plugin.dependent", translation.Option{
|
||||
Replace: map[string]string{
|
||||
"slug": slug,
|
||||
"dependency": require,
|
||||
},
|
||||
}),
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -63,7 +75,12 @@ func MustInstall() http.Middleware {
|
||||
_, excludeFound := pluginsMap[exclude]
|
||||
if excludeFound {
|
||||
ctx.Request().AbortWithStatusJson(http.StatusForbidden, http.Json{
|
||||
"message": "插件 " + slug + " 不兼容 " + exclude + " 插件",
|
||||
"message": translate.Get("errors.plugin.incompatible", translation.Option{
|
||||
Replace: map[string]string{
|
||||
"slug": slug,
|
||||
"exclude": exclude,
|
||||
},
|
||||
}),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package middleware
|
||||
|
||||
import (
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/facades"
|
||||
|
||||
"panel/internal"
|
||||
)
|
||||
@@ -9,25 +10,26 @@ import (
|
||||
// Status 检查程序状态
|
||||
func Status() http.Middleware {
|
||||
return func(ctx http.Context) {
|
||||
translate := facades.Lang(ctx)
|
||||
switch internal.Status {
|
||||
case internal.StatusUpgrade:
|
||||
ctx.Request().AbortWithStatusJson(http.StatusServiceUnavailable, http.Json{
|
||||
"message": "面板升级中,请稍后",
|
||||
"message": translate.Get("status.upgrade"),
|
||||
})
|
||||
return
|
||||
case internal.StatusMaintain:
|
||||
ctx.Request().AbortWithStatusJson(http.StatusServiceUnavailable, http.Json{
|
||||
"message": "面板正在运行维护,请稍后",
|
||||
"message": translate.Get("status.maintain"),
|
||||
})
|
||||
return
|
||||
case internal.StatusClosed:
|
||||
ctx.Request().AbortWithStatusJson(http.StatusForbidden, http.Json{
|
||||
"message": "面板已关闭",
|
||||
"message": translate.Get("status.closed"),
|
||||
})
|
||||
return
|
||||
case internal.StatusFailed:
|
||||
ctx.Request().AbortWithStatusJson(http.StatusInternalServerError, http.Json{
|
||||
"message": "面板运行出错,请检查排除或联系支持",
|
||||
"message": translate.Get("status.failed"),
|
||||
})
|
||||
return
|
||||
default:
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
)
|
||||
|
||||
type CertDeploy struct {
|
||||
ID uint `form:"id" json:"id"`
|
||||
WebsiteID uint `form:"website_id" json:"website_id"`
|
||||
ID uint `form:"id" json:"id" filter:"uint"`
|
||||
WebsiteID uint `form:"website_id" json:"website_id" filter:"uint"`
|
||||
}
|
||||
|
||||
func (r *CertDeploy) Authorize(ctx http.Context) error {
|
||||
|
||||
@@ -26,7 +26,6 @@ func (r *DNSStore) Rules(ctx http.Context) map[string]string {
|
||||
"data.token": "required_if:type,dnspod",
|
||||
"data.access_key": "required_if:type,aliyun",
|
||||
"data.secret_key": "required_if:type,aliyun",
|
||||
"data.email": "required_if:type,cloudflare",
|
||||
"data.api_key": "required_if:type,cloudflare",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ func (r *Archive) Authorize(ctx http.Context) error {
|
||||
func (r *Archive) Rules(ctx http.Context) map[string]string {
|
||||
return map[string]string{
|
||||
"paths": "array",
|
||||
"paths.*": "regex:^/[a-zA-Z0-9_.@#$%-]+(\\/[a-zA-Z0-9_.@#$%-]+)*$|path_exists",
|
||||
"file": "regex:^/[a-zA-Z0-9_.@#$%-]+(\\/[a-zA-Z0-9_.@#$%-]+)*$|path_not_exists",
|
||||
"paths.*": `regex:^/[a-zA-Z0-9_.@#$%\-\s\[\]()]+(/[a-zA-Z0-9_.@#$%\-\s\[\]()]+)*$|path_exists`,
|
||||
"file": `regex:^/[a-zA-Z0-9_.@#$%\-\s\[\]()]+(/[a-zA-Z0-9_.@#$%\-\s\[\]()]+)*$|path_not_exists`,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
)
|
||||
|
||||
type Copy struct {
|
||||
Old string `form:"old" json:"old"`
|
||||
New string `form:"new" json:"new"`
|
||||
Source string `form:"source" json:"source"`
|
||||
Target string `form:"target" json:"target"`
|
||||
}
|
||||
|
||||
func (r *Copy) Authorize(ctx http.Context) error {
|
||||
@@ -16,8 +16,8 @@ func (r *Copy) Authorize(ctx http.Context) error {
|
||||
|
||||
func (r *Copy) Rules(ctx http.Context) map[string]string {
|
||||
return map[string]string{
|
||||
"old": "regex:^/[a-zA-Z0-9_.@#$%-]+(\\/[a-zA-Z0-9_.@#$%-]+)*$|path_exists",
|
||||
"new": "regex:^/[a-zA-Z0-9_.@#$%-]+(\\/[a-zA-Z0-9_.@#$%-]+)*$",
|
||||
"source": `regex:^/[a-zA-Z0-9_.@#$%\-\s\[\]()]+(/[a-zA-Z0-9_.@#$%\-\s\[\]()]+)*$|path_exists`,
|
||||
"target": `regex:^/[a-zA-Z0-9_.@#$%\-\s\[\]()]+(/[a-zA-Z0-9_.@#$%\-\s\[\]()]+)*$`,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ func (r *Exist) Authorize(ctx http.Context) error {
|
||||
|
||||
func (r *Exist) Rules(ctx http.Context) map[string]string {
|
||||
return map[string]string{
|
||||
"path": "regex:^/[a-zA-Z0-9_.@#$%-]+(\\/[a-zA-Z0-9_.@#$%-]+)*$|path_exists",
|
||||
"path": `regex:^/([a-zA-Z0-9_.@#$%\-\s\[\]()]+(/[a-zA-Z0-9_.@#$%\-\s\[\]()]+)*)?$|path_exists`,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
)
|
||||
|
||||
type Move struct {
|
||||
Old string `form:"old" json:"old"`
|
||||
New string `form:"new" json:"new"`
|
||||
Source string `form:"source" json:"source"`
|
||||
Target string `form:"target" json:"target"`
|
||||
}
|
||||
|
||||
func (r *Move) Authorize(ctx http.Context) error {
|
||||
@@ -16,8 +16,8 @@ func (r *Move) Authorize(ctx http.Context) error {
|
||||
|
||||
func (r *Move) Rules(ctx http.Context) map[string]string {
|
||||
return map[string]string{
|
||||
"old": "regex:^/[a-zA-Z0-9_.@#$%-]+(\\/[a-zA-Z0-9_.@#$%-]+)*$|path_exists",
|
||||
"new": "regex:^/[a-zA-Z0-9_.@#$%-]+(\\/[a-zA-Z0-9_.@#$%-]+)*$",
|
||||
"source": `regex:^/[a-zA-Z0-9_.@#$%\-\s\[\]()]+(/[a-zA-Z0-9_.@#$%\-\s\[\]()]+)*$|path_exists`,
|
||||
"target": `regex:^/[a-zA-Z0-9_.@#$%\-\s\[\]()]+(/[a-zA-Z0-9_.@#$%\-\s\[\]()]+)*$`,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ func (r *NotExist) Authorize(ctx http.Context) error {
|
||||
|
||||
func (r *NotExist) Rules(ctx http.Context) map[string]string {
|
||||
return map[string]string{
|
||||
"path": "regex:^/[a-zA-Z0-9_.@#$%-]+(\\/[a-zA-Z0-9_.@#$%-]+)*$|path_not_exists",
|
||||
"path": `regex:^/[a-zA-Z0-9_.@#$%\-\s\[\]()]+(/[a-zA-Z0-9_.@#$%\-\s\[\]()]+)*$|path_not_exists`,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ func (r *Permission) Authorize(ctx http.Context) error {
|
||||
|
||||
func (r *Permission) Rules(ctx http.Context) map[string]string {
|
||||
return map[string]string{
|
||||
"path": "regex:^/[a-zA-Z0-9_.@#$%-]+(\\/[a-zA-Z0-9_.@#$%-]+)*$|path_exists",
|
||||
"path": `regex:^/[a-zA-Z0-9_.@#$%\-\s\[\]()]+(/[a-zA-Z0-9_.@#$%\-\s\[\]()]+)*$|path_exists`,
|
||||
"mode": "regex:^[0-7]{3}$|uint",
|
||||
"owner": "regex:^[a-zA-Z0-9_-]+$",
|
||||
"group": "regex:^[a-zA-Z0-9_-]+$",
|
||||
|
||||
@@ -16,7 +16,7 @@ func (r *Save) Authorize(ctx http.Context) error {
|
||||
|
||||
func (r *Save) Rules(ctx http.Context) map[string]string {
|
||||
return map[string]string{
|
||||
"path": "regex:^/[a-zA-Z0-9_.@#$%-]+(\\/[a-zA-Z0-9_.@#$%-]+)*$|path_exists",
|
||||
"path": `regex:^/[a-zA-Z0-9_.@#$%\-\s\[\]()]+(/[a-zA-Z0-9_.@#$%\-\s\[\]()]+)*$|path_exists`,
|
||||
"content": "required|string",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func (r *Search) Authorize(ctx http.Context) error {
|
||||
|
||||
func (r *Search) Rules(ctx http.Context) map[string]string {
|
||||
return map[string]string{
|
||||
"path": "regex:^/[a-zA-Z0-9_.@#$%-]+(\\/[a-zA-Z0-9_.@#$%-]+)*$|path_exists",
|
||||
"path": `regex:^/[a-zA-Z0-9_.@#$%\-\s\[\]()]+(/[a-zA-Z0-9_.@#$%\-\s\[\]()]+)*$|path_exists`,
|
||||
"keyword": "required|string",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ func (r *UnArchive) Authorize(ctx http.Context) error {
|
||||
|
||||
func (r *UnArchive) Rules(ctx http.Context) map[string]string {
|
||||
return map[string]string{
|
||||
"file": "regex:^/[a-zA-Z0-9_.@#$%-]+(\\/[a-zA-Z0-9_.@#$%-]+)*$|path_exists",
|
||||
"path": "regex:^/[a-zA-Z0-9_.@#$%-]+(\\/[a-zA-Z0-9_.@#$%-]+)*$|path_not_exists",
|
||||
"file": `regex:^/[a-zA-Z0-9_.@#$%\-\s\[\]()]+(/[a-zA-Z0-9_.@#$%\-\s\[\]()]+)*$|path_exists`,
|
||||
"path": `regex:^/[a-zA-Z0-9_.@#$%\-\s\[\]()]+(/[a-zA-Z0-9_.@#$%\-\s\[\]()]+)*$|path_not_exists`,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ func (r *Upload) Authorize(ctx http.Context) error {
|
||||
|
||||
func (r *Upload) Rules(ctx http.Context) map[string]string {
|
||||
return map[string]string{
|
||||
"path": "regex:^/[a-zA-Z0-9_.@#$%-]+(\\/[a-zA-Z0-9_.@#$%-]+)*$",
|
||||
"path": `regex:^/[a-zA-Z0-9_.@#$%\-\s\[\]()]+(/[a-zA-Z0-9_.@#$%\-\s\[\]()]+)*$`,
|
||||
"file": "required",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func (r *Create) Authorize(ctx http.Context) error {
|
||||
func (r *Create) Rules(ctx http.Context) map[string]string {
|
||||
return map[string]string{
|
||||
"name": "required|regex:^[a-zA-Z0-9-_]+$",
|
||||
"path": "regex:^/[a-zA-Z0-9_.@#$%-]+(\\/[a-zA-Z0-9_.@#$%-]+)*$",
|
||||
"path": `regex:^/[a-zA-Z0-9_.@#$%\-\s\[\]()]+(/[a-zA-Z0-9_.@#$%\-\s\[\]()]+)*$`,
|
||||
"comment": "string",
|
||||
"auth_user": "required|regex:^[a-zA-Z0-9-_]+$",
|
||||
"secret": "required|min_len:8",
|
||||
|
||||
@@ -21,7 +21,7 @@ func (r *Update) Authorize(ctx http.Context) error {
|
||||
func (r *Update) Rules(ctx http.Context) map[string]string {
|
||||
return map[string]string{
|
||||
"name": "required|regex:^[a-zA-Z0-9-_]+$",
|
||||
"path": "regex:^/[a-zA-Z0-9_.@#$%-]+(\\/[a-zA-Z0-9_.@#$%-]+)*$",
|
||||
"path": `regex:^/[a-zA-Z0-9_.@#$%\-\s\[\]()]+(/[a-zA-Z0-9_.@#$%\-\s\[\]()]+)*$`,
|
||||
"comment": "string",
|
||||
"auth_user": "required|regex:^[a-zA-Z0-9-_]+$",
|
||||
"secret": "required|min_len:8",
|
||||
|
||||
@@ -27,7 +27,7 @@ func (r *Add) Rules(ctx http.Context) map[string]string {
|
||||
"name": "required|regex:^[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)*$|not_exists:websites,name|not_in:phpmyadmin,mysql,panel,ssh",
|
||||
"domains": "required|slice",
|
||||
"ports": "required|slice",
|
||||
"path": "regex:^/[a-zA-Z0-9_.@#$%-]+(\\/[a-zA-Z0-9_.@#$%-]+)*$",
|
||||
"path": `regex:^/[a-zA-Z0-9_.@#$%\-\s\[\]()]+(/[a-zA-Z0-9_.@#$%\-\s\[\]()]+)*$`,
|
||||
"php": "required",
|
||||
"db": "bool",
|
||||
"db_type": "required_if:db,true|in:0,mysql,postgresql",
|
||||
|
||||
+3
-1
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/goravel/framework/schedule"
|
||||
"github.com/goravel/framework/support/carbon"
|
||||
"github.com/goravel/framework/testing"
|
||||
"github.com/goravel/framework/translation"
|
||||
"github.com/goravel/framework/validation"
|
||||
"github.com/goravel/gin"
|
||||
|
||||
@@ -60,7 +61,7 @@ func init() {
|
||||
// The application locale determines the default locale that will be used
|
||||
// by the translation service provider.You are free to set this value
|
||||
// to any of the locales which will be supported by the application.
|
||||
"locale": "zh_CN",
|
||||
"locale": config.Env("APP_LOCALE", "zh_CN"),
|
||||
|
||||
// Application Fallback Locale
|
||||
//
|
||||
@@ -96,6 +97,7 @@ func init() {
|
||||
&crypt.ServiceProvider{},
|
||||
&filesystem.ServiceProvider{},
|
||||
&validation.ServiceProvider{},
|
||||
&translation.ServiceProvider{},
|
||||
&testing.ServiceProvider{},
|
||||
&providers.AppServiceProvider{},
|
||||
&providers.AuthServiceProvider{},
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ func init() {
|
||||
config := facades.Config()
|
||||
config.Add("panel", map[string]any{
|
||||
"name": "耗子 Linux 面板",
|
||||
"version": "v2.1.43",
|
||||
"version": "v2.2.2",
|
||||
"ssl": config.Env("APP_SSL", false),
|
||||
})
|
||||
}
|
||||
|
||||
+66
-12
@@ -270,6 +270,45 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/panel/cert/deploy": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"description": "部署面板证书管理的证书",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"证书管理"
|
||||
],
|
||||
"summary": "部署证书",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "data",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/requests.CertDeploy"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.SuccessResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/panel/cert/dns": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -535,9 +574,9 @@ const docTemplate = `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/acme.Resolve"
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/acme.DNSRecord"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2300,6 +2339,16 @@ const docTemplate = `{
|
||||
"type": "string",
|
||||
"name": "path",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "limit",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "page",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -3696,9 +3745,6 @@ const docTemplate = `{
|
||||
"api_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -3710,12 +3756,9 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"acme.Resolve": {
|
||||
"acme.DNSRecord": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"err": {
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -4074,6 +4117,17 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"requests.CertDeploy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"website_id": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requests.CertStore": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -4329,10 +4383,10 @@ const docTemplate = `{
|
||||
"requests.Move": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"new": {
|
||||
"source": {
|
||||
"type": "string"
|
||||
},
|
||||
"old": {
|
||||
"target": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
|
||||
+66
-12
@@ -263,6 +263,45 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/panel/cert/deploy": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"description": "部署面板证书管理的证书",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"证书管理"
|
||||
],
|
||||
"summary": "部署证书",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "data",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/requests.CertDeploy"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.SuccessResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/panel/cert/dns": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -528,9 +567,9 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/acme.Resolve"
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/acme.DNSRecord"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2293,6 +2332,16 @@
|
||||
"type": "string",
|
||||
"name": "path",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "limit",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "page",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -3689,9 +3738,6 @@
|
||||
"api_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -3703,12 +3749,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"acme.Resolve": {
|
||||
"acme.DNSRecord": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"err": {
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -4067,6 +4110,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"requests.CertDeploy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"website_id": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requests.CertStore": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -4322,10 +4376,10 @@
|
||||
"requests.Move": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"new": {
|
||||
"source": {
|
||||
"type": "string"
|
||||
},
|
||||
"old": {
|
||||
"target": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
|
||||
+43
-10
@@ -6,8 +6,6 @@ definitions:
|
||||
type: string
|
||||
api_key:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
secret_key:
|
||||
@@ -15,10 +13,8 @@ definitions:
|
||||
token:
|
||||
type: string
|
||||
type: object
|
||||
acme.Resolve:
|
||||
acme.DNSRecord:
|
||||
properties:
|
||||
err:
|
||||
type: string
|
||||
key:
|
||||
type: string
|
||||
value:
|
||||
@@ -257,6 +253,13 @@ definitions:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
requests.CertDeploy:
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
website_id:
|
||||
type: integer
|
||||
type: object
|
||||
requests.CertStore:
|
||||
properties:
|
||||
auto_renew:
|
||||
@@ -423,9 +426,9 @@ definitions:
|
||||
type: object
|
||||
requests.Move:
|
||||
properties:
|
||||
new:
|
||||
source:
|
||||
type: string
|
||||
old:
|
||||
target:
|
||||
type: string
|
||||
type: object
|
||||
requests.NetworkConnectDisConnect:
|
||||
@@ -797,6 +800,30 @@ paths:
|
||||
summary: 更新证书
|
||||
tags:
|
||||
- 证书管理
|
||||
/panel/cert/deploy:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 部署面板证书管理的证书
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: data
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/requests.CertDeploy'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/controllers.SuccessResponse'
|
||||
security:
|
||||
- BearerToken: []
|
||||
summary: 部署证书
|
||||
tags:
|
||||
- 证书管理
|
||||
/panel/cert/dns:
|
||||
get:
|
||||
description: 获取面板证书管理的 DNS 接口列表
|
||||
@@ -955,9 +982,9 @@ paths:
|
||||
- $ref: '#/definitions/controllers.SuccessResponse'
|
||||
- properties:
|
||||
data:
|
||||
additionalProperties:
|
||||
$ref: '#/definitions/acme.Resolve'
|
||||
type: object
|
||||
items:
|
||||
$ref: '#/definitions/acme.DNSRecord'
|
||||
type: array
|
||||
type: object
|
||||
security:
|
||||
- BearerToken: []
|
||||
@@ -2024,6 +2051,12 @@ paths:
|
||||
- in: query
|
||||
name: path
|
||||
type: string
|
||||
- in: query
|
||||
name: limit
|
||||
type: integer
|
||||
- in: query
|
||||
name: page
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
|
||||
@@ -3,17 +3,19 @@ module panel
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/docker/docker v25.0.5+incompatible
|
||||
github.com/docker/docker v26.1.3+incompatible
|
||||
github.com/docker/go-connections v0.5.0
|
||||
github.com/gertd/go-pluralize v0.2.1
|
||||
github.com/go-acme/lego/v4 v4.16.1
|
||||
github.com/go-resty/resty/v2 v2.13.1
|
||||
github.com/gookit/color v1.5.4
|
||||
github.com/gookit/validate v1.5.2
|
||||
github.com/goravel/framework v1.13.1-0.20240215091018-1a9f352e523c
|
||||
github.com/goravel/gin v1.1.6-0.20240215075927-ab614bfae9f3
|
||||
github.com/goravel/framework v1.13.1-0.20240430025652-cf76911d0286
|
||||
github.com/goravel/gin v1.1.6-0.20240426085159-718e4d6d5f4f
|
||||
github.com/gorilla/websocket v1.5.1
|
||||
github.com/iancoleman/strcase v0.3.0
|
||||
github.com/imroc/req/v3 v3.43.1
|
||||
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/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
|
||||
@@ -21,15 +23,18 @@ require (
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/swaggo/http-swagger/v2 v2.0.2
|
||||
github.com/swaggo/swag v1.16.3
|
||||
golang.org/x/crypto v0.21.0
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/crypto v0.23.0
|
||||
golang.org/x/net v0.25.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.111.0 // indirect
|
||||
cloud.google.com/go/compute v1.23.3 // indirect
|
||||
cloud.google.com/go v0.112.0 // indirect
|
||||
cloud.google.com/go/compute v1.24.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/iam v1.1.5 // indirect
|
||||
cloud.google.com/go/pubsub v1.33.0 // indirect
|
||||
cloud.google.com/go/iam v1.1.6 // indirect
|
||||
cloud.google.com/go/pubsub v1.36.1 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
@@ -40,34 +45,30 @@ require (
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae // indirect
|
||||
github.com/RichardKnop/machinery/v2 v2.0.12-0.20231012204029-bdb94a90ca41 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 // indirect
|
||||
github.com/RichardKnop/machinery/v2 v2.0.13 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/aws/aws-sdk-go v1.49.6 // indirect
|
||||
github.com/bytedance/sonic v1.11.0 // indirect
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.86.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/distribution/reference v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.9.1 // indirect
|
||||
github.com/glebarez/go-sqlite v1.22.0 // indirect
|
||||
github.com/glebarez/sqlite v1.10.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
|
||||
github.com/glebarez/sqlite v1.11.0 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
@@ -79,22 +80,20 @@ require (
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.15.5 // indirect
|
||||
github.com/go-redsync/redsync/v4 v4.8.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.1 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // 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
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0 // indirect
|
||||
github.com/golang-migrate/migrate/v4 v4.17.0 // indirect
|
||||
github.com/golang-module/carbon/v2 v2.3.8 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang-migrate/migrate/v4 v4.17.1 // indirect
|
||||
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/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/gomodule/redigo v2.0.0+incompatible // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
@@ -106,13 +105,12 @@ require (
|
||||
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-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgx/v5 v5.4.3 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
|
||||
github.com/jackc/pgx/v5 v5.5.5 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
@@ -120,7 +118,7 @@ require (
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kelseyhightower/envconfig v1.4.0 // indirect
|
||||
github.com/klauspost/compress v1.17.7 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
@@ -128,14 +126,13 @@ require (
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/microsoft/go-mssqldb v1.6.0 // indirect
|
||||
github.com/miekg/dns v1.1.58 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/montanaflynn/stats v0.7.0 // indirect
|
||||
github.com/nrdcg/dnspod-go v0.4.0 // indirect
|
||||
github.com/nwaples/rardecode v1.1.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.16.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
@@ -144,15 +141,13 @@ require (
|
||||
github.com/pierrec/lz4/v4 v4.1.16 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/quic-go v0.41.0 // indirect
|
||||
github.com/redis/go-redis/v9 v9.4.0 // indirect
|
||||
github.com/refraction-networking/utls v1.6.3 // indirect
|
||||
github.com/rabbitmq/amqp091-go v1.9.0 // indirect
|
||||
github.com/redis/go-redis/v9 v9.5.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/rotisserie/eris v0.5.4 // indirect
|
||||
github.com/rs/cors v1.10.1 // indirect
|
||||
github.com/rs/cors v1.11.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
@@ -162,7 +157,6 @@ require (
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.18.2 // indirect
|
||||
github.com/streadway/amqp v1.0.0 // indirect
|
||||
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
|
||||
@@ -173,51 +167,50 @@ require (
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||
github.com/unrolled/secure v1.14.0 // indirect
|
||||
github.com/urfave/cli/v2 v2.27.1 // indirect
|
||||
github.com/urfave/cli/v2 v2.27.2 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.1.1 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.3 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
go.mongodb.org/mongo-driver v1.11.3 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect
|
||||
go.opentelemetry.io/otel v1.22.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.22.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.22.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.7.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
|
||||
golang.org/x/image v0.13.0 // indirect
|
||||
golang.org/x/mod v0.16.0 // indirect
|
||||
golang.org/x/net v0.22.0 // indirect
|
||||
golang.org/x/oauth2 v0.16.0 // indirect
|
||||
golang.org/x/oauth2 v0.17.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.19.0 // indirect
|
||||
google.golang.org/api v0.153.0 // indirect
|
||||
google.golang.org/api v0.162.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect
|
||||
google.golang.org/grpc v1.61.1 // indirect
|
||||
google.golang.org/protobuf v1.32.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
google.golang.org/grpc v1.63.2 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gorm.io/driver/mysql v1.5.4 // indirect
|
||||
gorm.io/driver/postgres v1.5.6 // indirect
|
||||
gorm.io/driver/mysql v1.5.6 // indirect
|
||||
gorm.io/driver/postgres v1.5.7 // indirect
|
||||
gorm.io/driver/sqlserver v1.5.3 // indirect
|
||||
gorm.io/gorm v1.25.7 // indirect
|
||||
gorm.io/plugin/dbresolver v1.5.0 // indirect
|
||||
gorm.io/gorm v1.25.10 // indirect
|
||||
gorm.io/plugin/dbresolver v1.5.1 // indirect
|
||||
gotest.tools/v3 v3.5.0 // indirect
|
||||
modernc.org/libc v1.37.6 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
|
||||
@@ -16,38 +16,39 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
cloud.google.com/go v0.111.0 h1:YHLKNupSD1KqjDbQ3+LVdQ81h/UJbJyZG203cEfnQgM=
|
||||
cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU=
|
||||
cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM=
|
||||
cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
|
||||
cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
|
||||
cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg=
|
||||
cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI=
|
||||
cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8=
|
||||
cloud.google.com/go/kms v1.15.5 h1:pj1sRfut2eRbD9pFRjNnPNg/CzJPuQAzUujMIM1vVeM=
|
||||
cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI=
|
||||
cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc=
|
||||
cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI=
|
||||
cloud.google.com/go/kms v1.15.7 h1:7caV9K3yIxvlQPAcaFffhlT7d1qpxjB1wHBtjWa13SM=
|
||||
cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/pubsub v1.10.0/go.mod h1:eNpTrkOy7dCpkNyaSNetMa6udbgecJMd0ZsTJS/cuNo=
|
||||
cloud.google.com/go/pubsub v1.33.0 h1:6SPCPvWav64tj0sVX/+npCBKhUi/UjJehy9op/V3p2g=
|
||||
cloud.google.com/go/pubsub v1.33.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc=
|
||||
cloud.google.com/go/pubsub v1.36.1 h1:dfEPuGCHGbWUhaMCTHUFjfroILEkx55iUmKBZTP5f+Y=
|
||||
cloud.google.com/go/pubsub v1.36.1/go.mod h1:iYjCa9EzWOoBiTdd4ps7QoMtMln5NwaZQpK1hbRfBDE=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||
@@ -71,7 +72,6 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.22 h1:/GblQdIudfEM3AWWZ0mrYJQSd7JS4S/Mbzh6F0ov0Xc=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk=
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
|
||||
@@ -94,10 +94,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae h1:DcFpTQBYQ9Ct2d6sC7ol0/ynxc2pO1cpGUM+f4t5adg=
|
||||
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae/go.mod h1:rJJ84PyA/Wlmw1hO+xTzV2wsSUon6J5ktg0g8BF2PuU=
|
||||
github.com/RichardKnop/machinery/v2 v2.0.12-0.20231012204029-bdb94a90ca41 h1:7fLtRodG39Hn6AKqKE+ejO0Jj9B8O9RQOto13lkoDac=
|
||||
github.com/RichardKnop/machinery/v2 v2.0.12-0.20231012204029-bdb94a90ca41/go.mod h1:92dLVxckr2Lv7oKxoS3Uci0Of8Cuy+lmPi2dd6Euwkw=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 h1:J45/QHgrzUdqe/Vco/Vxk0wRvdS2nKUxmf/zLgvfass=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
|
||||
github.com/RichardKnop/machinery/v2 v2.0.13 h1:uo9htg+qNBi7UeUK3jcTBl3vTO/vvLKGaOdCOKePl50=
|
||||
github.com/RichardKnop/machinery/v2 v2.0.13/go.mod h1:Yc2X/QRm9rRfAjB+93NGR+kSUqtnqqs8kME4L+TKKiw=
|
||||
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=
|
||||
@@ -118,10 +116,10 @@ github.com/bsm/gomega v1.20.0/go.mod h1:JifAceMQ4crZIWYUKrlGcmbN3bqHogVTADMD2ATs
|
||||
github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
||||
github.com/bytedance/sonic v1.11.0 h1:FwNNv6Vu4z2Onf1++LNzxB/QhitD8wuTdpZzMTGITWo=
|
||||
github.com/bytedance/sonic v1.11.0/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
||||
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=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
@@ -130,21 +128,14 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
|
||||
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
|
||||
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||
github.com/cloudflare/cloudflare-go v0.86.0 h1:jEKN5VHNYNYtfDL2lUFLTRo+nOVNPFxpXTstVx0rqHI=
|
||||
github.com/cloudflare/cloudflare-go v0.86.0/go.mod h1:wYW/5UP02TUfBToa/yKbQHV+r6h1NnJ1Je7XjuGM4Jw=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
@@ -152,25 +143,27 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ=
|
||||
github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dhui/dktest v0.4.0 h1:z05UmuXZHO/bgj/ds2bGMBu8FI4WA+Ag/m3ghL+om7M=
|
||||
github.com/dhui/dktest v0.4.0/go.mod h1:v/Dbz1LgCBOi2Uki2nUqLBGa83hWBGFMu5MrgMDCc78=
|
||||
github.com/dhui/dktest v0.4.1 h1:/w+IWuDXVymg3IrRJCHHOkMK10m9aNVMOyD0X12YVTg=
|
||||
github.com/dhui/dktest v0.4.1/go.mod h1:DdOqcUpL7vgyP4GlF3X3w7HbSlz8cEQzwewPveYEQbA=
|
||||
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
|
||||
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 v25.0.5+incompatible h1:UmQydMduGkrD5nQde1mecF/YnSbTOaPeFIeP5C4W+DE=
|
||||
github.com/docker/docker v25.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v26.1.3+incompatible h1:lLCzRbrVZrljpVNobJu1J2FHk8V0s4BawoZippkc+xo=
|
||||
github.com/docker/docker v26.1.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=
|
||||
@@ -187,8 +180,8 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
@@ -199,8 +192,6 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gertd/go-pluralize v0.2.1 h1:M3uASbVjMnTsPb0PNqg+E/24Vwigyo/tvyMTtAlLgiA=
|
||||
github.com/gertd/go-pluralize v0.2.1/go.mod h1:rbYaKDbsXxmRfr8uygAEKhOWsjyrrqrkHVpZvoOp8zk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
@@ -208,15 +199,11 @@ github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
|
||||
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
|
||||
github.com/glebarez/sqlite v1.10.0 h1:u4gt8y7OND/cCei/NMHmfbLxF6xP2wgKcT/BJf2pYkc=
|
||||
github.com/glebarez/sqlite v1.10.0/go.mod h1:IJ+lfSOmiekhQsFTJRx/lHtGYmCdtAiTaf5wI9u5uHA=
|
||||
github.com/go-acme/lego/v4 v4.16.1 h1:JxZ93s4KG0jL27rZ30UsIgxap6VGzKuREsSkkyzeoCQ=
|
||||
github.com/go-acme/lego/v4 v4.16.1/go.mod h1:AVvwdPned/IWpD/ihHhMsKnveF7HHYAz/CmtXi7OZoE=
|
||||
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-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U=
|
||||
github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@@ -254,15 +241,15 @@ github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/go-redsync/redsync/v4 v4.8.1 h1:rq2RvdTI0obznMdxKUWGdmmulo7lS9yCzb8fgDKOlbM=
|
||||
github.com/go-redsync/redsync/v4 v4.8.1/go.mod h1:LmUAsQuQxhzZAoGY7JS6+dNhNmZyonMZiiEDY9plotM=
|
||||
github.com/go-resty/resty/v2 v2.13.1 h1:x+LHXBI2nMB1vqndymf26quycC4aggYJ7DECYbiz03g=
|
||||
github.com/go-resty/resty/v2 v2.13.1/go.mod h1:GznXlLxkq6Nh4sU59rPmUw3VtgpO3aS96ORAI6Q7d+0=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/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/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
|
||||
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
|
||||
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
|
||||
@@ -291,18 +278,17 @@ 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=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-migrate/migrate/v4 v4.17.0 h1:rd40H3QXU0AA4IoLllFcEAEo9dYKRHYND2gB4p7xcaU=
|
||||
github.com/golang-migrate/migrate/v4 v4.17.0/go.mod h1:+Cp2mtLP4/aXDTKb9wmXYitdrNx2HGs45rbWAo6OsKM=
|
||||
github.com/golang-module/carbon/v2 v2.3.8 h1:aowLnKwifhMqRDmzmXNtlxT7AJXZWKwaoTPcPLagAHg=
|
||||
github.com/golang-module/carbon/v2 v2.3.8/go.mod h1:XDALX7KgqmHk95xyLeaqX9/LJGbfLATyruTziq68SZ8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4=
|
||||
github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM=
|
||||
github.com/golang-module/carbon/v2 v2.3.12 h1:VC1DwN1kBwJkh5MjXmTFryjs5g4CWyoM8HAHffZPX/k=
|
||||
github.com/golang-module/carbon/v2 v2.3.12/go.mod h1:HNsedGzXGuNciZImYP2OMnpiwq/vhIstR/vn45ib5cI=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
@@ -338,13 +324,14 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||
github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
@@ -363,8 +350,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
@@ -408,10 +393,10 @@ 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.13.1-0.20240215091018-1a9f352e523c h1:PahMfqzP8tJqagVsiWt0D41zo0vhdNRZW7VvgCdJUio=
|
||||
github.com/goravel/framework v1.13.1-0.20240215091018-1a9f352e523c/go.mod h1:5yDV4oFTZCJibqKY4C+fxMq1/rtPoLg8evYVC2uGf+M=
|
||||
github.com/goravel/gin v1.1.6-0.20240215075927-ab614bfae9f3 h1:sqXKSv1yvUZwbpv595BsE2fA4mQjTaR4x2mB7uQrzOY=
|
||||
github.com/goravel/gin v1.1.6-0.20240215075927-ab614bfae9f3/go.mod h1:D/61whQQvC6HaUNSEy7UPJokJAa6csY8k9Cw37fpEQM=
|
||||
github.com/goravel/framework v1.13.1-0.20240430025652-cf76911d0286 h1:SHlT5KRxcIOBprz8awwKv9TmXVnyIqrOf7eXMeTyj88=
|
||||
github.com/goravel/framework v1.13.1-0.20240430025652-cf76911d0286/go.mod h1:ljsNqFtfujgHw/kq5T7J//3UEhHUV02COYUVr/gr6CI=
|
||||
github.com/goravel/gin v1.1.6-0.20240426085159-718e4d6d5f4f h1:4+sGiuMXtJRgP1vDxOm5wP4z9cXr/noWDxTBU2hnvRM=
|
||||
github.com/goravel/gin v1.1.6-0.20240426085159-718e4d6d5f4f/go.mod h1:kX2QEYJO8UJRTMb52g2L+Ec/53WdJ8CHwlFeqy4fRfo=
|
||||
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.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
@@ -423,15 +408,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
|
||||
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
@@ -439,19 +417,17 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
|
||||
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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
|
||||
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imroc/req/v3 v3.43.1 h1:tsWAhvxik4egtHAvMlxcjaWJtHlJL8EpBqJMOm5rmyQ=
|
||||
github.com/imroc/req/v3 v3.43.1/go.mod h1:SQIz5iYop16MJxbo8ib+4LnostGCok8NQf8ToyQc2xA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY=
|
||||
github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
|
||||
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
|
||||
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
|
||||
@@ -463,7 +439,6 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
@@ -471,7 +446,6 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
@@ -490,8 +464,8 @@ github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLA
|
||||
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
@@ -513,30 +487,39 @@ github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2t
|
||||
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ=
|
||||
github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE=
|
||||
github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054=
|
||||
github.com/libdns/cloudflare v0.1.1/go.mod h1:9VK91idpOjg6v7/WbjkEW49bSCxj00ALesIFDhJ8PBU=
|
||||
github.com/libdns/dnspod v0.0.3 h1:xJHDIujgLjvZnpB8/rMoCHUqA/KxSGBqRUXxSIzNzAA=
|
||||
github.com/libdns/dnspod v0.0.3/go.mod h1:XLnqMmK7QlLPEbHwcOxbRvlzRvDgaaUlthRNFOPjXPI=
|
||||
github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||
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/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
|
||||
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
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/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
|
||||
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
|
||||
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=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
||||
@@ -557,14 +540,10 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo/v2 v2.16.0 h1:7q1w9frJDzninhXxjZd+Y/x54XNjG/UlRLIYPZafsPM=
|
||||
github.com/onsi/ginkgo/v2 v2.16.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
|
||||
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
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=
|
||||
@@ -590,16 +569,12 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||
github.com/quic-go/quic-go v0.41.0 h1:aD8MmHfgqTURWNJy48IYFg2OnxwHT3JL7ahGs73lb4k=
|
||||
github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA=
|
||||
github.com/rabbitmq/amqp091-go v1.9.0 h1:qrQtyzB4H8BQgEuJwhmVQqVHB9O4+MNDJCCAcpc3Aoo=
|
||||
github.com/rabbitmq/amqp091-go v1.9.0/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc=
|
||||
github.com/redis/go-redis/v9 v9.0.2/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps=
|
||||
github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
|
||||
github.com/redis/go-redis/v9 v9.4.0 h1:Yzoz33UZw9I/mFhx4MNrB6Fk+XHO1VukNcCa1+lwyKk=
|
||||
github.com/redis/go-redis/v9 v9.4.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/refraction-networking/utls v1.6.3 h1:MFOfRN35sSx6K5AZNIoESsBuBxS2LCgRilRIdHb6fDc=
|
||||
github.com/refraction-networking/utls v1.6.3/go.mod h1:yil9+7qSl+gBwJqztoQseO6Pr3h62pQoY1lXiNR/FPs=
|
||||
github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8=
|
||||
github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo=
|
||||
@@ -614,8 +589,8 @@ github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDN
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/rotisserie/eris v0.5.4 h1:Il6IvLdAapsMhvuOahHWiBnl1G++Q0/L5UIkI5mARSk=
|
||||
github.com/rotisserie/eris v0.5.4/go.mod h1:Z/kgYTJiJtocxCbFfvRmO+QejApzG6zpyky9G1A4g9s=
|
||||
github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo=
|
||||
github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||
github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po=
|
||||
github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
@@ -645,8 +620,6 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
|
||||
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
|
||||
github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo=
|
||||
github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
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=
|
||||
@@ -694,8 +667,8 @@ github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0o
|
||||
github.com/unrolled/secure v1.14.0 h1:u9vJTU/pR4Bny0ntLUMxdfLtmIRGvQf2sEFuA0TG9AE=
|
||||
github.com/unrolled/secure v1.14.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
|
||||
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
|
||||
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||
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/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
|
||||
@@ -708,8 +681,8 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofm
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw=
|
||||
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -719,6 +692,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.einride.tech/aip v0.66.0 h1:XfV+NQX6L7EOYK11yoHHFtndeaWh3KbD9/cN/6iWEt8=
|
||||
go.einride.tech/aip v0.66.0/go.mod h1:qAhMsfT7plxBX+Oy7Huol6YUvZ0ZzdUz26yZsQwfl1M=
|
||||
go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
|
||||
go.mongodb.org/mongo-driver v1.11.3 h1:Ql6K6qYHEzB6xvu4+AU0BoRoqf9vFPcc4o7MUIdPW8Y=
|
||||
go.mongodb.org/mongo-driver v1.11.3/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
|
||||
@@ -730,6 +705,8 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 h1:UNQQKPfTDe1J81ViolILjTKPr9WetKW6uei2hFgJmFs=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw=
|
||||
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
|
||||
@@ -744,8 +721,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1
|
||||
go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg=
|
||||
go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY=
|
||||
go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=
|
||||
go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
|
||||
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
|
||||
go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=
|
||||
go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0=
|
||||
go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo=
|
||||
@@ -756,12 +733,15 @@ 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/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
||||
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=
|
||||
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
|
||||
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
@@ -782,8 +762,9 @@ golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -873,8 +854,9 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
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.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -884,8 +866,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210113205817-d3ed898aa8a3/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
|
||||
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
|
||||
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
|
||||
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -961,8 +943,9 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.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 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@@ -971,8 +954,9 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/term v0.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.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -987,8 +971,9 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -1079,8 +1064,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.39.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/api v0.153.0 h1:N1AwGhielyKFaUqH07/ZSIQR3uNPcV7NVw0vj+j4iR4=
|
||||
google.golang.org/api v0.153.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY=
|
||||
google.golang.org/api v0.162.0 h1:Vhs54HkaEpkMBdgGdOT2P6F0csGG/vxDS0hWHJzmmps=
|
||||
google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
@@ -1128,12 +1113,12 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac h1:ZL/Teoy/ZGnzyrqK/Optxxp2pmVh+fmJ97slxSRyzUg=
|
||||
google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
|
||||
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-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@@ -1153,8 +1138,8 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=
|
||||
google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
|
||||
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
||||
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||
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=
|
||||
@@ -1168,8 +1153,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/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=
|
||||
@@ -1177,7 +1162,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
@@ -1194,19 +1178,20 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
|
||||
gorm.io/driver/mysql v1.5.4 h1:igQmHfKcbaTVyAIHNhhB888vvxh8EdQ2uSUT0LPcBso=
|
||||
gorm.io/driver/mysql v1.5.4/go.mod h1:9rYxJph/u9SWkWc9yY4XJ1F/+xO0S/ChOmbk3+Z5Tvs=
|
||||
gorm.io/driver/postgres v1.5.6 h1:ydr9xEd5YAM0vxVDY0X139dyzNz10spDiDlC7+ibLeU=
|
||||
gorm.io/driver/postgres v1.5.6/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA=
|
||||
gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
|
||||
gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||
gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM=
|
||||
gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA=
|
||||
gorm.io/driver/sqlserver v1.5.3 h1:rjupPS4PVw+rjJkfvr8jn2lJ8BMhT4UW5FwuJY0P3Z0=
|
||||
gorm.io/driver/sqlserver v1.5.3/go.mod h1:B+CZ0/7oFJ6tAlefsKoyxdgDCXJKSgwS2bMOQZT0I00=
|
||||
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A=
|
||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/plugin/dbresolver v1.5.0 h1:XVHLxh775eP0CqVh3vcfJtYqja3uFl5Wr3cKlY8jgDY=
|
||||
gorm.io/plugin/dbresolver v1.5.0/go.mod h1:l4Cn87EHLEYuqUncpEeTC2tTJQkjngPSD+lo8hIvcT0=
|
||||
gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s=
|
||||
gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/plugin/dbresolver v1.5.1 h1:s9Dj9f7r+1rE3nx/Ywzc85nXptUEaeOO0pt27xdopM8=
|
||||
gorm.io/plugin/dbresolver v1.5.1/go.mod h1:l4Cn87EHLEYuqUncpEeTC2tTJQkjngPSD+lo8hIvcT0=
|
||||
gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
|
||||
gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
+5
-6
@@ -1,8 +1,6 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
|
||||
requests "panel/app/http/requests/cert"
|
||||
"panel/app/models"
|
||||
"panel/pkg/acme"
|
||||
@@ -21,8 +19,9 @@ type Cert interface {
|
||||
CertUpdate(request requests.CertUpdate) error
|
||||
CertShow(ID uint) (models.Cert, error)
|
||||
CertDestroy(ID uint) error
|
||||
ObtainAuto(ID uint) (certificate.Resource, error)
|
||||
ObtainManual(ID uint) (certificate.Resource, error)
|
||||
ManualDNS(ID uint) (map[string]acme.Resolve, error)
|
||||
Renew(ID uint) (certificate.Resource, error)
|
||||
ObtainAuto(ID uint) (acme.Certificate, error)
|
||||
ObtainManual(ID uint) (acme.Certificate, error)
|
||||
ManualDNS(ID uint) ([]acme.DNSRecord, error)
|
||||
Renew(ID uint) (acme.Certificate, error)
|
||||
Deploy(ID, WebsiteID uint) error
|
||||
}
|
||||
|
||||
@@ -20,4 +20,7 @@ type Plugin interface {
|
||||
All() []PanelPlugin
|
||||
GetBySlug(slug string) PanelPlugin
|
||||
GetInstalledBySlug(slug string) models.Plugin
|
||||
Install(slug string) error
|
||||
Uninstall(slug string) error
|
||||
Update(slug string) error
|
||||
}
|
||||
|
||||
+23
-11
@@ -19,7 +19,7 @@ var PluginMySQL57 = PanelPlugin{
|
||||
Slug: "mysql57",
|
||||
Version: "5.7.44",
|
||||
Requires: []string{},
|
||||
Excludes: []string{"mysql80"},
|
||||
Excludes: []string{"mysql80", "mysql84"},
|
||||
Install: `bash /www/panel/scripts/mysql/install.sh 57`,
|
||||
Uninstall: `bash /www/panel/scripts/mysql/uninstall.sh 57`,
|
||||
Update: `bash /www/panel/scripts/mysql/update.sh 57`,
|
||||
@@ -29,19 +29,31 @@ var PluginMySQL80 = PanelPlugin{
|
||||
Name: "MySQL-8.0",
|
||||
Description: "MySQL 是最流行的关系型数据库管理系统之一,Oracle 旗下产品。(建议内存 > 2G 安装)",
|
||||
Slug: "mysql80",
|
||||
Version: "8.0.36",
|
||||
Version: "8.0.37",
|
||||
Requires: []string{},
|
||||
Excludes: []string{"mysql57"},
|
||||
Excludes: []string{"mysql57", "mysql84"},
|
||||
Install: `bash /www/panel/scripts/mysql/install.sh 80`,
|
||||
Uninstall: `bash /www/panel/scripts/mysql/uninstall.sh 80`,
|
||||
Update: `bash /www/panel/scripts/mysql/update.sh 80`,
|
||||
}
|
||||
|
||||
var PluginMySQL84 = PanelPlugin{
|
||||
Name: "MySQL-8.4",
|
||||
Description: "MySQL 是最流行的关系型数据库管理系统之一,Oracle 旗下产品。(建议内存 > 2G 安装)",
|
||||
Slug: "mysql84",
|
||||
Version: "8.4.0",
|
||||
Requires: []string{},
|
||||
Excludes: []string{"mysql57", "mysql80"},
|
||||
Install: `bash /www/panel/scripts/mysql/install.sh 84`,
|
||||
Uninstall: `bash /www/panel/scripts/mysql/uninstall.sh 84`,
|
||||
Update: `bash /www/panel/scripts/mysql/update.sh 84`,
|
||||
}
|
||||
|
||||
var PluginPostgreSQL15 = PanelPlugin{
|
||||
Name: "PostgreSQL-15",
|
||||
Description: "PostgreSQL 是世界上最先进的开源关系数据库,在类似 BSD 与 MIT 许可的 PostgreSQL 许可下发行。",
|
||||
Slug: "postgresql15",
|
||||
Version: "15.6",
|
||||
Version: "15.7",
|
||||
Requires: []string{},
|
||||
Excludes: []string{"postgresql16"},
|
||||
Install: `bash /www/panel/scripts/postgresql/install.sh 15`,
|
||||
@@ -53,7 +65,7 @@ var PluginPostgreSQL16 = PanelPlugin{
|
||||
Name: "PostgreSQL-16",
|
||||
Description: "PostgreSQL 是世界上最先进的开源关系数据库,在类似 BSD 与 MIT 许可的 PostgreSQL 许可下发行。",
|
||||
Slug: "postgresql16",
|
||||
Version: "16.2",
|
||||
Version: "16.3",
|
||||
Requires: []string{},
|
||||
Excludes: []string{"postgresql15"},
|
||||
Install: `bash /www/panel/scripts/postgresql/install.sh 16`,
|
||||
@@ -89,7 +101,7 @@ var PluginPHP81 = PanelPlugin{
|
||||
Name: "PHP-8.1",
|
||||
Description: "PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。",
|
||||
Slug: "php81",
|
||||
Version: "8.1.27",
|
||||
Version: "8.1.28",
|
||||
Requires: []string{},
|
||||
Excludes: []string{},
|
||||
Install: `bash /www/panel/scripts/php/install.sh 81`,
|
||||
@@ -101,7 +113,7 @@ var PluginPHP82 = PanelPlugin{
|
||||
Name: "PHP-8.2",
|
||||
Description: "PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。",
|
||||
Slug: "php82",
|
||||
Version: "8.2.16",
|
||||
Version: "8.2.19",
|
||||
Requires: []string{},
|
||||
Excludes: []string{},
|
||||
Install: `bash /www/panel/scripts/php/install.sh 82`,
|
||||
@@ -113,7 +125,7 @@ var PluginPHP83 = PanelPlugin{
|
||||
Name: "PHP-8.3",
|
||||
Description: "PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。",
|
||||
Slug: "php83",
|
||||
Version: "8.3.3",
|
||||
Version: "8.3.7",
|
||||
Requires: []string{},
|
||||
Excludes: []string{},
|
||||
Install: `bash /www/panel/scripts/php/install.sh 83`,
|
||||
@@ -197,7 +209,7 @@ var PluginFail2ban = PanelPlugin{
|
||||
Name: "Fail2ban",
|
||||
Description: "Fail2ban 扫描系统日志文件并从中找出多次尝试失败的IP地址,将该IP地址加入防火墙的拒绝访问列表中。",
|
||||
Slug: "fail2ban",
|
||||
Version: "1.0.0",
|
||||
Version: "1.0.2",
|
||||
Requires: []string{},
|
||||
Excludes: []string{},
|
||||
Install: `bash /www/panel/scripts/fail2ban/install.sh`,
|
||||
@@ -212,7 +224,7 @@ var PluginToolBox = PanelPlugin{
|
||||
Version: "1.0.0",
|
||||
Requires: []string{},
|
||||
Excludes: []string{},
|
||||
Install: `panel writePlugin toolbox 1.0`,
|
||||
Install: `panel writePlugin toolbox 1.0.0`,
|
||||
Uninstall: `panel deletePlugin toolbox`,
|
||||
Update: `panel writePlugin toolbox 1.0`,
|
||||
Update: `panel writePlugin toolbox 1.0.0`,
|
||||
}
|
||||
|
||||
+148
-157
@@ -2,10 +2,11 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/goravel/framework/facades"
|
||||
|
||||
requests "panel/app/http/requests/cert"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
)
|
||||
|
||||
type CertImpl struct {
|
||||
client *acme.Client
|
||||
}
|
||||
|
||||
func NewCertImpl() *CertImpl {
|
||||
@@ -34,15 +36,15 @@ func (s *CertImpl) UserStore(request requests.UserStore) error {
|
||||
var client *acme.Client
|
||||
switch user.CA {
|
||||
case "letsencrypt":
|
||||
client, err = acme.NewRegisterClient(user.Email, acme.CALetEncrypt, certcrypto.KeyType(user.KeyType))
|
||||
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CALetsEncrypt, nil, acme.KeyType(user.KeyType))
|
||||
case "buypass":
|
||||
client, err = acme.NewRegisterClient(user.Email, acme.CABuypass, certcrypto.KeyType(user.KeyType))
|
||||
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CABuypass, nil, acme.KeyType(user.KeyType))
|
||||
case "zerossl":
|
||||
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CAZeroSSL, certcrypto.KeyType(user.KeyType))
|
||||
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CAZeroSSL, &acme.EAB{KeyID: *user.Kid, MACKey: *user.HmacEncoded}, acme.KeyType(user.KeyType))
|
||||
case "sslcom":
|
||||
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CASSLcom, certcrypto.KeyType(user.KeyType))
|
||||
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CASSLcom, &acme.EAB{KeyID: *user.Kid, MACKey: *user.HmacEncoded}, acme.KeyType(user.KeyType))
|
||||
case "google":
|
||||
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CAGoogle, certcrypto.KeyType(user.KeyType))
|
||||
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CAGoogle, &acme.EAB{KeyID: *user.Kid, MACKey: *user.HmacEncoded}, acme.KeyType(user.KeyType))
|
||||
default:
|
||||
return errors.New("CA 提供商不支持")
|
||||
}
|
||||
@@ -51,7 +53,7 @@ func (s *CertImpl) UserStore(request requests.UserStore) error {
|
||||
return errors.New("向 CA 注册账号失败,请检查参数是否正确")
|
||||
}
|
||||
|
||||
privateKey, err := acme.GetPrivateKey(client.User.GetPrivateKey(), acme.KeyType(user.KeyType))
|
||||
privateKey, err := acme.EncodePrivateKey(client.Account.PrivateKey)
|
||||
if err != nil {
|
||||
return errors.New("获取私钥失败")
|
||||
}
|
||||
@@ -77,15 +79,15 @@ func (s *CertImpl) UserUpdate(request requests.UserUpdate) error {
|
||||
var client *acme.Client
|
||||
switch user.CA {
|
||||
case "letsencrypt":
|
||||
client, err = acme.NewRegisterClient(user.Email, acme.CALetEncrypt, certcrypto.KeyType(user.KeyType))
|
||||
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CALetsEncrypt, nil, acme.KeyType(user.KeyType))
|
||||
case "buypass":
|
||||
client, err = acme.NewRegisterClient(user.Email, acme.CABuypass, certcrypto.KeyType(user.KeyType))
|
||||
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CABuypass, nil, acme.KeyType(user.KeyType))
|
||||
case "zerossl":
|
||||
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CAZeroSSL, certcrypto.KeyType(user.KeyType))
|
||||
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CAZeroSSL, &acme.EAB{KeyID: *user.Kid, MACKey: *user.HmacEncoded}, acme.KeyType(user.KeyType))
|
||||
case "sslcom":
|
||||
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CASSLcom, certcrypto.KeyType(user.KeyType))
|
||||
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CASSLcom, &acme.EAB{KeyID: *user.Kid, MACKey: *user.HmacEncoded}, acme.KeyType(user.KeyType))
|
||||
case "google":
|
||||
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CAGoogle, certcrypto.KeyType(user.KeyType))
|
||||
client, err = acme.NewRegisterAccount(context.Background(), user.Email, acme.CAGoogle, &acme.EAB{KeyID: *user.Kid, MACKey: *user.HmacEncoded}, acme.KeyType(user.KeyType))
|
||||
default:
|
||||
return errors.New("CA 提供商不支持")
|
||||
}
|
||||
@@ -94,7 +96,7 @@ func (s *CertImpl) UserUpdate(request requests.UserUpdate) error {
|
||||
return errors.New("向 CA 注册账号失败,请检查参数是否正确")
|
||||
}
|
||||
|
||||
privateKey, err := acme.GetPrivateKey(client.User.GetPrivateKey(), acme.KeyType(user.KeyType))
|
||||
privateKey, err := acme.EncodePrivateKey(client.Account.PrivateKey)
|
||||
if err != nil {
|
||||
return errors.New("获取私钥失败")
|
||||
}
|
||||
@@ -183,13 +185,8 @@ func (s *CertImpl) CertStore(request requests.CertStore) error {
|
||||
cert.Domains = request.Domains
|
||||
cert.AutoRenew = request.AutoRenew
|
||||
cert.UserID = request.UserID
|
||||
|
||||
if request.DNSID != nil {
|
||||
cert.DNSID = request.DNSID
|
||||
}
|
||||
if request.WebsiteID != nil {
|
||||
cert.WebsiteID = request.WebsiteID
|
||||
}
|
||||
cert.DNSID = request.DNSID
|
||||
cert.WebsiteID = request.WebsiteID
|
||||
|
||||
return facades.Orm().Query().Create(&cert)
|
||||
}
|
||||
@@ -206,13 +203,8 @@ func (s *CertImpl) CertUpdate(request requests.CertUpdate) error {
|
||||
cert.Domains = request.Domains
|
||||
cert.AutoRenew = request.AutoRenew
|
||||
cert.UserID = request.UserID
|
||||
|
||||
if request.DNSID != nil {
|
||||
cert.DNSID = request.DNSID
|
||||
}
|
||||
if request.WebsiteID != nil {
|
||||
cert.WebsiteID = request.WebsiteID
|
||||
}
|
||||
cert.DNSID = request.DNSID
|
||||
cert.WebsiteID = request.WebsiteID
|
||||
|
||||
return facades.Orm().Query().Save(&cert)
|
||||
}
|
||||
@@ -238,67 +230,55 @@ func (s *CertImpl) CertDestroy(ID uint) error {
|
||||
}
|
||||
|
||||
// ObtainAuto 自动签发证书
|
||||
func (s *CertImpl) ObtainAuto(ID uint) (certificate.Resource, error) {
|
||||
func (s *CertImpl) ObtainAuto(ID uint) (acme.Certificate, error) {
|
||||
var cert models.Cert
|
||||
err := facades.Orm().Query().With("Website").With("User").With("DNS").Where("id = ?", ID).First(&cert)
|
||||
if err != nil {
|
||||
return certificate.Resource{}, err
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
|
||||
var ca string
|
||||
switch cert.User.CA {
|
||||
case "letsencrypt":
|
||||
ca = acme.CALetEncrypt
|
||||
case "buypass":
|
||||
ca = acme.CABuypass
|
||||
case "zerossl":
|
||||
ca = acme.CAZeroSSL
|
||||
case "sslcom":
|
||||
ca = acme.CASSLcom
|
||||
case "google":
|
||||
ca = acme.CAGoogle
|
||||
}
|
||||
|
||||
client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType))
|
||||
client, err := s.getClient(cert)
|
||||
if err != nil {
|
||||
return certificate.Resource{}, err
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
|
||||
if cert.DNS != nil {
|
||||
err = client.UseDns(acme.DnsType(cert.DNS.Type), cert.DNS.Data)
|
||||
client.UseDns(acme.DnsType(cert.DNS.Type), cert.DNS.Data)
|
||||
} else {
|
||||
if cert.Website == nil {
|
||||
return certificate.Resource{}, errors.New("该证书没有关联网站,无法自动签发")
|
||||
return acme.Certificate{}, errors.New("该证书没有关联网站,无法自动签发")
|
||||
} else {
|
||||
err = client.UseHTTP(cert.Website.Path)
|
||||
for _, domain := range cert.Domains {
|
||||
if strings.Contains(domain, "*") {
|
||||
return acme.Certificate{}, errors.New("通配符域名无法使用 HTTP 验证")
|
||||
}
|
||||
}
|
||||
client.UseHTTP(cert.Website.Path)
|
||||
}
|
||||
}
|
||||
|
||||
ssl, err := client.ObtainSSL(context.Background(), cert.Domains, acme.KeyType(cert.Type))
|
||||
if err != nil {
|
||||
return certificate.Resource{}, err
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
|
||||
ssl, err := client.ObtainSSL(cert.Domains)
|
||||
if err != nil {
|
||||
return certificate.Resource{}, err
|
||||
}
|
||||
|
||||
cert.CertURL = &ssl.CertURL
|
||||
cert.Cert = string(ssl.Certificate)
|
||||
cert.CertURL = &ssl.URL
|
||||
cert.Cert = string(ssl.ChainPEM)
|
||||
cert.Key = string(ssl.PrivateKey)
|
||||
err = facades.Orm().Query().Save(&cert)
|
||||
if err != nil {
|
||||
return certificate.Resource{}, err
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
|
||||
if cert.Website != nil {
|
||||
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", string(ssl.Certificate), 0644); err != nil {
|
||||
return certificate.Resource{}, err
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", cert.Cert, 0644); err != nil {
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", string(ssl.PrivateKey), 0644); err != nil {
|
||||
return certificate.Resource{}, err
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", cert.Key, 0644); err != nil {
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
if _, err := tools.Exec("systemctl reload openresty"); err != nil {
|
||||
return certificate.Resource{}, err
|
||||
if err = tools.ServiceReload("openresty"); err != nil {
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,59 +286,39 @@ func (s *CertImpl) ObtainAuto(ID uint) (certificate.Resource, error) {
|
||||
}
|
||||
|
||||
// ObtainManual 手动签发证书
|
||||
func (s *CertImpl) ObtainManual(ID uint) (certificate.Resource, error) {
|
||||
func (s *CertImpl) ObtainManual(ID uint) (acme.Certificate, error) {
|
||||
var cert models.Cert
|
||||
err := facades.Orm().Query().With("User").Where("id = ?", ID).First(&cert)
|
||||
if err != nil {
|
||||
return certificate.Resource{}, err
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
|
||||
var ca string
|
||||
switch cert.User.CA {
|
||||
case "letsencrypt":
|
||||
ca = acme.CALetEncrypt
|
||||
case "buypass":
|
||||
ca = acme.CABuypass
|
||||
case "zerossl":
|
||||
ca = acme.CAZeroSSL
|
||||
case "sslcom":
|
||||
ca = acme.CASSLcom
|
||||
case "google":
|
||||
ca = acme.CAGoogle
|
||||
if s.client == nil {
|
||||
return acme.Certificate{}, errors.New("请重新获取 DNS 解析记录")
|
||||
}
|
||||
|
||||
client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType))
|
||||
ssl, err := s.client.ObtainSSLManual()
|
||||
if err != nil {
|
||||
return certificate.Resource{}, err
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
|
||||
err = client.UseManualDns()
|
||||
if err != nil {
|
||||
return certificate.Resource{}, err
|
||||
}
|
||||
|
||||
ssl, err := client.ObtainSSL(cert.Domains)
|
||||
if err != nil {
|
||||
return certificate.Resource{}, err
|
||||
}
|
||||
|
||||
cert.CertURL = &ssl.CertURL
|
||||
cert.Cert = string(ssl.Certificate)
|
||||
cert.CertURL = &ssl.URL
|
||||
cert.Cert = string(ssl.ChainPEM)
|
||||
cert.Key = string(ssl.PrivateKey)
|
||||
err = facades.Orm().Query().Save(&cert)
|
||||
if err != nil {
|
||||
return certificate.Resource{}, err
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
|
||||
if cert.Website != nil {
|
||||
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", string(ssl.Certificate), 0644); err != nil {
|
||||
return certificate.Resource{}, err
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", cert.Cert, 0644); err != nil {
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", string(ssl.PrivateKey), 0644); err != nil {
|
||||
return certificate.Resource{}, err
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", cert.Key, 0644); err != nil {
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
if _, err := tools.Exec("systemctl reload openresty"); err != nil {
|
||||
return certificate.Resource{}, err
|
||||
if err = tools.ServiceReload("openresty"); err != nil {
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,108 +326,139 @@ func (s *CertImpl) ObtainManual(ID uint) (certificate.Resource, error) {
|
||||
}
|
||||
|
||||
// ManualDNS 获取手动 DNS 解析信息
|
||||
func (s *CertImpl) ManualDNS(ID uint) (map[string]acme.Resolve, error) {
|
||||
func (s *CertImpl) ManualDNS(ID uint) ([]acme.DNSRecord, error) {
|
||||
var cert models.Cert
|
||||
err := facades.Orm().Query().With("User").Where("id = ?", ID).First(&cert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ca string
|
||||
switch cert.User.CA {
|
||||
case "letsencrypt":
|
||||
ca = acme.CALetEncrypt
|
||||
case "buypass":
|
||||
ca = acme.CABuypass
|
||||
case "zerossl":
|
||||
ca = acme.CAZeroSSL
|
||||
case "sslcom":
|
||||
ca = acme.CASSLcom
|
||||
case "google":
|
||||
ca = acme.CAGoogle
|
||||
}
|
||||
|
||||
client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType))
|
||||
client, err := s.getClient(cert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = client.UseManualDns()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.UseManualDns(len(cert.Domains))
|
||||
records, err := client.GetDNSRecords(context.Background(), cert.Domains, acme.KeyType(cert.Type))
|
||||
|
||||
return client.GetDNSResolve(cert.Domains)
|
||||
// 15 分钟后清理客户端
|
||||
s.client = client
|
||||
time.AfterFunc(15*time.Minute, func() {
|
||||
s.client = nil
|
||||
})
|
||||
|
||||
return records, err
|
||||
}
|
||||
|
||||
// Renew 续签证书
|
||||
func (s *CertImpl) Renew(ID uint) (certificate.Resource, error) {
|
||||
func (s *CertImpl) Renew(ID uint) (acme.Certificate, error) {
|
||||
var cert models.Cert
|
||||
err := facades.Orm().Query().With("Website").With("User").With("DNS").Where("id = ?", ID).First(&cert)
|
||||
if err != nil {
|
||||
return certificate.Resource{}, err
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
|
||||
var ca string
|
||||
switch cert.User.CA {
|
||||
case "letsencrypt":
|
||||
ca = acme.CALetEncrypt
|
||||
case "buypass":
|
||||
ca = acme.CABuypass
|
||||
case "zerossl":
|
||||
ca = acme.CAZeroSSL
|
||||
case "sslcom":
|
||||
ca = acme.CASSLcom
|
||||
case "google":
|
||||
ca = acme.CAGoogle
|
||||
}
|
||||
|
||||
client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType))
|
||||
client, err := s.getClient(cert)
|
||||
if err != nil {
|
||||
return certificate.Resource{}, err
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
|
||||
if cert.CertURL == nil {
|
||||
return certificate.Resource{}, errors.New("该证书没有签发成功,无法续签")
|
||||
return acme.Certificate{}, errors.New("该证书没有签发成功,无法续签")
|
||||
}
|
||||
|
||||
if cert.DNS != nil {
|
||||
err = client.UseDns(acme.DnsType(cert.DNS.Type), cert.DNS.Data)
|
||||
client.UseDns(acme.DnsType(cert.DNS.Type), cert.DNS.Data)
|
||||
} else {
|
||||
if cert.Website == nil {
|
||||
return certificate.Resource{}, errors.New("该证书没有关联网站,无法续签,可以尝试手动签发")
|
||||
return acme.Certificate{}, errors.New("该证书没有关联网站,无法续签,可以尝试手动签发")
|
||||
} else {
|
||||
err = client.UseHTTP(cert.Website.Path)
|
||||
for _, domain := range cert.Domains {
|
||||
if strings.Contains(domain, "*") {
|
||||
return acme.Certificate{}, errors.New("通配符域名无法使用 HTTP 验证")
|
||||
}
|
||||
}
|
||||
client.UseHTTP(cert.Website.Path)
|
||||
}
|
||||
}
|
||||
|
||||
ssl, err := client.RenewSSL(context.Background(), *cert.CertURL, cert.Domains, acme.KeyType(cert.Type))
|
||||
if err != nil {
|
||||
return certificate.Resource{}, err
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
|
||||
ssl, err := client.RenewSSL(*cert.CertURL)
|
||||
if err != nil {
|
||||
return certificate.Resource{}, err
|
||||
}
|
||||
|
||||
cert.CertURL = &ssl.CertURL
|
||||
cert.Cert = string(ssl.Certificate)
|
||||
cert.CertURL = &ssl.URL
|
||||
cert.Cert = string(ssl.ChainPEM)
|
||||
cert.Key = string(ssl.PrivateKey)
|
||||
err = facades.Orm().Query().Save(&cert)
|
||||
if err != nil {
|
||||
return certificate.Resource{}, err
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
|
||||
if cert.Website != nil {
|
||||
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", string(ssl.Certificate), 0644); err != nil {
|
||||
return certificate.Resource{}, err
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", cert.Cert, 0644); err != nil {
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", string(ssl.PrivateKey), 0644); err != nil {
|
||||
return certificate.Resource{}, err
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", cert.Key, 0644); err != nil {
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
if _, err := tools.Exec("systemctl reload openresty"); err != nil {
|
||||
return certificate.Resource{}, err
|
||||
if err = tools.ServiceReload("openresty"); err != nil {
|
||||
return acme.Certificate{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return ssl, nil
|
||||
}
|
||||
|
||||
// Deploy 部署证书
|
||||
func (s *CertImpl) Deploy(ID, WebsiteID uint) error {
|
||||
var cert models.Cert
|
||||
err := facades.Orm().Query().Where("id = ?", ID).First(&cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cert.Cert == "" || cert.Key == "" {
|
||||
return errors.New("该证书没有签发成功,无法部署")
|
||||
}
|
||||
|
||||
website := models.Website{}
|
||||
err = facades.Orm().Query().Where("id = ?", WebsiteID).First(&website)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+website.Name+".pem", cert.Cert, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+website.Name+".key", cert.Key, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = tools.ServiceReload("openresty"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CertImpl) getClient(cert models.Cert) (*acme.Client, error) {
|
||||
var ca string
|
||||
var eab *acme.EAB
|
||||
switch cert.User.CA {
|
||||
case "letsencrypt":
|
||||
ca = acme.CALetsEncrypt
|
||||
case "buypass":
|
||||
ca = acme.CABuypass
|
||||
case "zerossl":
|
||||
ca = acme.CAZeroSSL
|
||||
eab = &acme.EAB{KeyID: *cert.User.Kid, MACKey: *cert.User.HmacEncoded}
|
||||
case "sslcom":
|
||||
ca = acme.CASSLcom
|
||||
eab = &acme.EAB{KeyID: *cert.User.Kid, MACKey: *cert.User.HmacEncoded}
|
||||
case "google":
|
||||
ca = acme.CAGoogle
|
||||
eab = &acme.EAB{KeyID: *cert.User.Kid, MACKey: *cert.User.HmacEncoded}
|
||||
}
|
||||
|
||||
return acme.NewPrivateKeyAccount(cert.User.Email, cert.User.PrivateKey, ca, eab)
|
||||
}
|
||||
|
||||
@@ -245,14 +245,14 @@ func (r *Container) NetworkPrune() error {
|
||||
|
||||
// ImageList 列出镜像
|
||||
func (r *Container) ImageList() ([]image.Summary, error) {
|
||||
return r.client.ImageList(context.Background(), types.ImageListOptions{
|
||||
return r.client.ImageList(context.Background(), image.ListOptions{
|
||||
All: true,
|
||||
})
|
||||
}
|
||||
|
||||
// ImageExist 判断镜像是否存在
|
||||
func (r *Container) ImageExist(id string) (bool, error) {
|
||||
var options types.ImageListOptions
|
||||
var options image.ListOptions
|
||||
options.Filters = filters.NewArgs(filters.Arg("reference", id))
|
||||
images, err := r.client.ImageList(context.Background(), options)
|
||||
if err != nil {
|
||||
@@ -264,7 +264,7 @@ func (r *Container) ImageExist(id string) (bool, error) {
|
||||
|
||||
// ImagePull 拉取镜像
|
||||
func (r *Container) ImagePull(config requests.ImagePull) error {
|
||||
options := types.ImagePullOptions{}
|
||||
options := image.PullOptions{}
|
||||
if config.Auth {
|
||||
authConfig := registry.AuthConfig{
|
||||
Username: config.Username,
|
||||
@@ -290,7 +290,7 @@ func (r *Container) ImagePull(config requests.ImagePull) error {
|
||||
|
||||
// ImageRemove 删除镜像
|
||||
func (r *Container) ImageRemove(id string) error {
|
||||
_, err := r.client.ImageRemove(context.Background(), id, types.ImageRemoveOptions{
|
||||
_, err := r.client.ImageRemove(context.Background(), id, image.RemoveOptions{
|
||||
Force: true,
|
||||
PruneChildren: true,
|
||||
})
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/goravel/framework/facades"
|
||||
"github.com/imroc/req/v3"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"panel/app/models"
|
||||
@@ -73,9 +73,9 @@ func (r *PHPImpl) SaveFPMConfig(config string) error {
|
||||
}
|
||||
|
||||
func (r *PHPImpl) Load() ([]types.NV, error) {
|
||||
client := req.C().SetTimeout(10 * time.Second)
|
||||
client := resty.New().SetTimeout(10 * time.Second)
|
||||
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + r.version)
|
||||
if err != nil || !resp.IsSuccessState() {
|
||||
if err != nil || !resp.IsSuccess() {
|
||||
return nil, errors.New("获取 PHP-" + r.version + " 运行状态失败")
|
||||
}
|
||||
|
||||
|
||||
+145
-1
@@ -2,6 +2,8 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/goravel/framework/facades"
|
||||
|
||||
"panel/app/models"
|
||||
@@ -9,10 +11,13 @@ import (
|
||||
)
|
||||
|
||||
type PluginImpl struct {
|
||||
task internal.Task
|
||||
}
|
||||
|
||||
func NewPluginImpl() *PluginImpl {
|
||||
return &PluginImpl{}
|
||||
return &PluginImpl{
|
||||
task: NewTaskImpl(),
|
||||
}
|
||||
}
|
||||
|
||||
// AllInstalled 获取已安装的所有插件
|
||||
@@ -31,6 +36,7 @@ func (r *PluginImpl) All() []internal.PanelPlugin {
|
||||
internal.PluginOpenResty,
|
||||
internal.PluginMySQL57,
|
||||
internal.PluginMySQL80,
|
||||
internal.PluginMySQL84,
|
||||
internal.PluginPostgreSQL15,
|
||||
internal.PluginPostgreSQL16,
|
||||
internal.PluginPHP74,
|
||||
@@ -71,3 +77,141 @@ func (r *PluginImpl) GetInstalledBySlug(slug string) models.Plugin {
|
||||
|
||||
return plugin
|
||||
}
|
||||
|
||||
// Install 安装插件
|
||||
func (r *PluginImpl) Install(slug string) error {
|
||||
plugin := r.GetBySlug(slug)
|
||||
installedPlugin := r.GetInstalledBySlug(slug)
|
||||
installedPlugins, err := r.AllInstalled()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if installedPlugin.ID != 0 {
|
||||
return errors.New("插件已安装")
|
||||
}
|
||||
|
||||
pluginsMap := make(map[string]bool)
|
||||
|
||||
for _, p := range installedPlugins {
|
||||
pluginsMap[p.Slug] = true
|
||||
}
|
||||
|
||||
for _, require := range plugin.Requires {
|
||||
_, requireFound := pluginsMap[require]
|
||||
if !requireFound {
|
||||
return errors.New("插件 " + slug + " 需要依赖 " + require + " 插件")
|
||||
}
|
||||
}
|
||||
|
||||
for _, exclude := range plugin.Excludes {
|
||||
_, excludeFound := pluginsMap[exclude]
|
||||
if excludeFound {
|
||||
return errors.New("插件 " + slug + " 不兼容 " + exclude + " 插件")
|
||||
}
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "安装插件 " + plugin.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = plugin.Install + ` >> '/tmp/` + plugin.Slug + `.log' 2>&1`
|
||||
task.Log = "/tmp/" + plugin.Slug + ".log"
|
||||
if err = facades.Orm().Query().Create(&task); err != nil {
|
||||
return errors.New("创建任务失败")
|
||||
}
|
||||
|
||||
r.task.Process(task.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uninstall 卸载插件
|
||||
func (r *PluginImpl) Uninstall(slug string) error {
|
||||
plugin := r.GetBySlug(slug)
|
||||
installedPlugin := r.GetInstalledBySlug(slug)
|
||||
installedPlugins, err := r.AllInstalled()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if installedPlugin.ID == 0 {
|
||||
return errors.New("插件未安装")
|
||||
}
|
||||
|
||||
pluginsMap := make(map[string]bool)
|
||||
|
||||
for _, p := range installedPlugins {
|
||||
pluginsMap[p.Slug] = true
|
||||
}
|
||||
|
||||
for _, require := range plugin.Requires {
|
||||
_, requireFound := pluginsMap[require]
|
||||
if !requireFound {
|
||||
return errors.New("插件 " + slug + " 需要依赖 " + require + " 插件")
|
||||
}
|
||||
}
|
||||
|
||||
for _, exclude := range plugin.Excludes {
|
||||
_, excludeFound := pluginsMap[exclude]
|
||||
if excludeFound {
|
||||
return errors.New("插件 " + slug + " 不兼容 " + exclude + " 插件")
|
||||
}
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "卸载插件 " + plugin.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = plugin.Uninstall + " >> /tmp/" + plugin.Slug + ".log 2>&1"
|
||||
task.Log = "/tmp/" + plugin.Slug + ".log"
|
||||
if err = facades.Orm().Query().Create(&task); err != nil {
|
||||
return errors.New("创建任务失败")
|
||||
}
|
||||
|
||||
r.task.Process(task.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update 更新插件
|
||||
func (r *PluginImpl) Update(slug string) error {
|
||||
plugin := r.GetBySlug(slug)
|
||||
installedPlugin := r.GetInstalledBySlug(slug)
|
||||
installedPlugins, err := r.AllInstalled()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if installedPlugin.ID == 0 {
|
||||
return errors.New("插件未安装")
|
||||
}
|
||||
|
||||
pluginsMap := make(map[string]bool)
|
||||
|
||||
for _, p := range installedPlugins {
|
||||
pluginsMap[p.Slug] = true
|
||||
}
|
||||
|
||||
for _, require := range plugin.Requires {
|
||||
_, requireFound := pluginsMap[require]
|
||||
if !requireFound {
|
||||
return errors.New("插件 " + slug + " 需要依赖 " + require + " 插件")
|
||||
}
|
||||
}
|
||||
|
||||
for _, exclude := range plugin.Excludes {
|
||||
_, excludeFound := pluginsMap[exclude]
|
||||
if excludeFound {
|
||||
return errors.New("插件 " + slug + " 不兼容 " + exclude + " 插件")
|
||||
}
|
||||
}
|
||||
|
||||
var task models.Task
|
||||
task.Name = "更新插件 " + plugin.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = plugin.Update + " >> /tmp/" + plugin.Slug + ".log 2>&1"
|
||||
task.Log = "/tmp/" + plugin.Slug + ".log"
|
||||
if err = facades.Orm().Query().Create(&task); err != nil {
|
||||
return errors.New("创建任务失败")
|
||||
}
|
||||
|
||||
r.task.Process(task.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -393,10 +393,10 @@ func (r *WebsiteImpl) SaveConfig(config requests.SaveConfig) error {
|
||||
// SSL
|
||||
ssl := config.Ssl
|
||||
website.Ssl = ssl
|
||||
if err := tools.Write("/www/server/vhost/ssl/"+website.Name+".pem", config.SslCertificate, 0644); err != nil {
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+website.Name+".pem", config.SslCertificate, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tools.Write("/www/server/vhost/ssl/"+website.Name+".key", config.SslCertificateKey, 0644); err != nil {
|
||||
if err = tools.Write("/www/server/vhost/ssl/"+website.Name+".key", config.SslCertificateKey, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if ssl {
|
||||
@@ -620,3 +620,13 @@ func (r *WebsiteImpl) GetConfigByName(name string) (internal.WebsiteSetting, err
|
||||
|
||||
return r.GetConfig(website.ID)
|
||||
}
|
||||
|
||||
// GetIDByName 根据网站名称获取网站ID
|
||||
func (r *WebsiteImpl) GetIDByName(name string) (uint, error) {
|
||||
var website models.Website
|
||||
if err := facades.Orm().Query().Where("name", name).First(&website); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return website.ID, nil
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ type Website interface {
|
||||
Delete(id uint) error
|
||||
GetConfig(id uint) (WebsiteSetting, error)
|
||||
GetConfigByName(name string) (WebsiteSetting, error)
|
||||
GetIDByName(name string) (uint, error)
|
||||
}
|
||||
|
||||
type PanelWebsite struct {
|
||||
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
{
|
||||
"auth": {
|
||||
"token": {
|
||||
"expired": "login has expired",
|
||||
"missing": "not logged in"
|
||||
}
|
||||
},
|
||||
"commands": {
|
||||
"panel:cert-renew": {
|
||||
"description": "[Panel] Certificate renewal"
|
||||
},
|
||||
"panel:monitoring": {
|
||||
"description": "[Panel] System Monitoring",
|
||||
"fail": "[Panel] System monitoring failed to save"
|
||||
},
|
||||
"panel": {
|
||||
"description": "[Panel] Command line",
|
||||
"forDeveloper": "Please use the following commands under the guidance of the developer",
|
||||
"use": "Please use the following commands",
|
||||
"tool": "command line tool",
|
||||
"portFail": "failed to get panel port",
|
||||
"port": "Panel port",
|
||||
"entrance": "Panel entrance",
|
||||
"update": {
|
||||
"description": "update/fix panel to latest version",
|
||||
"taskCheck": "There is currently a task being executed and updates are prohibited.",
|
||||
"versionFail": "failed to get latest version",
|
||||
"fail": "update failed",
|
||||
"success": "update completed"
|
||||
},
|
||||
"getInfo": {
|
||||
"description": "reinitialize panel account information",
|
||||
"adminGetFail": "failed to get administrator information",
|
||||
"adminSaveFail": "failed to save administrator information",
|
||||
"passwordGenerationFail": "failed to generate password",
|
||||
"username": "Username",
|
||||
"password": "Password",
|
||||
"address": "Panel address"
|
||||
},
|
||||
"getPort": {
|
||||
"description": "get the panel access port"
|
||||
},
|
||||
"getEntrance": {
|
||||
"description": "get panel access"
|
||||
},
|
||||
"deleteEntrance": {
|
||||
"description": "delete panel access",
|
||||
"fail": "failed to delete panel entrance",
|
||||
"success": "panel entrance deleted successfully"
|
||||
},
|
||||
"cleanTask": {
|
||||
"description": "clean up running and waiting tasks in the panel [used when tasks are stuck]",
|
||||
"fail": "failed to clean up tasks",
|
||||
"success": "tasks cleaned up successfully"
|
||||
},
|
||||
"backup": {
|
||||
"description": "back up website/MySQL database/PostgreSQL database to the specified directory and retain the specified amount",
|
||||
"paramFail": "backup type, path, name and keep amount are required",
|
||||
"start": "start backup",
|
||||
"backupDirFail": "failed to create backup directory",
|
||||
"targetSite": "target website",
|
||||
"siteNotExist": "website does not exist",
|
||||
"backupFail": "backup failed",
|
||||
"backupSuccess": "backup successful",
|
||||
"mysqlBackupFail": "MySQL database backup failed",
|
||||
"targetMysql": "target MySQL database",
|
||||
"startExport": "start exporting",
|
||||
"exportFail": "export failed",
|
||||
"exportSuccess": "export successful",
|
||||
"startCompress": "start compressing",
|
||||
"compressFail": "compression failed",
|
||||
"compressSuccess": "compression successful",
|
||||
"startMove": "start moving",
|
||||
"moveFail": "move failed",
|
||||
"moveSuccess": "move successful",
|
||||
"databaseGetFail": "failed to get database",
|
||||
"databaseNotExist": "database does not exist",
|
||||
"targetPostgres": "target PostgreSQL database",
|
||||
"cleanBackup" : "clean backup",
|
||||
"cleanupFail": "cleanup failed",
|
||||
"cleanupSuccess": "cleanup successful",
|
||||
"deleteFail": "failed to delete",
|
||||
"success": "backup completed"
|
||||
},
|
||||
"cutoff": {
|
||||
"description": "cut website logs and keep specified amount",
|
||||
"paramFail": "website domain name and keep amount are required",
|
||||
"start": "start cutting",
|
||||
"targetSite": "target website",
|
||||
"siteNotExist": "website does not exist",
|
||||
"logNotExist": "log file does not exist",
|
||||
"backupFail": "backup failed",
|
||||
"clearFail": "clearing failed",
|
||||
"cleanupFail": "cleanup failed",
|
||||
"clearLog": "clear log",
|
||||
"cutSuccess": "cutting successful",
|
||||
"cleanupSuccess": "cleanup successful",
|
||||
"end": "cutting completed"
|
||||
},
|
||||
"installPlugin": {
|
||||
"description": "install plugin",
|
||||
"paramFail": "plugin slug is required",
|
||||
"success": "task has been submitted"
|
||||
},
|
||||
"uninstallPlugin": {
|
||||
"description": "uninstall plugin",
|
||||
"paramFail": "plugin slug is required",
|
||||
"success": "task has been submitted"
|
||||
},
|
||||
"updatePlugin": {
|
||||
"description": "update plugin",
|
||||
"paramFail": "plugin slug is required",
|
||||
"success": "task has been submitted"
|
||||
},
|
||||
"addSite": {
|
||||
"description": "add website [domain name and port separated by commas]",
|
||||
"paramFail": "name, domain, port and path are required",
|
||||
"siteExist": "website already exists",
|
||||
"success": "website added successfully"
|
||||
},
|
||||
"removeSite": {
|
||||
"description": "remove website",
|
||||
"paramFail": "name is required",
|
||||
"siteNotExist": "website does not exist",
|
||||
"success": "website deleted successfully"
|
||||
},
|
||||
"init": {
|
||||
"description": "initialize the panel",
|
||||
"exist": "panel has been initialized",
|
||||
"success": "initialization successful",
|
||||
"adminFound": "failed to create administrator",
|
||||
"fail": "initialization failed"
|
||||
},
|
||||
"writePlugin": {
|
||||
"description": "write plugin installation status",
|
||||
"paramFail": "plugin slug and version are required",
|
||||
"fail": "failed to write plugin installation status",
|
||||
"success": "plugin installation status written successfully"
|
||||
},
|
||||
"deletePlugin": {
|
||||
"description": "delete plugin installation status",
|
||||
"paramFail": "plugin slug is required",
|
||||
"fail": "failed to remove plugin installation status",
|
||||
"success": "plugin installation status removed successfully"
|
||||
},
|
||||
"writeMysqlPassword": {
|
||||
"description": "write MySQL root password",
|
||||
"paramFail": "MySQL root password is required",
|
||||
"fail": "failed to write MySQL root password",
|
||||
"success": "MySQL root password written successfully"
|
||||
},
|
||||
"writeSite": {
|
||||
"description": "write website data to the panel",
|
||||
"paramFail": "name and path are required",
|
||||
"siteExist": "website already exists",
|
||||
"pathNotExist": "website directory does not exist",
|
||||
"fail": "failed to write to website",
|
||||
"success": "writing to website successfully"
|
||||
},
|
||||
"deleteSite": {
|
||||
"description": "delete panel website data",
|
||||
"paramFail": "website name is required",
|
||||
"fail": "failed to delete website",
|
||||
"success": "website deleted successfully"
|
||||
},
|
||||
"getSetting": {
|
||||
"description": "get panel setting data",
|
||||
"paramFail": "key is required"
|
||||
},
|
||||
"writeSetting": {
|
||||
"description": "write/update panel setting data",
|
||||
"paramFail": "key and value are required",
|
||||
"fail": "Writing settings failed",
|
||||
"success": "settings written successfully"
|
||||
},
|
||||
"deleteSetting": {
|
||||
"description": "delete panel setting data",
|
||||
"paramFail": "key is required",
|
||||
"fail": "failed to delete settings",
|
||||
"success": "settings deleted successfully"
|
||||
}
|
||||
},
|
||||
"panel:task": {
|
||||
"description": "[Panel] Daily tasks"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"internal": "internal system error",
|
||||
"plugin": {
|
||||
"notExist": "plugin does not exist",
|
||||
"notInstalled": "plugin :slug is not installed",
|
||||
"dependent": "plugin :slug requires dependency :dependency",
|
||||
"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.",
|
||||
"closed": "Panel is closed.",
|
||||
"failed": "Panel encountered an error during operation. Please check the troubleshooting or contact support."
|
||||
}
|
||||
}
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
{
|
||||
"auth": {
|
||||
"token": {
|
||||
"expired": "登录已过期",
|
||||
"missing": "未登录"
|
||||
}
|
||||
},
|
||||
"commands": {
|
||||
"panel:cert-renew": {
|
||||
"description": "[面板] 证书续签"
|
||||
},
|
||||
"panel:monitoring": {
|
||||
"description": "[面板] 系统监控",
|
||||
"fail": "[面板] 系统监控保存失败"
|
||||
},
|
||||
"panel": {
|
||||
"description": "[面板] 命令行",
|
||||
"forDeveloper": "以下命令请在开发者指导下使用",
|
||||
"use": "请使用以下命令",
|
||||
"tool": "命令行工具",
|
||||
"port": "面板端口",
|
||||
"portFail": "获取面板端口失败",
|
||||
"entrance": "面板入口",
|
||||
"update": {
|
||||
"description": "更新 / 修复面板到最新版本",
|
||||
"taskCheck": "当前有任务正在执行,禁止更新",
|
||||
"versionFail": "获取最新版本失败",
|
||||
"fail": "更新失败",
|
||||
"success": "更新成功"
|
||||
},
|
||||
"getInfo": {
|
||||
"description": "重新初始化面板账号信息",
|
||||
"adminGetFail": "获取管理员信息失败",
|
||||
"adminSaveFail": "保存管理员信息失败",
|
||||
"passwordGenerationFail": "生成密码失败",
|
||||
"username": "用户名",
|
||||
"password": "密码",
|
||||
"address": "面板地址"
|
||||
},
|
||||
"getPort": {
|
||||
"description": "获取面板访问端口"
|
||||
},
|
||||
"getEntrance": {
|
||||
"description": "获取面板访问入口"
|
||||
},
|
||||
"deleteEntrance": {
|
||||
"description": "删除面板访问入口",
|
||||
"fail": "删除面板入口失败",
|
||||
"success": "删除面板入口成功"
|
||||
},
|
||||
"cleanTask": {
|
||||
"description": "清理面板运行中和等待中的任务[任务卡住时使用]",
|
||||
"fail": "清理任务失败",
|
||||
"success": "清理任务成功"
|
||||
},
|
||||
"backup": {
|
||||
"description": "备份网站 / MySQL数据库 / PostgreSQL数据库到指定目录并保留指定数量",
|
||||
"paramFail": "参数错误",
|
||||
"start": "开始备份",
|
||||
"backupDirFail": "创建备份目录失败",
|
||||
"targetSite": "目标网站",
|
||||
"siteNotExist": "网站不存在",
|
||||
"backupFail": "备份失败",
|
||||
"backupSuccess": "备份成功",
|
||||
"mysqlBackupFail": "备份MySQL数据库失败",
|
||||
"targetMysql": "目标MySQL数据库",
|
||||
"startExport": "开始导出",
|
||||
"exportFail": "导出失败",
|
||||
"exportSuccess": "导出成功",
|
||||
"startCompress": "开始压缩",
|
||||
"compressFail": "压缩失败",
|
||||
"compressSuccess": "压缩成功",
|
||||
"startMove": "开始移动",
|
||||
"moveFail": "移动失败",
|
||||
"moveSuccess": "移动成功",
|
||||
"databaseGetFail": "获取数据库失败",
|
||||
"databaseNotExist": "数据库不存在",
|
||||
"targetPostgres": "目标PostgreSQL数据库",
|
||||
"cleanBackup": "清理备份",
|
||||
"cleanupFail": "清理失败",
|
||||
"cleanupSuccess": "清理完成",
|
||||
"deleteFail": "删除失败",
|
||||
"success": "备份完成"
|
||||
},
|
||||
"cutoff": {
|
||||
"description": "切割网站日志并保留指定数量",
|
||||
"paramFail": "参数错误",
|
||||
"start": "开始切割",
|
||||
"targetSite": "目标网站",
|
||||
"siteNotExist": "网站不存在",
|
||||
"logNotExist": "日志文件不存在",
|
||||
"backupFail": "备份失败",
|
||||
"clearFail": "清空失败",
|
||||
"cleanupFail": "清理失败",
|
||||
"clearLog": "清理日志",
|
||||
"cutSuccess": "切割成功",
|
||||
"cleanupSuccess": "清理完成",
|
||||
"end": "切割完成"
|
||||
},
|
||||
"installPlugin": {
|
||||
"description": "安装插件",
|
||||
"paramFail": "参数错误",
|
||||
"success": "任务已提交"
|
||||
},
|
||||
"uninstallPlugin": {
|
||||
"description": "卸载插件",
|
||||
"paramFail": "参数错误",
|
||||
"success": "任务已提交"
|
||||
},
|
||||
"updatePlugin": {
|
||||
"description": "更新插件",
|
||||
"paramFail": "参数错误",
|
||||
"success": "任务已提交"
|
||||
},
|
||||
"addSite": {
|
||||
"description": "添加网站[域名和端口用英文逗号分隔]",
|
||||
"paramFail": "参数错误",
|
||||
"siteExist": "网站名已存在",
|
||||
"success": "网站添加成功"
|
||||
},
|
||||
"removeSite": {
|
||||
"description": "删除网站",
|
||||
"paramFail": "参数错误",
|
||||
"siteNotExist": "网站名不存在",
|
||||
"success": "网站删除成功"
|
||||
},
|
||||
"init": {
|
||||
"description": "初始化面板",
|
||||
"exist": "面板已初始化",
|
||||
"success": "初始化成功",
|
||||
"adminFail": "创建管理员失败",
|
||||
"fail": "初始化失败"
|
||||
},
|
||||
"writePlugin": {
|
||||
"description": "写入插件安装状态",
|
||||
"paramFail": "参数错误",
|
||||
"fail": "写入插件安装状态失败",
|
||||
"success": "写入插件安装状态成功"
|
||||
},
|
||||
"deletePlugin": {
|
||||
"description": "移除插件安装状态",
|
||||
"paramFail": "参数错误",
|
||||
"fail": "移除插件安装状态失败",
|
||||
"success": "移除插件安装状态成功"
|
||||
},
|
||||
"writeMysqlPassword": {
|
||||
"description": "写入MySQL root密码",
|
||||
"paramFail": "参数错误",
|
||||
"fail": "写入MySQL root密码失败",
|
||||
"success": "写入MySQL root密码成功"
|
||||
},
|
||||
"writeSite": {
|
||||
"description": "写入网站数据到面板",
|
||||
"paramFail": "参数错误",
|
||||
"siteExist": "网站已存在",
|
||||
"pathNotExist": "网站目录不存在",
|
||||
"fail": "写入网站失败",
|
||||
"success": "写入网站成功"
|
||||
},
|
||||
"deleteSite": {
|
||||
"description": "删除面板网站数据",
|
||||
"paramFail": "参数错误",
|
||||
"fail": "删除网站失败",
|
||||
"success": "删除网站成功"
|
||||
},
|
||||
"getSetting": {
|
||||
"description": "获取面板设置数据",
|
||||
"paramFail": "参数错误"
|
||||
},
|
||||
"writeSetting": {
|
||||
"description": "写入 / 更新面板设置数据",
|
||||
"paramFail": "参数错误",
|
||||
"fail": "写入设置失败",
|
||||
"success": "写入设置成功"
|
||||
},
|
||||
"deleteSetting": {
|
||||
"description": "删除面板设置数据",
|
||||
"paramFail": "参数错误",
|
||||
"fail": "删除设置失败",
|
||||
"success": "删除设置成功"
|
||||
}
|
||||
},
|
||||
"panel:task": {
|
||||
"description": "[面板] 每日任务"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"internal": "系统内部错误",
|
||||
"plugin": {
|
||||
"notExist": "插件不存在",
|
||||
"notInstalled": "插件 :slug 未安装",
|
||||
"dependent": "插件 :slug 需要依赖 :dependency 插件",
|
||||
"incompatible": "插件 :slug 不兼容 :exclude 插件"
|
||||
}
|
||||
},
|
||||
"messages": {
|
||||
"mistake": "错误"
|
||||
},
|
||||
"status": {
|
||||
"upgrade": "面板升级中,请稍后",
|
||||
"maintain": "面板正在运行维护,请稍后",
|
||||
"closed": "面板已关闭",
|
||||
"failed": "面板运行出错,请检查排除或联系支持"
|
||||
}
|
||||
}
|
||||
@@ -4,5 +4,6 @@ APP_DEBUG=false
|
||||
APP_PORT=8888
|
||||
APP_ENTRANCE=/
|
||||
APP_SSL=false
|
||||
APP_LOCALE=zh_CN
|
||||
|
||||
JWT_SECRET=
|
||||
|
||||
+155
-137
@@ -1,177 +1,195 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
"github.com/mholt/acmez/v2"
|
||||
"github.com/mholt/acmez/v2/acme"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
CALetEncrypt = "https://acme-v02.api.letsencrypt.org/directory"
|
||||
CAZeroSSL = "https://acme.zerossl.com/v2/DV90"
|
||||
CAGoogle = "https://dv.acme-v02.api.pki.goog/directory"
|
||||
CABuypass = "https://api.buypass.com/acme/directory"
|
||||
CASSLcom = "https://acme.ssl.com/sslcom-dv-rsa"
|
||||
CALetsEncryptStaging = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
CALetsEncrypt = "https://acme-v02.api.letsencrypt.org/directory"
|
||||
CAZeroSSL = "https://acme.zerossl.com/v2/DV90"
|
||||
CAGoogle = "https://dv.acme-v02.api.pki.goog/directory"
|
||||
CABuypass = "https://api.buypass.com/acme/directory"
|
||||
CASSLcom = "https://acme.ssl.com/sslcom-dv-rsa"
|
||||
)
|
||||
|
||||
type KeyType = certcrypto.KeyType
|
||||
type KeyType string
|
||||
|
||||
const (
|
||||
KeyEC256 = certcrypto.EC256
|
||||
KeyEC384 = certcrypto.EC384
|
||||
KeyRSA2048 = certcrypto.RSA2048
|
||||
KeyRSA3072 = certcrypto.RSA3072
|
||||
KeyRSA4096 = certcrypto.RSA4096
|
||||
KeyEC256 = KeyType("P256")
|
||||
KeyEC384 = KeyType("P384")
|
||||
KeyRSA2048 = KeyType("2048")
|
||||
KeyRSA3072 = KeyType("3072")
|
||||
KeyRSA4096 = KeyType("4096")
|
||||
)
|
||||
|
||||
type domainError struct {
|
||||
Domain string
|
||||
Error error
|
||||
}
|
||||
type EAB = acme.EAB
|
||||
|
||||
type User struct {
|
||||
Email string
|
||||
Registration *registration.Resource
|
||||
Key crypto.PrivateKey
|
||||
}
|
||||
func NewRegisterAccount(ctx context.Context, email, CA string, eab *EAB, keyType KeyType) (*Client, error) {
|
||||
client, err := getClient(CA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (u *User) GetEmail() string {
|
||||
return u.Email
|
||||
}
|
||||
|
||||
func (u *User) GetRegistration() *registration.Resource {
|
||||
return u.Registration
|
||||
}
|
||||
func (u *User) GetPrivateKey() crypto.PrivateKey {
|
||||
return u.Key
|
||||
}
|
||||
|
||||
func GetPrivateKey(priKey crypto.PrivateKey, keyType KeyType) ([]byte, error) {
|
||||
var marshal []byte
|
||||
var block *pem.Block
|
||||
var err error
|
||||
|
||||
switch keyType {
|
||||
case KeyEC256, KeyEC384:
|
||||
key := priKey.(*ecdsa.PrivateKey)
|
||||
marshal, err = x509.MarshalECPrivateKey(key)
|
||||
accountPrivateKey, err := generatePrivateKey(keyType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
account := acme.Account{
|
||||
Contact: []string{"mailto:" + email},
|
||||
TermsOfServiceAgreed: true,
|
||||
PrivateKey: accountPrivateKey,
|
||||
}
|
||||
if eab != nil {
|
||||
err = account.SetExternalAccountBinding(ctx, client.Client, *eab)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
block = &pem.Block{
|
||||
Type: "PRIVATE KEY",
|
||||
Bytes: marshal,
|
||||
}
|
||||
case KeyRSA2048, KeyRSA3072, KeyRSA4096:
|
||||
key := priKey.(*rsa.PrivateKey)
|
||||
marshal = x509.MarshalPKCS1PrivateKey(key)
|
||||
block = &pem.Block{
|
||||
Type: "privateKey",
|
||||
Bytes: marshal,
|
||||
}
|
||||
|
||||
account, err = client.NewAccount(ctx, account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Client{Account: account, zClient: client}, nil
|
||||
}
|
||||
|
||||
func NewPrivateKeyAccount(email string, privateKey string, CA string, eab *EAB) (*Client, error) {
|
||||
client, err := getClient(CA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key, err := parsePrivateKey([]byte(privateKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
account := acme.Account{
|
||||
Contact: []string{"mailto:" + email},
|
||||
TermsOfServiceAgreed: true,
|
||||
PrivateKey: key,
|
||||
}
|
||||
if eab != nil {
|
||||
err = account.SetExternalAccountBinding(context.Background(), client.Client, *eab)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return pem.EncodeToMemory(block), nil
|
||||
account, err = client.GetAccount(context.Background(), account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Client{Account: account, zClient: client}, nil
|
||||
}
|
||||
|
||||
func NewRegisterClient(email string, CA string, keyType certcrypto.KeyType) (*Client, error) {
|
||||
privateKey, err := certcrypto.GeneratePrivateKey(keyType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func parsePrivateKey(key []byte) (crypto.Signer, error) {
|
||||
keyBlockDER, _ := pem.Decode(key)
|
||||
if keyBlockDER == nil {
|
||||
return nil, errors.New("invalid PEM block")
|
||||
}
|
||||
|
||||
user := &User{
|
||||
Email: email,
|
||||
Key: privateKey,
|
||||
}
|
||||
config := lego.NewConfig(user)
|
||||
config.CADirURL = CA
|
||||
config.Certificate.KeyType = keyType
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user.Registration = reg
|
||||
|
||||
acmeClient := &Client{
|
||||
User: user,
|
||||
Client: client,
|
||||
Config: config,
|
||||
if keyBlockDER.Type != "PRIVATE KEY" && !strings.HasSuffix(keyBlockDER.Type, " PRIVATE KEY") {
|
||||
return nil, fmt.Errorf("unknown PEM header %q", keyBlockDER.Type)
|
||||
}
|
||||
|
||||
return acmeClient, nil
|
||||
if parse, err := x509.ParsePKCS1PrivateKey(keyBlockDER.Bytes); err == nil {
|
||||
return parse, nil
|
||||
}
|
||||
|
||||
if parse, err := x509.ParsePKCS8PrivateKey(keyBlockDER.Bytes); err == nil {
|
||||
switch parse.(type) {
|
||||
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
|
||||
return parse.(crypto.Signer), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("found unknown private key type in PKCS#8 wrapping: %T", key)
|
||||
}
|
||||
}
|
||||
|
||||
if parse, err := x509.ParseECPrivateKey(keyBlockDER.Bytes); err == nil {
|
||||
return parse, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("解析私钥失败")
|
||||
}
|
||||
|
||||
func NewRegisterWithExternalAccountBindingClient(email, kid, hmac, CA string, keyType certcrypto.KeyType) (*Client, error) {
|
||||
privateKey, err := certcrypto.GeneratePrivateKey(keyType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func generatePrivateKey(keyType KeyType) (crypto.Signer, error) {
|
||||
switch keyType {
|
||||
case KeyEC256:
|
||||
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
case KeyEC384:
|
||||
return ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
case KeyRSA2048:
|
||||
return rsa.GenerateKey(rand.Reader, 2048)
|
||||
case KeyRSA3072:
|
||||
return rsa.GenerateKey(rand.Reader, 3072)
|
||||
case KeyRSA4096:
|
||||
return rsa.GenerateKey(rand.Reader, 4096)
|
||||
}
|
||||
|
||||
user := &User{
|
||||
Email: email,
|
||||
Key: privateKey,
|
||||
}
|
||||
config := lego.NewConfig(user)
|
||||
config.CADirURL = CA
|
||||
config.Certificate.KeyType = keyType
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reg, err := client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{TermsOfServiceAgreed: true, Kid: kid, HmacEncoded: hmac})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user.Registration = reg
|
||||
|
||||
acmeClient := &Client{
|
||||
User: user,
|
||||
Client: client,
|
||||
Config: config,
|
||||
}
|
||||
|
||||
return acmeClient, nil
|
||||
return nil, errors.New("未知的密钥类型")
|
||||
}
|
||||
|
||||
func NewPrivateKeyClient(email string, privateKey string, CA string, keyType certcrypto.KeyType) (*Client, error) {
|
||||
key, err := certcrypto.ParsePEMPrivateKey([]byte(privateKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func EncodePrivateKey(key crypto.Signer) ([]byte, error) {
|
||||
var pemType string
|
||||
var keyBytes []byte
|
||||
switch key := key.(type) {
|
||||
case *ecdsa.PrivateKey:
|
||||
var err error
|
||||
pemType = "EC"
|
||||
keyBytes, err = x509.MarshalECPrivateKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *rsa.PrivateKey:
|
||||
pemType = "RSA"
|
||||
keyBytes = x509.MarshalPKCS1PrivateKey(key)
|
||||
case ed25519.PrivateKey:
|
||||
var err error
|
||||
pemType = "ED25519"
|
||||
keyBytes, err = x509.MarshalPKCS8PrivateKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("未知的密钥类型 %T", key)
|
||||
}
|
||||
|
||||
user := &User{
|
||||
Email: email,
|
||||
Key: key,
|
||||
}
|
||||
config := lego.NewConfig(user)
|
||||
config.CADirURL = CA
|
||||
config.Certificate.KeyType = keyType
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reg, err := client.Registration.ResolveAccountByKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user.Registration = reg
|
||||
|
||||
acmeClient := &Client{
|
||||
User: user,
|
||||
Client: client,
|
||||
Config: config,
|
||||
}
|
||||
|
||||
return acmeClient, nil
|
||||
pemKey := pem.Block{Type: pemType + " PRIVATE KEY", Bytes: keyBytes}
|
||||
return pem.EncodeToMemory(&pemKey), nil
|
||||
}
|
||||
|
||||
func getClient(CA string) (acmez.Client, error) {
|
||||
logger, err := zap.NewProduction()
|
||||
if err != nil {
|
||||
return acmez.Client{}, err
|
||||
}
|
||||
|
||||
client := acmez.Client{
|
||||
Client: &acme.Client{
|
||||
Directory: CA,
|
||||
HTTPClient: http.DefaultClient,
|
||||
Logger: logger,
|
||||
},
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
+104
-157
@@ -1,192 +1,139 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"time"
|
||||
"context"
|
||||
"sort"
|
||||
|
||||
"github.com/go-acme/lego/v4/acme"
|
||||
"github.com/go-acme/lego/v4/acme/api"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/providers/dns/alidns"
|
||||
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
|
||||
"github.com/go-acme/lego/v4/providers/dns/dnspod"
|
||||
"github.com/go-acme/lego/v4/providers/http/webroot"
|
||||
"github.com/libdns/libdns"
|
||||
"github.com/mholt/acmez/v2"
|
||||
"github.com/mholt/acmez/v2/acme"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
Config *lego.Config
|
||||
Client *lego.Client
|
||||
User *User
|
||||
type Certificate struct {
|
||||
PrivateKey []byte
|
||||
acme.Certificate
|
||||
}
|
||||
|
||||
type DnsType string
|
||||
|
||||
const (
|
||||
DnsPod DnsType = "dnspod"
|
||||
AliYun DnsType = "aliyun"
|
||||
CloudFlare DnsType = "cloudflare"
|
||||
)
|
||||
|
||||
type DNSParam struct {
|
||||
ID string `form:"id" json:"id"`
|
||||
Token string `form:"token" json:"token"`
|
||||
AccessKey string `form:"access_key" json:"access_key"`
|
||||
SecretKey string `form:"secret_key" json:"secret_key"`
|
||||
Email string `form:"email" json:"email"`
|
||||
APIkey string `form:"api_key" json:"api_key"`
|
||||
type Client struct {
|
||||
Account acme.Account
|
||||
zClient acmez.Client
|
||||
// 手动 DNS 所需的信号通道
|
||||
manualDNSSolver
|
||||
}
|
||||
|
||||
// UseDns 使用 DNS 接口验证
|
||||
func (c *Client) UseDns(dnsType DnsType, param DNSParam) error {
|
||||
var p challenge.Provider
|
||||
var err error
|
||||
if dnsType == DnsPod {
|
||||
dnsPodConfig := dnspod.NewDefaultConfig()
|
||||
dnsPodConfig.LoginToken = param.ID + "," + param.Token
|
||||
p, err = dnspod.NewDNSProviderConfig(dnsPodConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (c *Client) UseDns(dnsType DnsType, param DNSParam) {
|
||||
c.zClient.ChallengeSolvers = map[string]acmez.Solver{
|
||||
acme.ChallengeTypeDNS01: dnsSolver{
|
||||
dns: dnsType,
|
||||
param: param,
|
||||
records: &[]libdns.Record{},
|
||||
},
|
||||
}
|
||||
if dnsType == AliYun {
|
||||
aliyunConfig := alidns.NewDefaultConfig()
|
||||
aliyunConfig.SecretKey = param.SecretKey
|
||||
aliyunConfig.APIKey = param.AccessKey
|
||||
p, err = alidns.NewDNSProviderConfig(aliyunConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if dnsType == CloudFlare {
|
||||
cloudflareConfig := cloudflare.NewDefaultConfig()
|
||||
cloudflareConfig.AuthEmail = param.Email
|
||||
cloudflareConfig.AuthToken = param.APIkey
|
||||
p, err = cloudflare.NewDNSProviderConfig(cloudflareConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(3*time.Minute))
|
||||
}
|
||||
|
||||
// UseManualDns 使用手动 DNS 验证
|
||||
func (c *Client) UseManualDns(checkDns ...bool) error {
|
||||
p := &manualDnsProvider{}
|
||||
var err error
|
||||
|
||||
if len(checkDns) > 0 && !checkDns[0] {
|
||||
err = c.Client.Challenge.SetDNS01Provider(p, dns01.DisableCompletePropagationRequirement())
|
||||
} else {
|
||||
err = c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(3*time.Minute))
|
||||
func (c *Client) UseManualDns(total int, check ...bool) {
|
||||
c.controlChan = make(chan struct{})
|
||||
c.dataChan = make(chan any)
|
||||
c.zClient.ChallengeSolvers = map[string]acmez.Solver{
|
||||
acme.ChallengeTypeDNS01: manualDNSSolver{
|
||||
check: len(check) > 0 && check[0],
|
||||
controlChan: c.controlChan,
|
||||
dataChan: c.dataChan,
|
||||
records: &[]DNSRecord{},
|
||||
},
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// UseHTTP 使用 HTTP 验证
|
||||
func (c *Client) UseHTTP(path string) error {
|
||||
httpProvider, err := webroot.NewHTTPProvider(path)
|
||||
if err != nil {
|
||||
return err
|
||||
func (c *Client) UseHTTP(path string) {
|
||||
c.zClient.ChallengeSolvers = map[string]acmez.Solver{
|
||||
acme.ChallengeTypeHTTP01: httpSolver{
|
||||
path: path,
|
||||
},
|
||||
}
|
||||
|
||||
err = c.Client.Challenge.SetHTTP01Provider(httpProvider)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ObtainSSL 签发 SSL 证书
|
||||
func (c *Client) ObtainSSL(domains []string) (certificate.Resource, error) {
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: domains,
|
||||
Bundle: true,
|
||||
MustStaple: false,
|
||||
}
|
||||
|
||||
certificates, err := c.Client.Certificate.Obtain(request)
|
||||
func (c *Client) ObtainSSL(ctx context.Context, domains []string, keyType KeyType) (Certificate, error) {
|
||||
certPrivateKey, err := generatePrivateKey(keyType)
|
||||
if err != nil {
|
||||
return certificate.Resource{}, err
|
||||
return Certificate{}, err
|
||||
}
|
||||
pemPrivateKey, err := EncodePrivateKey(certPrivateKey)
|
||||
if err != nil {
|
||||
return Certificate{}, err
|
||||
}
|
||||
|
||||
return *certificates, nil
|
||||
certs, err := c.zClient.ObtainCertificateForSANs(ctx, c.Account, certPrivateKey, domains)
|
||||
if err != nil {
|
||||
return Certificate{}, err
|
||||
}
|
||||
|
||||
cert := c.selectPreferredChain(certs)
|
||||
return Certificate{PrivateKey: pemPrivateKey, Certificate: cert}, nil
|
||||
}
|
||||
|
||||
// ObtainSSLManual 手动验证 SSL 证书
|
||||
func (c *Client) ObtainSSLManual() (Certificate, error) {
|
||||
// 发送信号,开始验证
|
||||
c.controlChan <- struct{}{}
|
||||
// 等待验证完成
|
||||
data := <-c.dataChan
|
||||
|
||||
if err, ok := data.(error); ok {
|
||||
return Certificate{}, err
|
||||
}
|
||||
|
||||
return data.(Certificate), nil
|
||||
}
|
||||
|
||||
// RenewSSL 续签 SSL 证书
|
||||
func (c *Client) RenewSSL(certUrl string) (certificate.Resource, error) {
|
||||
certificates, err := c.Client.Certificate.Get(certUrl, true)
|
||||
func (c *Client) RenewSSL(ctx context.Context, certUrl string, domains []string, keyType KeyType) (Certificate, error) {
|
||||
_, err := c.zClient.GetCertificateChain(ctx, c.Account, certUrl)
|
||||
if err != nil {
|
||||
return certificate.Resource{}, err
|
||||
return Certificate{}, err
|
||||
}
|
||||
|
||||
certificates, err = c.Client.Certificate.RenewWithOptions(*certificates, &certificate.RenewOptions{
|
||||
Bundle: true,
|
||||
MustStaple: false,
|
||||
return c.ObtainSSL(ctx, domains, keyType)
|
||||
}
|
||||
|
||||
// GetDNSRecords 获取 DNS 解析(手动设置)
|
||||
func (c *Client) GetDNSRecords(ctx context.Context, domains []string, keyType KeyType) ([]DNSRecord, error) {
|
||||
go func(ctx context.Context, domains []string, keyType KeyType) {
|
||||
certs, err := c.ObtainSSL(ctx, domains, keyType)
|
||||
// 将证书和错误信息发送到 dataChan
|
||||
if err != nil {
|
||||
c.dataChan <- err
|
||||
return
|
||||
}
|
||||
c.dataChan <- certs
|
||||
}(ctx, domains, keyType)
|
||||
|
||||
// 这里要少一次循环,因为需要卡住最后一次的 dataChan,等待手动 DNS 验证完成
|
||||
for i := 1; i < len(domains); i++ {
|
||||
<-c.dataChan
|
||||
c.controlChan <- struct{}{}
|
||||
}
|
||||
|
||||
// 因为上面少了一次循环,所以这里接收到的即为完整的 DNS 记录切片
|
||||
data := <-c.dataChan
|
||||
if err, ok := data.(error); ok {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data.([]DNSRecord), nil
|
||||
}
|
||||
|
||||
func (c *Client) selectPreferredChain(certChains []acme.Certificate) acme.Certificate {
|
||||
if len(certChains) == 1 {
|
||||
return certChains[0]
|
||||
}
|
||||
|
||||
sort.Slice(certChains, func(i, j int) bool {
|
||||
return len(certChains[i].ChainPEM) < len(certChains[j].ChainPEM)
|
||||
})
|
||||
if err != nil {
|
||||
return certificate.Resource{}, err
|
||||
}
|
||||
|
||||
return *certificates, nil
|
||||
}
|
||||
|
||||
// GetDNSResolve 获取 DNS 解析(手动设置)
|
||||
func (c *Client) GetDNSResolve(domains []string) (map[string]Resolve, error) {
|
||||
core, err := api.New(c.Config.HTTPClient, c.Config.UserAgent, c.Config.CADirURL, c.User.Registration.URI, c.User.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
order, err := core.Orders.New(domains)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resolves := make(map[string]Resolve)
|
||||
resChan, errChan := make(chan acme.Authorization), make(chan domainError)
|
||||
for _, authzURL := range order.Authorizations {
|
||||
go func(authzURL string) {
|
||||
authz, err := core.Authorizations.Get(authzURL)
|
||||
if err != nil {
|
||||
errChan <- domainError{Domain: authz.Identifier.Value, Error: err}
|
||||
return
|
||||
}
|
||||
resChan <- authz
|
||||
}(authzURL)
|
||||
}
|
||||
|
||||
var responses []acme.Authorization
|
||||
for i := 0; i < len(order.Authorizations); i++ {
|
||||
select {
|
||||
case res := <-resChan:
|
||||
responses = append(responses, res)
|
||||
case err := <-errChan:
|
||||
resolves[err.Domain] = Resolve{Err: err.Error.Error()}
|
||||
}
|
||||
}
|
||||
close(resChan)
|
||||
close(errChan)
|
||||
|
||||
for _, auth := range responses {
|
||||
domain := challenge.GetTargetedDomain(auth)
|
||||
acmeChallenge, err := challenge.FindChallenge(challenge.DNS01, auth)
|
||||
if err != nil {
|
||||
resolves[domain] = Resolve{Err: err.Error()}
|
||||
continue
|
||||
}
|
||||
keyAuth, err := core.GetKeyAuthorization(acmeChallenge.Token)
|
||||
if err != nil {
|
||||
resolves[domain] = Resolve{Err: err.Error()}
|
||||
continue
|
||||
}
|
||||
challengeInfo := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
resolves[domain] = Resolve{
|
||||
Key: challengeInfo.FQDN,
|
||||
Value: challengeInfo.Value,
|
||||
}
|
||||
}
|
||||
|
||||
return resolves, nil
|
||||
return certChains[0]
|
||||
}
|
||||
|
||||
+13
-11
@@ -1,7 +1,7 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
@@ -16,24 +16,26 @@ func TestClientTestSuite(t *testing.T) {
|
||||
}
|
||||
|
||||
func (s *ClientTestSuite) TestObtainSSL() {
|
||||
client, err := NewRegisterClient("ci@haozi.net", "https://acme-staging-v02.api.letsencrypt.org/directory", KeyEC256)
|
||||
ctx := context.Background()
|
||||
client, err := NewRegisterAccount(ctx, "ci@haozi.net", CALetsEncryptStaging, nil, KeyEC256)
|
||||
s.Nil(err)
|
||||
|
||||
err = client.UseDns(DnsPod, DNSParam{
|
||||
ID: "xxx",
|
||||
Token: "xxx",
|
||||
client.UseDns(DnsPod, DNSParam{
|
||||
ID: "123456",
|
||||
Token: "654321",
|
||||
})
|
||||
s.Nil(err)
|
||||
|
||||
err = client.UseManualDns(false)
|
||||
s.Nil(err)
|
||||
/*client.UseManualDns(2)
|
||||
|
||||
resolves, err := client.GetDNSResolve([]string{"haozi.dev"})
|
||||
resolves, err := client.GetDNSRecords(ctx, []string{"*.haozi.net", "haozi.net"}, KeyEC256)
|
||||
debug.Dump(resolves)
|
||||
s.Nil(err)
|
||||
s.NotNil(resolves)
|
||||
|
||||
ssl, err := client.ObtainSSL([]string{"haozi.dev"})
|
||||
fmt.Println(err.Error())
|
||||
time.Sleep(2 * time.Minute)
|
||||
|
||||
ssl, err := client.ObtainSSLManual()*/
|
||||
ssl, err := client.ObtainSSL(ctx, []string{"*.haozi.net", "haozi.net"}, KeyEC256)
|
||||
s.Error(err)
|
||||
s.NotNil(ssl)
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package acme
|
||||
|
||||
type Resolve struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
Err string `json:"err"`
|
||||
}
|
||||
|
||||
type manualDnsProvider struct {
|
||||
Resolve *Resolve
|
||||
}
|
||||
|
||||
func (p *manualDnsProvider) Present(domain, token, keyAuth string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *manualDnsProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/libdns/alidns"
|
||||
"github.com/libdns/cloudflare"
|
||||
"github.com/libdns/dnspod"
|
||||
"github.com/libdns/libdns"
|
||||
"github.com/mholt/acmez/v2/acme"
|
||||
"golang.org/x/net/publicsuffix"
|
||||
)
|
||||
|
||||
type httpSolver struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func (s httpSolver) Present(_ context.Context, challenge acme.Challenge) error {
|
||||
var err error
|
||||
if s.path == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
challengeFilePath := filepath.Join(s.path, challenge.HTTP01ResourcePath())
|
||||
err = os.MkdirAll(filepath.Dir(challengeFilePath), 0o755)
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法在网站目录创建 HTTP 挑战所需的目录: %w", err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(challengeFilePath, []byte(challenge.KeyAuthorization), 0o644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法在网站目录创建 HTTP 挑战所需的文件: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp cleans up the HTTP server if it is the last one to finish.
|
||||
func (s httpSolver) CleanUp(_ context.Context, challenge acme.Challenge) error {
|
||||
if s.path == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := os.Remove(filepath.Join(s.path, challenge.HTTP01ResourcePath()))
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法删除 HTTP 挑战文件: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type dnsSolver struct {
|
||||
dns DnsType
|
||||
param DNSParam
|
||||
records *[]libdns.Record
|
||||
}
|
||||
|
||||
func (s dnsSolver) Present(ctx context.Context, challenge acme.Challenge) error {
|
||||
dnsName := challenge.DNS01TXTRecordName()
|
||||
keyAuth := challenge.DNS01KeyAuthorization()
|
||||
provider, err := s.getDNSProvider()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取 DNS 提供商失败: %w", err)
|
||||
}
|
||||
zone, err := publicsuffix.EffectiveTLDPlusOne(dnsName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取域名 %q 的顶级域失败: %w", dnsName, err)
|
||||
}
|
||||
|
||||
rec := libdns.Record{
|
||||
Type: "TXT",
|
||||
Name: libdns.RelativeName(dnsName+".", zone+"."),
|
||||
Value: keyAuth,
|
||||
}
|
||||
|
||||
results, err := provider.AppendRecords(ctx, zone+".", []libdns.Record{rec})
|
||||
if err != nil {
|
||||
return fmt.Errorf("域名 %q 添加临时记录 %q 失败: %w", zone, dnsName, err)
|
||||
}
|
||||
if len(results) != 1 {
|
||||
return fmt.Errorf("预期添加 1 条记录,但实际添加了 %d 条记录", len(results))
|
||||
}
|
||||
|
||||
s.records = &results
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s dnsSolver) CleanUp(ctx context.Context, challenge acme.Challenge) error {
|
||||
dnsName := challenge.DNS01TXTRecordName()
|
||||
provider, err := s.getDNSProvider()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取 DNS 提供商失败: %w", err)
|
||||
}
|
||||
zone, err := publicsuffix.EffectiveTLDPlusOne(dnsName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取域名 %q 的顶级域失败: %w", dnsName, err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
_, err = provider.DeleteRecords(ctx, zone+".", *s.records)
|
||||
if err != nil {
|
||||
return fmt.Errorf("域名 %q 删除临时记录 %q 失败: %w", zone, dnsName, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s dnsSolver) getDNSProvider() (DNSProvider, error) {
|
||||
var dns DNSProvider
|
||||
|
||||
switch s.dns {
|
||||
case DnsPod:
|
||||
dns = &dnspod.Provider{
|
||||
APIToken: s.param.ID + "," + s.param.Token,
|
||||
}
|
||||
case AliYun:
|
||||
dns = &alidns.Provider{
|
||||
AccKeyID: s.param.AccessKey,
|
||||
AccKeySecret: s.param.SecretKey,
|
||||
}
|
||||
case CloudFlare:
|
||||
dns = &cloudflare.Provider{
|
||||
APIToken: s.param.APIkey,
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("未知的 DNS 提供商 %q", s.dns)
|
||||
}
|
||||
|
||||
return dns, nil
|
||||
}
|
||||
|
||||
type DnsType string
|
||||
|
||||
const (
|
||||
DnsPod DnsType = "dnspod"
|
||||
AliYun DnsType = "aliyun"
|
||||
CloudFlare DnsType = "cloudflare"
|
||||
)
|
||||
|
||||
type DNSParam struct {
|
||||
ID string `form:"id" json:"id"`
|
||||
Token string `form:"token" json:"token"`
|
||||
AccessKey string `form:"access_key" json:"access_key"`
|
||||
SecretKey string `form:"secret_key" json:"secret_key"`
|
||||
APIkey string `form:"api_key" json:"api_key"`
|
||||
}
|
||||
|
||||
type DNSProvider interface {
|
||||
libdns.RecordAppender
|
||||
libdns.RecordDeleter
|
||||
}
|
||||
|
||||
type manualDNSSolver struct {
|
||||
check bool
|
||||
controlChan chan struct{}
|
||||
dataChan chan any
|
||||
records *[]DNSRecord
|
||||
}
|
||||
|
||||
func (s manualDNSSolver) Present(ctx context.Context, challenge acme.Challenge) error {
|
||||
dnsName := challenge.DNS01TXTRecordName()
|
||||
keyAuth := challenge.DNS01KeyAuthorization()
|
||||
|
||||
// 追加记录到 records 中
|
||||
*s.records = append(*s.records, DNSRecord{
|
||||
Key: dnsName,
|
||||
Value: keyAuth,
|
||||
})
|
||||
s.dataChan <- *s.records
|
||||
|
||||
_, cancel := context.WithTimeout(ctx, 2*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
// 等待信号以继续
|
||||
<-s.controlChan
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s manualDNSSolver) CleanUp(_ context.Context, _ acme.Challenge) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type DNSRecord struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
// Package str 字符串辅助方法
|
||||
package str
|
||||
|
||||
import (
|
||||
"github.com/gertd/go-pluralize"
|
||||
"github.com/iancoleman/strcase"
|
||||
)
|
||||
|
||||
// Plural 转为复数 user -> users
|
||||
func Plural(word string) string {
|
||||
return pluralize.NewClient().Plural(word)
|
||||
}
|
||||
|
||||
// Singular 转为单数 users -> user
|
||||
func Singular(word string) string {
|
||||
return pluralize.NewClient().Singular(word)
|
||||
}
|
||||
|
||||
// Snake 转为 snake_case,如 TopicComment -> topic_comment
|
||||
func Snake(s string) string {
|
||||
return strcase.ToSnake(s)
|
||||
}
|
||||
|
||||
// Camel 转为 CamelCase,如 topic_comment -> TopicComment
|
||||
func Camel(s string) string {
|
||||
return strcase.ToCamel(s)
|
||||
}
|
||||
|
||||
// LowerCamel 转为 lowerCamelCase,如 TopicComment -> topicComment
|
||||
func LowerCamel(s string) string {
|
||||
return strcase.ToLowerCamel(s)
|
||||
}
|
||||
|
||||
func ContainsString(arr []string, str string) bool {
|
||||
for _, s := range arr {
|
||||
if s == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package str
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type StrTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func TestStrTestSuite(t *testing.T) {
|
||||
suite.Run(t, &StrTestSuite{})
|
||||
}
|
||||
|
||||
func (s *StrTestSuite) TestPlural() {
|
||||
s.Equal("users", Plural("user"))
|
||||
s.Equal("users", Plural("users"))
|
||||
}
|
||||
|
||||
func (s *StrTestSuite) TestSingular() {
|
||||
s.Equal("user", Singular("users"))
|
||||
s.Equal("user", Singular("user"))
|
||||
}
|
||||
|
||||
func (s *StrTestSuite) TestSnake() {
|
||||
s.Equal("topic_comment", Snake("TopicComment"))
|
||||
s.Equal("topic_comment", Snake("topic_comment"))
|
||||
}
|
||||
|
||||
func (s *StrTestSuite) TestCamel() {
|
||||
s.Equal("TopicComment", Camel("topic_comment"))
|
||||
s.Equal("TopicComment", Camel("TopicComment"))
|
||||
}
|
||||
|
||||
func (s *StrTestSuite) TestLowerCamel() {
|
||||
s.Equal("topicComment", LowerCamel("topic_comment"))
|
||||
s.Equal("topicComment", LowerCamel("TopicComment"))
|
||||
}
|
||||
|
||||
func (s *StrTestSuite) TestContainsString() {
|
||||
s.True(ContainsString([]string{"a", "b", "c"}, "a"))
|
||||
s.False(ContainsString([]string{"a", "b", "c"}, "d"))
|
||||
}
|
||||
+46
-1
@@ -7,12 +7,14 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/goravel/framework/support"
|
||||
"github.com/goravel/framework/support/env"
|
||||
"github.com/mholt/archiver/v3"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
// Write 写入文件
|
||||
@@ -111,7 +113,12 @@ func Mkdir(path string, permission os.FileMode) error {
|
||||
|
||||
// Chmod 修改文件/目录权限
|
||||
func Chmod(path string, permission os.FileMode) error {
|
||||
return os.Chmod(path, permission)
|
||||
if env.IsWindows() {
|
||||
return errors.New("chmod is not supported on Windows")
|
||||
}
|
||||
|
||||
cmd := exec.Command("chmod", "-R", permission.String(), path)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// Chown 修改文件或目录所有者
|
||||
@@ -258,3 +265,41 @@ func TempDir(prefix string) (string, error) {
|
||||
func TempFile(prefix string) (*os.File, error) {
|
||||
return os.CreateTemp("", prefix)
|
||||
}
|
||||
|
||||
// IsSymlink 判读是否为软链接
|
||||
func IsSymlink(mode os.FileMode) bool {
|
||||
return mode&os.ModeSymlink != 0
|
||||
}
|
||||
|
||||
// IsHidden 判断是否为隐藏文件
|
||||
func IsHidden(path string) bool {
|
||||
_, file := filepath.Split(path)
|
||||
return strings.HasPrefix(file, ".")
|
||||
}
|
||||
|
||||
// GetSymlink 获取软链接目标
|
||||
func GetSymlink(path string) string {
|
||||
linkPath, err := os.Readlink(path)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return linkPath
|
||||
}
|
||||
|
||||
// GetUser 通过 uid 获取用户名
|
||||
func GetUser(uid uint32) string {
|
||||
usr, err := user.LookupId(cast.ToString(uid))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return usr.Username
|
||||
}
|
||||
|
||||
// GetGroup 通过 gid 获取组名
|
||||
func GetGroup(gid uint32) string {
|
||||
usr, err := user.LookupGroupId(cast.ToString(gid))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return usr.Name
|
||||
}
|
||||
|
||||
@@ -175,8 +175,8 @@ func (s *SystemHelperTestSuite) TestChmod() {
|
||||
err := Write(filePath.Name(), "test data", 0644)
|
||||
s.Nil(err)
|
||||
|
||||
s.Nil(Chmod(filePath.Name(), 0755))
|
||||
s.Nil(filePath.Close())
|
||||
s.Nil(Chmod(filePath.Name(), 0755))
|
||||
s.Nil(Remove(filePath.Name()))
|
||||
}
|
||||
|
||||
|
||||
+7
-12
@@ -9,10 +9,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/gookit/color"
|
||||
"github.com/goravel/framework/support/carbon"
|
||||
"github.com/goravel/framework/support/env"
|
||||
"github.com/imroc/req/v3"
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
"github.com/shirou/gopsutil/disk"
|
||||
"github.com/shirou/gopsutil/host"
|
||||
@@ -407,7 +407,6 @@ func UpdatePanel(panelInfo PanelInfo) error {
|
||||
|
||||
color.Greenln("前置检查...")
|
||||
if Exists("/tmp/panel-storage.zip") || Exists("/tmp/panel.conf.bak") {
|
||||
color.Redln("检测到 /tmp 存在临时文件,可能是上次更新失败导致的,请谨慎排除后重试")
|
||||
return errors.New("检测到 /tmp 存在临时文件,可能是上次更新失败导致的,请谨慎排除后重试")
|
||||
}
|
||||
|
||||
@@ -426,7 +425,6 @@ func UpdatePanel(panelInfo PanelInfo) error {
|
||||
return err
|
||||
}
|
||||
if !Exists("/tmp/panel-storage.zip") || !Exists("/tmp/panel.conf.bak") {
|
||||
color.Redln("备份面板数据失败")
|
||||
return errors.New("备份面板数据失败")
|
||||
}
|
||||
color.Greenln("备份完成")
|
||||
@@ -487,7 +485,6 @@ func UpdatePanel(panelInfo PanelInfo) error {
|
||||
return err
|
||||
}
|
||||
if !Exists("/www/panel/storage/panel.db") || !Exists("/www/panel/panel.conf") {
|
||||
color.Redln("恢复面板数据失败")
|
||||
return errors.New("恢复面板数据失败")
|
||||
}
|
||||
color.Greenln("恢复完成")
|
||||
@@ -529,13 +526,12 @@ func RestartPanel() {
|
||||
|
||||
// IsChina 是否中国大陆
|
||||
func IsChina() bool {
|
||||
client := req.C()
|
||||
client := resty.New()
|
||||
client.SetTimeout(5 * time.Second)
|
||||
client.SetCommonRetryCount(2)
|
||||
client.ImpersonateSafari()
|
||||
client.SetRetryCount(2)
|
||||
|
||||
resp, err := client.R().Get("https://www.cloudflare-cn.com/cdn-cgi/trace")
|
||||
if err != nil || !resp.IsSuccessState() {
|
||||
if err != nil || !resp.IsSuccess() {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -548,13 +544,12 @@ func IsChina() bool {
|
||||
|
||||
// GetPublicIP 获取公网IP
|
||||
func GetPublicIP() (string, error) {
|
||||
client := req.C()
|
||||
client := resty.New()
|
||||
client.SetTimeout(5 * time.Second)
|
||||
client.SetCommonRetryCount(2)
|
||||
client.ImpersonateSafari()
|
||||
client.SetRetryCount(2)
|
||||
|
||||
resp, err := client.R().Get("https://www.cloudflare-cn.com/cdn-cgi/trace")
|
||||
if err != nil || !resp.IsSuccessState() {
|
||||
if err != nil || !resp.IsSuccess() {
|
||||
return "", errors.New("获取公网IP失败")
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ func Api() {
|
||||
r.Post("obtain", certController.Obtain)
|
||||
r.Post("renew", certController.Renew)
|
||||
r.Post("manualDNS", certController.ManualDNS)
|
||||
r.Post("deploy", certController.Deploy)
|
||||
})
|
||||
r.Prefix("plugin").Middleware(middleware.Jwt()).Group(func(r route.Router) {
|
||||
pluginController := controllers.NewPluginController()
|
||||
|
||||
+86
-56
@@ -25,64 +25,94 @@ func Plugin() {
|
||||
route.Post("clearErrorLog", openRestyController.ClearErrorLog)
|
||||
})
|
||||
r.Prefix("mysql57").Group(func(route route.Router) {
|
||||
mysql57Controller := plugins.NewMysql57Controller()
|
||||
route.Get("status", mysql57Controller.Status)
|
||||
route.Post("reload", mysql57Controller.Reload)
|
||||
route.Post("start", mysql57Controller.Start)
|
||||
route.Post("stop", mysql57Controller.Stop)
|
||||
route.Post("restart", mysql57Controller.Restart)
|
||||
route.Get("load", mysql57Controller.Load)
|
||||
route.Get("config", mysql57Controller.GetConfig)
|
||||
route.Post("config", mysql57Controller.SaveConfig)
|
||||
route.Get("errorLog", mysql57Controller.ErrorLog)
|
||||
route.Post("clearErrorLog", mysql57Controller.ClearErrorLog)
|
||||
route.Get("slowLog", mysql57Controller.SlowLog)
|
||||
route.Post("clearSlowLog", mysql57Controller.ClearSlowLog)
|
||||
route.Get("rootPassword", mysql57Controller.GetRootPassword)
|
||||
route.Post("rootPassword", mysql57Controller.SetRootPassword)
|
||||
route.Get("databases", mysql57Controller.DatabaseList)
|
||||
route.Post("databases", mysql57Controller.AddDatabase)
|
||||
route.Delete("databases", mysql57Controller.DeleteDatabase)
|
||||
route.Get("backups", mysql57Controller.BackupList)
|
||||
route.Post("backups", mysql57Controller.CreateBackup)
|
||||
route.Put("backups", mysql57Controller.UploadBackup)
|
||||
route.Delete("backups", mysql57Controller.DeleteBackup)
|
||||
route.Post("backups/restore", mysql57Controller.RestoreBackup)
|
||||
route.Get("users", mysql57Controller.UserList)
|
||||
route.Post("users", mysql57Controller.AddUser)
|
||||
route.Delete("users", mysql57Controller.DeleteUser)
|
||||
route.Post("users/password", mysql57Controller.SetUserPassword)
|
||||
route.Post("users/privileges", mysql57Controller.SetUserPrivileges)
|
||||
mySQLController := plugins.NewMySQLController()
|
||||
route.Get("status", mySQLController.Status)
|
||||
route.Post("reload", mySQLController.Reload)
|
||||
route.Post("start", mySQLController.Start)
|
||||
route.Post("stop", mySQLController.Stop)
|
||||
route.Post("restart", mySQLController.Restart)
|
||||
route.Get("load", mySQLController.Load)
|
||||
route.Get("config", mySQLController.GetConfig)
|
||||
route.Post("config", mySQLController.SaveConfig)
|
||||
route.Get("errorLog", mySQLController.ErrorLog)
|
||||
route.Post("clearErrorLog", mySQLController.ClearErrorLog)
|
||||
route.Get("slowLog", mySQLController.SlowLog)
|
||||
route.Post("clearSlowLog", mySQLController.ClearSlowLog)
|
||||
route.Get("rootPassword", mySQLController.GetRootPassword)
|
||||
route.Post("rootPassword", mySQLController.SetRootPassword)
|
||||
route.Get("databases", mySQLController.DatabaseList)
|
||||
route.Post("databases", mySQLController.AddDatabase)
|
||||
route.Delete("databases", mySQLController.DeleteDatabase)
|
||||
route.Get("backups", mySQLController.BackupList)
|
||||
route.Post("backups", mySQLController.CreateBackup)
|
||||
route.Put("backups", mySQLController.UploadBackup)
|
||||
route.Delete("backups", mySQLController.DeleteBackup)
|
||||
route.Post("backups/restore", mySQLController.RestoreBackup)
|
||||
route.Get("users", mySQLController.UserList)
|
||||
route.Post("users", mySQLController.AddUser)
|
||||
route.Delete("users", mySQLController.DeleteUser)
|
||||
route.Post("users/password", mySQLController.SetUserPassword)
|
||||
route.Post("users/privileges", mySQLController.SetUserPrivileges)
|
||||
})
|
||||
r.Prefix("mysql80").Group(func(route route.Router) {
|
||||
mysql80Controller := plugins.NewMysql80Controller()
|
||||
route.Get("status", mysql80Controller.Status)
|
||||
route.Post("reload", mysql80Controller.Reload)
|
||||
route.Post("start", mysql80Controller.Start)
|
||||
route.Post("stop", mysql80Controller.Stop)
|
||||
route.Post("restart", mysql80Controller.Restart)
|
||||
route.Get("load", mysql80Controller.Load)
|
||||
route.Get("config", mysql80Controller.GetConfig)
|
||||
route.Post("config", mysql80Controller.SaveConfig)
|
||||
route.Get("errorLog", mysql80Controller.ErrorLog)
|
||||
route.Post("clearErrorLog", mysql80Controller.ClearErrorLog)
|
||||
route.Get("slowLog", mysql80Controller.SlowLog)
|
||||
route.Post("clearSlowLog", mysql80Controller.ClearSlowLog)
|
||||
route.Get("rootPassword", mysql80Controller.GetRootPassword)
|
||||
route.Post("rootPassword", mysql80Controller.SetRootPassword)
|
||||
route.Get("databases", mysql80Controller.DatabaseList)
|
||||
route.Post("databases", mysql80Controller.AddDatabase)
|
||||
route.Delete("databases", mysql80Controller.DeleteDatabase)
|
||||
route.Get("backups", mysql80Controller.BackupList)
|
||||
route.Post("backups", mysql80Controller.CreateBackup)
|
||||
route.Put("backups", mysql80Controller.UploadBackup)
|
||||
route.Delete("backups", mysql80Controller.DeleteBackup)
|
||||
route.Post("backups/restore", mysql80Controller.RestoreBackup)
|
||||
route.Get("users", mysql80Controller.UserList)
|
||||
route.Post("users", mysql80Controller.AddUser)
|
||||
route.Delete("users", mysql80Controller.DeleteUser)
|
||||
route.Post("users/password", mysql80Controller.SetUserPassword)
|
||||
route.Post("users/privileges", mysql80Controller.SetUserPrivileges)
|
||||
mySQLController := plugins.NewMySQLController()
|
||||
route.Get("status", mySQLController.Status)
|
||||
route.Post("reload", mySQLController.Reload)
|
||||
route.Post("start", mySQLController.Start)
|
||||
route.Post("stop", mySQLController.Stop)
|
||||
route.Post("restart", mySQLController.Restart)
|
||||
route.Get("load", mySQLController.Load)
|
||||
route.Get("config", mySQLController.GetConfig)
|
||||
route.Post("config", mySQLController.SaveConfig)
|
||||
route.Get("errorLog", mySQLController.ErrorLog)
|
||||
route.Post("clearErrorLog", mySQLController.ClearErrorLog)
|
||||
route.Get("slowLog", mySQLController.SlowLog)
|
||||
route.Post("clearSlowLog", mySQLController.ClearSlowLog)
|
||||
route.Get("rootPassword", mySQLController.GetRootPassword)
|
||||
route.Post("rootPassword", mySQLController.SetRootPassword)
|
||||
route.Get("databases", mySQLController.DatabaseList)
|
||||
route.Post("databases", mySQLController.AddDatabase)
|
||||
route.Delete("databases", mySQLController.DeleteDatabase)
|
||||
route.Get("backups", mySQLController.BackupList)
|
||||
route.Post("backups", mySQLController.CreateBackup)
|
||||
route.Put("backups", mySQLController.UploadBackup)
|
||||
route.Delete("backups", mySQLController.DeleteBackup)
|
||||
route.Post("backups/restore", mySQLController.RestoreBackup)
|
||||
route.Get("users", mySQLController.UserList)
|
||||
route.Post("users", mySQLController.AddUser)
|
||||
route.Delete("users", mySQLController.DeleteUser)
|
||||
route.Post("users/password", mySQLController.SetUserPassword)
|
||||
route.Post("users/privileges", mySQLController.SetUserPrivileges)
|
||||
})
|
||||
r.Prefix("mysql84").Group(func(route route.Router) {
|
||||
mySQLController := plugins.NewMySQLController()
|
||||
route.Get("status", mySQLController.Status)
|
||||
route.Post("reload", mySQLController.Reload)
|
||||
route.Post("start", mySQLController.Start)
|
||||
route.Post("stop", mySQLController.Stop)
|
||||
route.Post("restart", mySQLController.Restart)
|
||||
route.Get("load", mySQLController.Load)
|
||||
route.Get("config", mySQLController.GetConfig)
|
||||
route.Post("config", mySQLController.SaveConfig)
|
||||
route.Get("errorLog", mySQLController.ErrorLog)
|
||||
route.Post("clearErrorLog", mySQLController.ClearErrorLog)
|
||||
route.Get("slowLog", mySQLController.SlowLog)
|
||||
route.Post("clearSlowLog", mySQLController.ClearSlowLog)
|
||||
route.Get("rootPassword", mySQLController.GetRootPassword)
|
||||
route.Post("rootPassword", mySQLController.SetRootPassword)
|
||||
route.Get("databases", mySQLController.DatabaseList)
|
||||
route.Post("databases", mySQLController.AddDatabase)
|
||||
route.Delete("databases", mySQLController.DeleteDatabase)
|
||||
route.Get("backups", mySQLController.BackupList)
|
||||
route.Post("backups", mySQLController.CreateBackup)
|
||||
route.Put("backups", mySQLController.UploadBackup)
|
||||
route.Delete("backups", mySQLController.DeleteBackup)
|
||||
route.Post("backups/restore", mySQLController.RestoreBackup)
|
||||
route.Get("users", mySQLController.UserList)
|
||||
route.Post("users", mySQLController.AddUser)
|
||||
route.Delete("users", mySQLController.DeleteUser)
|
||||
route.Post("users/password", mySQLController.SetUserPassword)
|
||||
route.Post("users/privileges", mySQLController.SetUserPrivileges)
|
||||
})
|
||||
r.Prefix("postgresql15").Group(func(route route.Router) {
|
||||
postgresql15Controller := plugins.NewPostgresql15Controller()
|
||||
|
||||
@@ -111,7 +111,7 @@ Prepare_System() {
|
||||
fi
|
||||
|
||||
# 自动开启 BBR
|
||||
isBBRSupported=$(sysctl net.ipv4.tcp_available_congestion_control | grep -c bbr)
|
||||
isBBRSupported=$(ls -l /lib/modules/*/kernel/net/ipv4/tcp_bbr.ko.xz | grep -c bbr)
|
||||
if [ "${isBBRSupported}" != "0" ]; then
|
||||
qdisc=$(sysctl net.core.default_qdisc | awk '{print $3}')
|
||||
echo "net.core.default_qdisc=${qdisc}" >> /etc/sysctl.conf
|
||||
@@ -140,21 +140,21 @@ Prepare_System() {
|
||||
sed -i 's|^metalink|#metalink|' /etc/yum.repos.d/epel*
|
||||
dnf makecache -y
|
||||
fi
|
||||
dnf config-manager --set-enabled PowerTools
|
||||
# EL 8
|
||||
dnf config-manager --set-enabled powertools
|
||||
dnf config-manager --set-enabled CRB
|
||||
dnf config-manager --set-enabled Crb
|
||||
# EL 9
|
||||
dnf config-manager --set-enabled crb
|
||||
# Rocky Linux
|
||||
/usr/bin/crb enable
|
||||
dnf makecache -y
|
||||
dnf install -y curl wget zip unzip tar git jq git-core dos2unix podman
|
||||
dnf install -y curl wget zip unzip tar p7zip p7zip-plugins git jq git-core dos2unix podman rsyslog
|
||||
elif [ "${OS}" == "debian" ]; then
|
||||
if ${inChina}; then
|
||||
sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list
|
||||
sed -i 's/security.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list
|
||||
fi
|
||||
apt-get update -y
|
||||
apt-get install -y curl wget zip unzip tar git jq git dos2unix podman rsyslog
|
||||
apt-get install -y curl wget zip unzip tar p7zip p7zip-full git jq git dos2unix podman rsyslog
|
||||
fi
|
||||
if [ "$?" != "0" ]; then
|
||||
echo -e $HR
|
||||
@@ -162,7 +162,9 @@ Prepare_System() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
systemctl enable rsyslog
|
||||
systemctl enable podman
|
||||
systemctl start rsyslog
|
||||
systemctl start podman
|
||||
if [ "$?" != "0" ]; then
|
||||
echo -e $HR
|
||||
@@ -273,7 +275,7 @@ Init_Panel() {
|
||||
chmod -R 700 /usr/bin/panel
|
||||
# 防火墙放行
|
||||
if [ "${OS}" == "centos" ]; then
|
||||
yum install firewalld -y
|
||||
dnf install firewalld -y
|
||||
systemctl enable firewalld
|
||||
systemctl start firewalld
|
||||
firewall-cmd --set-default-zone=public > /dev/null 2>&1
|
||||
|
||||
+23
-39
@@ -22,7 +22,7 @@ HR="+----------------------------------------------------"
|
||||
ARCH=$(uname -m)
|
||||
memTotal=$(LC_ALL=C free -m | grep Mem | awk '{print $2}')
|
||||
OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown")
|
||||
downloadUrl="https://git.haozi.net/opensource/download/-/raw/main/panel/mysql"
|
||||
downloadUrl="https://dl.cdn.haozi.net/panel/mysql"
|
||||
setupPath="/www"
|
||||
mysqlPath="${setupPath}/server/mysql"
|
||||
mysqlVersion=""
|
||||
@@ -31,8 +31,11 @@ cpuCore=$(cat /proc/cpuinfo | grep "processor" | wc -l)
|
||||
source ${setupPath}/panel/scripts/calculate_j.sh
|
||||
j=$(calculate_j)
|
||||
|
||||
if [[ "${1}" == "80" ]]; then
|
||||
mysqlVersion="8.0.36"
|
||||
if [[ "${1}" == "84" ]]; then
|
||||
mysqlVersion="8.4.0"
|
||||
j=$(calculate_j2)
|
||||
elif [[ "${1}" == "80" ]]; then
|
||||
mysqlVersion="8.0.37"
|
||||
j=$(calculate_j2)
|
||||
elif [[ "${1}" == "57" ]]; then
|
||||
mysqlVersion="5.7.44"
|
||||
@@ -69,53 +72,34 @@ mkdir -p ${mysqlPath}
|
||||
cd ${mysqlPath}
|
||||
|
||||
# 下载源码
|
||||
wget -T 120 -t 3 -O ${mysqlPath}/mysql-boost-${mysqlVersion}.tar.gz ${downloadUrl}/mysql-boost-${mysqlVersion}.tar.gz
|
||||
wget -T 20 -t 3 -O ${mysqlPath}/mysql-boost-${mysqlVersion}.tar.gz.checksum.txt ${downloadUrl}/mysql-boost-${mysqlVersion}.tar.gz.checksum.txt
|
||||
|
||||
# 校验
|
||||
if ! sha256sum --status -c mysql-boost-${mysqlVersion}.tar.gz.checksum.txt; then
|
||||
wget -T 120 -t 3 -O ${mysqlPath}/mysql-${mysqlVersion}.7z ${downloadUrl}/mysql-${mysqlVersion}.7z
|
||||
wget -T 20 -t 3 -O ${mysqlPath}/mysql-${mysqlVersion}.7z.checksum.txt ${downloadUrl}/mysql-${mysqlVersion}.7z.checksum.txt
|
||||
if ! sha256sum --status -c mysql-${mysqlVersion}.7z.checksum.txt; then
|
||||
echo -e $HR
|
||||
echo "错误:MySQL 源码 checksum 校验失败,文件可能被篡改或不完整,已终止操作"
|
||||
rm -rf ${mysqlPath}
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tar -zxvf mysql-boost-${mysqlVersion}.tar.gz
|
||||
rm -f mysql-boost-${mysqlVersion}.tar.gz
|
||||
rm -f mysql-boost-${mysqlVersion}.tar.gz.checksum.txt
|
||||
mv mysql-${mysqlVersion} src
|
||||
|
||||
# openssl
|
||||
wget -T 120 -t 3 -O ${mysqlPath}/openssl-1.1.1w.tar.gz ${downloadUrl}/openssl/openssl-1.1.1w.tar.gz
|
||||
wget -T 20 -t 3 -O ${mysqlPath}/openssl-1.1.1w.tar.gz.checksum.txt ${downloadUrl}/openssl/openssl-1.1.1w.tar.gz.checksum.txt
|
||||
|
||||
# 校验
|
||||
if ! sha256sum --status -c openssl-1.1.1w.tar.gz.checksum.txt; then
|
||||
echo -e $HR
|
||||
echo "错误:OpenSSL 源码 checksum 校验失败,文件可能被篡改或不完整,已终止操作"
|
||||
rm -rf ${mysqlPath}
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tar -zxvf openssl-1.1.1w.tar.gz
|
||||
rm -f openssl-1.1.1w.tar.gz
|
||||
rm -f openssl-1.1.1w.tar.gz.checksum.txt
|
||||
mv openssl-1.1.1w openssl
|
||||
cd openssl
|
||||
./config --prefix=/usr/local/openssl-1.1 --openssldir=/usr/local/openssl-1.1 no-tests
|
||||
make -j$(nproc)
|
||||
make install
|
||||
echo "/usr/local/openssl-1.1/lib" > /etc/ld.so.conf.d/openssl-1.1.conf
|
||||
ldconfig
|
||||
cd ..
|
||||
rm -rf openssl
|
||||
7z x mysql-${mysqlVersion}.7z
|
||||
rm -f mysql-${mysqlVersion}.7z
|
||||
rm -f mysql-${mysqlVersion}.7z.checksum.txt
|
||||
|
||||
# 编译
|
||||
mv mysql-${mysqlVersion} src
|
||||
chmod -R 755 src
|
||||
cd src
|
||||
rm mysql-test/CMakeLists.txt
|
||||
sed -i 's/ADD_SUBDIRECTORY(mysql-test)//g' CMakeLists.txt
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=${mysqlPath} -DMYSQL_DATADIR=${mysqlPath}/data -DSYSCONFDIR=${mysqlPath}/conf -DWITH_MYISAM_STORAGE_ENGINE=1 -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_ARCHIVE_STORAGE_ENGINE=1 -DWITH_FEDERATED_STORAGE_ENGINE=1 -DWITH_BLACKHOLE_STORAGE_ENGINE=1 -DDEFAULT_CHARSET=utf8mb4 -DDEFAULT_COLLATION=utf8mb4_general_ci -DENABLED_LOCAL_INFILE=1 -DWITH_DEBUG=0 -DWITH_UNIT_TESTS=OFF -DCMAKE_BUILD_TYPE=Release -DWITH_SYSTEMD=1 -DSYSTEMD_PID_DIR=${mysqlPath} -DWITH_SSL=/usr/local/openssl-1.1 -DWITH_BOOST=../boost
|
||||
# 5.7 需要 boost
|
||||
if [[ "${1}" == "57" ]]; then
|
||||
MAYBE_WITH_BOOST="-DWITH_BOOST=../boost"
|
||||
fi
|
||||
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=${mysqlPath} -DMYSQL_DATADIR=${mysqlPath}/data -DSYSCONFDIR=${mysqlPath}/conf -DWITH_MYISAM_STORAGE_ENGINE=1 -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_ARCHIVE_STORAGE_ENGINE=1 -DWITH_FEDERATED_STORAGE_ENGINE=1 -DWITH_BLACKHOLE_STORAGE_ENGINE=1 -DDEFAULT_CHARSET=utf8mb4 -DDEFAULT_COLLATION=utf8mb4_general_ci -DENABLED_LOCAL_INFILE=1 -DWITH_DEBUG=0 -DWITH_UNIT_TESTS=OFF -DINSTALL_MYSQLTESTDIR= -DCMAKE_BUILD_TYPE=Release -DWITH_SYSTEMD=1 -DSYSTEMD_PID_DIR=${mysqlPath} ${MAYBE_WITH_BOOST}
|
||||
if [ "$?" != "0" ]; then
|
||||
echo -e $HR
|
||||
echo "错误:MySQL 编译初始化失败,请截图错误信息寻求帮助。"
|
||||
@@ -210,7 +194,7 @@ EOF
|
||||
sed -i 's/innodb_write_io_threads = 4/innodb_write_io_threads = '${cpuCore}'/g' ${mysqlPath}/conf/my.cnf
|
||||
sed -i 's/innodb_read_io_threads = 4/innodb_read_io_threads = '${cpuCore}'/g' ${mysqlPath}/conf/my.cnf
|
||||
|
||||
if [[ "${1}" == "80" ]]; then
|
||||
if [[ "${1}" == "84" ]] || [[ "${1}" == "80" ]]; then
|
||||
sed -i '/query_cache_size/d' ${mysqlPath}/conf/my.cnf
|
||||
fi
|
||||
if [[ "${1}" == "57" ]]; then
|
||||
|
||||
+24
-37
@@ -22,7 +22,7 @@ HR="+----------------------------------------------------"
|
||||
ARCH=$(uname -m)
|
||||
memTotal=$(LC_ALL=C free -m | grep Mem | awk '{print $2}')
|
||||
OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown")
|
||||
downloadUrl="https://git.haozi.net/opensource/download/-/raw/main/panel/mysql"
|
||||
downloadUrl="https://dl.cdn.haozi.net/panel/mysql"
|
||||
setupPath="/www"
|
||||
mysqlPath="${setupPath}/server/mysql"
|
||||
mysqlVersion=""
|
||||
@@ -32,8 +32,11 @@ cpuCore=$(cat /proc/cpuinfo | grep "processor" | wc -l)
|
||||
source ${setupPath}/panel/scripts/calculate_j.sh
|
||||
j=$(calculate_j)
|
||||
|
||||
if [[ "${1}" == "80" ]]; then
|
||||
mysqlVersion="8.0.36"
|
||||
if [[ "${1}" == "84" ]]; then
|
||||
mysqlVersion="8.4.0"
|
||||
j=$(calculate_j2)
|
||||
elif [[ "${1}" == "80" ]]; then
|
||||
mysqlVersion="8.0.37"
|
||||
j=$(calculate_j2)
|
||||
elif [[ "${1}" == "57" ]]; then
|
||||
mysqlVersion="5.7.44"
|
||||
@@ -47,11 +50,11 @@ fi
|
||||
if [ "${OS}" == "centos" ]; then
|
||||
dnf makecache -y
|
||||
dnf groupinstall "Development Tools" -y
|
||||
dnf install cmake bison ncurses-devel libtirpc-devel openssl-devel pkg-config openldap-devel libudev-devel cyrus-sasl-devel patchelf rpcgen rpcsvc-proto-devel -y
|
||||
dnf install cmake bison ncurses-devel libtirpc-devel openssl-devel pkg-config openldap-devel libudev-devel cyrus-sasl-devel patchelf rpcgen rpcsvc-proto-devel p7zip p7zip-plugins -y
|
||||
dnf install gcc-toolset-12-gcc gcc-toolset-12-gcc-c++ gcc-toolset-12-binutils gcc-toolset-12-annobin-annocheck gcc-toolset-12-annobin-plugin-gcc -y
|
||||
elif [ "${OS}" == "debian" ]; then
|
||||
apt-get update
|
||||
apt-get install build-essential cmake bison libncurses5-dev libtirpc-dev libssl-dev pkg-config libldap2-dev libudev-dev libsasl2-dev patchelf -y
|
||||
apt-get install build-essential cmake bison libncurses5-dev libtirpc-dev libssl-dev pkg-config libldap2-dev libudev-dev libsasl2-dev patchelf p7zip p7zip-full -y
|
||||
else
|
||||
echo -e $HR
|
||||
echo "错误:耗子 Linux 面板不支持该系统"
|
||||
@@ -65,48 +68,32 @@ systemctl stop mysqld
|
||||
cd ${mysqlPath}
|
||||
|
||||
# 下载源码
|
||||
wget -T 120 -t 3 -O ${mysqlPath}/mysql-boost-${mysqlVersion}.tar.gz ${downloadUrl}/mysql-boost-${mysqlVersion}.tar.gz
|
||||
wget -T 20 -t 3 -O ${mysqlPath}/mysql-boost-${mysqlVersion}.tar.gz.checksum.txt ${downloadUrl}/mysql-boost-${mysqlVersion}.tar.gz.checksum.txt
|
||||
|
||||
if ! sha256sum --status -c mysql-boost-${mysqlVersion}.tar.gz.checksum.txt; then
|
||||
wget -T 120 -t 3 -O ${mysqlPath}/mysql-${mysqlVersion}.7z ${downloadUrl}/mysql-${mysqlVersion}.7z
|
||||
wget -T 20 -t 3 -O ${mysqlPath}/mysql-${mysqlVersion}.7z.checksum.txt ${downloadUrl}/mysql-${mysqlVersion}.7z.checksum.txt
|
||||
if ! sha256sum --status -c mysql-${mysqlVersion}.7z.checksum.txt; then
|
||||
echo -e $HR
|
||||
echo "错误:MySQL 源码 checksum 校验失败,文件可能被篡改或不完整,已终止操作"
|
||||
rm -rf ${mysqlPath}
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tar -zxvf mysql-boost-${mysqlVersion}.tar.gz
|
||||
rm -f mysql-boost-${mysqlVersion}.tar.gz
|
||||
rm -f mysql-boost-${mysqlVersion}.tar.gz.checksum.txt
|
||||
mv mysql-${mysqlVersion} src
|
||||
|
||||
# openssl
|
||||
wget -T 120 -t 3 -O ${mysqlPath}/openssl-1.1.1w.tar.gz ${downloadUrl}/openssl/openssl-1.1.1w.tar.gz
|
||||
wget -T 20 -t 3 -O ${mysqlPath}/openssl-1.1.1w.tar.gz.checksum.txt ${downloadUrl}/openssl/openssl-1.1.1w.tar.gz.checksum.txt
|
||||
|
||||
if ! sha256sum --status -c openssl-1.1.1w.tar.gz.checksum.txt; then
|
||||
echo -e $HR
|
||||
echo "错误:OpenSSL 源码 checksum 校验失败,文件可能被篡改或不完整,已终止操作"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tar -zxvf openssl-1.1.1w.tar.gz
|
||||
rm -f openssl-1.1.1w.tar.gz
|
||||
rm -f openssl-1.1.1w.tar.gz.checksum.txt
|
||||
mv openssl-1.1.1w openssl
|
||||
cd openssl
|
||||
./config --prefix=/usr/local/openssl-1.1 --openssldir=/usr/local/openssl-1.1 no-tests
|
||||
make "-j${j}"
|
||||
make install
|
||||
echo "/usr/local/openssl-1.1/lib" > /etc/ld.so.conf.d/openssl-1.1.conf
|
||||
ldconfig
|
||||
cd ..
|
||||
rm -rf openssl
|
||||
7z x mysql-${mysqlVersion}.7z
|
||||
rm -f mysql-${mysqlVersion}.7z
|
||||
rm -f mysql-${mysqlVersion}.7z.checksum.txt
|
||||
|
||||
# 编译
|
||||
mv mysql-${mysqlVersion} src
|
||||
chmod -R 755 src
|
||||
cd src
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=${mysqlPath} -DMYSQL_DATADIR=${mysqlPath}/data -DSYSCONFDIR=${mysqlPath}/conf -DWITH_MYISAM_STORAGE_ENGINE=1 -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_PARTITION_STORAGE_ENGINE=1 -DWITH_ARCHIVE_STORAGE_ENGINE=1 -DWITH_FEDERATED_STORAGE_ENGINE=1 -DWITH_BLACKHOLE_STORAGE_ENGINE=1 -DWITH_EXTRA_CHARSETS=all -DEXTRA_CHARSETS=all -DDEFAULT_CHARSET=utf8mb4 -DDEFAULT_COLLATION=utf8mb4_general_ci -DENABLED_LOCAL_INFILE=1 -DWITH_SYSTEMD=1 -DSYSTEMD_PID_DIR=${mysqlPath} -DWITH_SSL=/usr/local/openssl-1.1 -DWITH_BOOST=../boost
|
||||
|
||||
# 5.7 需要 boost
|
||||
if [[ "${1}" == "57" ]]; then
|
||||
MAYBE_WITH_BOOST="-DWITH_BOOST=../boost"
|
||||
fi
|
||||
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=${mysqlPath} -DMYSQL_DATADIR=${mysqlPath}/data -DSYSCONFDIR=${mysqlPath}/conf -DWITH_MYISAM_STORAGE_ENGINE=1 -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_ARCHIVE_STORAGE_ENGINE=1 -DWITH_FEDERATED_STORAGE_ENGINE=1 -DWITH_BLACKHOLE_STORAGE_ENGINE=1 -DDEFAULT_CHARSET=utf8mb4 -DDEFAULT_COLLATION=utf8mb4_general_ci -DENABLED_LOCAL_INFILE=1 -DWITH_DEBUG=0 -DWITH_UNIT_TESTS=OFF -DINSTALL_MYSQLTESTDIR= -DCMAKE_BUILD_TYPE=Release -DWITH_SYSTEMD=1 -DSYSTEMD_PID_DIR=${mysqlPath} ${MAYBE_WITH_BOOST}
|
||||
if [ "$?" != "0" ]; then
|
||||
echo -e $HR
|
||||
echo "错误:MySQL 编译初始化失败,请截图错误信息寻求帮助。"
|
||||
|
||||
@@ -21,7 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
HR="+----------------------------------------------------"
|
||||
ARCH=$(uname -m)
|
||||
OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown")
|
||||
downloadUrl="https://git.haozi.net/opensource/download/-/raw/main/panel/openresty"
|
||||
downloadUrl="https://dl.cdn.haozi.net/panel/openresty"
|
||||
setupPath="/www"
|
||||
openrestyPath="${setupPath}/server/openresty"
|
||||
openrestyVersion="1.25.3.1"
|
||||
@@ -66,20 +66,21 @@ mv openresty-${openrestyVersion} src
|
||||
cd src
|
||||
|
||||
# openssl
|
||||
wget -T 120 -t 3 -O openssl-3.0.12.tar.gz ${downloadUrl}/openssl/openssl-3.0.12.tar.gz
|
||||
wget -T 20 -t 3 -O openssl-3.0.12.tar.gz.checksum.txt ${downloadUrl}/openssl/openssl-3.0.12.tar.gz.checksum.txt
|
||||
wget -T 120 -t 3 -O openssl-3.0.12.7z ${downloadUrl}/openssl/openssl-3.0.12.7z
|
||||
wget -T 20 -t 3 -O openssl-3.0.12.7z.checksum.txt ${downloadUrl}/openssl/openssl-3.0.12.7z.checksum.txt
|
||||
|
||||
if ! sha256sum --status -c openssl-3.0.12.tar.gz.checksum.txt; then
|
||||
if ! sha256sum --status -c openssl-3.0.12.7z.checksum.txt; then
|
||||
echo -e $HR
|
||||
echo "错误:OpenSSL 源码 checksum 校验失败,文件可能被篡改或不完整,已终止操作"
|
||||
rm -rf ${openrestyPath}
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tar -zxvf openssl-3.0.12.tar.gz
|
||||
rm -f openssl-3.0.12.tar.gz
|
||||
rm -f openssl-3.0.12.tar.gz.checksum.txt
|
||||
7z x openssl-3.0.12.7z
|
||||
rm -f openssl-3.0.12.7z
|
||||
rm -f openssl-3.0.12.7z.checksum.txt
|
||||
mv openssl-3.0.12 openssl
|
||||
chmod -R 755 openssl
|
||||
|
||||
# patch openssl
|
||||
cd openssl
|
||||
@@ -99,20 +100,21 @@ rm -f openssl-3.0.12-sess_set_get_cb_yield.patch.checksum.txt
|
||||
cd ../
|
||||
|
||||
# pcre2
|
||||
wget -T 60 -t 3 -O pcre2-10.42.tar.gz ${downloadUrl}/pcre/pcre2-10.42.tar.gz
|
||||
wget -T 20 -t 3 -O pcre2-10.42.tar.gz.checksum.txt ${downloadUrl}/pcre/pcre2-10.42.tar.gz.checksum.txt
|
||||
wget -T 60 -t 3 -O pcre2-10.43.7z ${downloadUrl}/pcre/pcre2-10.43.7z
|
||||
wget -T 20 -t 3 -O pcre2-10.43.7z.checksum.txt ${downloadUrl}/pcre/pcre2-10.43.7z.checksum.txt
|
||||
|
||||
if ! sha256sum --status -c pcre2-10.42.tar.gz.checksum.txt; then
|
||||
if ! sha256sum --status -c pcre2-10.43.7z.checksum.txt; then
|
||||
echo -e $HR
|
||||
echo "错误:pcre2 源码 checksum 校验失败,文件可能被篡改或不完整,已终止操作"
|
||||
rm -rf ${openrestyPath}
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tar -zxvf pcre2-10.42.tar.gz
|
||||
rm -f pcre2-10.42.tar.gz
|
||||
rm -f pcre2-10.42.tar.gz.checksum.txt
|
||||
mv pcre2-10.42 pcre2
|
||||
7z x pcre2-10.43.7z
|
||||
rm -f pcre2-10.43.7z
|
||||
rm -f pcre2-10.43.7z.checksum.txt
|
||||
mv pcre2-10.43 pcre2
|
||||
chmod -R 755 pcre2
|
||||
|
||||
# ngx_cache_purge
|
||||
wget -T 20 -t 3 -O ngx_cache_purge-2.3.tar.gz ${downloadUrl}/modules/ngx_cache_purge-2.3.tar.gz
|
||||
|
||||
+11
-10
@@ -21,7 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
HR="+----------------------------------------------------"
|
||||
ARCH=$(uname -m)
|
||||
OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown")
|
||||
downloadUrl="https://git.haozi.net/opensource/download/-/raw/main/panel/php"
|
||||
downloadUrl="https://dl.cdn.haozi.net/panel/php"
|
||||
setupPath="/www"
|
||||
phpVersion="${1}"
|
||||
phpVersionCode=""
|
||||
@@ -65,31 +65,32 @@ if [ "${phpVersion}" == "74" ]; then
|
||||
elif [ "${phpVersion}" == "80" ]; then
|
||||
phpVersionCode="8.0.30"
|
||||
elif [ "${phpVersion}" == "81" ]; then
|
||||
phpVersionCode="8.1.27"
|
||||
phpVersionCode="8.1.28"
|
||||
elif [ "${phpVersion}" == "82" ]; then
|
||||
phpVersionCode="8.2.16"
|
||||
phpVersionCode="8.2.19"
|
||||
elif [ "${phpVersion}" == "83" ]; then
|
||||
phpVersionCode="8.3.3"
|
||||
phpVersionCode="8.3.7"
|
||||
else
|
||||
echo -e $HR
|
||||
echo "错误:PHP-${phpVersion}不支持,请检查版本号是否正确。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
wget -T 120 -t 3 -O ${phpPath}/php-${phpVersionCode}.tar.gz ${downloadUrl}/php-${phpVersionCode}.tar.gz
|
||||
wget -T 20 -t 3 -O ${phpPath}/php-${phpVersionCode}.tar.gz.checksum.txt ${downloadUrl}/php-${phpVersionCode}.tar.gz.checksum.txt
|
||||
wget -T 120 -t 3 -O ${phpPath}/php-${phpVersionCode}.7z ${downloadUrl}/php-${phpVersionCode}.7z
|
||||
wget -T 20 -t 3 -O ${phpPath}/php-${phpVersionCode}.7z.checksum.txt ${downloadUrl}/php-${phpVersionCode}.7z.checksum.txt
|
||||
|
||||
if ! sha256sum --status -c php-${phpVersionCode}.tar.gz.checksum.txt; then
|
||||
if ! sha256sum --status -c php-${phpVersionCode}.7z.checksum.txt; then
|
||||
echo -e $HR
|
||||
echo "错误:PHP-${phpVersion}源码 checksum 校验失败,文件可能被篡改或不完整,已终止操作"
|
||||
rm -rf ${phpPath}
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tar -xvf php-${phpVersionCode}.tar.gz
|
||||
rm -f php-${phpVersionCode}.tar.gz
|
||||
rm -f php-${phpVersionCode}.tar.gz.checksum.txt
|
||||
7z x php-${phpVersionCode}.7z
|
||||
rm -f php-${phpVersionCode}.7z
|
||||
rm -f php-${phpVersionCode}.7z.checksum.txt
|
||||
mv php-* src
|
||||
chmod -R 755 src
|
||||
|
||||
if [ "${phpVersion}" -le "80" ]; then
|
||||
wget -T 120 -t 3 -O ${phpPath}/openssl-1.1.1w.tar.gz ${downloadUrl}/openssl/openssl-1.1.1w.tar.gz
|
||||
|
||||
@@ -20,7 +20,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
HR="+----------------------------------------------------"
|
||||
|
||||
downloadUrl="https://git.haozi.net/opensource/download/-/raw/main/panel/php_extensions"
|
||||
downloadUrl="https://dl.cdn.haozi.net/panel/php_extensions"
|
||||
action="$1"
|
||||
phpVersion="$2"
|
||||
swowVersion="1.4.1"
|
||||
|
||||
@@ -20,7 +20,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
HR="+----------------------------------------------------"
|
||||
|
||||
downloadUrl="https://git.haozi.net/opensource/download/-/raw/main/panel/php_extensions"
|
||||
downloadUrl="https://dl.cdn.haozi.net/panel/php_extensions"
|
||||
action="$1"
|
||||
phpVersion="$2"
|
||||
igbinaryVersion="3.2.15"
|
||||
|
||||
@@ -21,7 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
HR="+----------------------------------------------------"
|
||||
OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown")
|
||||
|
||||
downloadUrl="https://git.haozi.net/opensource/download/-/raw/main/panel/php_extensions"
|
||||
downloadUrl="https://dl.cdn.haozi.net/panel/php_extensions"
|
||||
action="$1"
|
||||
phpVersion="$2"
|
||||
imagickVersion="3.7.0"
|
||||
|
||||
@@ -20,7 +20,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
HR="+----------------------------------------------------"
|
||||
|
||||
downloadUrl="https://git.haozi.net/opensource/download/-/raw/main/panel/php_extensions"
|
||||
downloadUrl="https://dl.cdn.haozi.net/panel/php_extensions"
|
||||
action="$1"
|
||||
phpVersion="$2"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
HR="+----------------------------------------------------"
|
||||
|
||||
downloadUrl="https://git.haozi.net/opensource/download/-/raw/main/panel/php_extensions"
|
||||
downloadUrl="https://dl.cdn.haozi.net/panel/php_extensions"
|
||||
action="$1"
|
||||
phpVersion="$2"
|
||||
phpredisVersion="5.3.7"
|
||||
|
||||
@@ -20,7 +20,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
HR="+----------------------------------------------------"
|
||||
|
||||
downloadUrl="https://git.haozi.net/opensource/download/-/raw/main/panel/php_extensions"
|
||||
downloadUrl="https://dl.cdn.haozi.net/panel/php_extensions"
|
||||
action="$1"
|
||||
phpVersion="$2"
|
||||
swooleVersion="5.1.2"
|
||||
|
||||
@@ -21,7 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
HR="+----------------------------------------------------"
|
||||
ARCH=$(uname -m)
|
||||
OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown")
|
||||
downloadUrl="https://git.haozi.net/opensource/download/-/raw/main/panel/phpmyadmin"
|
||||
downloadUrl="https://dl.cdn.haozi.net/panel/phpmyadmin"
|
||||
setupPath="/www"
|
||||
phpmyadminPath="${setupPath}/server/phpmyadmin"
|
||||
phpmyadminVersion="5.2.1"
|
||||
|
||||
@@ -22,7 +22,7 @@ HR="+----------------------------------------------------"
|
||||
ARCH=$(uname -m)
|
||||
memTotal=$(LC_ALL=C free -m | grep Mem | awk '{print $2}')
|
||||
OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown")
|
||||
downloadUrl="https://git.haozi.net/opensource/download/-/raw/main/panel/postgresql"
|
||||
downloadUrl="https://dl.cdn.haozi.net/panel/postgresql"
|
||||
setupPath="/www"
|
||||
postgresqlPath="${setupPath}/server/postgresql"
|
||||
postgresqlVersion=""
|
||||
@@ -31,9 +31,9 @@ source ${setupPath}/panel/scripts/calculate_j.sh
|
||||
j=$(calculate_j)
|
||||
|
||||
if [[ "${1}" == "15" ]]; then
|
||||
postgresqlVersion="15.6"
|
||||
postgresqlVersion="15.7"
|
||||
elif [[ "${1}" == "16" ]]; then
|
||||
postgresqlVersion="16.2"
|
||||
postgresqlVersion="16.3"
|
||||
else
|
||||
echo -e $HR
|
||||
echo "错误:不支持的 PostgreSQL 版本!"
|
||||
@@ -66,20 +66,21 @@ mkdir -p ${postgresqlPath}
|
||||
cd ${postgresqlPath}
|
||||
|
||||
# 下载源码
|
||||
wget -T 120 -t 3 -O ${postgresqlPath}/postgresql-${postgresqlVersion}.tar.gz ${downloadUrl}/postgresql-${postgresqlVersion}.tar.gz
|
||||
wget -T 20 -t 3 -O ${postgresqlPath}/postgresql-${postgresqlVersion}.tar.gz.checksum.txt ${downloadUrl}/postgresql-${postgresqlVersion}.tar.gz.checksum.txt
|
||||
wget -T 120 -t 3 -O ${postgresqlPath}/postgresql-${postgresqlVersion}.7z ${downloadUrl}/postgresql-${postgresqlVersion}.7z
|
||||
wget -T 20 -t 3 -O ${postgresqlPath}/postgresql-${postgresqlVersion}.7z.checksum.txt ${downloadUrl}/postgresql-${postgresqlVersion}.7z.checksum.txt
|
||||
|
||||
if ! sha256sum --status -c postgresql-${postgresqlVersion}.tar.gz.checksum.txt; then
|
||||
if ! sha256sum --status -c postgresql-${postgresqlVersion}.7z.checksum.txt; then
|
||||
echo -e $HR
|
||||
echo "错误:PostgreSQL 源码 checksum 校验失败,文件可能被篡改或不完整,已终止操作"
|
||||
rm -rf ${postgresqlPath}
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tar -zxvf postgresql-${postgresqlVersion}.tar.gz
|
||||
rm -f postgresql-${postgresqlVersion}.tar.gz
|
||||
rm -f postgresql-${postgresqlVersion}.tar.gz.checksum.txt
|
||||
7z x postgresql-${postgresqlVersion}.7z
|
||||
rm -f postgresql-${postgresqlVersion}.7z
|
||||
rm -f postgresql-${postgresqlVersion}.7z.checksum.txt
|
||||
mv postgresql-${postgresqlVersion} src
|
||||
chmod -R 755 src
|
||||
|
||||
# 编译
|
||||
cd src
|
||||
|
||||
@@ -22,7 +22,7 @@ HR="+----------------------------------------------------"
|
||||
ARCH=$(uname -m)
|
||||
memTotal=$(LC_ALL=C free -m | grep Mem | awk '{print $2}')
|
||||
OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown")
|
||||
downloadUrl="https://git.haozi.net/opensource/download/-/raw/main/panel/postgresql"
|
||||
downloadUrl="https://dl.cdn.haozi.net/panel/postgresql"
|
||||
setupPath="/www"
|
||||
postgresqlPath="${setupPath}/server/postgresql"
|
||||
postgresqlVersion=""
|
||||
@@ -31,9 +31,9 @@ source ${setupPath}/panel/scripts/calculate_j.sh
|
||||
j=$(calculate_j)
|
||||
|
||||
if [[ "${1}" == "15" ]]; then
|
||||
postgresqlVersion="15.6"
|
||||
postgresqlVersion="15.7"
|
||||
elif [[ "${1}" == "16" ]]; then
|
||||
postgresqlVersion="16.2"
|
||||
postgresqlVersion="16.3"
|
||||
else
|
||||
echo -e $HR
|
||||
echo "错误:不支持的 PostgreSQL 版本!"
|
||||
@@ -62,19 +62,20 @@ rm -rf ${postgresqlPath}/src
|
||||
cd ${postgresqlPath}
|
||||
|
||||
# 下载源码
|
||||
wget -T 120 -t 3 -O ${postgresqlPath}/postgresql-${postgresqlVersion}.tar.gz ${downloadUrl}/postgresql-${postgresqlVersion}.tar.gz
|
||||
wget -T 20 -t 3 -O ${postgresqlPath}/postgresql-${postgresqlVersion}.tar.gz.checksum.txt ${downloadUrl}/postgresql-${postgresqlVersion}.tar.gz.checksum.txt
|
||||
wget -T 120 -t 3 -O ${postgresqlPath}/postgresql-${postgresqlVersion}.7z ${downloadUrl}/postgresql-${postgresqlVersion}.7z
|
||||
wget -T 20 -t 3 -O ${postgresqlPath}/postgresql-${postgresqlVersion}.7z.checksum.txt ${downloadUrl}/postgresql-${postgresqlVersion}.7z.checksum.txt
|
||||
|
||||
if ! sha256sum --status -c postgresql-${postgresqlVersion}.tar.gz.checksum.txt; then
|
||||
if ! sha256sum --status -c postgresql-${postgresqlVersion}.7z.checksum.txt; then
|
||||
echo -e $HR
|
||||
echo "错误:PostgreSQL 源码 checksum 校验失败,文件可能被篡改或不完整,已终止操作"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tar -zxvf postgresql-${postgresqlVersion}.tar.gz
|
||||
rm -f postgresql-${postgresqlVersion}.tar.gz
|
||||
rm -f postgresql-${postgresqlVersion}.tar.gz.checksum.txt
|
||||
7z x postgresql-${postgresqlVersion}.7z
|
||||
rm -f postgresql-${postgresqlVersion}.7z
|
||||
rm -f postgresql-${postgresqlVersion}.7z.checksum.txt
|
||||
mv postgresql-${postgresqlVersion} src
|
||||
chmod -R 755 src
|
||||
|
||||
# 编译
|
||||
cd src
|
||||
|
||||
@@ -20,7 +20,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
HR="+----------------------------------------------------"
|
||||
OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown")
|
||||
downloadUrl="https://git.haozi.net/opensource/download/-/raw/main/panel/pure-ftpd"
|
||||
downloadUrl="https://dl.cdn.haozi.net/panel/pure-ftpd"
|
||||
setupPath="/www"
|
||||
pureftpdPath="${setupPath}/server/pure-ftpd"
|
||||
pureftpdVersion="1.0.50"
|
||||
|
||||
@@ -20,7 +20,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
HR="+----------------------------------------------------"
|
||||
OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown")
|
||||
downloadUrl="https://git.haozi.net/opensource/download/-/raw/main/panel/pure-ftpd"
|
||||
downloadUrl="https://dl.cdn.haozi.net/panel/pure-ftpd"
|
||||
setupPath="/www"
|
||||
pureftpdPath="${setupPath}/server/pure-ftpd"
|
||||
pureftpdVersion="1.0.50"
|
||||
|
||||
@@ -21,7 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
HR="+----------------------------------------------------"
|
||||
ARCH=$(uname -m)
|
||||
OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown")
|
||||
downloadUrl="https://git.haozi.net/opensource/download/-/raw/main/panel/redis"
|
||||
downloadUrl="https://dl.cdn.haozi.net/panel/redis"
|
||||
setupPath="/www"
|
||||
redisPath="${setupPath}/server/redis"
|
||||
redisVersion="7.2.4"
|
||||
|
||||
@@ -21,7 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
HR="+----------------------------------------------------"
|
||||
ARCH=$(uname -m)
|
||||
OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown")
|
||||
downloadUrl="https://git.haozi.net/opensource/download/-/raw/main/panel/redis"
|
||||
downloadUrl="https://dl.cdn.haozi.net/panel/redis"
|
||||
setupPath="/www"
|
||||
redisPath="${setupPath}/server/redis"
|
||||
redisVersion="7.2.4"
|
||||
|
||||
+17
-2
@@ -90,8 +90,8 @@ if version_lt "$oldVersion" "2.1.39"; then
|
||||
echo "更新面板到 v2.1.39 ..."
|
||||
echo "Update panel to v2.1.39 ..."
|
||||
if [ "${OS}" == "centos" ]; then
|
||||
yum makecache
|
||||
yum install -y podman
|
||||
dnf makecache
|
||||
dnf install -y podman
|
||||
else
|
||||
apt-get update -y
|
||||
apt-get install -y podman
|
||||
@@ -101,6 +101,21 @@ if version_lt "$oldVersion" "2.1.39"; then
|
||||
systemctl start podman
|
||||
fi
|
||||
|
||||
if version_lt "$oldVersion" "2.2.0"; then
|
||||
echo "更新面板到 v2.2.0 ..."
|
||||
echo "Update panel to v2.2.0 ..."
|
||||
echo "APP_LOCALE=zh_CN" >> $panelPath/panel.conf
|
||||
if [ "${OS}" == "centos" ]; then
|
||||
dnf makecache
|
||||
dnf install -y p7zip p7zip-plugins rsyslog
|
||||
systemctl enable rsyslog
|
||||
systemctl start rsyslog
|
||||
else
|
||||
apt-get update -y
|
||||
apt-get install -y p7zip p7zip-full
|
||||
fi
|
||||
fi
|
||||
|
||||
echo $HR
|
||||
echo "更新结束"
|
||||
echo "Update finished"
|
||||
|
||||
Reference in New Issue
Block a user