Compare commits

...

105 Commits

Author SHA1 Message Date
耗子 07c4083005 feat: 发布 v2.0.54 2023-10-16 20:28:12 +08:00
耗子 032133b54a ci: 在极狐发布版本 2023-10-16 20:27:06 +08:00
耗子 e10e0d4b9b feat: 将限速的阿里云镜像更换为腾讯云镜像 2023-10-16 19:44:23 +08:00
耗子 22b9b7f2aa feat: 将限速的阿里云镜像更换为腾讯云镜像 2023-10-16 19:39:12 +08:00
耗子 97562f189e fix: 修复在有 swap 的情况下无法安装的 bug 2023-10-15 22:31:25 +08:00
耗子 50daebeb0e Merge remote-tracking branch 'origin/main' 2023-10-10 20:04:40 +08:00
耗子 4bf23ff7ba feat: 更新依赖机器人配置 2023-10-10 20:04:28 +08:00
耗子 013b70d7ad Merge branch 'renovate/github.com-bytedance-sonic-1.x' into 'main'
chore(deps): Update module github.com/bytedance/sonic to v1.10.2

See merge request haozi-team/panel!21
2023-10-10 02:00:28 +08:00
耗子 aecb0a2ee6 Merge remote-tracking branch 'origin/main' 2023-10-10 00:59:19 +08:00
耗子 74d2bda1fe feat: 发布 v2.0.53 2023-10-10 00:59:12 +08:00
Renovate 918bdd0d95 chore(deps): Update module github.com/bytedance/sonic to v1.10.2 2023-10-08 04:07:18 +00:00
耗子 d5108ae684 Merge branch 'renovate/github.com-goravel-framework-1.x' into 'main'
chore(deps): Update module github.com/goravel/framework to v1.13.2

See merge request haozi-team/panel!20
2023-10-07 20:23:34 +08:00
耗子 1165d149f3 Merge branch 'renovate/golang.org-x-exp-digest' into 'main'
chore(deps): Update golang.org/x/exp digest to 7918f67

See merge request haozi-team/panel!19
2023-10-07 20:23:20 +08:00
耗子 4c8f4fa4ea feat: 卸载面板删除 /usr/bin/panel 2023-10-07 20:05:07 +08:00
耗子 be74716f74 feat: 操作 swap 后检查 /etc/fstab 是否正确 2023-10-07 20:03:29 +08:00
耗子 fdfe5725fd Merge remote-tracking branch 'origin/main' 2023-10-07 19:55:11 +08:00
耗子 8f33693228 feat: 添加面板卸载脚本 2023-10-07 19:55:04 +08:00
Renovate d9b5ed096b chore(deps): Update module github.com/goravel/framework to v1.13.2 2023-10-07 10:12:51 +00:00
Renovate 4cae56a56e chore(deps): Update golang.org/x/exp digest to 7918f67 2023-10-06 14:06:37 +00:00
耗子 2898801b27 Merge branch 'renovate/github.com-imroc-req-v3-3.x' into 'main'
chore(deps): Update module github.com/imroc/req/v3 to v3.42.1

See merge request haozi-team/panel!18
2023-10-06 20:56:54 +08:00
Renovate 5a22fc8fe5 chore(deps): Update module github.com/imroc/req/v3 to v3.42.1 2023-10-06 11:06:40 +00:00
耗子 ac9642d344 Merge remote-tracking branch 'origin/main' 2023-10-06 14:59:41 +08:00
耗子 f542491a38 ci: 调整作业产物为 3 天过期 2023-10-06 14:59:34 +08:00
耗子 64163575e2 Merge branch 'renovate/golang.org-x-exp-digest' into 'main'
chore(deps): Update golang.org/x/exp digest to 3e424a5

See merge request haozi-team/panel!17
2023-10-06 14:26:11 +08:00
耗子 2e07a26646 fix: 创建网站同步创建 PostgreSQL 数据库 2023-10-06 14:07:53 +08:00
Renovate 36b1478e32 chore(deps): Update golang.org/x/exp digest to 3e424a5 2023-10-05 20:06:26 +00:00
耗子 e5b24f25bd docs: 更新 README 2023-10-06 03:25:34 +08:00
耗子 6a100245de Merge branch 'renovate/golang.org-x-crypto-0.x' into 'main'
chore(deps): Update module golang.org/x/crypto to v0.14.0

See merge request haozi-team/panel!16
2023-10-06 02:27:59 +08:00
耗子 046c28a387 feat: 安装脚本优化 2023-10-06 00:22:09 +08:00
Renovate 937e940dd8 chore(deps): Update module golang.org/x/crypto to v0.14.0 2023-10-05 16:18:22 +00:00
耗子 be7063b4fc fix: 回滚,OpenResty 需更新 1.22 后才能支持 OpenSSL 3 编译 2023-10-05 00:05:11 +08:00
耗子 70e9342c5d feat: 发布 v2.0.51 2023-10-04 23:49:34 +08:00
耗子 0756a7b507 feat: OpenResty 使用 OpenSSL 3 编译 2023-10-04 23:49:05 +08:00
耗子 47f104bcae feat: 发布 v2.0.50 2023-10-04 23:08:56 +08:00
耗子 d20e57dca9 feat: 移除 OpenResty 的 GeoIP 模块 2023-10-04 23:08:34 +08:00
耗子 09cc10a563 docs: 文档更新 2023-10-03 20:47:58 +08:00
耗子 b188b3f1b8 feat: 优化 arm 判断 2023-10-02 16:32:05 +08:00
耗子 99dac8721e feat: 更新 PostgreSQL 的描述 2023-09-30 23:42:56 +08:00
耗子 c93eb4b309 feat: 优化网站添加 2023-09-29 17:37:01 +08:00
耗子 266f12d15a feat: 发布 v2.0.49 2023-09-29 17:02:12 +08:00
耗子 3a18b36df2 feat: 优化面板更新校验 2023-09-28 20:23:08 +08:00
耗子 ebcc022418 fix: 发布 v2.0.48 2023-09-28 19:50:23 +08:00
耗子 aa0fb2afcc fix: 修复 MySQL 更新 2023-09-28 19:50:03 +08:00
耗子 bde2c50696 fix: 修复 MySQL 版本号未更新 2023-09-28 19:39:26 +08:00
耗子 0945949b37 fix: 多打了一个引号 2023-09-28 17:01:37 +08:00
耗子 4fcd65da70 feat: 发布 v2.0.45 2023-09-28 13:25:09 +08:00
耗子 afb12dd664 feat: 更新 MySQL 版本 2023-09-28 13:20:08 +08:00
耗子 8c5d0e905e feat: 优化 MySQL systemd 判断 2023-09-28 13:06:31 +08:00
耗子 92e77720f7 feat: 优化 MySQL EL9 兼容性 2023-09-28 02:48:24 +08:00
耗子 124912368e feat: 发布 v2.0.44 2023-09-26 17:58:30 +08:00
耗子 084f0a06ca fix: 修复 PostgreSQL 创建数据库未修改所有者 2023-09-26 16:48:36 +08:00
耗子 c08fd0af7f fix: 调整 fail2ban 安装 2023-09-26 01:58:40 +08:00
耗子 3229685729 fix: 修复 logind 的问题 2023-09-26 01:19:05 +08:00
耗子 77ae021a2f feat: 发布 v2.0.43 2023-09-26 01:15:09 +08:00
耗子 69cf5dcb65 fix: PostgreSQL 16 不讲武德,来骗,来偷袭 2023-09-26 01:14:44 +08:00
耗子 9bd9833ca9 feat: 发布 v2.0.42 2023-09-26 00:42:36 +08:00
耗子 5fb58476ba feat: 优化 PostgreSQL 安装更新 2023-09-26 00:41:55 +08:00
耗子 da4a04ad87 fix: 安装 PostgreSQL 后重启一次 2023-09-26 00:32:12 +08:00
耗子 ae4736d86e feat: 发布 v2.0.41 2023-09-26 00:07:10 +08:00
耗子 198c56cb25 feat: 更新 PostgreSQL 16.0 和 15.4 2023-09-25 23:59:24 +08:00
耗子 4950b48fc9 Merge remote-tracking branch 'origin/main' 2023-09-18 10:34:36 +08:00
耗子 3f323588c0 fix: 修复空目录打包 2023-09-18 10:34:29 +08:00
耗子 e651a8ed6b Merge branch 'renovate/goreleaser-goreleaser-action-5.x' into 'main'
chore(deps): Update goreleaser/goreleaser-action action to v5

See merge request haozi-team/panel!15
2023-09-16 11:06:16 +08:00
耗子 bc9274a8b7 Merge branch 'renovate/github.com-imroc-req-v3-3.x' into 'main'
chore(deps): Update module github.com/imroc/req/v3 to v3.42.0

See merge request haozi-team/panel!14
2023-09-16 02:52:17 +08:00
耗子 1047374c2b Merge branch 'renovate/github.com-bytedance-sonic-1.x' into 'main'
chore(deps): Update module github.com/bytedance/sonic to v1.10.1

See merge request haozi-team/panel!13
2023-09-16 02:40:17 +08:00
Renovate 1f2f51fb55 chore(deps): Update goreleaser/goreleaser-action action to v5 2023-09-15 18:25:32 +00:00
Renovate 872af0b298 chore(deps): Update module github.com/imroc/req/v3 to v3.42.0 2023-09-15 18:25:29 +00:00
Renovate bc22e23cda chore(deps): Update module github.com/bytedance/sonic to v1.10.1 2023-09-15 18:25:19 +00:00
耗子 4ca2446dc3 docs: 更新赞助商信息 2023-09-12 00:13:58 +08:00
耗子 840d6824f0 docs: 更新赞助商信息 2023-09-11 20:44:50 +08:00
耗子 f366e4e267 workflow: 更新版本 2023-09-11 20:17:15 +08:00
耗子 25436325b2 chore: 框架更新的其他调整和更新 PHP 版本号 2023-09-11 19:33:29 +08:00
耗子 25d295f4a1 chore: 框架更新的其他调整 2023-09-11 17:02:14 +08:00
耗子 f8d874b9d7 fix: 路由报错 2023-09-11 16:55:54 +08:00
耗子 959fdaa6de fix: 路由报错 2023-09-11 16:54:37 +08:00
耗子 c4d672173e chore: update framework 2023-09-11 16:43:39 +08:00
耗子 262e72c17a feat: 优化安装脚本 2023-08-28 01:16:10 +08:00
耗子 e260652373 feat: release v2.0.39 2023-08-27 11:49:41 +08:00
耗子 427f7dc230 feat: 添加网站前确保记录不存在 2023-08-27 11:48:38 +08:00
耗子 c32ae4a733 fix: src error 2023-08-19 00:03:29 +08:00
耗子 34d9d245e1 refactor: replace apt with apt-get 2023-08-18 14:28:04 +08:00
耗子 2a2008b959 feat: add rsyslog for Debian 12 2023-08-18 13:07:42 +08:00
耗子 bab4efa807 feat: release v2.0.37 2023-08-17 12:02:48 +08:00
耗子 5052170046 feat(toolbox): swap support btrfs 2023-08-17 12:02:08 +08:00
耗子 32fef1e9e4 fix(toolbox): swap permission 2023-08-17 11:54:46 +08:00
耗子 67ff105cba fix(toolbox): kb 2023-08-17 11:53:34 +08:00
耗子 514baf4fb3 feat: release v2.0.36 2023-08-16 21:21:17 +08:00
耗子 30f9fada39 feat: add avif for php >= 81 2023-08-16 13:35:42 +08:00
耗子 8b01035d1a docs: drop fastdun 2023-08-15 11:20:17 +08:00
耗子 883e489ca1 feat: release v2.0.35 2023-08-15 02:10:05 +08:00
耗子 d16854ceaf fix(website): close ssl port error 2023-08-15 02:09:11 +08:00
耗子 bdb5442659 feat(task): auto reload page 2023-08-15 02:02:50 +08:00
耗子 f991586f94 feat: release v2.0.34 2023-08-15 01:43:30 +08:00
耗子 b7869c36dc feat: add ssl info 2023-08-15 01:42:20 +08:00
耗子 83d7aec8f5 feat: release v2.0.33 2023-08-15 00:40:28 +08:00
耗子 b43e5b5c80 feat: toolbox plugin 2023-08-15 00:39:11 +08:00
耗子 acb792950a fix: tests 2023-08-14 22:40:37 +08:00
耗子 41d83719ff fix: tests 2023-08-14 22:29:32 +08:00
耗子 de1c269a4a refactor: tools 2023-08-14 22:26:42 +08:00
耗子 c3bf62ebf7 feat: new tools methods 2023-08-14 00:53:16 +08:00
耗子 317851d982 fix: php pdo_pgsql install 2023-08-14 00:51:10 +08:00
耗子 f52d95ae11 fix: arm mysql install 2023-08-14 00:38:22 +08:00
耗子 6420092c90 feat: release v2.0.32 2023-08-13 22:39:58 +08:00
耗子 2fa108dd67 feat: remove unnecessary bootstrap 2023-08-13 22:38:39 +08:00
耗子 86bfcfb9f1 fix(monitor): select date 2023-08-13 22:37:42 +08:00
111 changed files with 5503 additions and 2789 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
goarch: [ amd64, arm64 ]
fail-fast: true
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
with:
cache: true
+1 -1
View File
@@ -8,7 +8,7 @@ jobs:
codecov:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
with:
go-version: 'stable'
+2 -2
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
@@ -18,7 +18,7 @@ jobs:
with:
go-version: 'stable'
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v4
uses: goreleaser/goreleaser-action@v5
with:
version: latest
args: release --clean
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
with:
go-version: 'stable'
+1 -1
View File
@@ -8,7 +8,7 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
with:
go-version: 'stable'
+15 -1
View File
@@ -49,4 +49,18 @@ build:
name: "$OUTPUT_NAME"
paths:
- $OUTPUT_NAME
expire_in: 1 week
expire_in: 3 days
release:
stage: release
image:
name: goreleaser/goreleaser
entrypoint: ['']
only:
- tags
variables:
# Disable shallow cloning so that goreleaser can diff between tags to
# generate a changelog.
GIT_DEPTH: 0
script:
- goreleaser release --clean
+19
View File
@@ -30,4 +30,23 @@ archives:
- storage/*
- database/*
- scripts/*
- resources/*
- panel-example.conf
gitlab_urls:
api: https://jihulab.com/api/v4/
download: https://jihulab.com
# 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
use_job_token: true
+17
View File
@@ -0,0 +1,17 @@
## 行为准则
耗子Linux面板遵守业界通用的行为准则。任何违反行为准则的行为都可以报告给我们:
- 参与者将容忍反对意见。
- 参与者必须确保他们的语言和行为没有人身攻击和贬低个人言论。
- 在解释他人的言行时,参与者应始终保持良好的意图。
- 不能容忍可合理视为骚扰的行为。
## Code of Conduct
The HaoZi Linux Panel complies with the industry's common code of conduct. Any breach of the Code of Conduct can be reported to us:
- Participants will be tolerant of opposing views.
- Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
- When interpreting the words and actions of others, participants should always assume good intentions.
- Behavior that can be reasonably considered harassment will not be tolerated.
+54 -49
View File
@@ -1,39 +1,49 @@
# 耗子Linux面板
<h1 align="center">耗子 Linux 面板</h1>
[![Release](https://img.shields.io/github/release/haozi-team/panel.svg)](https://github.com/haozi-team/panel/releases)
[![Test](https://github.com/haozi-team/panel/actions/workflows/test.yml/badge.svg)](https://github.com/haozi-team/panel/actions)
[![Report Card](https://goreportcard.com/badge/github.com/haozi-team/panel)](https://goreportcard.com/report/github.com/haozi-team/panel)
[![Codecov](https://codecov.io/gh/haozi-team/panel/branch/main/graph/badge.svg?token=XFT5NGNSRG)](https://codecov.io/gh/haozi-team/panel)
![License](https://img.shields.io/github/license/haozi-team/panel)
<p align="center">
<a href="https://github.com/haozi-team/panel/releases"><img src="https://img.shields.io/github/release/haozi-team/panel.svg"></a>
<a href="https://github.com/haozi-team/panel/actions"><img src="https://github.com/haozi-team/panel/actions/workflows/test.yml/badge.svg"></a>
<a href="https://goreportcard.com/report/github.com/haozi-team/panel"><img src="https://goreportcard.com/badge/github.com/haozi-team/panel"></a>
<a href="https://codecov.io/gh/haozi-team/panel"><img src="https://codecov.io/gh/haozi-team/panel/branch/main/graph/badge.svg?token=XFT5NGNSRG"></a>
<a href="https://img.shields.io/github/license/haozi-team/panel"><img src="https://img.shields.io/github/license/haozi-team/panel"></a>
</p>
这是耗子Linux面板,一种使用GO开发的轻量Linux服务器运维管理面板。
<p align="center">
[简体中文] | [<a href="README_EN.md">English</a>]
</p>
通知:严禁使用耗子Linux面板从事任何非法活动,非法站点请勿向我们请求任何形式的技术支持,如果在技术支持过程中发现非法内容,我们将立即停止技术支持并留存相关证据
耗子 Linux 面板是针对我们自身业务需要使用 Golang 开发的轻量 Linux 服务器运维管理面板,以 Apache 2.0 协议开源
#### 交流QQ群:[12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | QQ频道:[pd.qq.com/s/fyol46wfy](https://pd.qq.com/s/fyol46wfy)
免责声明:严禁使用耗子 Linux 面板从事任何非法活动,非法站点请勿向我们请求任何形式的技术支持,如果在技术支持过程中发现非法内容,我们将立即停止技术支持并留存相关证据。
广告: [`WeAvatar` 多端多元化的统一头像服务](https://weavatar.com)
交流QQ群:[12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | QQ频道:[pd.qq.com/s/fyol46wfy](https://pd.qq.com/s/fyol46wfy)
## UI 展示
![UI展示](ui.png)
## 运行环境
面板仅支持主流 `amd64` | `arm64` 系统的最新版本,且随着系统版本的更新,面板也会逐步停止对旧版本系统的支持
耗子Linux面板仅支持 `amd64` | `arm64` 架构下的主流系统的最新版本,不支持 `Ubuntu`,因为其发版太过频繁,难以维护
不支持 `Ubuntu`,因为其发版太过频繁,难以维护
低配机器建议使用 `Debian`,资源占用较 `RHEL` 系更低。其他机器建议使用 `AlmaLinux` | `RockyLinux`,维护周期更长也更稳定
低配机器建议使用 `Debian`,资源占用较 `RHEL` 系更低。其他机器建议使用 `RockyLinux` | `AlmaLinux`,维护周期更长
不在下表中的其他系统(OpenCloudOS 8、Anolis 8、CentOS Stream 8/9、Debian 11等),可自行尝试安装,但不保证能够正常运行,且不提供无偿技术支持(理论上不会有大问题)
不在下表中的其他系统(OpenCloudOS 8、Anolis 8、CentOS Stream 8/9、Debian 11等),可自行尝试安装,但不保证能够正常运行,且不提供技术支持(理论上不会有大问题)。
CentOS Stream 可使用迁移脚本迁移至支持的系统: [CentOS 8/9 迁移脚本](https://github.com/haozi-team/byecentos)
| 系统 | 版本 |
|------------|----|
| RHEL | 9 |
| RockyLinux | 9 |
| AlmaLinux | 9 |
| RockyLinux | 9 |
| Debian | 12 |
随着系统版本的不断更新,我们亦可能会终止部分过于老旧的系统的支持,以保证面板的稳定性。
## 安装面板
安装面板前,你需要了解LNMP环境的基本知识,以及如何处理常见的LNMP环境问题,我们不建议0基础的用户安装和使用耗子Linux面板。
安装面板前,你需要了解LNMP环境的基本知识,以及如何处理常见的LNMP环境问题,不建议0基础的用户安装和使用耗子Linux面板[推荐: 宝塔 - 简单好用服务器运维面板](https://www.bt.cn/?invite_code=M190eXRpZWE=)
如果你决定继续,请以`root`用户登录服务器,执行以下命令安装面板:
@@ -41,6 +51,18 @@
bash <(curl -sSL https://dl.cdn.haozi.net/panel/install_panel.sh)
```
## 卸载面板
优先建议备份数据重装系统,这样可以保证系统纯净。
如果你无法重装系统,请以`root`用户登录服务器,执行以下命令卸载面板:
```shell
bash <(curl -sSL https://dl.cdn.haozi.net/panel/uninstall_panel.sh)
```
卸载面板前请务必备份好所有数据,提前卸载面板全部插件。卸载后数据将**无法恢复**!
## 日常维护
使用`panel`命令进行日常维护:
@@ -49,25 +71,24 @@ bash <(curl -sSL https://dl.cdn.haozi.net/panel/install_panel.sh)
panel
```
在 [Wiki](https://github.com/haozi-team/panel/wiki) 中查看更多使用方法和技巧。
## 问题反馈
使用类问题,可在 [WePublish社区论坛](https://wepublish.cn/forums) 提问或QQ群`@汤姆猫`寻求帮助
使用类问题,可在 [WePublish 社区论坛](https://wepublish.cn/forums) 提问或QQ群`@坤坤`寻求 AI 帮助,亦可在群里寻求付费支持
面板自身问题,可在 GitHub 的`Issues`
页面提交问题反馈,注意[提问的智慧](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md)。
面板自身问题,可在 GitHub 的`Issues`页面提交问题反馈,注意[提问的智慧](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md)。
## 贡献代码
### 寻找/创建 Issue
您可以在 [Issue 列表](https://github.com/haozi-team/panel/issues) 中寻找或创建一个 Issue,留言表达想要处理该 Issue
的意愿,得到维护者的确认后,即可开始处理。
您可以在 [Issue 列表](https://github.com/haozi-team/panel/issues) 中寻找或创建一个 Issue,留言表达想要处理该 Issue 的意愿,得到维护者的确认后,即可开始处理。
### 创建 PR
- 在开发过程中,如果遇到问题可以随时在 Issue 中详尽描述该问题,以进一步沟通,但在此之前请确保自己已通过 Google
等方式尽可能的尝试解决问题
- PR 须提交至我们的极狐GitLab仓库[https://jihulab.com/haozi-team/panel](https://jihulab.com/haozi-team/panel)
- 在开发过程中,如果遇到问题可以随时在 Issue 中详尽描述该问题,以进一步沟通,但在此之前请确保自己已通过 Google 等方式尽可能的尝试解决问题;
- PR 须提交至我们的极狐 GitLab 仓库[https://jihulab.com/haozi-team/panel](https://jihulab.com/haozi-team/panel),勿在 GitHub 上提交
- 当 PR 开发完毕后,请为其添加 `🚀 Review Ready` 标签,维护者将及时进行评审;
- 我们非常欢迎您的贡献,将在下次发版时将您添加到首页贡献者中;❤️
@@ -79,39 +100,23 @@ panel
### CDN
- [盾云CDN](http://cdn.ddunyun.com/)
- [无畏云加速](https://su.sctes.com/register?code=8st689ujpmm2p)
- [又拍云](https://www.upyun.com/?utm_source=lianmeng&utm_medium=referral)
- [AnyCast.Ai](https://www.anycast.ai/)
- [迅捷](https://console.fastdun.com/)
- [云CDN](http://cdn.ddunyun.com/)
### 对象存储
- [又拍云](https://www.upyun.com/?utm_source=lianmeng&utm_medium=referral)
### DevOps
- [极狐GitLab](https://www.jihulab.com/)
- [极狐 GitLab](https://www.jihulab.com/)
#### 接受云资源和资金赞助,可通过QQ群咨询联系
## 行为准则
耗子Linux面板遵守业界通用的行为准则。任何违反行为准则的行为都可以报告给我们:
- 参与者将容忍反对意见。
- 参与者必须确保他们的语言和行为没有人身攻击和贬低个人言论。
- 在解释他人的言行时,参与者应始终保持良好的意图。
- 不能容忍可合理视为骚扰的行为。
## 安全说明
安全性是我们最关心的问题之一,我们已在生产环境广泛应用耗子Linux面板。
耗子Linux面板采用业界多种方案尽可能保证面板的安全性,但是我们不能保证面板的绝对安全性,**因此我们不对面板的安全性做任何保证**。
如果您在使用面板的过程中发现任何安全问题,请勿提交 Issue,可通过以下方式直接联系我们:
- 邮箱:i@haozi.net
- QQ826896000
**接受云资源和资金赞助,可通过QQ群咨询联系**
## 贡献者
+135
View File
@@ -0,0 +1,135 @@
<h1 align="center">HaoZi Linux Panel</h1>
<p align="center">
<a href="https://github.com/haozi-team/panel/releases"><img src="https://img.shields.io/github/release/haozi-team/panel.svg"></a>
<a href="https://github.com/haozi-team/panel/actions"><img src="https://github.com/haozi-team/panel/actions/workflows/test.yml/badge.svg"></a>
<a href="https://goreportcard.com/report/github.com/haozi-team/panel"><img src="https://goreportcard.com/badge/github.com/haozi-team/panel"></a>
<a href="https://codecov.io/gh/haozi-team/panel"><img src="https://codecov.io/gh/haozi-team/panel/branch/main/graph/badge.svg?token=XFT5NGNSRG"></a>
<a href="https://img.shields.io/github/license/haozi-team/panel"><img src="https://img.shields.io/github/license/haozi-team/panel"></a>
</p>
<p align="center">
[<a href="README.md">简体中文</a>] | [English]
</p>
The HaoZi Linux Panel is a lightweight Linux server operation and maintenance management panel developed using Golang for our own business needs. It is open source under the Apache 2.0 protocol.
Disclaimer: It is strictly prohibited to use the HaoZi Linux Panel to engage in any illegal activities. Please do not request any form of technical support from us for illegal sites. If illegal content is discovered during the technical support process, we will immediately stop technical support and retain relevant evidence.
Communication QQ group: [12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | QQ channel: [pd.qq.com/s/fyol46wfy](https://pd.qq.com/s/fyol46wfy)
## UI Screenshots
![UI Screenshots](ui.png)
## Operating Environment
HaoZi Linux Panel only supports the latest version of mainstream systems under the `amd64` | `arm64` architecture. It does not support `Ubuntu` because its releases are too frequent and difficult to maintain.
Recommended to use `Debian` for low-configuration machines, as its resource usage is lower than that of the `RHEL` system. For other machines, recommended to use `RockyLinux` | `AlmaLinux`, which has a longer maintenance cycle and is more stable.
For other systems not in the table below (OpenCloudOS 8, Anolis 8, CentOS Stream 8/9, Debian 11, etc.), you can try to install it yourself, but normal operation is not guaranteed, and free technical support is not provided (theoretically there will be no major question).
CentOS Stream can be migrated to a supported system using the migration script: [CentOS 8/9 Migration Script](https://github.com/haozi-team/byecentos)
| OS | Version |
|------------|---------|
| RHEL | 9 |
| RockyLinux | 9 |
| AlmaLinux | 9 |
| Debian | 12 |
As system versions are constantly updated, we may also terminate support for some older systems to ensure the stability of the panel.
## Install Panel
Before installing the panel, you need to understand the basic knowledge of the LNMP environment and how to deal with common LNMP environment problems. It is not recommended for users with zero basic knowledge to install and use HaoZi Linux Panel ([Recommended: aaPanel - easy-to-use server operation and maintenance panel](https://aapanel.com)).
If you decide to continue, please log in to the server as `root` user and execute the following command to install the panel:
```shell
bash <(curl -sSL https://dl.cdn.haozi.net/panel/install_panel.sh)
```
## Uninstall Panel
Recommended to back up data and reinstall the system first, so that the system can be kept clean.
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
bash <(curl -sSL https://dl.cdn.haozi.net/panel/uninstall_panel.sh)
```
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!
## Daily Maintenance
Use `panel` command for daily maintenance:
```shell
panel
```
See more usage methods and tips in [Wiki](https://github.com/haozi-team/panel/wiki).
## Feedback
For usage issues, you can ask questions in the [WePublish Community Forum](https://wepublish.cn/forums) or seek AI help in the QQ group `@坤坤`. 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/haozi-team/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://jihulab.com/haozi-team/panel](https://jihulab.com/haozi-team/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
### Server
- [盾云](https://www.ddunyun.com/aff/PNYAXMKI)
### CDN
- [无畏云加速](https://su.sctes.com/register?code=8st689ujpmm2p)
- [又拍云](https://www.upyun.com/?utm_source=lianmeng&utm_medium=referral)
- [AnyCast.Ai](https://www.anycast.ai/)
- [盾云CDN](http://cdn.ddunyun.com/)
### Object Storage
- [又拍云](https://www.upyun.com/?utm_source=lianmeng&utm_medium=referral)
### DevOps
- [JiHu GitLab](https://www.jihulab.com/)
**Accept cloud resources and financial sponsorship, you can contact us through QQ group**
## 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>
## Star History
<a href="https://star-history.com/#haozi-team/panel&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=haozi-team/panel&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=haozi-team/panel&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=haozi-team/panel&type=Date" />
</picture>
</a>
+25
View File
@@ -0,0 +1,25 @@
## 安全说明
安全性是我们最关心的问题之一,我们已在多个不同应用的生产环境广泛应用耗子Linux面板,至今无一例安全事故。
耗子Linux面板采用业界多种方案尽可能保证面板的安全性,但是我们不能保证面板的绝对安全性,**因此我们不对面板的安全性做任何保证**。
如果您在使用面板的过程中发现任何安全问题,请勿提交 Issue,可通过以下方式直接联系我们:
- 邮箱:i@haozi.net
- QQ826896000
致某些 CVE 爱好者:通过面板 `access_token` 执行的任何操作(包括且不限于:获取 root 权限、读取/写入系统敏感文件、执行任意 shell 命令等)均不被认为是安全问题,请不要刷此类报告浪费彼此的时间,这类低水平的报告对你的简历也没有任何帮助。
## Security Policy
Security is one of our biggest concerns. We have widely used HaoZi Linux Panel in production environments for many different applications, and there has been no safety incident so far.
HaoZi Linux Panel adopts various solutions in the industry to ensure the security of the panel as much as possible, but we cannot guarantee the absolute security of the panel, **therefore we do not make any guarantees on the security of the panel**.
If you find any security issues while using the panel, please do not submit an Issue. You can contact us directly through the following methods:
- Email: i@haozi.net
- Telegram: @devhaozi
To some CVE enthusiasts: Any operation performed through the panel `access_token` (including but not limited to: obtaining root permissions, reading/writing system sensitive files, executing arbitrary shell commands, etc.) is not considered a security issue, please Dont waste each others time with these types of reports. These low-level reports wont do anything to help your resume.
+22 -16
View File
@@ -123,7 +123,7 @@ func (receiver *Panel) Handle(ctx console.Context) error {
return nil
}
port := tools.ExecShell(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
port := tools.Exec(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
color.Greenln("用户名: " + user.Username)
color.Greenln("密码: " + password)
@@ -131,7 +131,7 @@ func (receiver *Panel) Handle(ctx console.Context) error {
color.Greenln("面板入口: " + services.NewSettingImpl().Get(models.SettingKeyEntrance, "/"))
case "getPort":
port := tools.ExecShell("cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}'")
port := tools.Exec("cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}'")
color.Greenln("面板端口: " + port)
case "getEntrance":
@@ -243,7 +243,7 @@ func (receiver *Panel) Handle(ctx console.Context) error {
}
backupFile := path + "/" + website.Name + "_" + carbon.Now().ToShortDateTimeString() + ".zip"
tools.ExecShell(`cd '` + website.Path + `' && zip -r '` + backupFile + `' .`)
tools.Exec(`cd '` + website.Path + `' && zip -r '` + backupFile + `' .`)
color.Greenln("|-备份成功")
case "mysql":
@@ -259,21 +259,24 @@ func (receiver *Panel) Handle(ctx console.Context) error {
color.Greenln("|-目标MySQL数据库: " + name)
color.Greenln("|-开始导出")
tools.ExecShell(`mysqldump -uroot ` + name + ` > /tmp/` + backupFile + ` 2>&1`)
tools.Exec(`mysqldump -uroot ` + name + ` > /tmp/` + backupFile + ` 2>&1`)
color.Greenln("|-导出成功")
color.Greenln("|-开始压缩")
tools.ExecShell("cd /tmp && zip -r " + backupFile + ".zip " + backupFile)
tools.RemoveFile("/tmp/" + backupFile)
tools.Exec("cd /tmp && zip -r " + backupFile + ".zip " + backupFile)
tools.Remove("/tmp/" + backupFile)
color.Greenln("|-压缩成功")
color.Greenln("|-开始移动")
tools.Mv("/tmp/"+backupFile+".zip", path+"/"+backupFile+".zip")
if _, err := tools.Mv("/tmp/"+backupFile+".zip", path+"/"+backupFile+".zip"); err != nil {
color.Redln("|-移动失败: " + err.Error())
return nil
}
color.Greenln("|-移动成功")
_ = os.Unsetenv("MYSQL_PWD")
color.Greenln("|-备份成功")
case "postgresql":
backupFile := name + "_" + carbon.Now().ToShortDateTimeString() + ".sql"
check := tools.ExecShell(`su - postgres -c "psql -l" 2>&1`)
check := tools.Exec(`su - postgres -c "psql -l" 2>&1`)
if strings.Contains(check, name) {
color.Redln("|-数据库不存在")
color.Greenln(hr)
@@ -282,14 +285,17 @@ func (receiver *Panel) Handle(ctx console.Context) error {
color.Greenln("|-目标PostgreSQL数据库: " + name)
color.Greenln("|-开始导出")
tools.ExecShell(`su - postgres -c "pg_dump '` + name + `'" > /tmp/` + backupFile + ` 2>&1`)
tools.Exec(`su - postgres -c "pg_dump '` + name + `'" > /tmp/` + backupFile + ` 2>&1`)
color.Greenln("|-导出成功")
color.Greenln("|-开始压缩")
tools.ExecShell("cd /tmp && zip -r " + backupFile + ".zip " + backupFile)
tools.RemoveFile("/tmp/" + backupFile)
tools.Exec("cd /tmp && zip -r " + backupFile + ".zip " + backupFile)
tools.Remove("/tmp/" + backupFile)
color.Greenln("|-压缩成功")
color.Greenln("|-开始移动")
tools.Mv("/tmp/"+backupFile+".zip", path+"/"+backupFile+".zip")
if _, err := tools.Mv("/tmp/"+backupFile+".zip", path+"/"+backupFile+".zip"); err != nil {
color.Redln("|-移动失败: " + err.Error())
return nil
}
color.Greenln("|-移动成功")
color.Greenln("|-备份成功")
}
@@ -316,7 +322,7 @@ 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)
tools.RemoveFile(fileToDelete)
tools.Remove(fileToDelete)
}
color.Greenln("|-清理完成")
color.Greenln(hr)
@@ -352,8 +358,8 @@ func (receiver *Panel) Handle(ctx console.Context) error {
}
backupPath := "/www/wwwlogs/" + website.Name + "_" + carbon.Now().ToShortDateTimeString() + ".log.zip"
tools.ExecShell(`cd /www/wwwlogs && zip -r ` + backupPath + ` ` + website.Name + ".log")
tools.ExecShell(`echo "" > ` + logPath)
tools.Exec(`cd /www/wwwlogs && zip -r ` + backupPath + ` ` + website.Name + ".log")
tools.Exec(`echo "" > ` + logPath)
color.Greenln("|-切割成功")
color.Greenln(hr)
@@ -378,7 +384,7 @@ 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)
tools.RemoveFile(fileToDelete)
tools.Remove(fileToDelete)
}
color.Greenln("|-清理完成")
color.Greenln(hr)
+49 -67
View File
@@ -25,7 +25,8 @@ func NewCronController() *CronController {
}
}
func (c *CronController) List(ctx http.Context) {
// List 获取计划任务列表
func (c *CronController) List(ctx http.Context) http.Response {
limit := ctx.Request().QueryInt("limit")
page := ctx.Request().QueryInt("page")
@@ -34,17 +35,17 @@ func (c *CronController) List(ctx http.Context) {
err := facades.Orm().Query().Paginate(page, limit, &crons, &total)
if err != nil {
facades.Log().Error("[面板][CronController] 查询计划任务列表失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
Success(ctx, http.Json{
return Success(ctx, http.Json{
"total": total,
"items": crons,
})
}
func (c *CronController) Add(ctx http.Context) {
// Add 添加计划任务
func (c *CronController) Add(ctx http.Context) http.Response {
validator, err := ctx.Request().Validate(map[string]string{
"name": "required|min_len:1|max_len:255",
"time": "required",
@@ -53,18 +54,15 @@ func (c *CronController) Add(ctx http.Context) {
"backup_type": "required_if:type,backup|in:website,mysql,postgresql",
})
if err != nil {
Error(ctx, http.StatusBadRequest, err.Error())
return
return Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
// 单独验证时间格式
if !regexp.MustCompile(`^((\*|\d+|\d+-\d+|\d+/\d+|\d+-\d+/\d+|\*/\d+)(,(\*|\d+|\d+-\d+|\d+/\d+|\d+-\d+/\d+|\*/\d+))*\s?){5}$`).MatchString(ctx.Request().Input("time")) {
Error(ctx, http.StatusBadRequest, "时间格式错误")
return
return Error(ctx, http.StatusBadRequest, "时间格式错误")
}
shell := ctx.Request().Input("script")
@@ -114,21 +112,18 @@ panel cutoff ${name} ${save} 2>&1
shellLogDir := "/www/server/cron/logs/"
if !tools.Exists(shellDir) {
facades.Log().Error("[面板][CronController] 计划任务目录不存在")
Error(ctx, http.StatusInternalServerError, "计划任务目录不存在")
return
return Error(ctx, http.StatusInternalServerError, "计划任务目录不存在")
}
if !tools.Exists(shellLogDir) {
facades.Log().Error("[面板][CronController] 计划任务日志目录不存在")
Error(ctx, http.StatusInternalServerError, "计划任务日志目录不存在")
return
return Error(ctx, http.StatusInternalServerError, "计划任务日志目录不存在")
}
shellFile := strconv.Itoa(int(carbon.Now().Timestamp())) + tools.RandomString(16)
if !tools.WriteFile(shellDir+shellFile+".sh", shell, 0700) {
if !tools.Write(shellDir+shellFile+".sh", shell, 0700) {
facades.Log().Error("[面板][CronController] 创建计划任务脚本失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
tools.ExecShell("dos2unix " + shellDir + shellFile + ".sh")
tools.Exec("dos2unix " + shellDir + shellFile + ".sh")
var cron models.Cron
cron.Name = ctx.Request().Input("name")
@@ -141,60 +136,54 @@ panel cutoff ${name} ${save} 2>&1
err = facades.Orm().Query().Create(&cron)
if err != nil {
facades.Log().Error("[面板][CronController] 创建计划任务失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
c.cron.AddToSystem(cron)
Success(ctx, http.Json{
return Success(ctx, http.Json{
"id": cron.ID,
})
}
// Script 获取脚本内容
func (c *CronController) Script(ctx http.Context) {
func (c *CronController) Script(ctx http.Context) http.Response {
var cron models.Cron
err := facades.Orm().Query().Where("id", ctx.Request().Input("id")).FirstOrFail(&cron)
if err != nil {
Error(ctx, http.StatusBadRequest, "计划任务不存在")
return
return Error(ctx, http.StatusBadRequest, "计划任务不存在")
}
Success(ctx, tools.ReadFile(cron.Shell))
return Success(ctx, tools.Read(cron.Shell))
}
func (c *CronController) Update(ctx http.Context) {
// Update 更新计划任务
func (c *CronController) Update(ctx http.Context) http.Response {
validator, err := ctx.Request().Validate(map[string]string{
"name": "required|min_len:1|max_len:255",
"time": "required",
"script": "required",
})
if err != nil {
Error(ctx, http.StatusBadRequest, err.Error())
return
return Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
// 单独验证时间格式
if !regexp.MustCompile(`^((\*|\d+|\d+-\d+|\d+/\d+|\d+-\d+/\d+|\*/\d+)(,(\*|\d+|\d+-\d+|\d+/\d+|\d+-\d+/\d+|\*/\d+))*\s?){5}$`).MatchString(ctx.Request().Input("time")) {
Error(ctx, http.StatusBadRequest, "时间格式错误")
return
return Error(ctx, http.StatusBadRequest, "时间格式错误")
}
var cron models.Cron
err = facades.Orm().Query().Where("id", ctx.Request().Input("id")).FirstOrFail(&cron)
if err != nil {
Error(ctx, http.StatusBadRequest, "计划任务不存在")
return
return Error(ctx, http.StatusBadRequest, "计划任务不存在")
}
if !cron.Status {
Error(ctx, http.StatusBadRequest, "计划任务已禁用")
return
return Error(ctx, http.StatusBadRequest, "计划任务已禁用")
}
cron.Time = ctx.Request().Input("time")
@@ -202,72 +191,66 @@ func (c *CronController) Update(ctx http.Context) {
err = facades.Orm().Query().Save(&cron)
if err != nil {
facades.Log().Error("[面板][CronController] 更新计划任务失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
if !tools.WriteFile(cron.Shell, ctx.Request().Input("script"), 0644) {
if !tools.Write(cron.Shell, ctx.Request().Input("script"), 0644) {
facades.Log().Error("[面板][CronController] 更新计划任务脚本失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
tools.ExecShell("dos2unix " + cron.Shell)
tools.Exec("dos2unix " + cron.Shell)
c.cron.DeleteFromSystem(cron)
if cron.Status {
c.cron.AddToSystem(cron)
}
Success(ctx, nil)
return Success(ctx, nil)
}
func (c *CronController) Delete(ctx http.Context) {
// Delete 删除计划任务
func (c *CronController) Delete(ctx http.Context) http.Response {
var cron models.Cron
err := facades.Orm().Query().Where("id", ctx.Request().Input("id")).FirstOrFail(&cron)
if err != nil {
Error(ctx, http.StatusBadRequest, "计划任务不存在")
return
return Error(ctx, http.StatusBadRequest, "计划任务不存在")
}
c.cron.DeleteFromSystem(cron)
tools.RemoveFile(cron.Shell)
tools.Remove(cron.Shell)
_, err = facades.Orm().Query().Delete(&cron)
if err != nil {
facades.Log().Error("[面板][CronController] 删除计划任务失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
Success(ctx, nil)
return Success(ctx, nil)
}
func (c *CronController) Status(ctx http.Context) {
// Status 更新计划任务状态
func (c *CronController) Status(ctx http.Context) http.Response {
validator, err := ctx.Request().Validate(map[string]string{
"status": "bool",
})
if err != nil {
Error(ctx, http.StatusBadRequest, err.Error())
return
return Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
var cron models.Cron
err = facades.Orm().Query().Where("id", ctx.Request().Input("id")).FirstOrFail(&cron)
if err != nil {
Error(ctx, http.StatusBadRequest, "计划任务不存在")
return
return Error(ctx, http.StatusBadRequest, "计划任务不存在")
}
cron.Status = ctx.Request().InputBool("status")
err = facades.Orm().Query().Save(&cron)
if err != nil {
facades.Log().Error("[面板][CronController] 更新计划任务状态失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
c.cron.DeleteFromSystem(cron)
@@ -275,21 +258,20 @@ func (c *CronController) Status(ctx http.Context) {
c.cron.AddToSystem(cron)
}
Success(ctx, nil)
return Success(ctx, nil)
}
func (c *CronController) Log(ctx http.Context) {
// Log 获取计划任务日志
func (c *CronController) Log(ctx http.Context) http.Response {
var cron models.Cron
err := facades.Orm().Query().Where("id", ctx.Request().Input("id")).FirstOrFail(&cron)
if err != nil {
Error(ctx, http.StatusBadRequest, "计划任务不存在")
return
return Error(ctx, http.StatusBadRequest, "计划任务不存在")
}
if !tools.Exists(cron.Log) {
Error(ctx, http.StatusBadRequest, "日志文件不存在")
return
return Error(ctx, http.StatusBadRequest, "日志文件不存在")
}
Success(ctx, tools.ReadFile(cron.Log))
return Success(ctx, tools.Read(cron.Log))
}
-7
View File
@@ -1,9 +1,5 @@
package controllers
import (
"github.com/goravel/framework/contracts/http"
)
type FileController struct {
// Dependent services
}
@@ -13,6 +9,3 @@ func NewFileController() *FileController {
// Inject services
}
}
func (r *FileController) Index(ctx http.Context) {
}
+10 -14
View File
@@ -9,35 +9,33 @@ import (
"panel/app/services"
)
func Success(ctx http.Context, data any) {
ctx.Response().Success().Json(http.Json{
func Success(ctx http.Context, data any) http.Response {
return ctx.Response().Success().Json(http.Json{
"code": 0,
"message": "success",
"data": data,
})
}
func Error(ctx http.Context, code int, message any) {
ctx.Response().Json(http.StatusOK, http.Json{
func Error(ctx http.Context, code int, message any) http.Response {
return ctx.Response().Json(http.StatusOK, http.Json{
"code": code,
"message": message,
})
}
// Check 检查插件是否可用
func Check(ctx http.Context, slug string) bool {
func Check(ctx http.Context, slug string) http.Response {
plugin := services.NewPluginImpl().GetBySlug(slug)
installedPlugin := services.NewPluginImpl().GetInstalledBySlug(slug)
installedPlugins, err := services.NewPluginImpl().AllInstalled()
if err != nil {
facades.Log().Error("[面板][插件] 获取已安装插件失败")
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return false
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
if installedPlugin.Version != plugin.Version || installedPlugin.Slug != plugin.Slug {
Error(ctx, http.StatusForbidden, "插件 "+slug+" 需要更新至 "+plugin.Version+" 版本")
return false
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 需要更新至 "+plugin.Version+" 版本")
}
var lock sync.RWMutex
@@ -54,8 +52,7 @@ func Check(ctx http.Context, slug string) bool {
_, requireFound := pluginsMap[require]
lock.RUnlock()
if !requireFound {
Error(ctx, http.StatusForbidden, "插件 "+slug+" 需要依赖 "+require+" 插件")
return false
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 需要依赖 "+require+" 插件")
}
}
@@ -64,10 +61,9 @@ func Check(ctx http.Context, slug string) bool {
_, excludeFound := pluginsMap[exclude]
lock.RUnlock()
if excludeFound {
Error(ctx, http.StatusForbidden, "插件 "+slug+" 不兼容 "+exclude+" 插件")
return false
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 不兼容 "+exclude+" 插件")
}
}
return true
return nil
}
+36 -35
View File
@@ -30,22 +30,23 @@ func NewInfoController() *InfoController {
}
}
func (c *InfoController) Name(ctx http.Context) {
// Name 获取面板名称
func (c *InfoController) Name(ctx http.Context) http.Response {
var setting models.Setting
err := facades.Orm().Query().Where("key", "name").First(&setting)
if err != nil {
facades.Log().Error("[面板][InfoController] 查询面板名称失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
Success(ctx, http.Json{
return Success(ctx, http.Json{
"name": setting.Value,
})
}
func (c *InfoController) Menu(ctx http.Context) {
Success(ctx, []MenuItem{
// Menu 获取面板菜单
func (c *InfoController) Menu(ctx http.Context) http.Response {
return Success(ctx, []MenuItem{
{Name: "home", Title: "主页", Icon: "layui-icon-home", Jump: "/"},
{Name: "website", Title: "网站管理", Icon: "layui-icon-website", Jump: "website/list"},
{Name: "monitor", Title: "资源监控", Icon: "layui-icon-chart-screen", Jump: "monitor"},
@@ -58,13 +59,13 @@ func (c *InfoController) Menu(ctx http.Context) {
})
}
func (c *InfoController) HomePlugins(ctx http.Context) {
// HomePlugins 获取首页插件
func (c *InfoController) HomePlugins(ctx http.Context) http.Response {
var plugins []models.Plugin
err := facades.Orm().Query().Where("show", 1).Find(&plugins)
if err != nil {
facades.Log().Error("[面板][InfoController] 查询首页插件失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
type pluginsData struct {
@@ -80,29 +81,31 @@ func (c *InfoController) HomePlugins(ctx http.Context) {
})
}
Success(ctx, pluginsJson)
return Success(ctx, pluginsJson)
}
func (c *InfoController) NowMonitor(ctx http.Context) {
Success(ctx, tools.GetMonitoringInfo())
// NowMonitor 获取当前监控信息
func (c *InfoController) NowMonitor(ctx http.Context) http.Response {
return Success(ctx, tools.GetMonitoringInfo())
}
func (c *InfoController) SystemInfo(ctx http.Context) {
// SystemInfo 获取系统信息
func (c *InfoController) SystemInfo(ctx http.Context) http.Response {
monitorInfo := tools.GetMonitoringInfo()
Success(ctx, http.Json{
return Success(ctx, http.Json{
"os_name": monitorInfo.Host.Platform + " " + monitorInfo.Host.PlatformVersion,
"uptime": fmt.Sprintf("%.2f", float64(monitorInfo.Host.Uptime)/86400),
"panel_version": facades.Config().GetString("panel.version"),
})
}
func (c *InfoController) InstalledDbAndPhp(ctx http.Context) {
// InstalledDbAndPhp 获取已安装的数据库和 PHP 版本
func (c *InfoController) InstalledDbAndPhp(ctx http.Context) http.Response {
var php []models.Plugin
err := facades.Orm().Query().Where("slug like ?", "php%").Find(&php)
if err != nil {
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
var mysql models.Plugin
@@ -134,33 +137,32 @@ func (c *InfoController) InstalledDbAndPhp(ctx http.Context) {
phpData = append(phpData, data{Slug: strings.ReplaceAll(p.Slug, "php", ""), Name: c.plugin.GetBySlug(p.Slug).Name})
}
Success(ctx, http.Json{
return Success(ctx, http.Json{
"php": phpData,
"mysql": mysqlInstalled,
"postgresql": postgresqlInstalled,
})
}
func (c *InfoController) CheckUpdate(ctx http.Context) {
// CheckUpdate 检查面板更新
func (c *InfoController) CheckUpdate(ctx http.Context) http.Response {
version := facades.Config().GetString("panel.version")
remote, err := tools.GetLatestPanelVersion()
if err != nil {
Error(ctx, http.StatusInternalServerError, "获取最新版本失败")
return
return Error(ctx, http.StatusInternalServerError, "获取最新版本失败")
}
if version == remote.Version {
Success(ctx, http.Json{
return Success(ctx, http.Json{
"update": false,
"version": remote.Version,
"name": remote.Name,
"body": remote.Body,
"date": remote.Date,
})
return
}
Success(ctx, http.Json{
return Success(ctx, http.Json{
"update": true,
"version": remote.Version,
"name": remote.Name,
@@ -169,33 +171,32 @@ func (c *InfoController) CheckUpdate(ctx http.Context) {
})
}
func (c *InfoController) Update(ctx http.Context) {
// Update 更新面板
func (c *InfoController) Update(ctx http.Context) http.Response {
var task models.Task
err := facades.Orm().Query().Where("status", models.TaskStatusRunning).OrWhere("status", models.TaskStatusWaiting).FirstOrFail(&task)
if err == nil {
Error(ctx, http.StatusInternalServerError, "当前有任务正在执行,禁止更新")
return
return Error(ctx, http.StatusInternalServerError, "当前有任务正在执行,禁止更新")
}
proxy := ctx.Request().InputBool("proxy")
err = tools.UpdatePanel(proxy)
if err != nil {
facades.Log().Error("[面板][InfoController] 更新面板失败 ", err.Error())
Error(ctx, http.StatusInternalServerError, "更新失败: "+err.Error())
return
return Error(ctx, http.StatusInternalServerError, "更新失败: "+err.Error())
}
Success(ctx, nil)
return Success(ctx, nil)
}
func (c *InfoController) Restart(ctx http.Context) {
// Restart 重启面板
func (c *InfoController) Restart(ctx http.Context) http.Response {
var task models.Task
err := facades.Orm().Query().Where("status", models.TaskStatusRunning).OrWhere("status", models.TaskStatusWaiting).FirstOrFail(&task)
if err == nil {
Error(ctx, http.StatusInternalServerError, "当前有任务正在执行,禁止重启")
return
return Error(ctx, http.StatusInternalServerError, "当前有任务正在执行,禁止重启")
}
tools.ExecShell("systemctl restart panel")
Success(ctx, nil)
tools.Exec("systemctl restart panel")
return Success(ctx, nil)
}
+15 -20
View File
@@ -22,56 +22,53 @@ func NewMonitorController() *MonitorController {
}
// Switch 监控开关
func (r *MonitorController) Switch(ctx http.Context) {
func (r *MonitorController) Switch(ctx http.Context) http.Response {
value := ctx.Request().InputBool("switch")
err := r.setting.Set(models.SettingKeyMonitor, cast.ToString(value))
if err != nil {
facades.Log().Error("[面板][MonitorController] 更新监控开关失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
Success(ctx, nil)
return Success(ctx, nil)
}
// SaveDays 保存监控天数
func (r *MonitorController) SaveDays(ctx http.Context) {
func (r *MonitorController) SaveDays(ctx http.Context) http.Response {
days := ctx.Request().Input("days")
err := r.setting.Set(models.SettingKeyMonitorDays, days)
if err != nil {
facades.Log().Error("[面板][MonitorController] 更新监控天数失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
Success(ctx, nil)
return Success(ctx, nil)
}
// SwitchAndDays 监控开关和监控天数
func (r *MonitorController) SwitchAndDays(ctx http.Context) {
func (r *MonitorController) SwitchAndDays(ctx http.Context) http.Response {
monitor := r.setting.Get(models.SettingKeyMonitor)
monitorDays := r.setting.Get(models.SettingKeyMonitorDays)
Success(ctx, http.Json{
return Success(ctx, http.Json{
"switch": cast.ToBool(monitor),
"days": cast.ToInt(monitorDays),
})
}
// Clear 清空监控数据
func (r *MonitorController) Clear(ctx http.Context) {
func (r *MonitorController) Clear(ctx http.Context) http.Response {
_, err := facades.Orm().Query().Where("1 = 1").Delete(&models.Monitor{})
if err != nil {
facades.Log().Error("[面板][MonitorController] 清空监控数据失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
Success(ctx, nil)
return Success(ctx, nil)
}
// List 监控数据列表
func (r *MonitorController) List(ctx http.Context) {
func (r *MonitorController) List(ctx http.Context) http.Response {
start := ctx.Request().InputInt64("start")
end := ctx.Request().InputInt64("end")
startTime := carbon.FromTimestampMilli(start)
@@ -81,13 +78,11 @@ func (r *MonitorController) List(ctx http.Context) {
err := facades.Orm().Query().Where("created_at >= ?", startTime.ToDateTimeString()).Where("created_at <= ?", endTime.ToDateTimeString()).Get(&monitors)
if err != nil {
facades.Log().Error("[面板][MonitorController] 查询监控数据失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
if len(monitors) == 0 {
Error(ctx, http.StatusNotFound, "监控数据为空")
return
return Error(ctx, http.StatusNotFound, "监控数据为空")
}
type load struct {
@@ -172,5 +167,5 @@ func (r *MonitorController) List(ctx http.Context) {
bytesRecv2 = 0
}
Success(ctx, data)
return Success(ctx, data)
}
+29 -47
View File
@@ -23,11 +23,11 @@ func NewPluginController() *PluginController {
}
// List 列出所有插件
func (r *PluginController) List(ctx http.Context) {
func (r *PluginController) List(ctx http.Context) http.Response {
plugins := r.plugin.All()
installedPlugins, err := r.plugin.AllInstalled()
if err != nil {
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
var lock sync.RWMutex
@@ -72,24 +72,22 @@ func (r *PluginController) List(ctx http.Context) {
})
}
Success(ctx, p)
return Success(ctx, p)
}
// Install 安装插件
func (r *PluginController) Install(ctx http.Context) {
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().Error("[面板][PluginController] 获取已安装插件失败")
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
if installedPlugin.ID != 0 {
Error(ctx, http.StatusBadRequest, "插件已安装")
return
return Error(ctx, http.StatusBadRequest, "插件已安装")
}
var lock sync.RWMutex
@@ -106,8 +104,7 @@ func (r *PluginController) Install(ctx http.Context) {
_, requireFound := pluginsMap[require]
lock.RUnlock()
if !requireFound {
Error(ctx, http.StatusForbidden, "插件 "+slug+" 需要依赖 "+require+" 插件")
return
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 需要依赖 "+require+" 插件")
}
}
@@ -116,8 +113,7 @@ func (r *PluginController) Install(ctx http.Context) {
_, excludeFound := pluginsMap[exclude]
lock.RUnlock()
if excludeFound {
Error(ctx, http.StatusForbidden, "插件 "+slug+" 不兼容 "+exclude+" 插件")
return
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 不兼容 "+exclude+" 插件")
}
}
@@ -128,29 +124,26 @@ func (r *PluginController) Install(ctx http.Context) {
task.Log = "/tmp/" + plugin.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[面板][PluginController] 创建任务失败: " + err.Error())
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
r.task.Process(task.ID)
Success(ctx, "任务已提交")
return Success(ctx, "任务已提交")
}
// Uninstall 卸载插件
func (r *PluginController) Uninstall(ctx http.Context) {
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().Error("[面板][PluginController] 获取已安装插件失败")
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
if installedPlugin.ID == 0 {
Error(ctx, http.StatusBadRequest, "插件未安装")
return
return Error(ctx, http.StatusBadRequest, "插件未安装")
}
var lock sync.RWMutex
@@ -167,8 +160,7 @@ func (r *PluginController) Uninstall(ctx http.Context) {
_, requireFound := pluginsMap[require]
lock.RUnlock()
if !requireFound {
Error(ctx, http.StatusForbidden, "插件 "+slug+" 需要依赖 "+require+" 插件")
return
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 需要依赖 "+require+" 插件")
}
}
@@ -177,8 +169,7 @@ func (r *PluginController) Uninstall(ctx http.Context) {
_, excludeFound := pluginsMap[exclude]
lock.RUnlock()
if excludeFound {
Error(ctx, http.StatusForbidden, "插件 "+slug+" 不兼容 "+exclude+" 插件")
return
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 不兼容 "+exclude+" 插件")
}
}
@@ -189,29 +180,26 @@ func (r *PluginController) Uninstall(ctx http.Context) {
task.Log = "/tmp/" + plugin.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[面板][PluginController] 创建任务失败: " + err.Error())
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
r.task.Process(task.ID)
Success(ctx, "任务已提交")
return Success(ctx, "任务已提交")
}
// Update 更新插件
func (r *PluginController) Update(ctx http.Context) {
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().Error("[面板][PluginController] 获取已安装插件失败")
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
if installedPlugin.ID == 0 {
Error(ctx, http.StatusBadRequest, "插件未安装")
return
return Error(ctx, http.StatusBadRequest, "插件未安装")
}
var lock sync.RWMutex
@@ -228,8 +216,7 @@ func (r *PluginController) Update(ctx http.Context) {
_, requireFound := pluginsMap[require]
lock.RUnlock()
if !requireFound {
Error(ctx, http.StatusForbidden, "插件 "+slug+" 需要依赖 "+require+" 插件")
return
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 需要依赖 "+require+" 插件")
}
}
@@ -238,8 +225,7 @@ func (r *PluginController) Update(ctx http.Context) {
_, excludeFound := pluginsMap[exclude]
lock.RUnlock()
if excludeFound {
Error(ctx, http.StatusForbidden, "插件 "+slug+" 不兼容 "+exclude+" 插件")
return
return Error(ctx, http.StatusForbidden, "插件 "+slug+" 不兼容 "+exclude+" 插件")
}
}
@@ -250,36 +236,32 @@ func (r *PluginController) Update(ctx http.Context) {
task.Log = "/tmp/" + plugin.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[面板][PluginController] 创建任务失败: " + err.Error())
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
r.task.Process(task.ID)
Success(ctx, "任务已提交")
return Success(ctx, "任务已提交")
}
// UpdateShow 更新插件首页显示状态
func (r *PluginController) UpdateShow(ctx http.Context) {
func (r *PluginController) UpdateShow(ctx http.Context) http.Response {
slug := ctx.Request().Input("slug")
show := ctx.Request().InputBool("show")
var plugin models.Plugin
if err := facades.Orm().Query().Where("slug", slug).First(&plugin); err != nil {
facades.Log().Error("[面板][PluginController] 查询插件失败: " + err.Error())
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
if plugin.ID == 0 {
Error(ctx, http.StatusBadRequest, "插件未安装")
return
return Error(ctx, http.StatusBadRequest, "插件未安装")
}
plugin.Show = show
if err := facades.Orm().Query().Save(&plugin); err != nil {
facades.Log().Error("[面板][PluginController] 更新插件失败: " + err.Error())
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
Success(ctx, "操作成功")
return Success(ctx, "操作成功")
}
@@ -34,122 +34,121 @@ type Jail struct {
}
// Status 获取运行状态
func (c *Fail2banController) Status(ctx http.Context) {
if !controllers.Check(ctx, "fail2ban") {
return
func (c *Fail2banController) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
}
status := tools.ExecShell("systemctl status fail2ban | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status fail2ban | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取服务运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取服务运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Reload 重载配置
func (c *Fail2banController) Reload(ctx http.Context) {
if !controllers.Check(ctx, "fail2ban") {
return
func (c *Fail2banController) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
}
tools.ExecShell("systemctl reload fail2ban")
status := tools.ExecShell("systemctl status fail2ban | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl reload fail2ban")
status := tools.Exec("systemctl status fail2ban | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取服务运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取服务运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Restart 重启服务
func (c *Fail2banController) Restart(ctx http.Context) {
if !controllers.Check(ctx, "fail2ban") {
return
func (c *Fail2banController) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
}
tools.ExecShell("systemctl restart fail2ban")
status := tools.ExecShell("systemctl status fail2ban | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl restart fail2ban")
status := tools.Exec("systemctl status fail2ban | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取服务运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取服务运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Start 启动服务
func (c *Fail2banController) Start(ctx http.Context) {
if !controllers.Check(ctx, "fail2ban") {
return
func (c *Fail2banController) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
}
tools.ExecShell("systemctl start fail2ban")
status := tools.ExecShell("systemctl status fail2ban | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl start fail2ban")
status := tools.Exec("systemctl status fail2ban | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取服务运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取服务运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Stop 停止服务
func (c *Fail2banController) Stop(ctx http.Context) {
if !controllers.Check(ctx, "fail2ban") {
return
func (c *Fail2banController) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
}
tools.ExecShell("systemctl stop fail2ban")
status := tools.ExecShell("systemctl status fail2ban | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl stop fail2ban")
status := tools.Exec("systemctl status fail2ban | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取服务运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取服务运行状态失败")
}
if status != "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// List 所有 Fail2ban 规则
func (c *Fail2banController) List(ctx http.Context) {
if !controllers.Check(ctx, "fail2ban") {
return
func (c *Fail2banController) List(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
}
page := ctx.Request().QueryInt("page", 1)
limit := ctx.Request().QueryInt("limit", 10)
raw := tools.ReadFile("/etc/fail2ban/jail.local")
raw := tools.Read("/etc/fail2ban/jail.local")
if len(raw) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "Fail2ban 规则为空")
return
return controllers.Error(ctx, http.StatusBadRequest, "Fail2ban 规则为空")
}
jailList := regexp.MustCompile(`\[(.*?)]`).FindAllStringSubmatch(raw, -1)
if len(jailList) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "Fail2ban 规则为空")
return
return controllers.Error(ctx, http.StatusBadRequest, "Fail2ban 规则为空")
}
var jails []Jail
@@ -182,27 +181,27 @@ func (c *Fail2banController) List(ctx http.Context) {
startIndex := (page - 1) * limit
endIndex := page * limit
if startIndex > len(jails) {
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []Jail{},
})
return
}
if endIndex > len(jails) {
endIndex = len(jails)
}
pagedJails := jails[startIndex:endIndex]
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": len(jails),
"items": pagedJails,
})
}
// Add 添加 Fail2ban 规则
func (c *Fail2banController) Add(ctx http.Context) {
if !controllers.Check(ctx, "fail2ban") {
return
func (c *Fail2banController) Add(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -215,12 +214,10 @@ func (c *Fail2banController) Add(ctx http.Context) {
"website_path": "required_if:type,website",
})
if err != nil {
controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
return
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
jailName := ctx.Request().Input("name")
@@ -231,10 +228,9 @@ func (c *Fail2banController) Add(ctx http.Context) {
jailWebsiteMode := ctx.Request().Input("website_mode")
jailWebsitePath := ctx.Request().Input("website_path")
raw := tools.ReadFile("/etc/fail2ban/jail.local")
raw := tools.Read("/etc/fail2ban/jail.local")
if strings.Contains(raw, "["+jailName+"]") || (strings.Contains(raw, "["+jailName+"]"+"-cc") && jailWebsiteMode == "cc") || (strings.Contains(raw, "["+jailName+"]"+"-path") && jailWebsiteMode == "path") {
controllers.Error(ctx, http.StatusUnprocessableEntity, "规则已存在")
return
return controllers.Error(ctx, http.StatusUnprocessableEntity, "规则已存在")
}
switch jailType {
@@ -242,13 +238,11 @@ func (c *Fail2banController) Add(ctx http.Context) {
var website models.Website
err := facades.Orm().Query().Where("name", jailName).FirstOrFail(&website)
if err != nil {
controllers.Error(ctx, http.StatusUnprocessableEntity, "网站不存在")
return
return controllers.Error(ctx, http.StatusUnprocessableEntity, "网站不存在")
}
config, err := c.website.GetConfig(int(website.ID))
if err != nil {
controllers.Error(ctx, http.StatusUnprocessableEntity, "获取网站配置失败")
return
return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取网站配置失败")
}
var ports string
for _, port := range config.Ports {
@@ -273,7 +267,7 @@ logpath = /www/wwwlogs/` + website.Name + `.log
# ` + jailName + `-` + jailWebsiteMode + `-END
`
raw += rule
tools.WriteFile("/etc/fail2ban/jail.local", raw, 0644)
tools.Write("/etc/fail2ban/jail.local", raw, 0644)
var filter string
if jailWebsiteMode == "cc" {
@@ -289,7 +283,7 @@ failregex = ^<HOST>\s-.*\s` + jailWebsitePath + `.*HTTP/.*$
ignoreregex =
`
}
tools.WriteFile("/etc/fail2ban/filter.d/haozi-"+jailName+"-"+jailWebsiteMode+".conf", filter, 0644)
tools.Write("/etc/fail2ban/filter.d/haozi-"+jailName+"-"+jailWebsiteMode+".conf", filter, 0644)
case "service":
var logPath string
@@ -303,22 +297,20 @@ ignoreregex =
logPath = "/var/log/secure"
}
filter = "sshd"
port = tools.ExecShell("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
port = tools.Exec("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
case "mysql":
logPath = "/www/server/mysql/mysql-error.log"
filter = "mysqld-auth"
port = tools.ExecShell("cat /www/server/mysql/conf/my.cnf | grep 'port' | head -n 1 | awk '{print $3}'")
port = tools.Exec("cat /www/server/mysql/conf/my.cnf | grep 'port' | head -n 1 | awk '{print $3}'")
case "pure-ftpd":
logPath = "/var/log/messages"
filter = "pure-ftpd"
port = tools.ExecShell(`cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`)
port = tools.Exec(`cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`)
default:
controllers.Error(ctx, http.StatusUnprocessableEntity, "未知服务")
return
return controllers.Error(ctx, http.StatusUnprocessableEntity, "未知服务")
}
if len(port) == 0 {
controllers.Error(ctx, http.StatusUnprocessableEntity, "获取服务端口失败,请检查是否安装")
return
return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取服务端口失败,请检查是否安装")
}
rule := `
@@ -335,50 +327,50 @@ logpath = ` + logPath + `
# ` + jailName + `-END
`
raw += rule
tools.WriteFile("/etc/fail2ban/jail.local", raw, 0644)
tools.Write("/etc/fail2ban/jail.local", raw, 0644)
}
tools.ExecShell("fail2ban-client reload")
controllers.Success(ctx, nil)
tools.Exec("fail2ban-client reload")
return controllers.Success(ctx, nil)
}
// Delete 删除规则
func (c *Fail2banController) Delete(ctx http.Context) {
if !controllers.Check(ctx, "fail2ban") {
return
func (c *Fail2banController) Delete(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
}
jailName := ctx.Request().Input("name")
raw := tools.ReadFile("/etc/fail2ban/jail.local")
raw := tools.Read("/etc/fail2ban/jail.local")
if !strings.Contains(raw, "["+jailName+"]") {
controllers.Error(ctx, http.StatusUnprocessableEntity, "规则不存在")
return
return controllers.Error(ctx, http.StatusUnprocessableEntity, "规则不存在")
}
rule := tools.Cut(raw, "# "+jailName+"-START", "# "+jailName+"-END")
raw = strings.Replace(raw, "\n# "+jailName+"-START"+rule+"# "+jailName+"-END", "", -1)
raw = strings.TrimSpace(raw)
tools.WriteFile("/etc/fail2ban/jail.local", raw, 0644)
tools.Write("/etc/fail2ban/jail.local", raw, 0644)
tools.ExecShell("fail2ban-client reload")
controllers.Success(ctx, nil)
tools.Exec("fail2ban-client reload")
return controllers.Success(ctx, nil)
}
// BanList 获取封禁列表
func (c *Fail2banController) BanList(ctx http.Context) {
if !controllers.Check(ctx, "fail2ban") {
return
func (c *Fail2banController) BanList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
}
name := ctx.Request().Query("name")
if len(name) == 0 {
controllers.Error(ctx, http.StatusUnprocessableEntity, "缺少参数")
return
return controllers.Error(ctx, http.StatusUnprocessableEntity, "缺少参数")
}
currentlyBan := tools.ExecShell(`fail2ban-client status ` + name + ` | grep "Currently banned" | awk '{print $4}'`)
totalBan := tools.ExecShell(`fail2ban-client status ` + name + ` | grep "Total banned" | awk '{print $4}'`)
bannedIp := tools.ExecShell(`fail2ban-client status ` + name + ` | grep "Banned IP list" | awk -F ":" '{print $2}'`)
currentlyBan := tools.Exec(`fail2ban-client status ` + name + ` | grep "Currently banned" | awk '{print $4}'`)
totalBan := tools.Exec(`fail2ban-client status ` + name + ` | grep "Total banned" | awk '{print $4}'`)
bannedIp := tools.Exec(`fail2ban-client status ` + name + ` | grep "Banned IP list" | awk -F ":" '{print $2}'`)
bannedIpList := strings.Split(bannedIp, " ")
var list []map[string]string
@@ -391,7 +383,7 @@ func (c *Fail2banController) BanList(ctx http.Context) {
}
}
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"currentlyBan": currentlyBan,
"totalBan": totalBan,
"bannedIpList": list,
@@ -399,61 +391,61 @@ func (c *Fail2banController) BanList(ctx http.Context) {
}
// Unban 解封
func (c *Fail2banController) Unban(ctx http.Context) {
if !controllers.Check(ctx, "fail2ban") {
return
func (c *Fail2banController) Unban(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
}
name := ctx.Request().Input("name")
ip := ctx.Request().Input("ip")
if len(name) == 0 || len(ip) == 0 {
controllers.Error(ctx, http.StatusUnprocessableEntity, "缺少参数")
return
return controllers.Error(ctx, http.StatusUnprocessableEntity, "缺少参数")
}
tools.ExecShell("fail2ban-client set " + name + " unbanip " + ip)
controllers.Success(ctx, nil)
tools.Exec("fail2ban-client set " + name + " unbanip " + ip)
return controllers.Success(ctx, nil)
}
// SetWhiteList 设置白名单
func (c *Fail2banController) SetWhiteList(ctx http.Context) {
if !controllers.Check(ctx, "fail2ban") {
return
func (c *Fail2banController) SetWhiteList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
}
ip := ctx.Request().Input("ip")
if len(ip) == 0 {
controllers.Error(ctx, http.StatusUnprocessableEntity, "缺少参数")
return
return controllers.Error(ctx, http.StatusUnprocessableEntity, "缺少参数")
}
raw := tools.ReadFile("/etc/fail2ban/jail.local")
raw := tools.Read("/etc/fail2ban/jail.local")
// 正则替换
reg := regexp.MustCompile(`ignoreip\s*=\s*.*\n`)
if reg.MatchString(raw) {
raw = reg.ReplaceAllString(raw, "ignoreip = "+ip+"\n")
} else {
controllers.Error(ctx, http.StatusInternalServerError, "解析Fail2ban规则失败,Fail2ban可能已损坏")
return
return controllers.Error(ctx, http.StatusInternalServerError, "解析Fail2ban规则失败,Fail2ban可能已损坏")
}
tools.WriteFile("/etc/fail2ban/jail.local", raw, 0644)
tools.ExecShell("fail2ban-client reload")
controllers.Success(ctx, nil)
tools.Write("/etc/fail2ban/jail.local", raw, 0644)
tools.Exec("fail2ban-client reload")
return controllers.Success(ctx, nil)
}
// GetWhiteList 获取白名单
func (c *Fail2banController) GetWhiteList(ctx http.Context) {
if !controllers.Check(ctx, "fail2ban") {
return
func (c *Fail2banController) GetWhiteList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "fail2ban")
if check != nil {
return check
}
raw := tools.ReadFile("/etc/fail2ban/jail.local")
raw := tools.Read("/etc/fail2ban/jail.local")
reg := regexp.MustCompile(`ignoreip\s*=\s*(.*)\n`)
if reg.MatchString(raw) {
ignoreIp := reg.FindStringSubmatch(raw)[1]
controllers.Success(ctx, ignoreIp)
return controllers.Success(ctx, ignoreIp)
} else {
controllers.Error(ctx, http.StatusInternalServerError, "解析Fail2ban规则失败,Fail2ban可能已损坏")
return controllers.Error(ctx, http.StatusInternalServerError, "解析Fail2ban规则失败,Fail2ban可能已损坏")
}
}
@@ -29,166 +29,162 @@ func NewMysql57Controller() *Mysql57Controller {
}
// Status 获取运行状态
func (c *Mysql57Controller) Status(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Reload 重载配置
func (c *Mysql57Controller) Reload(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
tools.ExecShell("systemctl reload mysqld")
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl reload mysqld")
status := tools.Exec("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Restart 重启服务
func (c *Mysql57Controller) Restart(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
tools.ExecShell("systemctl restart mysqld")
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl restart mysqld")
status := tools.Exec("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Start 启动服务
func (c *Mysql57Controller) Start(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
tools.ExecShell("systemctl start mysqld")
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl start mysqld")
status := tools.Exec("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Stop 停止服务
func (c *Mysql57Controller) Stop(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
tools.ExecShell("systemctl stop mysqld")
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl stop mysqld")
status := tools.Exec("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
}
if status != "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// GetConfig 获取配置
func (c *Mysql57Controller) GetConfig(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
// 获取配置
config := tools.ReadFile("/www/server/mysql/conf/my.cnf")
config := tools.Read("/www/server/mysql/conf/my.cnf")
if len(config) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL配置失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL配置失败")
}
controllers.Success(ctx, config)
return controllers.Success(ctx, config)
}
// SaveConfig 保存配置
func (c *Mysql57Controller) SaveConfig(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
config := ctx.Request().Input("config")
if len(config) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "配置不能为空")
return
return controllers.Error(ctx, http.StatusBadRequest, "配置不能为空")
}
if !tools.WriteFile("/www/server/mysql/conf/my.cnf", config, 0644) {
controllers.Error(ctx, http.StatusInternalServerError, "写入MySQL配置失败")
return
if !tools.Write("/www/server/mysql/conf/my.cnf", config, 0644) {
return controllers.Error(ctx, http.StatusInternalServerError, "写入MySQL配置失败")
}
c.Restart(ctx)
return c.Restart(ctx)
}
// Load 获取负载
func (c *Mysql57Controller) Load(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
if len(rootPassword) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "MySQL root密码为空")
return
return controllers.Error(ctx, http.StatusBadRequest, "MySQL root密码为空")
}
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
if status != "active" {
controllers.Error(ctx, http.StatusInternalServerError, "MySQL 已停止运行")
return
return controllers.Error(ctx, http.StatusInternalServerError, "MySQL 已停止运行")
}
raw := tools.ExecShell("/www/server/mysql/bin/mysqladmin -uroot -p" + rootPassword + " extended-status 2>&1")
raw := tools.Exec("/www/server/mysql/bin/mysqladmin -uroot -p" + rootPassword + " extended-status 2>&1")
if strings.Contains(raw, "Access denied for user") {
controllers.Error(ctx, http.StatusBadRequest, "MySQL root密码错误")
return
return controllers.Error(ctx, http.StatusBadRequest, "MySQL root密码错误")
}
if !strings.Contains(raw, "Uptime") {
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL负载失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL负载失败")
}
data := make(map[int]map[string]string)
@@ -238,106 +234,108 @@ func (c *Mysql57Controller) Load(ctx http.Context) {
bufferPoolReadRequests := cast.ToFloat64(data[12]["value"])
data[10]["value"] = fmt.Sprintf("%.2f%%", bufferPoolReadRequests/(bufferPoolReads+bufferPoolReadRequests)*100)
controllers.Success(ctx, data)
return controllers.Success(ctx, data)
}
// ErrorLog 获取错误日志
func (c *Mysql57Controller) ErrorLog(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) ErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
log := tools.Escape(tools.ExecShell("tail -n 100 /www/server/mysql/mysql-error.log"))
controllers.Success(ctx, log)
log := tools.Escape(tools.Exec("tail -n 100 /www/server/mysql/mysql-error.log"))
return controllers.Success(ctx, log)
}
// ClearErrorLog 清空错误日志
func (c *Mysql57Controller) ClearErrorLog(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) ClearErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
tools.ExecShell("echo '' > /www/server/mysql/mysql-error.log")
controllers.Success(ctx, "清空错误日志成功")
tools.Exec("echo '' > /www/server/mysql/mysql-error.log")
return controllers.Success(ctx, "清空错误日志成功")
}
// SlowLog 获取慢查询日志
func (c *Mysql57Controller) SlowLog(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) SlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
log := tools.Escape(tools.ExecShell("tail -n 100 /www/server/mysql/mysql-slow.log"))
controllers.Success(ctx, log)
log := tools.Escape(tools.Exec("tail -n 100 /www/server/mysql/mysql-slow.log"))
return controllers.Success(ctx, log)
}
// ClearSlowLog 清空慢查询日志
func (c *Mysql57Controller) ClearSlowLog(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) ClearSlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
tools.ExecShell("echo '' > /www/server/mysql/mysql-slow.log")
controllers.Success(ctx, "清空慢查询日志成功")
tools.Exec("echo '' > /www/server/mysql/mysql-slow.log")
return controllers.Success(ctx, "清空慢查询日志成功")
}
// GetRootPassword 获取root密码
func (c *Mysql57Controller) GetRootPassword(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) GetRootPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
if len(rootPassword) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "MySQL root密码为空")
return
return controllers.Error(ctx, http.StatusBadRequest, "MySQL root密码为空")
}
controllers.Success(ctx, rootPassword)
return controllers.Success(ctx, rootPassword)
}
// SetRootPassword 设置root密码
func (c *Mysql57Controller) SetRootPassword(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) SetRootPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
}
if status != "active" {
controllers.Error(ctx, http.StatusInternalServerError, "MySQL 未运行")
return
return controllers.Error(ctx, http.StatusInternalServerError, "MySQL 未运行")
}
rootPassword := ctx.Request().Input(models.SettingKeyMysqlRootPassword)
if len(rootPassword) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "MySQL root密码不能为空")
return
return controllers.Error(ctx, http.StatusBadRequest, "MySQL root密码不能为空")
}
oldRootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
if oldRootPassword != rootPassword {
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + oldRootPassword + " -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '" + rootPassword + "';\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + oldRootPassword + " -e \"FLUSH PRIVILEGES;\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + oldRootPassword + " -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '" + rootPassword + "';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + oldRootPassword + " -e \"FLUSH PRIVILEGES;\"")
err := c.setting.Set(models.SettingKeyMysqlRootPassword, rootPassword)
if err != nil {
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '" + oldRootPassword + "';\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
controllers.Error(ctx, http.StatusInternalServerError, "设置root密码失败")
return
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '" + oldRootPassword + "';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
return controllers.Error(ctx, http.StatusInternalServerError, "设置root密码失败")
}
}
controllers.Success(ctx, "设置root密码成功")
return controllers.Success(ctx, "设置root密码成功")
}
// DatabaseList 获取数据库列表
func (c *Mysql57Controller) DatabaseList(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) DatabaseList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
@@ -348,16 +346,14 @@ func (c *Mysql57Controller) DatabaseList(ctx http.Context) {
db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/")
if err != nil {
facades.Log().Error("[MySQL57] 连接数据库失败" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "连接数据库失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "连接数据库失败")
}
defer db.Close()
rows, err := db.Query("SHOW DATABASES")
if err != nil {
facades.Log().Error("[MySQL57] 获取数据库列表失败" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "获取数据库列表失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取数据库列表失败")
}
defer rows.Close()
@@ -374,8 +370,7 @@ func (c *Mysql57Controller) DatabaseList(ctx http.Context) {
if err := rows.Err(); err != nil {
facades.Log().Error("[MySQL57] 获取数据库列表失败" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "获取数据库列表失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取数据库列表失败")
}
page := ctx.Request().QueryInt("page", 1)
@@ -383,27 +378,27 @@ func (c *Mysql57Controller) DatabaseList(ctx http.Context) {
startIndex := (page - 1) * limit
endIndex := page * limit
if startIndex > len(databases) {
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []database{},
})
return
}
if endIndex > len(databases) {
endIndex = len(databases)
}
pagedDatabases := databases[startIndex:endIndex]
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": len(databases),
"items": pagedDatabases,
})
}
// AddDatabase 添加数据库
func (c *Mysql57Controller) AddDatabase(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) AddDatabase(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -412,12 +407,10 @@ func (c *Mysql57Controller) AddDatabase(ctx http.Context) {
"password": "required|min_len:8|max_len:255",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
@@ -425,65 +418,64 @@ func (c *Mysql57Controller) AddDatabase(ctx http.Context) {
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE DATABASE IF NOT EXISTS " + database + " DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE DATABASE IF NOT EXISTS " + database + " DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
controllers.Success(ctx, "添加数据库成功")
return controllers.Success(ctx, "添加数据库成功")
}
// DeleteDatabase 删除数据库
func (c *Mysql57Controller) DeleteDatabase(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) DeleteDatabase(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
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 {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
database := ctx.Request().Input("database")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP DATABASE IF EXISTS " + database + ";\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP DATABASE IF EXISTS " + database + ";\"")
controllers.Success(ctx, "删除数据库成功")
return controllers.Success(ctx, "删除数据库成功")
}
// BackupList 获取备份列表
func (c *Mysql57Controller) BackupList(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) BackupList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
backupList, err := c.backup.MysqlList()
if err != nil {
facades.Log().Error("[MySQL57] 获取备份列表失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "获取备份列表失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取备份列表失败")
}
controllers.Success(ctx, backupList)
return controllers.Success(ctx, backupList)
}
// UploadBackup 上传备份
func (c *Mysql57Controller) UploadBackup(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) UploadBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
file, err := ctx.Request().File("file")
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
return
return controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/mysql"
@@ -494,71 +486,68 @@ func (c *Mysql57Controller) UploadBackup(ctx http.Context) {
name := file.GetClientOriginalName()
_, err = file.StoreAs(backupPath, name)
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
return
return controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
}
controllers.Success(ctx, "上传文件成功")
return controllers.Success(ctx, "上传文件成功")
}
// CreateBackup 创建备份
func (c *Mysql57Controller) CreateBackup(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) CreateBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
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 {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
database := ctx.Request().Input("database")
err = c.backup.MysqlBackup(database)
if err != nil {
facades.Log().Error("[MYSQL57] 创建备份失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "创建备份失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "创建备份失败")
}
controllers.Success(ctx, "备份成功")
return controllers.Success(ctx, "备份成功")
}
// DeleteBackup 删除备份
func (c *Mysql57Controller) DeleteBackup(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) DeleteBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
"name": "required|min_len:1|max_len:255",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/mysql"
fileName := ctx.Request().Input("name")
tools.RemoveFile(backupPath + "/" + fileName)
tools.Remove(backupPath + "/" + fileName)
controllers.Success(ctx, "删除备份成功")
return controllers.Success(ctx, "删除备份成功")
}
// RestoreBackup 还原备份
func (c *Mysql57Controller) RestoreBackup(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) RestoreBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -566,28 +555,26 @@ func (c *Mysql57Controller) RestoreBackup(ctx http.Context) {
"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 {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
err = c.backup.MysqlRestore(ctx.Request().Input("database"), ctx.Request().Input("name"))
if err != nil {
facades.Log().Error("[MYSQL57] 还原失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "还原失败: "+err.Error())
return
return controllers.Error(ctx, http.StatusInternalServerError, "还原失败: "+err.Error())
}
controllers.Success(ctx, "还原成功")
return controllers.Success(ctx, "还原成功")
}
// UserList 用户列表
func (c *Mysql57Controller) UserList(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) UserList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
type user struct {
@@ -600,14 +587,14 @@ func (c *Mysql57Controller) UserList(ctx http.Context) {
db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/")
if err != nil {
facades.Log().Error("[MYSQL57] 连接数据库失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "连接数据库失败")
return controllers.Error(ctx, http.StatusInternalServerError, "连接数据库失败")
}
defer db.Close()
rows, err := db.Query("SELECT user, host FROM mysql.user")
if err != nil {
facades.Log().Error("[MYSQL57] 查询数据库失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "查询数据库失败")
return controllers.Error(ctx, http.StatusInternalServerError, "查询数据库失败")
}
defer rows.Close()
@@ -645,8 +632,7 @@ func (c *Mysql57Controller) UserList(ctx http.Context) {
}
if err := rows.Err(); err != nil {
controllers.Error(ctx, http.StatusInternalServerError, "获取用户列表失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取用户列表失败")
}
page := ctx.Request().QueryInt("page", 1)
@@ -654,27 +640,27 @@ func (c *Mysql57Controller) UserList(ctx http.Context) {
startIndex := (page - 1) * limit
endIndex := page * limit
if startIndex > len(userGrants) {
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []user{},
})
return
}
if endIndex > len(userGrants) {
endIndex = len(userGrants)
}
pagedUserGrants := userGrants[startIndex:endIndex]
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": len(userGrants),
"items": pagedUserGrants,
})
}
// AddUser 添加用户
func (c *Mysql57Controller) AddUser(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) AddUser(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -683,54 +669,52 @@ func (c *Mysql57Controller) AddUser(ctx http.Context) {
"password": "required|min_len:8|max_len:255",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
database := ctx.Request().Input("database")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + ";'\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + ";'\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
controllers.Success(ctx, "添加成功")
return controllers.Success(ctx, "添加成功")
}
// DeleteUser 删除用户
func (c *Mysql57Controller) DeleteUser(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) DeleteUser(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
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 {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP USER '" + user + "'@'localhost';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP USER '" + user + "'@'localhost';\"")
controllers.Success(ctx, "删除成功")
return controllers.Success(ctx, "删除成功")
}
// SetUserPassword 设置用户密码
func (c *Mysql57Controller) SetUserPassword(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) SetUserPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -738,27 +722,26 @@ func (c *Mysql57Controller) SetUserPassword(ctx http.Context) {
"password": "required|min_len:8|max_len:255",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
controllers.Success(ctx, "修改成功")
return controllers.Success(ctx, "修改成功")
}
// SetUserPrivileges 设置用户权限
func (c *Mysql57Controller) SetUserPrivileges(ctx http.Context) {
if !controllers.Check(ctx, "mysql57") {
return
func (c *Mysql57Controller) SetUserPrivileges(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql57")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -766,20 +749,18 @@ func (c *Mysql57Controller) SetUserPrivileges(ctx http.Context) {
"database": "required|min_len:1|max_len:255",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
database := ctx.Request().Input("database")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"REVOKE ALL PRIVILEGES ON *.* FROM '" + user + "'@'localhost';\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"REVOKE ALL PRIVILEGES ON *.* FROM '" + user + "'@'localhost';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
controllers.Success(ctx, "修改成功")
return controllers.Success(ctx, "修改成功")
}
@@ -29,166 +29,162 @@ func NewMysql80Controller() *Mysql80Controller {
}
// Status 获取运行状态
func (c *Mysql80Controller) Status(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Reload 重载配置
func (c *Mysql80Controller) Reload(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
tools.ExecShell("systemctl reload mysqld")
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl reload mysqld")
status := tools.Exec("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Restart 重启服务
func (c *Mysql80Controller) Restart(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
tools.ExecShell("systemctl restart mysqld")
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl restart mysqld")
status := tools.Exec("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Start 启动服务
func (c *Mysql80Controller) Start(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
tools.ExecShell("systemctl start mysqld")
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl start mysqld")
status := tools.Exec("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Stop 停止服务
func (c *Mysql80Controller) Stop(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
tools.ExecShell("systemctl stop mysqld")
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl stop mysqld")
status := tools.Exec("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
}
if status != "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// GetConfig 获取配置
func (c *Mysql80Controller) GetConfig(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
// 获取配置
config := tools.ReadFile("/www/server/mysql/conf/my.cnf")
config := tools.Read("/www/server/mysql/conf/my.cnf")
if len(config) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL配置失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL配置失败")
}
controllers.Success(ctx, config)
return controllers.Success(ctx, config)
}
// SaveConfig 保存配置
func (c *Mysql80Controller) SaveConfig(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
config := ctx.Request().Input("config")
if len(config) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "配置不能为空")
return
return controllers.Error(ctx, http.StatusBadRequest, "配置不能为空")
}
if !tools.WriteFile("/www/server/mysql/conf/my.cnf", config, 0644) {
controllers.Error(ctx, http.StatusInternalServerError, "写入MySQL配置失败")
return
if !tools.Write("/www/server/mysql/conf/my.cnf", config, 0644) {
return controllers.Error(ctx, http.StatusInternalServerError, "写入MySQL配置失败")
}
c.Restart(ctx)
return c.Restart(ctx)
}
// Load 获取负载
func (c *Mysql80Controller) Load(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
if len(rootPassword) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "MySQL root密码为空")
return
return controllers.Error(ctx, http.StatusBadRequest, "MySQL root密码为空")
}
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
if status != "active" {
controllers.Error(ctx, http.StatusInternalServerError, "MySQL 已停止运行")
return
return controllers.Error(ctx, http.StatusInternalServerError, "MySQL 已停止运行")
}
raw := tools.ExecShell("/www/server/mysql/bin/mysqladmin -uroot -p" + rootPassword + " extended-status 2>&1")
raw := tools.Exec("/www/server/mysql/bin/mysqladmin -uroot -p" + rootPassword + " extended-status 2>&1")
if strings.Contains(raw, "Access denied for user") {
controllers.Error(ctx, http.StatusBadRequest, "MySQL root密码错误")
return
return controllers.Error(ctx, http.StatusBadRequest, "MySQL root密码错误")
}
if !strings.Contains(raw, "Uptime") {
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL负载失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL负载失败")
}
data := make(map[int]map[string]string)
@@ -238,106 +234,108 @@ func (c *Mysql80Controller) Load(ctx http.Context) {
bufferPoolReadRequests := cast.ToFloat64(data[12]["value"])
data[10]["value"] = fmt.Sprintf("%.2f%%", bufferPoolReadRequests/(bufferPoolReads+bufferPoolReadRequests)*100)
controllers.Success(ctx, data)
return controllers.Success(ctx, data)
}
// ErrorLog 获取错误日志
func (c *Mysql80Controller) ErrorLog(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) ErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
log := tools.Escape(tools.ExecShell("tail -n 100 /www/server/mysql/mysql-error.log"))
controllers.Success(ctx, log)
log := tools.Escape(tools.Exec("tail -n 100 /www/server/mysql/mysql-error.log"))
return controllers.Success(ctx, log)
}
// ClearErrorLog 清空错误日志
func (c *Mysql80Controller) ClearErrorLog(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) ClearErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
tools.ExecShell("echo '' > /www/server/mysql/mysql-error.log")
controllers.Success(ctx, "清空错误日志成功")
tools.Exec("echo '' > /www/server/mysql/mysql-error.log")
return controllers.Success(ctx, "清空错误日志成功")
}
// SlowLog 获取慢查询日志
func (c *Mysql80Controller) SlowLog(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) SlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
log := tools.Escape(tools.ExecShell("tail -n 100 /www/server/mysql/mysql-slow.log"))
controllers.Success(ctx, log)
log := tools.Escape(tools.Exec("tail -n 100 /www/server/mysql/mysql-slow.log"))
return controllers.Success(ctx, log)
}
// ClearSlowLog 清空慢查询日志
func (c *Mysql80Controller) ClearSlowLog(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) ClearSlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
tools.ExecShell("echo '' > /www/server/mysql/mysql-slow.log")
controllers.Success(ctx, "清空慢查询日志成功")
tools.Exec("echo '' > /www/server/mysql/mysql-slow.log")
return controllers.Success(ctx, "清空慢查询日志成功")
}
// GetRootPassword 获取root密码
func (c *Mysql80Controller) GetRootPassword(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) GetRootPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
if len(rootPassword) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "MySQL root密码为空")
return
return controllers.Error(ctx, http.StatusBadRequest, "MySQL root密码为空")
}
controllers.Success(ctx, rootPassword)
return controllers.Success(ctx, rootPassword)
}
// SetRootPassword 设置root密码
func (c *Mysql80Controller) SetRootPassword(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) SetRootPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
}
if status != "active" {
controllers.Error(ctx, http.StatusInternalServerError, "MySQL 未运行")
return
return controllers.Error(ctx, http.StatusInternalServerError, "MySQL 未运行")
}
rootPassword := ctx.Request().Input(models.SettingKeyMysqlRootPassword)
if len(rootPassword) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "MySQL root密码不能为空")
return
return controllers.Error(ctx, http.StatusBadRequest, "MySQL root密码不能为空")
}
oldRootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
if oldRootPassword != rootPassword {
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + oldRootPassword + " -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '" + rootPassword + "';\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + oldRootPassword + " -e \"FLUSH PRIVILEGES;\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + oldRootPassword + " -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '" + rootPassword + "';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + oldRootPassword + " -e \"FLUSH PRIVILEGES;\"")
err := c.setting.Set(models.SettingKeyMysqlRootPassword, rootPassword)
if err != nil {
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '" + oldRootPassword + "';\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
controllers.Error(ctx, http.StatusInternalServerError, "设置root密码失败")
return
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '" + oldRootPassword + "';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
return controllers.Error(ctx, http.StatusInternalServerError, "设置root密码失败")
}
}
controllers.Success(ctx, "设置root密码成功")
return controllers.Success(ctx, "设置root密码成功")
}
// DatabaseList 获取数据库列表
func (c *Mysql80Controller) DatabaseList(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) DatabaseList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
@@ -348,16 +346,14 @@ func (c *Mysql80Controller) DatabaseList(ctx http.Context) {
db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/")
if err != nil {
facades.Log().Error("[MySQL80] 连接数据库失败" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "连接数据库失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "连接数据库失败")
}
defer db.Close()
rows, err := db.Query("SHOW DATABASES")
if err != nil {
facades.Log().Error("[MySQL80] 获取数据库列表失败" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "获取数据库列表失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取数据库列表失败")
}
defer rows.Close()
@@ -374,8 +370,7 @@ func (c *Mysql80Controller) DatabaseList(ctx http.Context) {
if err := rows.Err(); err != nil {
facades.Log().Error("[MySQL80] 获取数据库列表失败" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "获取数据库列表失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取数据库列表失败")
}
page := ctx.Request().QueryInt("page", 1)
@@ -383,27 +378,27 @@ func (c *Mysql80Controller) DatabaseList(ctx http.Context) {
startIndex := (page - 1) * limit
endIndex := page * limit
if startIndex > len(databases) {
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []database{},
})
return
}
if endIndex > len(databases) {
endIndex = len(databases)
}
pagedDatabases := databases[startIndex:endIndex]
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": len(databases),
"items": pagedDatabases,
})
}
// AddDatabase 添加数据库
func (c *Mysql80Controller) AddDatabase(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) AddDatabase(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -412,12 +407,10 @@ func (c *Mysql80Controller) AddDatabase(ctx http.Context) {
"password": "required|min_len:8|max_len:255",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
@@ -425,65 +418,64 @@ func (c *Mysql80Controller) AddDatabase(ctx http.Context) {
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE DATABASE IF NOT EXISTS " + database + " DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE DATABASE IF NOT EXISTS " + database + " DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
controllers.Success(ctx, "添加数据库成功")
return controllers.Success(ctx, "添加数据库成功")
}
// DeleteDatabase 删除数据库
func (c *Mysql80Controller) DeleteDatabase(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) DeleteDatabase(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
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 {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
database := ctx.Request().Input("database")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP DATABASE IF EXISTS " + database + ";\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP DATABASE IF EXISTS " + database + ";\"")
controllers.Success(ctx, "删除数据库成功")
return controllers.Success(ctx, "删除数据库成功")
}
// BackupList 获取备份列表
func (c *Mysql80Controller) BackupList(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) BackupList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
backupList, err := c.backup.MysqlList()
if err != nil {
facades.Log().Error("[MySQL80] 获取备份列表失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "获取备份列表失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取备份列表失败")
}
controllers.Success(ctx, backupList)
return controllers.Success(ctx, backupList)
}
// UploadBackup 上传备份
func (c *Mysql80Controller) UploadBackup(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) UploadBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
file, err := ctx.Request().File("file")
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
return
return controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/mysql"
@@ -494,71 +486,68 @@ func (c *Mysql80Controller) UploadBackup(ctx http.Context) {
name := file.GetClientOriginalName()
_, err = file.StoreAs(backupPath, name)
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
return
return controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
}
controllers.Success(ctx, "上传文件成功")
return controllers.Success(ctx, "上传文件成功")
}
// CreateBackup 创建备份
func (c *Mysql80Controller) CreateBackup(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) CreateBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
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 {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
database := ctx.Request().Input("database")
err = c.backup.MysqlBackup(database)
if err != nil {
facades.Log().Error("[MYSQL80] 创建备份失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "创建备份失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "创建备份失败")
}
controllers.Success(ctx, "备份成功")
return controllers.Success(ctx, "备份成功")
}
// DeleteBackup 删除备份
func (c *Mysql80Controller) DeleteBackup(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) DeleteBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
"name": "required|min_len:1|max_len:255",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/mysql"
fileName := ctx.Request().Input("name")
tools.RemoveFile(backupPath + "/" + fileName)
tools.Remove(backupPath + "/" + fileName)
controllers.Success(ctx, "删除备份成功")
return controllers.Success(ctx, "删除备份成功")
}
// RestoreBackup 还原备份
func (c *Mysql80Controller) RestoreBackup(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) RestoreBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -566,28 +555,26 @@ func (c *Mysql80Controller) RestoreBackup(ctx http.Context) {
"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 {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
err = c.backup.MysqlRestore(ctx.Request().Input("database"), ctx.Request().Input("name"))
if err != nil {
facades.Log().Error("[MYSQL80] 还原失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "还原失败: "+err.Error())
return
return controllers.Error(ctx, http.StatusInternalServerError, "还原失败: "+err.Error())
}
controllers.Success(ctx, "还原成功")
return controllers.Success(ctx, "还原成功")
}
// UserList 用户列表
func (c *Mysql80Controller) UserList(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) UserList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
type user struct {
@@ -600,14 +587,14 @@ func (c *Mysql80Controller) UserList(ctx http.Context) {
db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/")
if err != nil {
facades.Log().Error("[MYSQL80] 连接数据库失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "连接数据库失败")
return controllers.Error(ctx, http.StatusInternalServerError, "连接数据库失败")
}
defer db.Close()
rows, err := db.Query("SELECT user, host FROM mysql.user")
if err != nil {
facades.Log().Error("[MYSQL80] 查询数据库失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "查询数据库失败")
return controllers.Error(ctx, http.StatusInternalServerError, "查询数据库失败")
}
defer rows.Close()
@@ -645,8 +632,7 @@ func (c *Mysql80Controller) UserList(ctx http.Context) {
}
if err := rows.Err(); err != nil {
controllers.Error(ctx, http.StatusInternalServerError, "获取用户列表失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取用户列表失败")
}
page := ctx.Request().QueryInt("page", 1)
@@ -654,27 +640,27 @@ func (c *Mysql80Controller) UserList(ctx http.Context) {
startIndex := (page - 1) * limit
endIndex := page * limit
if startIndex > len(userGrants) {
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []user{},
})
return
}
if endIndex > len(userGrants) {
endIndex = len(userGrants)
}
pagedUserGrants := userGrants[startIndex:endIndex]
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": len(userGrants),
"items": pagedUserGrants,
})
}
// AddUser 添加用户
func (c *Mysql80Controller) AddUser(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) AddUser(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -683,54 +669,52 @@ func (c *Mysql80Controller) AddUser(ctx http.Context) {
"password": "required|min_len:8|max_len:255",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
database := ctx.Request().Input("database")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + ";'\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + ";'\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
controllers.Success(ctx, "添加成功")
return controllers.Success(ctx, "添加成功")
}
// DeleteUser 删除用户
func (c *Mysql80Controller) DeleteUser(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) DeleteUser(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
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 {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP USER '" + user + "'@'localhost';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP USER '" + user + "'@'localhost';\"")
controllers.Success(ctx, "删除成功")
return controllers.Success(ctx, "删除成功")
}
// SetUserPassword 设置用户密码
func (c *Mysql80Controller) SetUserPassword(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) SetUserPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -738,27 +722,26 @@ func (c *Mysql80Controller) SetUserPassword(ctx http.Context) {
"password": "required|min_len:8|max_len:255",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
controllers.Success(ctx, "修改成功")
return controllers.Success(ctx, "修改成功")
}
// SetUserPrivileges 设置用户权限
func (c *Mysql80Controller) SetUserPrivileges(ctx http.Context) {
if !controllers.Check(ctx, "mysql80") {
return
func (c *Mysql80Controller) SetUserPrivileges(ctx http.Context) http.Response {
check := controllers.Check(ctx, "mysql80")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -766,20 +749,18 @@ func (c *Mysql80Controller) SetUserPrivileges(ctx http.Context) {
"database": "required|min_len:1|max_len:255",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
database := ctx.Request().Input("database")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"REVOKE ALL PRIVILEGES ON *.* FROM '" + user + "'@'localhost';\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\"")
tools.ExecShell("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"REVOKE ALL PRIVILEGES ON *.* FROM '" + user + "'@'localhost';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\"")
tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"")
controllers.Success(ctx, "修改成功")
return controllers.Success(ctx, "修改成功")
}
@@ -24,176 +24,176 @@ func NewOpenrestyController() *OpenRestyController {
}
// Status 获取运行状态
func (c *OpenRestyController) Status(ctx http.Context) {
if !controllers.Check(ctx, "openresty") {
return
func (c *OpenRestyController) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
}
status := tools.ExecShell("systemctl status openresty | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status openresty | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Reload 重载配置
func (c *OpenRestyController) Reload(ctx http.Context) {
if !controllers.Check(ctx, "openresty") {
return
func (c *OpenRestyController) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
}
tools.ExecShell("systemctl reload openresty")
status := tools.ExecShell("systemctl status openresty | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl reload openresty")
status := tools.Exec("systemctl status openresty | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty状态失败")
}
if status == "active" {
controllers.Success(ctx, "重载OpenResty成功")
return controllers.Success(ctx, "重载OpenResty成功")
} else {
controllers.Error(ctx, 1, "重载OpenResty失败: "+status)
return controllers.Error(ctx, 1, "重载OpenResty失败: "+status)
}
}
// Start 启动OpenResty
func (c *OpenRestyController) Start(ctx http.Context) {
if !controllers.Check(ctx, "openresty") {
return
func (c *OpenRestyController) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
}
tools.ExecShell("systemctl start openresty")
status := tools.ExecShell("systemctl status openresty | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl start openresty")
status := tools.Exec("systemctl status openresty | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty状态失败")
}
if status == "active" {
controllers.Success(ctx, "启动OpenResty成功")
return controllers.Success(ctx, "启动OpenResty成功")
} else {
controllers.Error(ctx, 1, "启动OpenResty失败: "+status)
return controllers.Error(ctx, 1, "启动OpenResty失败: "+status)
}
}
// Stop 停止OpenResty
func (c *OpenRestyController) Stop(ctx http.Context) {
if !controllers.Check(ctx, "openresty") {
return
func (c *OpenRestyController) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
}
tools.ExecShell("systemctl stop openresty")
status := tools.ExecShell("systemctl status openresty | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl stop openresty")
status := tools.Exec("systemctl status openresty | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty状态失败")
}
if status != "active" {
controllers.Success(ctx, "停止OpenResty成功")
return controllers.Success(ctx, "停止OpenResty成功")
} else {
controllers.Error(ctx, 1, "停止OpenResty失败: "+status)
return controllers.Error(ctx, 1, "停止OpenResty失败: "+status)
}
}
// Restart 重启OpenResty
func (c *OpenRestyController) Restart(ctx http.Context) {
if !controllers.Check(ctx, "openresty") {
return
func (c *OpenRestyController) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
}
tools.ExecShell("systemctl restart openresty")
status := tools.ExecShell("systemctl status openresty | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl restart openresty")
status := tools.Exec("systemctl status openresty | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty状态失败")
}
if status == "active" {
controllers.Success(ctx, "重启OpenResty成功")
return controllers.Success(ctx, "重启OpenResty成功")
} else {
controllers.Error(ctx, 1, "重启OpenResty失败: "+status)
return controllers.Error(ctx, 1, "重启OpenResty失败: "+status)
}
}
// GetConfig 获取配置
func (c *OpenRestyController) GetConfig(ctx http.Context) {
if !controllers.Check(ctx, "openresty") {
return
func (c *OpenRestyController) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
}
config := tools.ReadFile("/www/server/openresty/conf/nginx.conf")
config := tools.Read("/www/server/openresty/conf/nginx.conf")
if len(config) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty配置失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty配置失败")
}
controllers.Success(ctx, config)
return controllers.Success(ctx, config)
}
// SaveConfig 保存配置
func (c *OpenRestyController) SaveConfig(ctx http.Context) {
if !controllers.Check(ctx, "openresty") {
return
func (c *OpenRestyController) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
}
config := ctx.Request().Input("config")
if len(config) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "配置不能为空")
return
return controllers.Error(ctx, http.StatusInternalServerError, "配置不能为空")
}
if !tools.WriteFile("/www/server/openresty/conf/nginx.conf", config, 0644) {
controllers.Error(ctx, http.StatusInternalServerError, "保存OpenResty配置失败")
return
if !tools.Write("/www/server/openresty/conf/nginx.conf", config, 0644) {
return controllers.Error(ctx, http.StatusInternalServerError, "保存OpenResty配置失败")
}
c.Reload(ctx)
return c.Reload(ctx)
}
// ErrorLog 获取错误日志
func (c *OpenRestyController) ErrorLog(ctx http.Context) {
if !controllers.Check(ctx, "openresty") {
return
func (c *OpenRestyController) ErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
}
if !tools.Exists("/www/wwwlogs/nginx_error.log") {
controllers.Success(ctx, "")
return
return controllers.Success(ctx, "")
}
out := tools.ExecShell("tail -n 100 /www/wwwlogs/nginx_error.log")
controllers.Success(ctx, out)
out := tools.Exec("tail -n 100 /www/wwwlogs/nginx_error.log")
return controllers.Success(ctx, out)
}
// ClearErrorLog 清空错误日志
func (c *OpenRestyController) ClearErrorLog(ctx http.Context) {
if !controllers.Check(ctx, "openresty") {
return
func (c *OpenRestyController) ClearErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
}
tools.ExecShell("echo '' > /www/wwwlogs/nginx_error.log")
controllers.Success(ctx, "清空OpenResty错误日志成功")
tools.Exec("echo '' > /www/wwwlogs/nginx_error.log")
return controllers.Success(ctx, "清空OpenResty错误日志成功")
}
// Load 获取负载
func (c *OpenRestyController) Load(ctx http.Context) {
if !controllers.Check(ctx, "openresty") {
return
func (c *OpenRestyController) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "openresty")
if check != nil {
return check
}
client := req.C().SetTimeout(10 * time.Second)
resp, err := client.R().Get("http://127.0.0.1/nginx_status")
if err != nil || !resp.IsSuccessState() {
facades.Log().Error("[OpenResty] 获取OpenResty负载失败: " + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty负载失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty负载失败")
}
raw := resp.String()
@@ -203,13 +203,13 @@ func (c *OpenRestyController) Load(ctx http.Context) {
}
var data []nginxStatus
workers := tools.ExecShell("ps aux | grep nginx | grep 'worker process' | wc -l")
workers := tools.Exec("ps aux | grep nginx | grep 'worker process' | wc -l")
data = append(data, nginxStatus{
Name: "工作进程",
Value: workers,
})
out := tools.ExecShell("ps aux | grep nginx | grep 'worker process' | awk '{memsum+=$6};END {print memsum}'")
out := tools.Exec("ps aux | grep nginx | grep 'worker process' | awk '{memsum+=$6};END {print memsum}'")
mem := tools.FormatBytes(cast.ToFloat64(out))
data = append(data, nginxStatus{
Name: "内存占用",
@@ -256,5 +256,5 @@ func (c *OpenRestyController) Load(ctx http.Context) {
})
}
controllers.Success(ctx, data)
return controllers.Success(ctx, data)
}
@@ -30,134 +30,136 @@ func NewPhp74Controller() *Php74Controller {
}
}
func (c *Php74Controller) Status(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php74Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
status := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php74Controller) Reload(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php74Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("systemctl reload php-fpm-" + c.version)
out := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl reload php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php74Controller) Start(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php74Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("systemctl start php-fpm-" + c.version)
out := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl start php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php74Controller) Stop(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php74Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("systemctl stop php-fpm-" + c.version)
out := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl stop php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status != "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php74Controller) Restart(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php74Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("systemctl restart php-fpm-" + c.version)
out := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl restart php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php74Controller) GetConfig(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php74Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
config := tools.ReadFile("/www/server/php/" + c.version + "/etc/php.ini")
controllers.Success(ctx, config)
config := tools.Read("/www/server/php/" + c.version + "/etc/php.ini")
return controllers.Success(ctx, config)
}
func (c *Php74Controller) SaveConfig(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php74Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
config := ctx.Request().Input("config")
tools.WriteFile("/www/server/php/"+c.version+"/etc/php.ini", config, 0644)
c.Reload(ctx)
tools.Write("/www/server/php/"+c.version+"/etc/php.ini", config, 0644)
return c.Reload(ctx)
}
func (c *Php74Controller) Load(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php74Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
client := req.C().SetTimeout(10 * time.Second)
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + c.version)
if err != nil || !resp.IsSuccessState() {
facades.Log().Error("获取PHP-" + c.version + "运行状态失败")
controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+c.version+"] 获取运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+c.version+"] 获取运行状态失败")
}
raw := resp.String()
@@ -180,43 +182,47 @@ func (c *Php74Controller) Load(ctx http.Context) {
}
}
controllers.Success(ctx, data)
return controllers.Success(ctx, data)
}
func (c *Php74Controller) ErrorLog(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php74Controller) ErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
log := tools.Escape(tools.ExecShell("tail -n 100 /www/server/php/" + c.version + "/var/log/php-fpm.log"))
controllers.Success(ctx, log)
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/php-fpm.log"))
return controllers.Success(ctx, log)
}
func (c *Php74Controller) SlowLog(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php74Controller) SlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
log := tools.Escape(tools.ExecShell("tail -n 100 /www/server/php/" + c.version + "/var/log/slow.log"))
controllers.Success(ctx, log)
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/slow.log"))
return controllers.Success(ctx, log)
}
func (c *Php74Controller) ClearErrorLog(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php74Controller) ClearErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("echo '' > /www/server/php/" + c.version + "/var/log/php-fpm.log")
controllers.Success(ctx, true)
tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/php-fpm.log")
return controllers.Success(ctx, true)
}
func (c *Php74Controller) ClearSlowLog(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php74Controller) ClearSlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("echo '' > /www/server/php/" + c.version + "/var/log/slow.log")
controllers.Success(ctx, true)
tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/slow.log")
return controllers.Success(ctx, true)
}
type Extension struct {
@@ -226,32 +232,32 @@ type Extension struct {
Installed bool `json:"installed"`
}
func (c *Php74Controller) GetExtensionList(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php74Controller) GetExtensionList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
extensions := c.GetExtensions()
controllers.Success(ctx, extensions)
return controllers.Success(ctx, extensions)
}
func (c *Php74Controller) InstallExtension(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php74Controller) InstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
slug := ctx.Request().Input("slug")
if len(slug) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "参数错误")
return
return controllers.Error(ctx, http.StatusBadRequest, "参数错误")
}
extensions := c.GetExtensions()
for _, item := range extensions {
if item.Slug == slug {
if item.Installed {
controllers.Error(ctx, http.StatusBadRequest, "扩展已安装")
return
return controllers.Error(ctx, http.StatusBadRequest, "扩展已安装")
}
var task models.Task
@@ -261,37 +267,34 @@ func (c *Php74Controller) InstallExtension(ctx http.Context) {
task.Log = "/tmp/" + item.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[PHP-" + c.version + "] 创建安装拓展任务失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
c.task.Process(task.ID)
controllers.Success(ctx, true)
return
return controllers.Success(ctx, true)
}
}
controllers.Error(ctx, http.StatusBadRequest, "扩展不存在")
return controllers.Error(ctx, http.StatusBadRequest, "扩展不存在")
}
func (c *Php74Controller) UninstallExtension(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php74Controller) UninstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
slug := ctx.Request().Input("slug")
if len(slug) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "参数错误")
return
return controllers.Error(ctx, http.StatusBadRequest, "参数错误")
}
extensions := c.GetExtensions()
for _, item := range extensions {
if item.Slug == slug {
if !item.Installed {
controllers.Error(ctx, http.StatusBadRequest, "扩展未安装")
return
return controllers.Error(ctx, http.StatusBadRequest, "扩展未安装")
}
var task models.Task
@@ -301,18 +304,16 @@ func (c *Php74Controller) UninstallExtension(ctx http.Context) {
task.Log = "/tmp/" + item.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[PHP-" + c.version + "] 创建卸载拓展任务失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
c.task.Process(task.ID)
controllers.Success(ctx, true)
return
return controllers.Success(ctx, true)
}
}
controllers.Error(ctx, http.StatusBadRequest, "扩展不存在")
return controllers.Error(ctx, http.StatusBadRequest, "扩展不存在")
}
func (c *Php74Controller) GetExtensions() []Extension {
@@ -354,7 +355,7 @@ func (c *Php74Controller) GetExtensions() []Extension {
Installed: false,
})
raw := tools.ExecShell("/www/server/php/" + c.version + "/bin/php -m")
raw := tools.Exec("/www/server/php/" + c.version + "/bin/php -m")
rawExtensionList := strings.Split(raw, "\n")
for _, item := range rawExtensionList {
@@ -30,134 +30,136 @@ func NewPhp80Controller() *Php80Controller {
}
}
func (c *Php80Controller) Status(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php80Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
status := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php80Controller) Reload(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php80Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("systemctl reload php-fpm-" + c.version)
out := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl reload php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php80Controller) Start(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php80Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("systemctl start php-fpm-" + c.version)
out := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl start php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php80Controller) Stop(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php80Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("systemctl stop php-fpm-" + c.version)
out := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl stop php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status != "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php80Controller) Restart(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php80Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("systemctl restart php-fpm-" + c.version)
out := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl restart php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php80Controller) GetConfig(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php80Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
config := tools.ReadFile("/www/server/php/" + c.version + "/etc/php.ini")
controllers.Success(ctx, config)
config := tools.Read("/www/server/php/" + c.version + "/etc/php.ini")
return controllers.Success(ctx, config)
}
func (c *Php80Controller) SaveConfig(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php80Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
config := ctx.Request().Input("config")
tools.WriteFile("/www/server/php/"+c.version+"/etc/php.ini", config, 0644)
c.Reload(ctx)
tools.Write("/www/server/php/"+c.version+"/etc/php.ini", config, 0644)
return c.Reload(ctx)
}
func (c *Php80Controller) Load(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php80Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
client := req.C().SetTimeout(10 * time.Second)
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + c.version)
if err != nil || !resp.IsSuccessState() {
facades.Log().Error("获取PHP-" + c.version + "运行状态失败")
controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+c.version+"] 获取运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+c.version+"] 获取运行状态失败")
}
raw := resp.String()
@@ -180,43 +182,47 @@ func (c *Php80Controller) Load(ctx http.Context) {
}
}
controllers.Success(ctx, data)
return controllers.Success(ctx, data)
}
func (c *Php80Controller) ErrorLog(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php80Controller) ErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
log := tools.Escape(tools.ExecShell("tail -n 100 /www/server/php/" + c.version + "/var/log/php-fpm.log"))
controllers.Success(ctx, log)
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/php-fpm.log"))
return controllers.Success(ctx, log)
}
func (c *Php80Controller) SlowLog(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php80Controller) SlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
log := tools.Escape(tools.ExecShell("tail -n 100 /www/server/php/" + c.version + "/var/log/slow.log"))
controllers.Success(ctx, log)
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/slow.log"))
return controllers.Success(ctx, log)
}
func (c *Php80Controller) ClearErrorLog(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php80Controller) ClearErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("echo '' > /www/server/php/" + c.version + "/var/log/php-fpm.log")
controllers.Success(ctx, true)
tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/php-fpm.log")
return controllers.Success(ctx, true)
}
func (c *Php80Controller) ClearSlowLog(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php80Controller) ClearSlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("echo '' > /www/server/php/" + c.version + "/var/log/slow.log")
controllers.Success(ctx, true)
tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/slow.log")
return controllers.Success(ctx, true)
}
type Extension struct {
@@ -226,32 +232,32 @@ type Extension struct {
Installed bool `json:"installed"`
}
func (c *Php80Controller) GetExtensionList(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php80Controller) GetExtensionList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
extensions := c.GetExtensions()
controllers.Success(ctx, extensions)
return controllers.Success(ctx, extensions)
}
func (c *Php80Controller) InstallExtension(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php80Controller) InstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
slug := ctx.Request().Input("slug")
if len(slug) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "参数错误")
return
return controllers.Error(ctx, http.StatusBadRequest, "参数错误")
}
extensions := c.GetExtensions()
for _, item := range extensions {
if item.Slug == slug {
if item.Installed {
controllers.Error(ctx, http.StatusBadRequest, "扩展已安装")
return
return controllers.Error(ctx, http.StatusBadRequest, "扩展已安装")
}
var task models.Task
@@ -261,37 +267,34 @@ func (c *Php80Controller) InstallExtension(ctx http.Context) {
task.Log = "/tmp/" + item.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[PHP-" + c.version + "] 创建安装拓展任务失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
c.task.Process(task.ID)
controllers.Success(ctx, true)
return
return controllers.Success(ctx, true)
}
}
controllers.Error(ctx, http.StatusBadRequest, "扩展不存在")
return controllers.Error(ctx, http.StatusBadRequest, "扩展不存在")
}
func (c *Php80Controller) UninstallExtension(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php80Controller) UninstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
slug := ctx.Request().Input("slug")
if len(slug) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "参数错误")
return
return controllers.Error(ctx, http.StatusBadRequest, "参数错误")
}
extensions := c.GetExtensions()
for _, item := range extensions {
if item.Slug == slug {
if !item.Installed {
controllers.Error(ctx, http.StatusBadRequest, "扩展未安装")
return
return controllers.Error(ctx, http.StatusBadRequest, "扩展未安装")
}
var task models.Task
@@ -301,18 +304,16 @@ func (c *Php80Controller) UninstallExtension(ctx http.Context) {
task.Log = "/tmp/" + item.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[PHP-" + c.version + "] 创建卸载拓展任务失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
c.task.Process(task.ID)
controllers.Success(ctx, true)
return
return controllers.Success(ctx, true)
}
}
controllers.Error(ctx, http.StatusBadRequest, "扩展不存在")
return controllers.Error(ctx, http.StatusBadRequest, "扩展不存在")
}
func (c *Php80Controller) GetExtensions() []Extension {
@@ -354,7 +355,7 @@ func (c *Php80Controller) GetExtensions() []Extension {
Installed: false,
})
raw := tools.ExecShell("/www/server/php/" + c.version + "/bin/php -m")
raw := tools.Exec("/www/server/php/" + c.version + "/bin/php -m")
rawExtensionList := strings.Split(raw, "\n")
for _, item := range rawExtensionList {
@@ -30,134 +30,136 @@ func NewPhp81Controller() *Php81Controller {
}
}
func (c *Php81Controller) Status(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php81Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
status := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php81Controller) Reload(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php81Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("systemctl reload php-fpm-" + c.version)
out := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl reload php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php81Controller) Start(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php81Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("systemctl start php-fpm-" + c.version)
out := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl start php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php81Controller) Stop(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php81Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("systemctl stop php-fpm-" + c.version)
out := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl stop php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status != "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php81Controller) Restart(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php81Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("systemctl restart php-fpm-" + c.version)
out := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl restart php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php81Controller) GetConfig(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php81Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
config := tools.ReadFile("/www/server/php/" + c.version + "/etc/php.ini")
controllers.Success(ctx, config)
config := tools.Read("/www/server/php/" + c.version + "/etc/php.ini")
return controllers.Success(ctx, config)
}
func (c *Php81Controller) SaveConfig(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php81Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
config := ctx.Request().Input("config")
tools.WriteFile("/www/server/php/"+c.version+"/etc/php.ini", config, 0644)
c.Reload(ctx)
tools.Write("/www/server/php/"+c.version+"/etc/php.ini", config, 0644)
return c.Reload(ctx)
}
func (c *Php81Controller) Load(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php81Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
client := req.C().SetTimeout(10 * time.Second)
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + c.version)
if err != nil || !resp.IsSuccessState() {
facades.Log().Error("获取PHP-" + c.version + "运行状态失败")
controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+c.version+"] 获取运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+c.version+"] 获取运行状态失败")
}
raw := resp.String()
@@ -180,43 +182,47 @@ func (c *Php81Controller) Load(ctx http.Context) {
}
}
controllers.Success(ctx, data)
return controllers.Success(ctx, data)
}
func (c *Php81Controller) ErrorLog(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php81Controller) ErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
log := tools.Escape(tools.ExecShell("tail -n 100 /www/server/php/" + c.version + "/var/log/php-fpm.log"))
controllers.Success(ctx, log)
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/php-fpm.log"))
return controllers.Success(ctx, log)
}
func (c *Php81Controller) SlowLog(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php81Controller) SlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
log := tools.Escape(tools.ExecShell("tail -n 100 /www/server/php/" + c.version + "/var/log/slow.log"))
controllers.Success(ctx, log)
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/slow.log"))
return controllers.Success(ctx, log)
}
func (c *Php81Controller) ClearErrorLog(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php81Controller) ClearErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("echo '' > /www/server/php/" + c.version + "/var/log/php-fpm.log")
controllers.Success(ctx, true)
tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/php-fpm.log")
return controllers.Success(ctx, true)
}
func (c *Php81Controller) ClearSlowLog(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php81Controller) ClearSlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("echo '' > /www/server/php/" + c.version + "/var/log/slow.log")
controllers.Success(ctx, true)
tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/slow.log")
return controllers.Success(ctx, true)
}
type Extension struct {
@@ -226,32 +232,32 @@ type Extension struct {
Installed bool `json:"installed"`
}
func (c *Php81Controller) GetExtensionList(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php81Controller) GetExtensionList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
extensions := c.GetExtensions()
controllers.Success(ctx, extensions)
return controllers.Success(ctx, extensions)
}
func (c *Php81Controller) InstallExtension(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php81Controller) InstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
slug := ctx.Request().Input("slug")
if len(slug) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "参数错误")
return
return controllers.Error(ctx, http.StatusBadRequest, "参数错误")
}
extensions := c.GetExtensions()
for _, item := range extensions {
if item.Slug == slug {
if item.Installed {
controllers.Error(ctx, http.StatusBadRequest, "扩展已安装")
return
return controllers.Error(ctx, http.StatusBadRequest, "扩展已安装")
}
var task models.Task
@@ -261,37 +267,34 @@ func (c *Php81Controller) InstallExtension(ctx http.Context) {
task.Log = "/tmp/" + item.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[PHP-" + c.version + "] 创建安装拓展任务失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
c.task.Process(task.ID)
controllers.Success(ctx, true)
return
return controllers.Success(ctx, true)
}
}
controllers.Error(ctx, http.StatusBadRequest, "扩展不存在")
return controllers.Error(ctx, http.StatusBadRequest, "扩展不存在")
}
func (c *Php81Controller) UninstallExtension(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php81Controller) UninstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
slug := ctx.Request().Input("slug")
if len(slug) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "参数错误")
return
return controllers.Error(ctx, http.StatusBadRequest, "参数错误")
}
extensions := c.GetExtensions()
for _, item := range extensions {
if item.Slug == slug {
if !item.Installed {
controllers.Error(ctx, http.StatusBadRequest, "扩展未安装")
return
return controllers.Error(ctx, http.StatusBadRequest, "扩展未安装")
}
var task models.Task
@@ -301,18 +304,16 @@ func (c *Php81Controller) UninstallExtension(ctx http.Context) {
task.Log = "/tmp/" + item.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[PHP-" + c.version + "] 创建卸载拓展任务失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
c.task.Process(task.ID)
controllers.Success(ctx, true)
return
return controllers.Success(ctx, true)
}
}
controllers.Error(ctx, http.StatusBadRequest, "扩展不存在")
return controllers.Error(ctx, http.StatusBadRequest, "扩展不存在")
}
func (c *Php81Controller) GetExtensions() []Extension {
@@ -354,7 +355,7 @@ func (c *Php81Controller) GetExtensions() []Extension {
Installed: false,
})
raw := tools.ExecShell("/www/server/php/" + c.version + "/bin/php -m")
raw := tools.Exec("/www/server/php/" + c.version + "/bin/php -m")
rawExtensionList := strings.Split(raw, "\n")
for _, item := range rawExtensionList {
@@ -30,134 +30,136 @@ func NewPhp82Controller() *Php82Controller {
}
}
func (c *Php82Controller) Status(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php82Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
status := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php82Controller) Reload(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php82Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("systemctl reload php-fpm-" + c.version)
out := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl reload php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php82Controller) Start(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php82Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("systemctl start php-fpm-" + c.version)
out := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl start php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php82Controller) Stop(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php82Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("systemctl stop php-fpm-" + c.version)
out := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl stop php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status != "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php82Controller) Restart(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php82Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("systemctl restart php-fpm-" + c.version)
out := tools.ExecShell("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl restart php-fpm-" + c.version)
out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'")
status := strings.TrimSpace(out)
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
func (c *Php82Controller) GetConfig(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php82Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
config := tools.ReadFile("/www/server/php/" + c.version + "/etc/php.ini")
controllers.Success(ctx, config)
config := tools.Read("/www/server/php/" + c.version + "/etc/php.ini")
return controllers.Success(ctx, config)
}
func (c *Php82Controller) SaveConfig(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php82Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
config := ctx.Request().Input("config")
tools.WriteFile("/www/server/php/"+c.version+"/etc/php.ini", config, 0644)
c.Reload(ctx)
tools.Write("/www/server/php/"+c.version+"/etc/php.ini", config, 0644)
return c.Reload(ctx)
}
func (c *Php82Controller) Load(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php82Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
client := req.C().SetTimeout(10 * time.Second)
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + c.version)
if err != nil || !resp.IsSuccessState() {
facades.Log().Error("获取PHP-" + c.version + "运行状态失败")
controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+c.version+"] 获取运行状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+c.version+"] 获取运行状态失败")
}
raw := resp.String()
@@ -180,43 +182,47 @@ func (c *Php82Controller) Load(ctx http.Context) {
}
}
controllers.Success(ctx, data)
return controllers.Success(ctx, data)
}
func (c *Php82Controller) ErrorLog(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php82Controller) ErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
log := tools.Escape(tools.ExecShell("tail -n 100 /www/server/php/" + c.version + "/var/log/php-fpm.log"))
controllers.Success(ctx, log)
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/php-fpm.log"))
return controllers.Success(ctx, log)
}
func (c *Php82Controller) SlowLog(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php82Controller) SlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
log := tools.Escape(tools.ExecShell("tail -n 100 /www/server/php/" + c.version + "/var/log/slow.log"))
controllers.Success(ctx, log)
log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/slow.log"))
return controllers.Success(ctx, log)
}
func (c *Php82Controller) ClearErrorLog(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php82Controller) ClearErrorLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("echo '' > /www/server/php/" + c.version + "/var/log/php-fpm.log")
controllers.Success(ctx, true)
tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/php-fpm.log")
return controllers.Success(ctx, true)
}
func (c *Php82Controller) ClearSlowLog(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php82Controller) ClearSlowLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
tools.ExecShell("echo '' > /www/server/php/" + c.version + "/var/log/slow.log")
controllers.Success(ctx, true)
tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/slow.log")
return controllers.Success(ctx, true)
}
type Extension struct {
@@ -226,32 +232,32 @@ type Extension struct {
Installed bool `json:"installed"`
}
func (c *Php82Controller) GetExtensionList(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php82Controller) GetExtensionList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
extensions := c.GetExtensions()
controllers.Success(ctx, extensions)
return controllers.Success(ctx, extensions)
}
func (c *Php82Controller) InstallExtension(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php82Controller) InstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
slug := ctx.Request().Input("slug")
if len(slug) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "参数错误")
return
return controllers.Error(ctx, http.StatusBadRequest, "参数错误")
}
extensions := c.GetExtensions()
for _, item := range extensions {
if item.Slug == slug {
if item.Installed {
controllers.Error(ctx, http.StatusBadRequest, "扩展已安装")
return
return controllers.Error(ctx, http.StatusBadRequest, "扩展已安装")
}
var task models.Task
@@ -261,37 +267,34 @@ func (c *Php82Controller) InstallExtension(ctx http.Context) {
task.Log = "/tmp/" + item.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[PHP-" + c.version + "] 创建安装拓展任务失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
c.task.Process(task.ID)
controllers.Success(ctx, true)
return
return controllers.Success(ctx, true)
}
}
controllers.Error(ctx, http.StatusBadRequest, "扩展不存在")
return controllers.Error(ctx, http.StatusBadRequest, "扩展不存在")
}
func (c *Php82Controller) UninstallExtension(ctx http.Context) {
if !controllers.Check(ctx, "php"+c.version) {
return
func (c *Php82Controller) UninstallExtension(ctx http.Context) http.Response {
check := controllers.Check(ctx, "php"+c.version)
if check != nil {
return check
}
slug := ctx.Request().Input("slug")
if len(slug) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "参数错误")
return
return controllers.Error(ctx, http.StatusBadRequest, "参数错误")
}
extensions := c.GetExtensions()
for _, item := range extensions {
if item.Slug == slug {
if !item.Installed {
controllers.Error(ctx, http.StatusBadRequest, "扩展未安装")
return
return controllers.Error(ctx, http.StatusBadRequest, "扩展未安装")
}
var task models.Task
@@ -301,18 +304,16 @@ func (c *Php82Controller) UninstallExtension(ctx http.Context) {
task.Log = "/tmp/" + item.Slug + ".log"
if err := facades.Orm().Query().Create(&task); err != nil {
facades.Log().Error("[PHP-" + c.version + "] 创建卸载拓展任务失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
c.task.Process(task.ID)
controllers.Success(ctx, true)
return
return controllers.Success(ctx, true)
}
}
controllers.Error(ctx, http.StatusBadRequest, "扩展不存在")
return controllers.Error(ctx, http.StatusBadRequest, "扩展不存在")
}
func (c *Php82Controller) GetExtensions() []Extension {
@@ -348,7 +349,7 @@ func (c *Php82Controller) GetExtensions() []Extension {
Installed: false,
})
raw := tools.ExecShell("/www/server/php/" + c.version + "/bin/php -m")
raw := tools.Exec("/www/server/php/" + c.version + "/bin/php -m")
rawExtensionList := strings.Split(raw, "\n")
for _, item := range rawExtensionList {
@@ -18,15 +18,15 @@ func NewPhpMyAdminController() *PhpMyAdminController {
return &PhpMyAdminController{}
}
func (c *PhpMyAdminController) Info(ctx http.Context) {
if !controllers.Check(ctx, "phpmyadmin") {
return
func (c *PhpMyAdminController) Info(ctx http.Context) http.Response {
check := controllers.Check(ctx, "phpmyadmin")
if check != nil {
return check
}
files, err := os.ReadDir("/www/server/phpmyadmin")
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, "找不到 phpMyAdmin 目录")
return
return controllers.Error(ctx, http.StatusBadRequest, "找不到 phpMyAdmin 目录")
}
var phpmyadmin string
@@ -36,46 +36,44 @@ func (c *PhpMyAdminController) Info(ctx http.Context) {
}
}
if len(phpmyadmin) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "找不到 phpMyAdmin 目录")
return
return controllers.Error(ctx, http.StatusBadRequest, "找不到 phpMyAdmin 目录")
}
conf := tools.ReadFile("/www/server/vhost/phpmyadmin.conf")
conf := tools.Read("/www/server/vhost/phpmyadmin.conf")
match := regexp.MustCompile(`listen\s+(\d+);`).FindStringSubmatch(conf)
if len(match) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "找不到 phpMyAdmin 端口")
return
return controllers.Error(ctx, http.StatusBadRequest, "找不到 phpMyAdmin 端口")
}
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"phpmyadmin": phpmyadmin,
"port": match[1],
})
}
func (c *PhpMyAdminController) SetPort(ctx http.Context) {
if !controllers.Check(ctx, "phpmyadmin") {
return
func (c *PhpMyAdminController) SetPort(ctx http.Context) http.Response {
check := controllers.Check(ctx, "phpmyadmin")
if check != nil {
return check
}
port := ctx.Request().Input("port")
if len(port) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "端口不能为空")
return
return controllers.Error(ctx, http.StatusBadRequest, "端口不能为空")
}
conf := tools.ReadFile("/www/server/vhost/phpmyadmin.conf")
conf := tools.Read("/www/server/vhost/phpmyadmin.conf")
conf = regexp.MustCompile(`listen\s+(\d+);`).ReplaceAllString(conf, "listen "+port+";")
tools.WriteFile("/www/server/vhost/phpmyadmin.conf", conf, 0644)
tools.Write("/www/server/vhost/phpmyadmin.conf", conf, 0644)
if tools.IsRHEL() {
tools.ExecShell("firewall-cmd --zone=public --add-port=" + port + "/tcp --permanent")
tools.ExecShell("firewall-cmd --reload")
tools.Exec("firewall-cmd --zone=public --add-port=" + port + "/tcp --permanent")
tools.Exec("firewall-cmd --reload")
} else {
tools.ExecShell("ufw allow " + port + "/tcp")
tools.ExecShell("ufw reload")
tools.Exec("ufw allow " + port + "/tcp")
tools.Exec("ufw reload")
}
tools.ExecShell("systemctl reload openresty")
tools.Exec("systemctl reload openresty")
controllers.Success(ctx, nil)
return controllers.Success(ctx, nil)
}
@@ -31,232 +31,232 @@ func NewPostgresql15Controller() *Postgresql15Controller {
}
// Status 获取运行状态
func (c *Postgresql15Controller) Status(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
status := tools.ExecShell("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Reload 重载配置
func (c *Postgresql15Controller) Reload(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
tools.ExecShell("systemctl reload postgresql")
status := tools.ExecShell("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl reload postgresql")
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Restart 重启服务
func (c *Postgresql15Controller) Restart(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
tools.ExecShell("systemctl restart postgresql")
status := tools.ExecShell("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl restart postgresql")
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Start 启动服务
func (c *Postgresql15Controller) Start(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
tools.ExecShell("systemctl start postgresql")
status := tools.ExecShell("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl start postgresql")
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Stop 停止服务
func (c *Postgresql15Controller) Stop(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
tools.ExecShell("systemctl stop postgresql")
status := tools.ExecShell("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl stop postgresql")
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
}
if status != "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// GetConfig 获取配置
func (c *Postgresql15Controller) GetConfig(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
// 获取配置
config := tools.ReadFile("/www/server/postgresql/data/postgresql.conf")
config := tools.Read("/www/server/postgresql/data/postgresql.conf")
if len(config) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL配置失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL配置失败")
}
controllers.Success(ctx, config)
return controllers.Success(ctx, config)
}
// GetUserConfig 获取用户配置
func (c *Postgresql15Controller) GetUserConfig(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) GetUserConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
// 获取配置
config := tools.ReadFile("/www/server/postgresql/data/pg_hba.conf")
config := tools.Read("/www/server/postgresql/data/pg_hba.conf")
if len(config) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL配置失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL配置失败")
}
controllers.Success(ctx, config)
return controllers.Success(ctx, config)
}
// SaveConfig 保存配置
func (c *Postgresql15Controller) SaveConfig(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
config := ctx.Request().Input("config")
if len(config) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "配置不能为空")
return
return controllers.Error(ctx, http.StatusBadRequest, "配置不能为空")
}
if !tools.WriteFile("/www/server/postgresql/data/postgresql.conf", config, 0644) {
controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败")
return
if !tools.Write("/www/server/postgresql/data/postgresql.conf", config, 0644) {
return controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败")
}
c.Restart(ctx)
return c.Restart(ctx)
}
// SaveUserConfig 保存用户配置
func (c *Postgresql15Controller) SaveUserConfig(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) SaveUserConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
config := ctx.Request().Input("config")
if len(config) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "配置不能为空")
return
return controllers.Error(ctx, http.StatusBadRequest, "配置不能为空")
}
if !tools.WriteFile("/www/server/postgresql/data/pg_hba.conf", config, 0644) {
controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败")
return
if !tools.Write("/www/server/postgresql/data/pg_hba.conf", config, 0644) {
return controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败")
}
c.Restart(ctx)
return c.Restart(ctx)
}
// Load 获取负载
func (c *Postgresql15Controller) Load(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
status := tools.ExecShell("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if status != "active" {
controllers.Error(ctx, http.StatusInternalServerError, "PostgreSQL 已停止运行")
return
return controllers.Error(ctx, http.StatusInternalServerError, "PostgreSQL 已停止运行")
}
data := []Info{
{"启动时间", carbon.Parse(tools.ExecShell(`echo "select pg_postmaster_start_time();" | su - postgres -c "psql" | sed -n 3p | cut -d'.' -f1`)).ToDateTimeString()},
{"进程 PID", tools.ExecShell(`echo "select pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)},
{"进程数", tools.ExecShell(`ps aux | grep postgres | grep -v grep | wc -l`)},
{"总连接数", tools.ExecShell(`echo "SELECT count(*) FROM pg_stat_activity WHERE NOT pid=pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)},
{"空间占用", tools.ExecShell(`echo "select pg_size_pretty(pg_database_size('postgres'));" | su - postgres -c "psql" | sed -n 3p`)},
{"启动时间", carbon.Parse(tools.Exec(`echo "select pg_postmaster_start_time();" | su - postgres -c "psql" | sed -n 3p | cut -d'.' -f1`)).ToDateTimeString()},
{"进程 PID", tools.Exec(`echo "select pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)},
{"进程数", tools.Exec(`ps aux | grep postgres | grep -v grep | wc -l`)},
{"总连接数", tools.Exec(`echo "SELECT count(*) FROM pg_stat_activity WHERE NOT pid=pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)},
{"空间占用", tools.Exec(`echo "select pg_size_pretty(pg_database_size('postgres'));" | su - postgres -c "psql" | sed -n 3p`)},
}
controllers.Success(ctx, data)
return controllers.Success(ctx, data)
}
// Log 获取日志
func (c *Postgresql15Controller) Log(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) Log(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
log := tools.ExecShell("tail -n 100 /www/server/postgresql/logs/postgresql-" + carbon.Now().ToDateString() + ".log")
controllers.Success(ctx, log)
log := tools.Exec("tail -n 100 /www/server/postgresql/logs/postgresql-" + carbon.Now().ToDateString() + ".log")
return controllers.Success(ctx, log)
}
// ClearLog 清空日志
func (c *Postgresql15Controller) ClearLog(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) ClearLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
tools.ExecShell("echo '' > /www/server/postgresql/logs/postgresql-" + carbon.Now().ToDateString() + ".log")
controllers.Success(ctx, nil)
tools.Exec("echo '' > /www/server/postgresql/logs/postgresql-" + carbon.Now().ToDateString() + ".log")
return controllers.Success(ctx, nil)
}
// DatabaseList 获取数据库列表
func (c *Postgresql15Controller) DatabaseList(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) DatabaseList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
status := tools.ExecShell("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if status != "active" {
controllers.Error(ctx, http.StatusInternalServerError, "PostgreSQL 已停止运行")
return
return controllers.Error(ctx, http.StatusInternalServerError, "PostgreSQL 已停止运行")
}
raw := tools.ExecShell(`echo "\l" | su - postgres -c "psql"`)
raw := tools.Exec(`echo "\l" | su - postgres -c "psql"`)
databases := strings.Split(raw, "\n")
databases = databases[3 : len(databases)-1]
@@ -285,27 +285,27 @@ func (c *Postgresql15Controller) DatabaseList(ctx http.Context) {
startIndex := (page - 1) * limit
endIndex := page * limit
if startIndex > len(databaseList) {
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []database{},
})
return
}
if endIndex > len(databaseList) {
endIndex = len(databaseList)
}
pagedDatabases := databaseList[startIndex:endIndex]
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": len(databaseList),
"items": pagedDatabases,
})
}
// AddDatabase 添加数据库
func (c *Postgresql15Controller) AddDatabase(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) AddDatabase(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -314,78 +314,76 @@ func (c *Postgresql15Controller) AddDatabase(ctx http.Context) {
"password": "required|min_len:8|max_len:255",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
database := ctx.Request().Input("database")
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
tools.ExecShell(`echo "CREATE DATABASE ` + database + `;" | su - postgres -c "psql"`)
tools.ExecShell(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`)
tools.ExecShell(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`)
tools.Exec(`echo "CREATE DATABASE ` + database + `;" | su - postgres -c "psql"`)
tools.Exec(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`)
tools.Exec(`echo "ALTER DATABASE ` + database + ` OWNER TO ` + user + `;" | su - postgres -c "psql"`)
tools.Exec(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`)
userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256"
tools.ExecShell(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`)
tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`)
c.Reload(ctx)
return c.Reload(ctx)
}
// DeleteDatabase 删除数据库
func (c *Postgresql15Controller) DeleteDatabase(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) DeleteDatabase(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
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:postgres,template0,template1",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
database := ctx.Request().Input("database")
tools.ExecShell(`echo "DROP DATABASE ` + database + `;" | su - postgres -c "psql"`)
tools.Exec(`echo "DROP DATABASE ` + database + `;" | su - postgres -c "psql"`)
controllers.Success(ctx, nil)
return controllers.Success(ctx, nil)
}
// BackupList 获取备份列表
func (c *Postgresql15Controller) BackupList(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) BackupList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
backupList, err := c.backup.PostgresqlList()
if err != nil {
facades.Log().Error("[PostgreSQL] 获取备份列表失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "获取备份列表失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取备份列表失败")
}
controllers.Success(ctx, backupList)
return controllers.Success(ctx, backupList)
}
// UploadBackup 上传备份
func (c *Postgresql15Controller) UploadBackup(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) UploadBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
file, err := ctx.Request().File("file")
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
return
return controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
@@ -396,71 +394,68 @@ func (c *Postgresql15Controller) UploadBackup(ctx http.Context) {
name := file.GetClientOriginalName()
_, err = file.StoreAs(backupPath, name)
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
return
return controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
}
controllers.Success(ctx, nil)
return controllers.Success(ctx, nil)
}
// CreateBackup 创建备份
func (c *Postgresql15Controller) CreateBackup(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) CreateBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
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 {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
database := ctx.Request().Input("database")
err = c.backup.PostgresqlBackup(database)
if err != nil {
facades.Log().Error("[PostgreSQL] 创建备份失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "创建备份失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "创建备份失败")
}
controllers.Success(ctx, nil)
return controllers.Success(ctx, nil)
}
// DeleteBackup 删除备份
func (c *Postgresql15Controller) DeleteBackup(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) DeleteBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
"name": "required|min_len:1|max_len:255",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
fileName := ctx.Request().Input("name")
tools.RemoveFile(backupPath + "/" + fileName)
tools.Remove(backupPath + "/" + fileName)
controllers.Success(ctx, nil)
return controllers.Success(ctx, nil)
}
// RestoreBackup 还原备份
func (c *Postgresql15Controller) RestoreBackup(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) RestoreBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -468,28 +463,26 @@ func (c *Postgresql15Controller) RestoreBackup(ctx http.Context) {
"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 {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
err = c.backup.PostgresqlRestore(ctx.Request().Input("database"), ctx.Request().Input("name"))
if err != nil {
facades.Log().Error("[PostgreSQL] 还原失败:" + err.Error())
controllers.Error(ctx, http.StatusInternalServerError, "还原失败: "+err.Error())
return
return controllers.Error(ctx, http.StatusInternalServerError, "还原失败: "+err.Error())
}
controllers.Success(ctx, nil)
return controllers.Success(ctx, nil)
}
// UserList 用户列表
func (c *Postgresql15Controller) UserList(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) UserList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
type user struct {
@@ -497,11 +490,10 @@ func (c *Postgresql15Controller) UserList(ctx http.Context) {
Role string `json:"role"`
}
raw := tools.ExecShell(`echo "\du" | su - postgres -c "psql"`)
raw := tools.Exec(`echo "\du" | su - postgres -c "psql"`)
users := strings.Split(raw, "\n")
if len(users) < 4 {
controllers.Error(ctx, http.StatusInternalServerError, "用户列表为空")
return
return controllers.Error(ctx, http.StatusInternalServerError, "用户列表为空")
}
users = users[3:]
@@ -523,27 +515,27 @@ func (c *Postgresql15Controller) UserList(ctx http.Context) {
startIndex := (page - 1) * limit
endIndex := page * limit
if startIndex > len(userList) {
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []user{},
})
return
}
if endIndex > len(userList) {
endIndex = len(userList)
}
pagedUsers := userList[startIndex:endIndex]
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": len(userList),
"items": pagedUsers,
})
}
// AddUser 添加用户
func (c *Postgresql15Controller) AddUser(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) AddUser(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -552,55 +544,53 @@ func (c *Postgresql15Controller) AddUser(ctx http.Context) {
"password": "required|min_len:8|max_len:255",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
database := ctx.Request().Input("database")
tools.ExecShell(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`)
tools.ExecShell(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`)
tools.Exec(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`)
tools.Exec(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`)
userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256"
tools.ExecShell(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`)
tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`)
c.Reload(ctx)
return c.Reload(ctx)
}
// DeleteUser 删除用户
func (c *Postgresql15Controller) DeleteUser(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) DeleteUser(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
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 {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
user := ctx.Request().Input("user")
tools.ExecShell(`echo "DROP USER ` + user + `;" | su - postgres -c "psql"`)
tools.ExecShell(`sed -i '/` + user + `/d' /www/server/postgresql/data/pg_hba.conf`)
tools.Exec(`echo "DROP USER ` + user + `;" | su - postgres -c "psql"`)
tools.Exec(`sed -i '/` + user + `/d' /www/server/postgresql/data/pg_hba.conf`)
c.Reload(ctx)
return c.Reload(ctx)
}
// SetUserPassword 设置用户密码
func (c *Postgresql15Controller) SetUserPassword(ctx http.Context) {
if !controllers.Check(ctx, "postgresql15") {
return
func (c *Postgresql15Controller) SetUserPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql15")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -608,17 +598,15 @@ func (c *Postgresql15Controller) SetUserPassword(ctx http.Context) {
"password": "required|min_len:8|max_len:255",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
tools.ExecShell(`echo "ALTER USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`)
tools.Exec(`echo "ALTER USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`)
controllers.Success(ctx, nil)
return controllers.Success(ctx, nil)
}
@@ -0,0 +1,612 @@
package postgresql16
import (
"strings"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/carbon"
"panel/app/http/controllers"
"panel/app/models"
"panel/app/services"
"panel/pkg/tools"
)
type Postgresql16Controller struct {
setting services.Setting
backup services.Backup
}
type Info struct {
Name string `json:"name"`
Value string `json:"value"`
}
func NewPostgresql16Controller() *Postgresql16Controller {
return &Postgresql16Controller{
setting: services.NewSettingImpl(),
backup: services.NewBackupImpl(),
}
}
// Status 获取运行状态
func (c *Postgresql16Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
}
if status == "active" {
return controllers.Success(ctx, true)
} else {
return controllers.Success(ctx, false)
}
}
// Reload 重载配置
func (c *Postgresql16Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
tools.Exec("systemctl reload postgresql")
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
}
if status == "active" {
return controllers.Success(ctx, true)
} else {
return controllers.Success(ctx, false)
}
}
// Restart 重启服务
func (c *Postgresql16Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
tools.Exec("systemctl restart postgresql")
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
}
if status == "active" {
return controllers.Success(ctx, true)
} else {
return controllers.Success(ctx, false)
}
}
// Start 启动服务
func (c *Postgresql16Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
tools.Exec("systemctl start postgresql")
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
}
if status == "active" {
return controllers.Success(ctx, true)
} else {
return controllers.Success(ctx, false)
}
}
// Stop 停止服务
func (c *Postgresql16Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
tools.Exec("systemctl stop postgresql")
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
}
if status != "active" {
return controllers.Success(ctx, true)
} else {
return controllers.Success(ctx, false)
}
}
// GetConfig 获取配置
func (c *Postgresql16Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
// 获取配置
config := tools.Read("/www/server/postgresql/data/postgresql.conf")
if len(config) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL配置失败")
}
return controllers.Success(ctx, config)
}
// GetUserConfig 获取用户配置
func (c *Postgresql16Controller) GetUserConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
// 获取配置
config := tools.Read("/www/server/postgresql/data/pg_hba.conf")
if len(config) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL配置失败")
}
return controllers.Success(ctx, config)
}
// SaveConfig 保存配置
func (c *Postgresql16Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
config := ctx.Request().Input("config")
if len(config) == 0 {
return controllers.Error(ctx, http.StatusBadRequest, "配置不能为空")
}
if !tools.Write("/www/server/postgresql/data/postgresql.conf", config, 0644) {
return controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败")
}
return c.Restart(ctx)
}
// SaveUserConfig 保存用户配置
func (c *Postgresql16Controller) SaveUserConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
config := ctx.Request().Input("config")
if len(config) == 0 {
return controllers.Error(ctx, http.StatusBadRequest, "配置不能为空")
}
if !tools.Write("/www/server/postgresql/data/pg_hba.conf", config, 0644) {
return controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败")
}
return c.Restart(ctx)
}
// Load 获取负载
func (c *Postgresql16Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if status != "active" {
return controllers.Error(ctx, http.StatusInternalServerError, "PostgreSQL 已停止运行")
}
data := []Info{
{"启动时间", carbon.Parse(tools.Exec(`echo "select pg_postmaster_start_time();" | su - postgres -c "psql" | sed -n 3p | cut -d'.' -f1`)).ToDateTimeString()},
{"进程 PID", tools.Exec(`echo "select pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)},
{"进程数", tools.Exec(`ps aux | grep postgres | grep -v grep | wc -l`)},
{"总连接数", tools.Exec(`echo "SELECT count(*) FROM pg_stat_activity WHERE NOT pid=pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)},
{"空间占用", tools.Exec(`echo "select pg_size_pretty(pg_database_size('postgres'));" | su - postgres -c "psql" | sed -n 3p`)},
}
return controllers.Success(ctx, data)
}
// Log 获取日志
func (c *Postgresql16Controller) Log(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
log := tools.Exec("tail -n 100 /www/server/postgresql/logs/postgresql-" + carbon.Now().ToDateString() + ".log")
return controllers.Success(ctx, log)
}
// ClearLog 清空日志
func (c *Postgresql16Controller) ClearLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
tools.Exec("echo '' > /www/server/postgresql/logs/postgresql-" + carbon.Now().ToDateString() + ".log")
return controllers.Success(ctx, nil)
}
// DatabaseList 获取数据库列表
func (c *Postgresql16Controller) DatabaseList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if status != "active" {
return controllers.Error(ctx, http.StatusInternalServerError, "PostgreSQL 已停止运行")
}
raw := tools.Exec(`echo "\l" | su - postgres -c "psql"`)
databases := strings.Split(raw, "\n")
databases = databases[3 : len(databases)-1]
type database struct {
Name string `json:"name"`
Owner string `json:"owner"`
Encoding string `json:"encoding"`
}
var databaseList []database
for _, db := range databases {
parts := strings.Split(db, "|")
if len(parts) != 9 || len(strings.TrimSpace(parts[0])) == 0 {
continue
}
databaseList = append(databaseList, database{
Name: strings.TrimSpace(parts[0]),
Owner: strings.TrimSpace(parts[1]),
Encoding: strings.TrimSpace(parts[2]),
})
}
page := ctx.Request().QueryInt("page", 1)
limit := ctx.Request().QueryInt("limit", 10)
startIndex := (page - 1) * limit
endIndex := page * limit
if startIndex > len(databaseList) {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []database{},
})
}
if endIndex > len(databaseList) {
endIndex = len(databaseList)
}
pagedDatabases := databaseList[startIndex:endIndex]
return controllers.Success(ctx, http.Json{
"total": len(databaseList),
"items": pagedDatabases,
})
}
// AddDatabase 添加数据库
func (c *Postgresql16Controller) AddDatabase(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
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.StatusBadRequest, err.Error())
}
if validator.Fails() {
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
database := ctx.Request().Input("database")
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
tools.Exec(`echo "CREATE DATABASE ` + database + `;" | su - postgres -c "psql"`)
tools.Exec(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`)
tools.Exec(`echo "ALTER DATABASE ` + database + ` OWNER TO ` + user + `;" | su - postgres -c "psql"`)
tools.Exec(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`)
userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256"
tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`)
return c.Reload(ctx)
}
// DeleteDatabase 删除数据库
func (c *Postgresql16Controller) DeleteDatabase(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
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:postgres,template0,template1",
})
if err != nil {
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
database := ctx.Request().Input("database")
tools.Exec(`echo "DROP DATABASE ` + database + `;" | su - postgres -c "psql"`)
return controllers.Success(ctx, nil)
}
// BackupList 获取备份列表
func (c *Postgresql16Controller) BackupList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
backupList, err := c.backup.PostgresqlList()
if err != nil {
facades.Log().Error("[PostgreSQL] 获取备份列表失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "获取备份列表失败")
}
return controllers.Success(ctx, backupList)
}
// UploadBackup 上传备份
func (c *Postgresql16Controller) UploadBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
file, err := ctx.Request().File("file")
if err != nil {
return controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
if !tools.Exists(backupPath) {
tools.Mkdir(backupPath, 0644)
}
name := file.GetClientOriginalName()
_, err = file.StoreAs(backupPath, name)
if err != nil {
return controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
}
return controllers.Success(ctx, nil)
}
// CreateBackup 创建备份
func (c *Postgresql16Controller) CreateBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
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.StatusBadRequest, err.Error())
}
if validator.Fails() {
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
database := ctx.Request().Input("database")
err = c.backup.PostgresqlBackup(database)
if err != nil {
facades.Log().Error("[PostgreSQL] 创建备份失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "创建备份失败")
}
return controllers.Success(ctx, nil)
}
// DeleteBackup 删除备份
func (c *Postgresql16Controller) DeleteBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
"name": "required|min_len:1|max_len:255",
})
if err != nil {
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
fileName := ctx.Request().Input("name")
tools.Remove(backupPath + "/" + fileName)
return controllers.Success(ctx, nil)
}
// RestoreBackup 还原备份
func (c *Postgresql16Controller) RestoreBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
"name": "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.StatusBadRequest, err.Error())
}
if validator.Fails() {
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
err = c.backup.PostgresqlRestore(ctx.Request().Input("database"), ctx.Request().Input("name"))
if err != nil {
facades.Log().Error("[PostgreSQL] 还原失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "还原失败: "+err.Error())
}
return controllers.Success(ctx, nil)
}
// UserList 用户列表
func (c *Postgresql16Controller) UserList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
type user struct {
User string `json:"user"`
Role string `json:"role"`
}
raw := tools.Exec(`echo "\du" | su - postgres -c "psql"`)
users := strings.Split(raw, "\n")
if len(users) < 4 {
return controllers.Error(ctx, http.StatusInternalServerError, "用户列表为空")
}
users = users[3:]
var userList []user
for _, u := range users {
userInfo := strings.Split(u, "|")
if len(userInfo) != 2 {
continue
}
userList = append(userList, user{
User: strings.TrimSpace(userInfo[0]),
Role: strings.TrimSpace(userInfo[1]),
})
}
page := ctx.Request().QueryInt("page", 1)
limit := ctx.Request().QueryInt("limit", 10)
startIndex := (page - 1) * limit
endIndex := page * limit
if startIndex > len(userList) {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []user{},
})
}
if endIndex > len(userList) {
endIndex = len(userList)
}
pagedUsers := userList[startIndex:endIndex]
return controllers.Success(ctx, http.Json{
"total": len(userList),
"items": pagedUsers,
})
}
// AddUser 添加用户
func (c *Postgresql16Controller) AddUser(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
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.StatusBadRequest, err.Error())
}
if validator.Fails() {
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
database := ctx.Request().Input("database")
tools.Exec(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`)
tools.Exec(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`)
userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256"
tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`)
return c.Reload(ctx)
}
// DeleteUser 删除用户
func (c *Postgresql16Controller) DeleteUser(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
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.StatusBadRequest, err.Error())
}
if validator.Fails() {
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
user := ctx.Request().Input("user")
tools.Exec(`echo "DROP USER ` + user + `;" | su - postgres -c "psql"`)
tools.Exec(`sed -i '/` + user + `/d' /www/server/postgresql/data/pg_hba.conf`)
return c.Reload(ctx)
}
// SetUserPassword 设置用户密码
func (c *Postgresql16Controller) SetUserPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
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.StatusBadRequest, err.Error())
}
if validator.Fails() {
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
tools.Exec(`echo "ALTER USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`)
return controllers.Success(ctx, nil)
}
@@ -23,117 +23,117 @@ func NewPureFtpdController() *PureFtpdController {
}
// Status 获取运行状态
func (c *PureFtpdController) Status(ctx http.Context) {
if !controllers.Check(ctx, "pureftpd") {
return
func (c *PureFtpdController) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
}
status := tools.ExecShell("systemctl status pure-ftpd | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status pure-ftpd | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Reload 重载配置
func (c *PureFtpdController) Reload(ctx http.Context) {
if !controllers.Check(ctx, "pureftpd") {
return
func (c *PureFtpdController) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
}
tools.ExecShell("systemctl reload pure-ftpd")
status := tools.ExecShell("systemctl status pure-ftpd | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl reload pure-ftpd")
status := tools.Exec("systemctl status pure-ftpd | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Restart 重启服务
func (c *PureFtpdController) Restart(ctx http.Context) {
if !controllers.Check(ctx, "pureftpd") {
return
func (c *PureFtpdController) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
}
tools.ExecShell("systemctl restart pure-ftpd")
status := tools.ExecShell("systemctl status pure-ftpd | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl restart pure-ftpd")
status := tools.Exec("systemctl status pure-ftpd | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Start 启动服务
func (c *PureFtpdController) Start(ctx http.Context) {
if !controllers.Check(ctx, "pureftpd") {
return
func (c *PureFtpdController) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
}
tools.ExecShell("systemctl start pure-ftpd")
status := tools.ExecShell("systemctl status pure-ftpd | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl start pure-ftpd")
status := tools.Exec("systemctl status pure-ftpd | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Stop 停止服务
func (c *PureFtpdController) Stop(ctx http.Context) {
if !controllers.Check(ctx, "pureftpd") {
return
func (c *PureFtpdController) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
}
tools.ExecShell("systemctl stop pure-ftpd")
status := tools.ExecShell("systemctl status pure-ftpd | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl stop pure-ftpd")
status := tools.Exec("systemctl status pure-ftpd | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd状态失败")
}
if status != "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// List 获取用户列表
func (c *PureFtpdController) List(ctx http.Context) {
if !controllers.Check(ctx, "pureftpd") {
return
func (c *PureFtpdController) List(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
}
listRaw := tools.ExecShell("pure-pw list")
listRaw := tools.Exec("pure-pw list")
if len(listRaw) == 0 {
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []User{},
})
return
}
listArr := strings.Split(listRaw, "\n")
@@ -155,27 +155,27 @@ func (c *PureFtpdController) List(ctx http.Context) {
startIndex := (page - 1) * limit
endIndex := page * limit
if startIndex > len(users) {
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []User{},
})
return
}
if endIndex > len(users) {
endIndex = len(users)
}
pagedUsers := users[startIndex:endIndex]
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": len(users),
"items": pagedUsers,
})
}
// Add 添加用户
func (c *PureFtpdController) Add(ctx http.Context) {
if !controllers.Check(ctx, "pureftpd") {
return
func (c *PureFtpdController) Add(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -184,12 +184,10 @@ func (c *PureFtpdController) Add(ctx http.Context) {
"path": "required",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
username := ctx.Request().Input("username")
@@ -200,48 +198,47 @@ func (c *PureFtpdController) Add(ctx http.Context) {
path = "/" + path
}
if !tools.Exists(path) {
controllers.Error(ctx, http.StatusBadRequest, "目录不存在")
return
return controllers.Error(ctx, http.StatusBadRequest, "目录不存在")
}
tools.Chmod(path, 0755)
tools.Chown(path, "www", "www")
tools.ExecShell(`yes '` + password + `' | pure-pw useradd ` + username + ` -u www -g www -d ` + path)
tools.ExecShell("pure-pw mkdb")
tools.Exec(`yes '` + password + `' | pure-pw useradd ` + username + ` -u www -g www -d ` + path)
tools.Exec("pure-pw mkdb")
controllers.Success(ctx, nil)
return controllers.Success(ctx, nil)
}
// Delete 删除用户
func (c *PureFtpdController) Delete(ctx http.Context) {
if !controllers.Check(ctx, "pureftpd") {
return
func (c *PureFtpdController) Delete(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
"username": "required",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
username := ctx.Request().Input("username")
tools.ExecShell("pure-pw userdel " + username + " -m")
tools.ExecShell("pure-pw mkdb")
tools.Exec("pure-pw userdel " + username + " -m")
tools.Exec("pure-pw mkdb")
controllers.Success(ctx, nil)
return controllers.Success(ctx, nil)
}
// ChangePassword 修改密码
func (c *PureFtpdController) ChangePassword(ctx http.Context) {
if !controllers.Check(ctx, "pureftpd") {
return
func (c *PureFtpdController) ChangePassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -249,66 +246,63 @@ func (c *PureFtpdController) ChangePassword(ctx http.Context) {
"password": "required|min_len:6",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
username := ctx.Request().Input("username")
password := ctx.Request().Input("password")
tools.ExecShell(`yes '` + password + `' | pure-pw passwd ` + username + ` -m`)
tools.ExecShell("pure-pw mkdb")
tools.Exec(`yes '` + password + `' | pure-pw passwd ` + username + ` -m`)
tools.Exec("pure-pw mkdb")
controllers.Success(ctx, nil)
return controllers.Success(ctx, nil)
}
// GetPort 获取端口
func (c *PureFtpdController) GetPort(ctx http.Context) {
if !controllers.Check(ctx, "pureftpd") {
return
func (c *PureFtpdController) GetPort(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
}
port := tools.ExecShell(`cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`)
port := tools.Exec(`cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`)
if len(port) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd端口失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd端口失败")
}
controllers.Success(ctx, port)
return controllers.Success(ctx, port)
}
// SetPort 设置端口
func (c *PureFtpdController) SetPort(ctx http.Context) {
if !controllers.Check(ctx, "pureftpd") {
return
func (c *PureFtpdController) SetPort(ctx http.Context) http.Response {
check := controllers.Check(ctx, "pureftpd")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
"port": "required",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
port := ctx.Request().Input("port")
tools.ExecShell(`sed -i "s/Bind.*/Bind 0.0.0.0,` + port + `/g" /www/server/pure-ftpd/etc/pure-ftpd.conf`)
tools.Exec(`sed -i "s/Bind.*/Bind 0.0.0.0,` + port + `/g" /www/server/pure-ftpd/etc/pure-ftpd.conf`)
if tools.IsRHEL() {
tools.ExecShell("firewall-cmd --zone=public --add-port=" + port + "/tcp --permanent")
tools.ExecShell("firewall-cmd --reload")
tools.Exec("firewall-cmd --zone=public --add-port=" + port + "/tcp --permanent")
tools.Exec("firewall-cmd --reload")
} else {
tools.ExecShell("ufw allow " + port + "/tcp")
tools.ExecShell("ufw reload")
tools.Exec("ufw allow " + port + "/tcp")
tools.Exec("ufw reload")
}
tools.ExecShell("systemctl restart pure-ftpd")
tools.Exec("systemctl restart pure-ftpd")
controllers.Success(ctx, nil)
return controllers.Success(ctx, nil)
}
@@ -22,156 +22,154 @@ func NewRedisController() *RedisController {
}
// Status 获取运行状态
func (c *RedisController) Status(ctx http.Context) {
if !controllers.Check(ctx, "redis") {
return
func (c *RedisController) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "redis")
if check != nil {
return check
}
status := tools.ExecShell("systemctl status redis | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status redis | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取Redis状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取Redis状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Reload 重载配置
func (c *RedisController) Reload(ctx http.Context) {
if !controllers.Check(ctx, "redis") {
return
func (c *RedisController) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "redis")
if check != nil {
return check
}
tools.ExecShell("systemctl reload redis")
status := tools.ExecShell("systemctl status redis | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl reload redis")
status := tools.Exec("systemctl status redis | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取Redis状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取Redis状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Restart 重启服务
func (c *RedisController) Restart(ctx http.Context) {
if !controllers.Check(ctx, "redis") {
return
func (c *RedisController) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "redis")
if check != nil {
return check
}
tools.ExecShell("systemctl restart redis")
status := tools.ExecShell("systemctl status redis | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl restart redis")
status := tools.Exec("systemctl status redis | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取Redis状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取Redis状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Start 启动服务
func (c *RedisController) Start(ctx http.Context) {
if !controllers.Check(ctx, "redis") {
return
func (c *RedisController) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "redis")
if check != nil {
return check
}
tools.ExecShell("systemctl start redis")
status := tools.ExecShell("systemctl status redis | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl start redis")
status := tools.Exec("systemctl status redis | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取Redis状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取Redis状态失败")
}
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Stop 停止服务
func (c *RedisController) Stop(ctx http.Context) {
if !controllers.Check(ctx, "redis") {
return
func (c *RedisController) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "redis")
if check != nil {
return check
}
tools.ExecShell("systemctl stop redis")
status := tools.ExecShell("systemctl status redis | grep Active | grep -v grep | awk '{print $2}'")
tools.Exec("systemctl stop redis")
status := tools.Exec("systemctl status redis | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取Redis状态失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取Redis状态失败")
}
if status != "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// GetConfig 获取配置
func (c *RedisController) GetConfig(ctx http.Context) {
if !controllers.Check(ctx, "redis") {
return
func (c *RedisController) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "redis")
if check != nil {
return check
}
// 获取配置
config := tools.ReadFile("/www/server/redis/redis.conf")
config := tools.Read("/www/server/redis/redis.conf")
if len(config) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取Redis配置失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取Redis配置失败")
}
controllers.Success(ctx, config)
return controllers.Success(ctx, config)
}
// SaveConfig 保存配置
func (c *RedisController) SaveConfig(ctx http.Context) {
if !controllers.Check(ctx, "redis") {
return
func (c *RedisController) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "redis")
if check != nil {
return check
}
config := ctx.Request().Input("config")
if len(config) == 0 {
controllers.Error(ctx, http.StatusBadRequest, "配置不能为空")
return
return controllers.Error(ctx, http.StatusBadRequest, "配置不能为空")
}
if !tools.WriteFile("/www/server/redis/redis.conf", config, 0644) {
controllers.Error(ctx, http.StatusInternalServerError, "写入Redis配置失败")
return
if !tools.Write("/www/server/redis/redis.conf", config, 0644) {
return controllers.Error(ctx, http.StatusInternalServerError, "写入Redis配置失败")
}
c.Restart(ctx)
return c.Restart(ctx)
}
// Load 获取负载
func (c *RedisController) Load(ctx http.Context) {
if !controllers.Check(ctx, "redis") {
return
func (c *RedisController) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "redis")
if check != nil {
return check
}
status := tools.ExecShell("systemctl status redis | grep Active | grep -v grep | awk '{print $2}'")
status := tools.Exec("systemctl status redis | grep Active | grep -v grep | awk '{print $2}'")
if status != "active" {
controllers.Error(ctx, http.StatusInternalServerError, "Redis 已停止运行")
return
return controllers.Error(ctx, http.StatusInternalServerError, "Redis 已停止运行")
}
raw := tools.ExecShell("redis-cli info")
raw := tools.Exec("redis-cli info")
if len(raw) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取Redis负载失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取Redis负载失败")
}
infoLines := strings.Split(raw, "\n")
@@ -200,5 +198,5 @@ func (c *RedisController) Load(ctx http.Context) {
{"最近一次 fork() 操作耗费的毫秒数", dataRaw["latest_fork_usec"]},
}
controllers.Success(ctx, data)
return controllers.Success(ctx, data)
}
@@ -31,9 +31,10 @@ func NewS3fsController() *S3fsController {
}
// List 所有 S3fs 挂载
func (c *S3fsController) List(ctx http.Context) {
if !controllers.Check(ctx, "s3fs") {
return
func (c *S3fsController) List(ctx http.Context) http.Response {
check := controllers.Check(ctx, "s3fs")
if check != nil {
return check
}
page := ctx.Request().QueryInt("page", 1)
@@ -42,34 +43,33 @@ func (c *S3fsController) List(ctx http.Context) {
var s3fsList []s3fs
err := sonic.UnmarshalString(c.setting.Get("s3fs", "[]"), &s3fsList)
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, "获取 S3fs 挂载失败")
return
return controllers.Error(ctx, http.StatusBadRequest, "获取 S3fs 挂载失败")
}
startIndex := (page - 1) * limit
endIndex := page * limit
if startIndex > len(s3fsList) {
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []s3fs{},
})
return
}
if endIndex > len(s3fsList) {
endIndex = len(s3fsList)
}
pagedS3fsList := s3fsList[startIndex:endIndex]
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": len(s3fsList),
"items": pagedS3fsList,
})
}
// Add 添加 S3fs 挂载
func (c *S3fsController) Add(ctx http.Context) {
if !controllers.Check(ctx, "s3fs") {
return
func (c *S3fsController) Add(ctx http.Context) http.Response {
check := controllers.Check(ctx, "s3fs")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -80,12 +80,10 @@ func (c *S3fsController) Add(ctx http.Context) {
"path": "required|regex:^/[a-zA-Z0-9_-]+$",
})
if err != nil {
controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
return
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
ak := ctx.Request().Input("ak")
@@ -96,8 +94,7 @@ func (c *S3fsController) Add(ctx http.Context) {
// 检查下地域节点中是否包含bucket,如果包含了,肯定是错误的
if strings.Contains(url, bucket) {
controllers.Error(ctx, http.StatusUnprocessableEntity, "地域节点不能包含 Bucket 名称")
return
return controllers.Error(ctx, http.StatusUnprocessableEntity, "地域节点不能包含 Bucket 名称")
}
// 检查挂载目录是否存在且为空
@@ -105,39 +102,34 @@ func (c *S3fsController) Add(ctx http.Context) {
tools.Mkdir(path, 0755)
}
if !tools.Empty(path) {
controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载目录必须为空")
return
return controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载目录必须为空")
}
var s3fsList []s3fs
err = sonic.UnmarshalString(c.setting.Get("s3fs", "[]"), &s3fsList)
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, "获取 S3fs 挂载失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取 S3fs 挂载失败")
}
for _, s := range s3fsList {
if s.Path == path {
controllers.Error(ctx, http.StatusUnprocessableEntity, "路径已存在")
return
return controllers.Error(ctx, http.StatusUnprocessableEntity, "路径已存在")
}
}
id := carbon.Now().TimestampMilli()
password := ak + ":" + sk
tools.WriteFile("/etc/passwd-s3fs-"+cast.ToString(id), password, 0600)
tools.ExecShell(`echo 's3fs#` + bucket + ` ` + path + ` fuse _netdev,allow_other,nonempty,url=` + url + `,passwd_file=/etc/passwd-s3fs-` + cast.ToString(id) + ` 0 0' >> /etc/fstab`)
check := tools.ExecShell("mount -a 2>&1")
if len(check) != 0 {
tools.ExecShell(`sed -i 's@^s3fs#` + bucket + `\s` + path + `.*$@@g' /etc/fstab`)
controllers.Error(ctx, http.StatusInternalServerError, "检测到/etc/fstab有误: "+check)
return
tools.Write("/etc/passwd-s3fs-"+cast.ToString(id), password, 0600)
tools.Exec(`echo 's3fs#` + bucket + ` ` + path + ` fuse _netdev,allow_other,nonempty,url=` + url + `,passwd_file=/etc/passwd-s3fs-` + cast.ToString(id) + ` 0 0' >> /etc/fstab`)
mountCheck := tools.Exec("mount -a 2>&1")
if len(mountCheck) != 0 {
tools.Exec(`sed -i 's@^s3fs#` + bucket + `\s` + path + `.*$@@g' /etc/fstab`)
return controllers.Error(ctx, http.StatusInternalServerError, "检测到/etc/fstab有误: "+mountCheck)
}
check2 := tools.ExecShell("df -h | grep " + path + " 2>&1")
if len(check2) == 0 {
tools.ExecShell(`sed -i 's@^s3fs#` + bucket + `\s` + path + `.*$@@g' /etc/fstab`)
controllers.Error(ctx, http.StatusInternalServerError, "挂载失败,请检查配置是否正确")
return
dfCheck := tools.Exec("df -h | grep " + path + " 2>&1")
if len(dfCheck) == 0 {
tools.Exec(`sed -i 's@^s3fs#` + bucket + `\s` + path + `.*$@@g' /etc/fstab`)
return controllers.Error(ctx, http.StatusInternalServerError, "挂载失败,请检查配置是否正确")
}
s3fsList = append(s3fsList, s3fs{
@@ -148,35 +140,32 @@ func (c *S3fsController) Add(ctx http.Context) {
})
json, err := sonic.MarshalString(s3fsList)
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, "添加 S3fs 挂载失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "添加 S3fs 挂载失败")
}
err = c.setting.Set("s3fs", json)
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, "添加 S3fs 挂载失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "添加 S3fs 挂载失败")
}
controllers.Success(ctx, nil)
return controllers.Success(ctx, nil)
}
// Delete 删除 S3fs 挂载
func (c *S3fsController) Delete(ctx http.Context) {
if !controllers.Check(ctx, "s3fs") {
return
func (c *S3fsController) Delete(ctx http.Context) http.Response {
check := controllers.Check(ctx, "s3fs")
if check != nil {
return check
}
id := ctx.Request().Input("id")
if len(id) == 0 {
controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载ID不能为空")
return
return controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载ID不能为空")
}
var s3fsList []s3fs
err := sonic.UnmarshalString(c.setting.Get("s3fs", "[]"), &s3fsList)
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, "获取 S3fs 挂载失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "获取 S3fs 挂载失败")
}
var mount s3fs
@@ -187,19 +176,17 @@ func (c *S3fsController) Delete(ctx http.Context) {
}
}
if mount.ID == 0 {
controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载ID不存在")
return
return controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载ID不存在")
}
tools.ExecShell(`fusermount -u '` + mount.Path + `' 2>&1`)
tools.ExecShell(`umount '` + mount.Path + `' 2>&1`)
tools.ExecShell(`sed -i 's@^s3fs#` + mount.Bucket + `\s` + mount.Path + `.*$@@g' /etc/fstab`)
check := tools.ExecShell("mount -a 2>&1")
if len(check) != 0 {
controllers.Error(ctx, http.StatusInternalServerError, "检测到/etc/fstab有误: "+check)
return
tools.Exec(`fusermount -u '` + mount.Path + `' 2>&1`)
tools.Exec(`umount '` + mount.Path + `' 2>&1`)
tools.Exec(`sed -i 's@^s3fs#` + mount.Bucket + `\s` + mount.Path + `.*$@@g' /etc/fstab`)
mountCheck := tools.Exec("mount -a 2>&1")
if len(mountCheck) != 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "检测到/etc/fstab有误: "+mountCheck)
}
tools.RemoveFile("/etc/passwd-s3fs-" + cast.ToString(mount.ID))
tools.Remove("/etc/passwd-s3fs-" + cast.ToString(mount.ID))
var newS3fsList []s3fs
for _, s := range s3fsList {
@@ -209,14 +196,12 @@ func (c *S3fsController) Delete(ctx http.Context) {
}
json, err := sonic.MarshalString(newS3fsList)
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, "删除 S3fs 挂载失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "删除 S3fs 挂载失败")
}
err = c.setting.Set("s3fs", json)
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, "删除 S3fs 挂载失败")
return
return controllers.Error(ctx, http.StatusInternalServerError, "删除 S3fs 挂载失败")
}
controllers.Success(ctx, nil)
return controllers.Success(ctx, nil)
}
@@ -28,134 +28,144 @@ func NewSupervisorController() *SupervisorController {
}
// Status 状态
func (c *SupervisorController) Status(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
status := tools.ExecShell(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
status := tools.Exec(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Start 启动
func (c *SupervisorController) Start(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
tools.ExecShell(`systemctl start ` + c.ServiceName)
status := tools.ExecShell(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
tools.Exec(`systemctl start ` + c.ServiceName)
status := tools.Exec(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Stop 停止
func (c *SupervisorController) Stop(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
tools.ExecShell(`systemctl stop ` + c.ServiceName)
status := tools.ExecShell(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
tools.Exec(`systemctl stop ` + c.ServiceName)
status := tools.Exec(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
if status != "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Restart 重启
func (c *SupervisorController) Restart(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
tools.ExecShell(`systemctl restart ` + c.ServiceName)
status := tools.ExecShell(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
tools.Exec(`systemctl restart ` + c.ServiceName)
status := tools.Exec(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Reload 重载
func (c *SupervisorController) Reload(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
tools.ExecShell(`systemctl reload ` + c.ServiceName)
status := tools.ExecShell(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
tools.Exec(`systemctl reload ` + c.ServiceName)
status := tools.Exec(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`)
if status == "active" {
controllers.Success(ctx, true)
return controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
return controllers.Success(ctx, false)
}
}
// Log 日志
func (c *SupervisorController) Log(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) Log(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
log := tools.ExecShell(`tail -n 200 /var/log/supervisor/supervisord.log`)
controllers.Success(ctx, log)
log := tools.Exec(`tail -n 200 /var/log/supervisor/supervisord.log`)
return controllers.Success(ctx, log)
}
// ClearLog 清空日志
func (c *SupervisorController) ClearLog(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) ClearLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
tools.ExecShell(`echo "" > /var/log/supervisor/supervisord.log`)
controllers.Success(ctx, nil)
tools.Exec(`echo "" > /var/log/supervisor/supervisord.log`)
return controllers.Success(ctx, nil)
}
// Config 获取配置
func (c *SupervisorController) Config(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) Config(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
var config string
if tools.IsRHEL() {
config = tools.ReadFile(`/etc/supervisord.conf`)
config = tools.Read(`/etc/supervisord.conf`)
} else {
config = tools.ReadFile(`/etc/supervisor/supervisord.conf`)
config = tools.Read(`/etc/supervisor/supervisord.conf`)
}
controllers.Success(ctx, config)
return controllers.Success(ctx, config)
}
// SaveConfig 保存配置
func (c *SupervisorController) SaveConfig(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
config := ctx.Request().Input("config")
if tools.IsRHEL() {
tools.WriteFile(`/etc/supervisord.conf`, config, 0644)
tools.Write(`/etc/supervisord.conf`, config, 0644)
} else {
tools.WriteFile(`/etc/supervisor/supervisord.conf`, config, 0644)
tools.Write(`/etc/supervisor/supervisord.conf`, config, 0644)
}
c.Restart(ctx)
return c.Restart(ctx)
}
// Processes 进程列表
func (c *SupervisorController) Processes(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) Processes(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
page := ctx.Request().QueryInt("page", 1)
@@ -168,7 +178,7 @@ func (c *SupervisorController) Processes(ctx http.Context) {
Uptime string `json:"uptime"`
}
out := tools.ExecShell(`supervisorctl status | awk '{print $1}'`)
out := tools.Exec(`supervisorctl status | awk '{print $1}'`)
var processList []process
for _, line := range strings.Split(out, "\n") {
if len(line) == 0 {
@@ -177,10 +187,10 @@ func (c *SupervisorController) Processes(ctx http.Context) {
var p process
p.Name = line
p.Status = tools.ExecShell(`supervisorctl status ` + line + ` | awk '{print $2}'`)
p.Status = tools.Exec(`supervisorctl status ` + line + ` | awk '{print $2}'`)
if p.Status == "RUNNING" {
p.Pid = strings.ReplaceAll(tools.ExecShell(`supervisorctl status `+line+` | awk '{print $4}'`), ",", "")
p.Uptime = tools.ExecShell(`supervisorctl status ` + line + ` | awk '{print $6}'`)
p.Pid = strings.ReplaceAll(tools.Exec(`supervisorctl status `+line+` | awk '{print $4}'`), ",", "")
p.Uptime = tools.Exec(`supervisorctl status ` + line + ` | awk '{print $6}'`)
} else {
p.Pid = "-"
p.Uptime = "-"
@@ -191,133 +201,140 @@ func (c *SupervisorController) Processes(ctx http.Context) {
startIndex := (page - 1) * limit
endIndex := page * limit
if startIndex > len(processList) {
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []process{},
})
return
}
if endIndex > len(processList) {
endIndex = len(processList)
}
pagedProcessList := processList[startIndex:endIndex]
controllers.Success(ctx, http.Json{
return controllers.Success(ctx, http.Json{
"total": len(processList),
"items": pagedProcessList,
})
}
// StartProcess 启动进程
func (c *SupervisorController) StartProcess(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) StartProcess(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
process := ctx.Request().Input("process")
tools.ExecShell(`supervisorctl start ` + process)
controllers.Success(ctx, nil)
tools.Exec(`supervisorctl start ` + process)
return controllers.Success(ctx, nil)
}
// StopProcess 停止进程
func (c *SupervisorController) StopProcess(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) StopProcess(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
process := ctx.Request().Input("process")
tools.ExecShell(`supervisorctl stop ` + process)
controllers.Success(ctx, nil)
tools.Exec(`supervisorctl stop ` + process)
return controllers.Success(ctx, nil)
}
// RestartProcess 重启进程
func (c *SupervisorController) RestartProcess(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) RestartProcess(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
process := ctx.Request().Input("process")
tools.ExecShell(`supervisorctl restart ` + process)
controllers.Success(ctx, nil)
tools.Exec(`supervisorctl restart ` + process)
return controllers.Success(ctx, nil)
}
// ProcessLog 进程日志
func (c *SupervisorController) ProcessLog(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) ProcessLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
process := ctx.Request().Input("process")
var logPath string
if tools.IsRHEL() {
logPath = tools.ExecShell(`cat '/etc/supervisord.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
logPath = tools.Exec(`cat '/etc/supervisord.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
} else {
logPath = tools.ExecShell(`cat '/etc/supervisor/conf.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
logPath = tools.Exec(`cat '/etc/supervisor/conf.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
}
log := tools.ExecShell(`tail -n 200 ` + logPath)
controllers.Success(ctx, log)
log := tools.Exec(`tail -n 200 ` + logPath)
return controllers.Success(ctx, log)
}
// ClearProcessLog 清空进程日志
func (c *SupervisorController) ClearProcessLog(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) ClearProcessLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
process := ctx.Request().Input("process")
var logPath string
if tools.IsRHEL() {
logPath = tools.ExecShell(`cat '/etc/supervisord.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
logPath = tools.Exec(`cat '/etc/supervisord.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
} else {
logPath = tools.ExecShell(`cat '/etc/supervisor/conf.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
logPath = tools.Exec(`cat '/etc/supervisor/conf.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
}
tools.ExecShell(`echo "" > ` + logPath)
controllers.Success(ctx, nil)
tools.Exec(`echo "" > ` + logPath)
return controllers.Success(ctx, nil)
}
// ProcessConfig 获取进程配置
func (c *SupervisorController) ProcessConfig(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) ProcessConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
process := ctx.Request().Query("process")
var config string
if tools.IsRHEL() {
config = tools.ReadFile(`/etc/supervisord.d/` + process + `.conf`)
config = tools.Read(`/etc/supervisord.d/` + process + `.conf`)
} else {
config = tools.ReadFile(`/etc/supervisor/conf.d/` + process + `.conf`)
config = tools.Read(`/etc/supervisor/conf.d/` + process + `.conf`)
}
controllers.Success(ctx, config)
return controllers.Success(ctx, config)
}
// SaveProcessConfig 保存进程配置
func (c *SupervisorController) SaveProcessConfig(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) SaveProcessConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
process := ctx.Request().Input("process")
config := ctx.Request().Input("config")
if tools.IsRHEL() {
tools.WriteFile(`/etc/supervisord.d/`+process+`.conf`, config, 0644)
tools.Write(`/etc/supervisord.d/`+process+`.conf`, config, 0644)
} else {
tools.WriteFile(`/etc/supervisor/conf.d/`+process+`.conf`, config, 0644)
tools.Write(`/etc/supervisor/conf.d/`+process+`.conf`, config, 0644)
}
tools.ExecShell(`supervisorctl reread`)
tools.ExecShell(`supervisorctl update`)
tools.ExecShell(`supervisorctl start ` + process)
tools.Exec(`supervisorctl reread`)
tools.Exec(`supervisorctl update`)
tools.Exec(`supervisorctl start ` + process)
controllers.Success(ctx, nil)
return controllers.Success(ctx, nil)
}
// AddProcess 添加进程
func (c *SupervisorController) AddProcess(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) AddProcess(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
@@ -328,12 +345,10 @@ func (c *SupervisorController) AddProcess(ctx http.Context) {
"num": "required",
})
if err != nil {
controllers.Error(ctx, http.StatusBadRequest, err.Error())
return
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
name := ctx.Request().Input("name")
@@ -354,36 +369,37 @@ stdout_logfile=/var/log/supervisor/` + name + `.log
stdout_logfile_maxbytes=2MB
`
if tools.IsRHEL() {
tools.WriteFile(`/etc/supervisord.d/`+name+`.conf`, config, 0644)
tools.Write(`/etc/supervisord.d/`+name+`.conf`, config, 0644)
} else {
tools.WriteFile(`/etc/supervisor/conf.d/`+name+`.conf`, config, 0644)
tools.Write(`/etc/supervisor/conf.d/`+name+`.conf`, config, 0644)
}
tools.ExecShell(`supervisorctl reread`)
tools.ExecShell(`supervisorctl update`)
tools.ExecShell(`supervisorctl start ` + name)
tools.Exec(`supervisorctl reread`)
tools.Exec(`supervisorctl update`)
tools.Exec(`supervisorctl start ` + name)
controllers.Success(ctx, nil)
return controllers.Success(ctx, nil)
}
// DeleteProcess 删除进程
func (c *SupervisorController) DeleteProcess(ctx http.Context) {
if !controllers.Check(ctx, "supervisor") {
return
func (c *SupervisorController) DeleteProcess(ctx http.Context) http.Response {
check := controllers.Check(ctx, "supervisor")
if check != nil {
return check
}
process := ctx.Request().Input("process")
tools.ExecShell(`supervisorctl stop ` + process)
tools.Exec(`supervisorctl stop ` + process)
var logPath string
if tools.IsRHEL() {
logPath = tools.ExecShell(`cat '/etc/supervisord.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
tools.RemoveFile(`/etc/supervisord.d/` + process + `.conf`)
logPath = tools.Exec(`cat '/etc/supervisord.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
tools.Remove(`/etc/supervisord.d/` + process + `.conf`)
} else {
logPath = tools.ExecShell(`cat '/etc/supervisor/conf.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
tools.RemoveFile(`/etc/supervisor/conf.d/` + process + `.conf`)
logPath = tools.Exec(`cat '/etc/supervisor/conf.d/` + process + `.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`)
tools.Remove(`/etc/supervisor/conf.d/` + process + `.conf`)
}
tools.RemoveFile(logPath)
tools.ExecShell(`supervisorctl reread`)
tools.ExecShell(`supervisorctl update`)
tools.Remove(logPath)
tools.Exec(`supervisorctl reread`)
tools.Exec(`supervisorctl update`)
controllers.Success(ctx, nil)
return controllers.Success(ctx, nil)
}
@@ -0,0 +1,217 @@
package toolbox
import (
"regexp"
"strconv"
"strings"
"github.com/goravel/framework/contracts/http"
"github.com/spf13/cast"
"panel/app/http/controllers"
"panel/pkg/tools"
)
type ToolBoxController struct {
}
func NewToolBoxController() *ToolBoxController {
return &ToolBoxController{}
}
// GetDNS 获取 DNS 信息
func (c *ToolBoxController) GetDNS(ctx http.Context) http.Response {
check := controllers.Check(ctx, "toolbox")
if check != nil {
return check
}
raw := tools.Read("/etc/resolv.conf")
match := regexp.MustCompile(`nameserver\s+(\S+)`).FindAllStringSubmatch(raw, -1)
if len(match) == 0 {
return controllers.Error(ctx, http.StatusBadRequest, "找不到 DNS 信息")
}
var dns []string
for _, m := range match {
dns = append(dns, m[1])
}
return controllers.Success(ctx, dns)
}
// SetDNS 设置 DNS 信息
func (c *ToolBoxController) SetDNS(ctx http.Context) http.Response {
check := controllers.Check(ctx, "toolbox")
if check != nil {
return check
}
dns1 := ctx.Request().Input("dns1")
dns2 := ctx.Request().Input("dns2")
if len(dns1) == 0 || len(dns2) == 0 {
return controllers.Error(ctx, http.StatusBadRequest, "DNS 信息不能为空")
}
var dns string
dns += "nameserver " + dns1 + "\n"
dns += "nameserver " + dns2 + "\n"
tools.Write("/etc/resolv.conf", dns, 0644)
return controllers.Success(ctx, nil)
}
// GetSWAP 获取 SWAP 信息
func (c *ToolBoxController) GetSWAP(ctx http.Context) http.Response {
check := controllers.Check(ctx, "toolbox")
if check != nil {
return check
}
var total, size, used, free string
if tools.Exists("/www/swap") {
s, _ := tools.FileSize("/www/swap")
size = strconv.Itoa(int(s / 1024 / 1024))
total = tools.FormatBytes(float64(s))
} else {
size = "0.00 B"
total = "0.00 B"
}
raw := tools.Exec("LC_ALL=C free | grep Swap")
match := regexp.MustCompile(`Swap:\s+(\d+)\s+(\d+)\s+(\d+)`).FindStringSubmatch(raw)
if len(match) > 0 {
used = tools.FormatBytes(cast.ToFloat64(match[2]) * 1024)
free = tools.FormatBytes(cast.ToFloat64(match[3]) * 1024)
}
return controllers.Success(ctx, http.Json{
"total": total,
"size": size,
"used": used,
"free": free,
})
}
// SetSWAP 设置 SWAP 信息
func (c *ToolBoxController) SetSWAP(ctx http.Context) http.Response {
check := controllers.Check(ctx, "toolbox")
if check != nil {
return check
}
size := ctx.Request().InputInt("size")
if tools.Exists("/www/swap") {
tools.Exec("swapoff /www/swap")
tools.Exec("rm -f /www/swap")
tools.Exec("sed -i '/www\\/swap/d' /etc/fstab")
}
if size > 1 {
free := tools.Exec("df -k /www | awk '{print $4}' | tail -n 1")
if cast.ToInt64(free)*1024 < int64(size)*1024*1024 {
return controllers.Error(ctx, http.StatusBadRequest, "磁盘空间不足,当前剩余 "+tools.FormatBytes(cast.ToFloat64(free)))
}
if strings.Contains(tools.Exec("df -T /www | awk '{print $2}' | tail -n 1"), "btrfs") {
tools.Exec("btrfs filesystem mkswapfile --size " + cast.ToString(size) + "M --uuid clear /www/swap")
} else {
tools.Exec("dd if=/dev/zero of=/www/swap bs=1M count=" + cast.ToString(size))
tools.Exec("mkswap -f /www/swap")
tools.Chmod("/www/swap", 0600)
}
tools.Exec("swapon /www/swap")
tools.Exec("echo '/www/swap swap swap defaults 0 0' >> /etc/fstab")
}
return controllers.Success(ctx, nil)
}
// GetTimezone 获取时区
func (c *ToolBoxController) GetTimezone(ctx http.Context) http.Response {
check := controllers.Check(ctx, "toolbox")
if check != nil {
return check
}
raw := tools.Exec("LC_ALL=C timedatectl | grep zone")
match := regexp.MustCompile(`zone:\s+(\S+)`).FindStringSubmatch(raw)
if len(match) == 0 {
return controllers.Error(ctx, http.StatusBadRequest, "找不到时区信息")
}
zonesRaw := tools.Exec("LC_ALL=C timedatectl list-timezones")
zones := strings.Split(zonesRaw, "\n")
return controllers.Success(ctx, http.Json{
"zone": match[1],
"zones": zones,
})
}
// SetTimezone 设置时区
func (c *ToolBoxController) SetTimezone(ctx http.Context) http.Response {
check := controllers.Check(ctx, "toolbox")
if check != nil {
return check
}
timezone := ctx.Request().Input("timezone")
if len(timezone) == 0 {
return controllers.Error(ctx, http.StatusBadRequest, "时区不能为空")
}
tools.Exec("timedatectl set-timezone " + timezone)
return controllers.Success(ctx, nil)
}
// GetHosts 获取 hosts 信息
func (c *ToolBoxController) GetHosts(ctx http.Context) http.Response {
check := controllers.Check(ctx, "toolbox")
if check != nil {
return check
}
return controllers.Success(ctx, tools.Read("/etc/hosts"))
}
// SetHosts 设置 hosts 信息
func (c *ToolBoxController) SetHosts(ctx http.Context) http.Response {
check := controllers.Check(ctx, "toolbox")
if check != nil {
return check
}
hosts := ctx.Request().Input("hosts")
if len(hosts) == 0 {
return controllers.Error(ctx, http.StatusBadRequest, "hosts 信息不能为空")
}
tools.Write("/etc/hosts", hosts, 0644)
return controllers.Success(ctx, nil)
}
// SetRootPassword 设置 root 密码
func (c *ToolBoxController) SetRootPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "toolbox")
if check != nil {
return check
}
password := ctx.Request().Input("password")
if len(password) == 0 {
return controllers.Error(ctx, http.StatusBadRequest, "密码不能为空")
}
if !regexp.MustCompile(`^[a-zA-Z0-9·~!@#$%^&*()_+-=\[\]{};:'",./<>?]{6,20}$`).MatchString(password) {
return controllers.Error(ctx, http.StatusBadRequest, "密码必须为 6-20 位字母、数字或特殊字符")
}
password = strings.ReplaceAll(password, `'`, `\'`)
tools.Exec(`yes '` + password + `' | passwd root`)
return controllers.Success(ctx, nil)
}
+90 -88
View File
@@ -20,46 +20,47 @@ func NewSafeController() *SafeController {
}
}
func (r *SafeController) GetFirewallStatus(ctx http.Context) {
Success(ctx, r.firewallStatus())
// GetFirewallStatus 获取防火墙状态
func (r *SafeController) GetFirewallStatus(ctx http.Context) http.Response {
return Success(ctx, r.firewallStatus())
}
func (r *SafeController) SetFirewallStatus(ctx http.Context) {
// SetFirewallStatus 设置防火墙状态
func (r *SafeController) SetFirewallStatus(ctx http.Context) http.Response {
var out string
if ctx.Request().InputBool("status") {
if tools.IsRHEL() {
out = tools.ExecShell("systemctl start firewalld")
out = tools.Exec("systemctl start firewalld")
} else {
out = tools.ExecShell("echo y | ufw enable")
out = tools.Exec("echo y | ufw enable")
}
} else {
if tools.IsRHEL() {
out = tools.ExecShell("systemctl stop firewalld")
out = tools.Exec("systemctl stop firewalld")
} else {
out = tools.ExecShell("ufw disable")
out = tools.Exec("ufw disable")
}
}
Success(ctx, out)
return Success(ctx, out)
}
func (r *SafeController) GetFirewallRules(ctx http.Context) {
// GetFirewallRules 获取防火墙规则
func (r *SafeController) GetFirewallRules(ctx http.Context) http.Response {
if !r.firewallStatus() {
Success(ctx, nil)
return
return Success(ctx, nil)
}
page := ctx.Request().QueryInt("page", 1)
limit := ctx.Request().QueryInt("limit", 10)
if tools.IsRHEL() {
out := tools.ExecShell("firewall-cmd --list-all 2>&1")
out := tools.Exec("firewall-cmd --list-all 2>&1")
match := regexp.MustCompile(`ports: (.*)`).FindStringSubmatch(out)
if len(match) == 0 {
Success(ctx, http.Json{
return Success(ctx, http.Json{
"total": 0,
"items": []map[string]string{},
})
return
}
ports := strings.Split(match[1], " ")
var rules []map[string]string
@@ -74,29 +75,27 @@ func (r *SafeController) GetFirewallRules(ctx http.Context) {
startIndex := (page - 1) * limit
endIndex := page * limit
if startIndex > len(rules) {
Success(ctx, http.Json{
return Success(ctx, http.Json{
"total": 0,
"items": []map[string]string{},
})
return
}
if endIndex > len(rules) {
endIndex = len(rules)
}
pagedRules := rules[startIndex:endIndex]
Success(ctx, http.Json{
return Success(ctx, http.Json{
"total": len(rules),
"items": pagedRules,
})
} else {
out := tools.ExecShell("ufw status | grep -v '(v6)' | grep ALLOW | awk '{print $1}'")
out := tools.Exec("ufw status | grep -v '(v6)' | grep ALLOW | awk '{print $1}'")
if len(out) == 0 {
Success(ctx, http.Json{
return Success(ctx, http.Json{
"total": 0,
"items": []map[string]string{},
})
return
}
var rules []map[string]string
for _, port := range strings.Split(out, "\n") {
@@ -110,86 +109,84 @@ func (r *SafeController) GetFirewallRules(ctx http.Context) {
startIndex := (page - 1) * limit
endIndex := page * limit
if startIndex > len(rules) {
Success(ctx, http.Json{
return Success(ctx, http.Json{
"total": 0,
"items": []map[string]string{},
})
return
}
if endIndex > len(rules) {
endIndex = len(rules)
}
pagedRules := rules[startIndex:endIndex]
Success(ctx, http.Json{
return Success(ctx, http.Json{
"total": len(rules),
"items": pagedRules,
})
}
}
func (r *SafeController) AddFirewallRule(ctx http.Context) {
// AddFirewallRule 添加防火墙规则
func (r *SafeController) AddFirewallRule(ctx http.Context) http.Response {
if !r.firewallStatus() {
Error(ctx, http.StatusBadRequest, "防火墙未启动")
return
return Error(ctx, http.StatusBadRequest, "防火墙未启动")
}
port := ctx.Request().InputInt("port", 0)
protocol := ctx.Request().Input("protocol", "")
if port == 0 || protocol == "" {
Error(ctx, http.StatusBadRequest, "参数错误")
return
return Error(ctx, http.StatusBadRequest, "参数错误")
}
if tools.IsRHEL() {
tools.ExecShell("firewall-cmd --remove-port=" + cast.ToString(port) + "/" + protocol + " --permanent 2>&1")
tools.ExecShell("firewall-cmd --add-port=" + cast.ToString(port) + "/" + protocol + " --permanent 2>&1")
tools.ExecShell("firewall-cmd --reload")
tools.Exec("firewall-cmd --remove-port=" + cast.ToString(port) + "/" + protocol + " --permanent 2>&1")
tools.Exec("firewall-cmd --add-port=" + cast.ToString(port) + "/" + protocol + " --permanent 2>&1")
tools.Exec("firewall-cmd --reload")
} else {
tools.ExecShell("ufw delete allow " + cast.ToString(port) + "/" + protocol)
tools.ExecShell("ufw allow " + cast.ToString(port) + "/" + protocol)
tools.ExecShell("ufw reload")
tools.Exec("ufw delete allow " + cast.ToString(port) + "/" + protocol)
tools.Exec("ufw allow " + cast.ToString(port) + "/" + protocol)
tools.Exec("ufw reload")
}
Success(ctx, nil)
return Success(ctx, nil)
}
func (r *SafeController) DeleteFirewallRule(ctx http.Context) {
// DeleteFirewallRule 删除防火墙规则
func (r *SafeController) DeleteFirewallRule(ctx http.Context) http.Response {
if !r.firewallStatus() {
Error(ctx, http.StatusBadRequest, "防火墙未启动")
return
return Error(ctx, http.StatusBadRequest, "防火墙未启动")
}
port := ctx.Request().InputInt("port", 0)
protocol := ctx.Request().Input("protocol", "")
if port == 0 || protocol == "" {
Error(ctx, http.StatusBadRequest, "参数错误")
return
return Error(ctx, http.StatusBadRequest, "参数错误")
}
if tools.IsRHEL() {
tools.ExecShell("firewall-cmd --remove-port=" + cast.ToString(port) + "/" + protocol + " --permanent 2>&1")
tools.ExecShell("firewall-cmd --reload")
tools.Exec("firewall-cmd --remove-port=" + cast.ToString(port) + "/" + protocol + " --permanent 2>&1")
tools.Exec("firewall-cmd --reload")
} else {
tools.ExecShell("ufw delete allow " + cast.ToString(port) + "/" + protocol)
tools.ExecShell("ufw reload")
tools.Exec("ufw delete allow " + cast.ToString(port) + "/" + protocol)
tools.Exec("ufw reload")
}
Success(ctx, nil)
return Success(ctx, nil)
}
// firewallStatus 获取防火墙状态
func (r *SafeController) firewallStatus() bool {
var out string
var running bool
if tools.IsRHEL() {
out = tools.ExecShell("systemctl status firewalld | grep Active | awk '{print $3}'")
out = tools.Exec("systemctl status firewalld | grep Active | awk '{print $3}'")
if out == "(running)" {
running = true
} else {
running = false
}
} else {
out = tools.ExecShell("ufw status | grep Status | awk '{print $2}'")
out = tools.Exec("ufw status | grep Status | awk '{print $2}'")
if out == "active" {
running = true
} else {
@@ -200,12 +197,13 @@ func (r *SafeController) firewallStatus() bool {
return running
}
func (r *SafeController) GetSshStatus(ctx http.Context) {
// GetSshStatus 获取 SSH 状态
func (r *SafeController) GetSshStatus(ctx http.Context) http.Response {
var out string
if tools.IsRHEL() {
out = tools.ExecShell("systemctl status sshd | grep Active | awk '{print $3}'")
out = tools.Exec("systemctl status sshd | grep Active | awk '{print $3}'")
} else {
out = tools.ExecShell("systemctl status ssh | grep Active | awk '{print $3}'")
out = tools.Exec("systemctl status ssh | grep Active | awk '{print $3}'")
}
running := false
@@ -213,88 +211,92 @@ func (r *SafeController) GetSshStatus(ctx http.Context) {
running = true
}
Success(ctx, running)
return Success(ctx, running)
}
func (r *SafeController) SetSshStatus(ctx http.Context) {
// SetSshStatus 设置 SSH 状态
func (r *SafeController) SetSshStatus(ctx http.Context) http.Response {
if ctx.Request().InputBool("status") {
if tools.IsRHEL() {
tools.ExecShell("systemctl enable sshd")
tools.ExecShell("systemctl start sshd")
tools.Exec("systemctl enable sshd")
tools.Exec("systemctl start sshd")
} else {
tools.ExecShell("systemctl enable ssh")
tools.ExecShell("systemctl start ssh")
tools.Exec("systemctl enable ssh")
tools.Exec("systemctl start ssh")
}
} else {
if tools.IsRHEL() {
tools.ExecShell("systemctl stop sshd")
tools.ExecShell("systemctl disable sshd")
tools.Exec("systemctl stop sshd")
tools.Exec("systemctl disable sshd")
} else {
tools.ExecShell("systemctl stop ssh")
tools.ExecShell("systemctl disable ssh")
tools.Exec("systemctl stop ssh")
tools.Exec("systemctl disable ssh")
}
}
Success(ctx, nil)
return Success(ctx, nil)
}
func (r *SafeController) GetSshPort(ctx http.Context) {
out := tools.ExecShell("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
Success(ctx, out)
// GetSshPort 获取 SSH 端口
func (r *SafeController) GetSshPort(ctx http.Context) http.Response {
out := tools.Exec("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
return Success(ctx, out)
}
func (r *SafeController) SetSshPort(ctx http.Context) {
// SetSshPort 设置 SSH 端口
func (r *SafeController) SetSshPort(ctx http.Context) http.Response {
port := ctx.Request().InputInt("port", 0)
if port == 0 {
Error(ctx, http.StatusBadRequest, "参数错误")
return
return Error(ctx, http.StatusBadRequest, "参数错误")
}
oldPort := tools.ExecShell("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
tools.ExecShell("sed -i 's/#Port " + oldPort + "/Port " + cast.ToString(port) + "/g' /etc/ssh/sshd_config")
tools.ExecShell("sed -i 's/Port " + oldPort + "/Port " + cast.ToString(port) + "/g' /etc/ssh/sshd_config")
oldPort := tools.Exec("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
tools.Exec("sed -i 's/#Port " + oldPort + "/Port " + cast.ToString(port) + "/g' /etc/ssh/sshd_config")
tools.Exec("sed -i 's/Port " + oldPort + "/Port " + cast.ToString(port) + "/g' /etc/ssh/sshd_config")
if status := tools.ExecShell("systemctl status sshd | grep Active | awk '{print $3}'"); status == "(running)" {
tools.ExecShell("systemctl restart sshd")
if status := tools.Exec("systemctl status sshd | grep Active | awk '{print $3}'"); status == "(running)" {
tools.Exec("systemctl restart sshd")
}
Success(ctx, nil)
return Success(ctx, nil)
}
func (r *SafeController) GetPingStatus(ctx http.Context) {
// GetPingStatus 获取 Ping 状态
func (r *SafeController) GetPingStatus(ctx http.Context) http.Response {
if tools.IsRHEL() {
out := tools.ExecShell(`firewall-cmd --list-all 2>&1`)
out := tools.Exec(`firewall-cmd --list-all 2>&1`)
if !strings.Contains(out, `rule protocol value="icmp" drop`) {
Success(ctx, true)
return Success(ctx, true)
} else {
Success(ctx, false)
return Success(ctx, false)
}
} else {
config := tools.ReadFile("/etc/ufw/before.rules")
config := tools.Read("/etc/ufw/before.rules")
if strings.Contains(config, "-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT") {
Success(ctx, true)
return Success(ctx, true)
} else {
Success(ctx, false)
return Success(ctx, false)
}
}
}
func (r *SafeController) SetPingStatus(ctx http.Context) {
// SetPingStatus 设置 Ping 状态
func (r *SafeController) SetPingStatus(ctx http.Context) http.Response {
if tools.IsRHEL() {
if ctx.Request().InputBool("status") {
tools.ExecShell(`firewall-cmd --permanent --remove-rich-rule='rule protocol value=icmp drop'`)
tools.Exec(`firewall-cmd --permanent --remove-rich-rule='rule protocol value=icmp drop'`)
} else {
tools.ExecShell(`firewall-cmd --permanent --add-rich-rule='rule protocol value=icmp drop'`)
tools.Exec(`firewall-cmd --permanent --add-rich-rule='rule protocol value=icmp drop'`)
}
tools.ExecShell(`firewall-cmd --reload`)
tools.Exec(`firewall-cmd --reload`)
} else {
if ctx.Request().InputBool("status") {
tools.ExecShell(`sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j DROP/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/g' /etc/ufw/before.rules`)
tools.Exec(`sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j DROP/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/g' /etc/ufw/before.rules`)
} else {
tools.ExecShell(`sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/-A ufw-before-input -p icmp --icmp-type echo-request -j DROP/g' /etc/ufw/before.rules`)
tools.Exec(`sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/-A ufw-before-input -p icmp --icmp-type echo-request -j DROP/g' /etc/ufw/before.rules`)
}
tools.ExecShell(`ufw reload`)
tools.Exec(`ufw reload`)
}
Success(ctx, nil)
return Success(ctx, nil)
}
+18 -34
View File
@@ -19,14 +19,13 @@ func NewSettingController() *SettingController {
}
}
func (r *SettingController) List(ctx http.Context) {
// List 获取设置列表
func (r *SettingController) List(ctx http.Context) http.Response {
var settings []models.Setting
err := facades.Orm().Query().Get(&settings)
if err != nil {
facades.Log().Error("[面板][SettingController] 查询设置列表失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
var result = make(map[string]string)
@@ -42,19 +41,18 @@ func (r *SettingController) List(ctx http.Context) {
err = facades.Auth().User(ctx, &user)
if err != nil {
facades.Log().Error("[面板][SettingController] 获取用户失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
result["username"] = user.Username
result["email"] = user.Email
result["port"] = tools.ExecShell(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
result["port"] = tools.Exec(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
Success(ctx, result)
return Success(ctx, result)
}
func (r *SettingController) Save(ctx http.Context) {
// Save 保存设置
func (r *SettingController) Save(ctx http.Context) http.Response {
name := ctx.Request().Input("name")
port := ctx.Request().Input("port")
backupPath := ctx.Request().Input("backup_path")
@@ -67,13 +65,11 @@ func (r *SettingController) Save(ctx http.Context) {
err := r.setting.Set(models.SettingKeyName, name)
if err != nil {
facades.Log().Error("[面板][SettingController] 保存设置失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
oldPort := tools.ExecShell(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
oldPort := tools.Exec(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
if oldPort != port {
tools.ExecShell("sed -i 's/APP_PORT=" + oldPort + "/APP_PORT=" + port + "/g' /www/panel/panel.conf")
tools.Exec("sed -i 's/APP_PORT=" + oldPort + "/APP_PORT=" + port + "/g' /www/panel/panel.conf")
}
if !tools.Exists(backupPath) {
tools.Mkdir(backupPath, 0644)
@@ -81,9 +77,7 @@ func (r *SettingController) Save(ctx http.Context) {
err = r.setting.Set(models.SettingKeyBackupPath, backupPath)
if err != nil {
facades.Log().Error("[面板][SettingController] 保存设置失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
if !tools.Exists(websitePath) {
tools.Mkdir(websitePath, 0755)
@@ -92,25 +86,19 @@ func (r *SettingController) Save(ctx http.Context) {
err = r.setting.Set(models.SettingKeyWebsitePath, websitePath)
if err != nil {
facades.Log().Error("[面板][SettingController] 保存设置失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
err = r.setting.Set(models.SettingKeyEntrance, entrance)
if err != nil {
facades.Log().Error("[面板][SettingController] 保存设置失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
var user models.User
err = facades.Auth().User(ctx, &user)
if err != nil {
facades.Log().Error("[面板][SettingController] 获取用户失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
if len(username) > 0 {
@@ -123,19 +111,15 @@ func (r *SettingController) Save(ctx http.Context) {
hash, err := facades.Hash().Make(password)
if err != nil {
facades.Log().Error("[面板][SettingController] 保存设置失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
user.Password = hash
}
if err = facades.Orm().Query().Save(&user); err != nil {
facades.Log().Error("[面板][SettingController] 保存设置失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
Success(ctx, nil)
return Success(ctx, nil)
}
+20 -23
View File
@@ -28,17 +28,17 @@ func NewSshController() *SshController {
}
}
func (r *SshController) GetInfo(ctx http.Context) {
// GetInfo 获取 SSH 配置
func (r *SshController) GetInfo(ctx http.Context) http.Response {
host := r.setting.Get(models.SettingKeySshHost)
port := r.setting.Get(models.SettingKeySshPort)
user := r.setting.Get(models.SettingKeySshUser)
password := r.setting.Get(models.SettingKeySshPassword)
if len(host) == 0 || len(user) == 0 || len(password) == 0 {
Error(ctx, http.StatusInternalServerError, "SSH 配置不完整")
return
return Error(ctx, http.StatusInternalServerError, "SSH 配置不完整")
}
Success(ctx, http.Json{
return Success(ctx, http.Json{
"host": host,
"port": port,
"user": user,
@@ -46,7 +46,8 @@ func (r *SshController) GetInfo(ctx http.Context) {
})
}
func (r *SshController) UpdateInfo(ctx http.Context) {
// UpdateInfo 更新 SSH 配置
func (r *SshController) UpdateInfo(ctx http.Context) http.Response {
validator, err := ctx.Request().Validate(map[string]string{
"host": "required",
"port": "required",
@@ -54,12 +55,10 @@ func (r *SshController) UpdateInfo(ctx http.Context) {
"password": "required",
})
if err != nil {
Error(ctx, http.StatusBadRequest, err.Error())
return
return Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
host := ctx.Request().Input("host")
@@ -69,32 +68,29 @@ func (r *SshController) UpdateInfo(ctx http.Context) {
err = r.setting.Set(models.SettingKeySshHost, host)
if err != nil {
facades.Log().Error("[面板][SSH] 更新配置失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
err = r.setting.Set(models.SettingKeySshPort, port)
if err != nil {
facades.Log().Error("[面板][SSH] 更新配置失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
err = r.setting.Set(models.SettingKeySshUser, user)
if err != nil {
facades.Log().Error("[面板][SSH] 更新配置失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
err = r.setting.Set(models.SettingKeySshPassword, password)
if err != nil {
facades.Log().Error("[面板][SSH] 更新配置失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
Success(ctx, nil)
return Success(ctx, nil)
}
func (r *SshController) Session(ctx http.Context) {
// Session SSH 会话
func (r *SshController) Session(ctx http.Context) http.Response {
upGrader := websocket.Upgrader{
ReadBufferSize: 4096,
WriteBufferSize: 4096,
@@ -107,8 +103,7 @@ func (r *SshController) Session(ctx http.Context) {
ws, err := upGrader.Upgrade(ctx.Response().Writer(), ctx.Request().Origin(), nil)
if err != nil {
facades.Log().Error("[面板][SSH] 建立连接失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
defer ws.Close()
@@ -121,7 +116,7 @@ func (r *SshController) Session(ctx http.Context) {
if err != nil {
_ = ws.WriteControl(websocket.CloseMessage,
[]byte(err.Error()), time.Now().Add(time.Second))
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
defer client.Close()
@@ -129,7 +124,7 @@ func (r *SshController) Session(ctx http.Context) {
if err != nil {
_ = ws.WriteControl(websocket.CloseMessage,
[]byte(err.Error()), time.Now().Add(time.Second))
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
defer turn.Close()
@@ -161,4 +156,6 @@ func (r *SshController) Session(ctx http.Context) {
cancel()
}()
wg.Wait()
return Success(ctx, logBuff.String())
}
+17 -17
View File
@@ -17,22 +17,23 @@ func NewTaskController() *TaskController {
}
}
func (r *TaskController) Status(ctx http.Context) {
// Status 获取当前任务状态
func (r *TaskController) Status(ctx http.Context) http.Response {
var task models.Task
err := facades.Orm().Query().Where("status", models.TaskStatusWaiting).OrWhere("status", models.TaskStatusRunning).FirstOrFail(&task)
if err == nil {
Success(ctx, http.Json{
return Success(ctx, http.Json{
"task": true,
})
return
}
Success(ctx, http.Json{
return Success(ctx, http.Json{
"task": false,
})
}
func (r *TaskController) List(ctx http.Context) {
// List 获取任务列表
func (r *TaskController) List(ctx http.Context) http.Response {
status := ctx.Request().Query("status")
if len(status) == 0 {
status = models.TaskStatusWaiting
@@ -43,38 +44,37 @@ func (r *TaskController) List(ctx http.Context) {
err := facades.Orm().Query().Where("status", status).Paginate(ctx.Request().QueryInt("page"), ctx.Request().QueryInt("limit"), &tasks, &total)
if err != nil {
facades.Log().Error("[面板][TaskController] 查询任务列表失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
Success(ctx, http.Json{
return Success(ctx, http.Json{
"total": total,
"items": tasks,
})
}
func (r *TaskController) Log(ctx http.Context) {
// Log 获取任务日志
func (r *TaskController) Log(ctx http.Context) http.Response {
var task models.Task
err := facades.Orm().Query().Where("id", ctx.Request().QueryInt("id")).FirstOrFail(&task)
if err != nil {
facades.Log().Error("[面板][TaskController] 查询任务失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
log := tools.ExecShell("tail -n 30 " + task.Log)
log := tools.Exec("tail -n 30 " + task.Log)
Success(ctx, log)
return Success(ctx, log)
}
func (r *TaskController) Delete(ctx http.Context) {
// Delete 删除任务
func (r *TaskController) Delete(ctx http.Context) http.Response {
var task models.Task
_, err := facades.Orm().Query().Where("id", ctx.Request().Input("id")).Delete(&task)
if err != nil {
facades.Log().Error("[面板][TaskController] 删除任务失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
Success(ctx, nil)
return Success(ctx, nil)
}
+13 -18
View File
@@ -18,60 +18,55 @@ func NewUserController() *UserController {
}
}
func (r *UserController) Login(ctx http.Context) {
// Login 用户登录
func (r *UserController) Login(ctx http.Context) http.Response {
var loginRequest requests.LoginRequest
errors, err := ctx.Request().ValidateRequest(&loginRequest)
if err != nil {
Error(ctx, http.StatusUnprocessableEntity, err.Error())
return
return Error(ctx, http.StatusUnprocessableEntity, err.Error())
}
if errors != nil {
Error(ctx, http.StatusUnprocessableEntity, errors.All())
return
return Error(ctx, http.StatusUnprocessableEntity, errors.All())
}
var user models.User
err = facades.Orm().Query().Where("username", loginRequest.Username).First(&user)
if err != nil {
facades.Log().Error("[面板][UserController] 查询用户失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
if user.ID == 0 || !facades.Hash().Check(loginRequest.Password, user.Password) {
Error(ctx, http.StatusForbidden, "用户名或密码错误")
return
return Error(ctx, http.StatusForbidden, "用户名或密码错误")
}
if facades.Hash().NeedsRehash(user.Password) {
user.Password, err = facades.Hash().Make(loginRequest.Password)
if err != nil {
facades.Log().Error("[面板][UserController] 更新密码失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
}
token, loginErr := facades.Auth().LoginUsingID(ctx, user.ID)
if loginErr != nil {
facades.Log().Error("[面板][UserController] 登录失败 ", loginErr)
Error(ctx, http.StatusInternalServerError, loginErr.Error())
return
return Error(ctx, http.StatusInternalServerError, loginErr.Error())
}
Success(ctx, http.Json{
return Success(ctx, http.Json{
"access_token": token,
})
}
func (r *UserController) Info(ctx http.Context) {
// Info 用户信息
func (r *UserController) Info(ctx http.Context) http.Response {
user, ok := ctx.Value("user").(models.User)
if !ok {
Error(ctx, http.StatusUnauthorized, "登录已过期")
return
return Error(ctx, http.StatusUnauthorized, "登录已过期")
}
Success(ctx, http.Json{
return Success(ctx, http.Json{
"username": user.Username,
"email": user.Email,
})
+130 -169
View File
@@ -29,30 +29,30 @@ func NewWebsiteController() *WebsiteController {
}
// List 网站列表
func (c *WebsiteController) List(ctx http.Context) {
func (c *WebsiteController) List(ctx http.Context) http.Response {
limit := ctx.Request().QueryInt("limit")
page := ctx.Request().QueryInt("page")
total, websites, err := c.website.List(page, limit)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 获取网站列表失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
Success(ctx, http.Json{
return Success(ctx, http.Json{
"total": total,
"items": websites,
})
}
// Add 添加网站
func (c *WebsiteController) Add(ctx http.Context) {
if !Check(ctx, "openresty") {
return
func (c *WebsiteController) Add(ctx http.Context) http.Response {
check := Check(ctx, "openresty")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
"name": "required|regex:^[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)*$",
"name": "required|regex:^[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)*$|not_exists:websites,name",
"domain": "required",
"php": "required",
"db": "bool",
@@ -62,12 +62,10 @@ func (c *WebsiteController) Add(ctx http.Context) {
"db_password": "required_if:db,true",
})
if err != nil {
Error(ctx, http.StatusBadRequest, err.Error())
return
return Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
var website services.PanelWebsite
@@ -83,91 +81,90 @@ func (c *WebsiteController) Add(ctx http.Context) {
newSite, err := c.website.Add(website)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 添加网站失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
Success(ctx, newSite)
return Success(ctx, newSite)
}
// Delete 删除网站
func (c *WebsiteController) Delete(ctx http.Context) {
if !Check(ctx, "openresty") {
return
func (c *WebsiteController) Delete(ctx http.Context) http.Response {
check := Check(ctx, "openresty")
if check != nil {
return check
}
id := ctx.Request().InputInt("id")
err := c.website.Delete(id)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 删除网站失败 ", err)
Error(ctx, http.StatusInternalServerError, "删除网站失败: "+err.Error())
return
return Error(ctx, http.StatusInternalServerError, "删除网站失败: "+err.Error())
}
Success(ctx, nil)
return Success(ctx, nil)
}
// GetDefaultConfig 获取默认配置
func (c *WebsiteController) GetDefaultConfig(ctx http.Context) {
if !Check(ctx, "openresty") {
return
func (c *WebsiteController) GetDefaultConfig(ctx http.Context) http.Response {
check := Check(ctx, "openresty")
if check != nil {
return check
}
index := tools.ReadFile("/www/server/openresty/html/index.html")
stop := tools.ReadFile("/www/server/openresty/html/stop.html")
index := tools.Read("/www/server/openresty/html/index.html")
stop := tools.Read("/www/server/openresty/html/stop.html")
Success(ctx, http.Json{
return Success(ctx, http.Json{
"index": index,
"stop": stop,
})
}
// SaveDefaultConfig 保存默认配置
func (c *WebsiteController) SaveDefaultConfig(ctx http.Context) {
if !Check(ctx, "openresty") {
return
func (c *WebsiteController) SaveDefaultConfig(ctx http.Context) http.Response {
check := Check(ctx, "openresty")
if check != nil {
return check
}
index := ctx.Request().Input("index")
stop := ctx.Request().Input("stop")
if !tools.WriteFile("/www/server/openresty/html/index.html", index, 0644) {
if !tools.Write("/www/server/openresty/html/index.html", index, 0644) {
facades.Log().Error("[面板][WebsiteController] 保存默认配置失败")
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
if !tools.WriteFile("/www/server/openresty/html/stop.html", stop, 0644) {
if !tools.Write("/www/server/openresty/html/stop.html", stop, 0644) {
facades.Log().Error("[面板][WebsiteController] 保存默认配置失败")
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
Success(ctx, nil)
return Success(ctx, nil)
}
// GetConfig 获取配置
func (c *WebsiteController) GetConfig(ctx http.Context) {
if !Check(ctx, "openresty") {
return
func (c *WebsiteController) GetConfig(ctx http.Context) http.Response {
check := Check(ctx, "openresty")
if check != nil {
return check
}
id := ctx.Request().InputInt("id")
if id == 0 {
Error(ctx, http.StatusBadRequest, "参数错误")
return
return Error(ctx, http.StatusBadRequest, "参数错误")
}
config, err := c.website.GetConfig(id)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 获取网站配置失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
Success(ctx, config)
return Success(ctx, config)
}
// SaveConfig 保存配置
func (c *WebsiteController) SaveConfig(ctx http.Context) {
if !Check(ctx, "openresty") {
return
func (c *WebsiteController) SaveConfig(ctx http.Context) http.Response {
check := Check(ctx, "openresty")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
"id": "required",
@@ -190,39 +187,33 @@ func (c *WebsiteController) SaveConfig(ctx http.Context) {
"ssl_certificate_key": "required_if:ssl,true",
})
if err != nil {
Error(ctx, http.StatusBadRequest, err.Error())
return
return Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
Error(ctx, http.StatusBadRequest, validator.Errors().One())
return
return Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
var website models.Website
if facades.Orm().Query().Where("id", ctx.Request().Input("id")).FirstOrFail(&website) != nil {
Error(ctx, http.StatusBadRequest, "网站不存在")
return
return Error(ctx, http.StatusBadRequest, "网站不存在")
}
if !website.Status {
Error(ctx, http.StatusBadRequest, "网站已停用,请先启用")
return
return Error(ctx, http.StatusBadRequest, "网站已停用,请先启用")
}
// 原文
raw := tools.ReadFile("/www/server/vhost/" + website.Name + ".conf")
raw := tools.Read("/www/server/vhost/" + website.Name + ".conf")
if strings.TrimSpace(raw) != strings.TrimSpace(ctx.Request().Input("raw")) {
tools.WriteFile("/www/server/vhost/"+website.Name+".conf", ctx.Request().Input("raw"), 0644)
tools.ExecShell("systemctl reload openresty")
Success(ctx, nil)
return
tools.Write("/www/server/vhost/"+website.Name+".conf", ctx.Request().Input("raw"), 0644)
tools.Exec("systemctl reload openresty")
return Success(ctx, nil)
}
// 目录
path := ctx.Request().Input("path")
if !tools.Exists(path) {
Error(ctx, http.StatusBadRequest, "网站目录不存在")
return
return Error(ctx, http.StatusBadRequest, "网站目录不存在")
}
website.Path = path
@@ -230,8 +221,7 @@ func (c *WebsiteController) SaveConfig(ctx http.Context) {
domain := "server_name"
domains := strings.Split(ctx.Request().Input("domains"), "\n")
if len(domains) == 0 {
Error(ctx, http.StatusBadRequest, "域名不能为空")
return
return Error(ctx, http.StatusBadRequest, "域名不能为空")
}
for _, v := range domains {
if v == "" {
@@ -242,8 +232,7 @@ func (c *WebsiteController) SaveConfig(ctx http.Context) {
domain += ";"
domainConfigOld := tools.Cut(raw, "# server_name标记位开始", "# server_name标记位结束")
if len(strings.TrimSpace(domainConfigOld)) == 0 {
Error(ctx, http.StatusBadRequest, "配置文件中缺少server_name标记位")
return
return Error(ctx, http.StatusBadRequest, "配置文件中缺少server_name标记位")
}
raw = strings.Replace(raw, domainConfigOld, "\n "+domain+"\n ", -1)
@@ -251,13 +240,11 @@ func (c *WebsiteController) SaveConfig(ctx http.Context) {
var port strings.Builder
ports := strings.Split(ctx.Request().Input("ports"), "\n")
if len(ports) == 0 {
Error(ctx, http.StatusBadRequest, "端口不能为空")
return
return Error(ctx, http.StatusBadRequest, "端口不能为空")
}
for i, v := range ports {
if _, err := strconv.Atoi(v); err != nil && v != "443 ssl http2" {
Error(ctx, http.StatusBadRequest, "端口格式错误")
return
return Error(ctx, http.StatusBadRequest, "端口格式错误")
}
if v == "443" && ctx.Request().InputBool("ssl") {
v = "443 ssl http2"
@@ -270,21 +257,18 @@ func (c *WebsiteController) SaveConfig(ctx http.Context) {
}
portConfigOld := tools.Cut(raw, "# port标记位开始", "# port标记位结束")
if len(strings.TrimSpace(portConfigOld)) == 0 {
Error(ctx, http.StatusBadRequest, "配置文件中缺少port标记位")
return
return Error(ctx, http.StatusBadRequest, "配置文件中缺少port标记位")
}
raw = strings.Replace(raw, portConfigOld, "\n"+port.String()+"\n ", -1)
// 运行目录
root := tools.Cut(raw, "# root标记位开始", "# root标记位结束")
if len(strings.TrimSpace(root)) == 0 {
Error(ctx, http.StatusBadRequest, "配置文件中缺少root标记位")
return
return Error(ctx, http.StatusBadRequest, "配置文件中缺少root标记位")
}
match := regexp.MustCompile(`root\s+(.+);`).FindStringSubmatch(root)
if len(match) != 2 {
Error(ctx, http.StatusBadRequest, "配置文件中root标记位格式错误")
return
return Error(ctx, http.StatusBadRequest, "配置文件中root标记位格式错误")
}
rootNew := strings.Replace(root, match[1], ctx.Request().Input("root"), -1)
raw = strings.Replace(raw, root, rootNew, -1)
@@ -292,13 +276,11 @@ func (c *WebsiteController) SaveConfig(ctx http.Context) {
// 默认文件
index := tools.Cut(raw, "# index标记位开始", "# index标记位结束")
if len(strings.TrimSpace(index)) == 0 {
Error(ctx, http.StatusBadRequest, "配置文件中缺少index标记位")
return
return Error(ctx, http.StatusBadRequest, "配置文件中缺少index标记位")
}
match = regexp.MustCompile(`index\s+(.+);`).FindStringSubmatch(index)
if len(match) != 2 {
Error(ctx, http.StatusBadRequest, "配置文件中index标记位格式错误")
return
return Error(ctx, http.StatusBadRequest, "配置文件中index标记位格式错误")
}
indexNew := strings.Replace(index, match[1], ctx.Request().Input("index"), -1)
raw = strings.Replace(raw, index, indexNew, -1)
@@ -309,10 +291,10 @@ func (c *WebsiteController) SaveConfig(ctx http.Context) {
root += "/"
}
if ctx.Request().InputBool("open_basedir") {
tools.WriteFile(root+".user.ini", "open_basedir="+path+":/tmp/", 0644)
tools.Write(root+".user.ini", "open_basedir="+path+":/tmp/", 0644)
} else {
if tools.Exists(root + ".user.ini") {
tools.RemoveFile(root + ".user.ini")
tools.Remove(root + ".user.ini")
}
}
@@ -337,8 +319,8 @@ func (c *WebsiteController) SaveConfig(ctx http.Context) {
// SSL
ssl := ctx.Request().InputBool("ssl")
website.Ssl = ssl
tools.WriteFile("/www/server/vhost/ssl/"+website.Name+".pem", ctx.Request().Input("ssl_certificate"), 0644)
tools.WriteFile("/www/server/vhost/ssl/"+website.Name+".key", ctx.Request().Input("ssl_certificate_key"), 0644)
tools.Write("/www/server/vhost/ssl/"+website.Name+".pem", ctx.Request().Input("ssl_certificate"), 0644)
tools.Write("/www/server/vhost/ssl/"+website.Name+".key", ctx.Request().Input("ssl_certificate_key"), 0644)
if ssl {
sslConfig := `# ssl标记位开始
ssl_certificate /www/server/vhost/ssl/` + website.Name + `.pem;
@@ -391,112 +373,102 @@ func (c *WebsiteController) SaveConfig(ctx http.Context) {
err = facades.Orm().Query().Save(&website)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 保存网站配置失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
tools.WriteFile("/www/server/vhost/"+website.Name+".conf", raw, 0644)
tools.WriteFile("/www/server/vhost/rewrite/"+website.Name+".conf", ctx.Request().Input("rewrite"), 0644)
tools.ExecShell("systemctl reload openresty")
tools.Write("/www/server/vhost/"+website.Name+".conf", raw, 0644)
tools.Write("/www/server/vhost/rewrite/"+website.Name+".conf", ctx.Request().Input("rewrite"), 0644)
tools.Exec("systemctl reload openresty")
Success(ctx, nil)
return Success(ctx, nil)
}
// ClearLog 清空日志
func (c *WebsiteController) ClearLog(ctx http.Context) {
if !Check(ctx, "openresty") {
return
func (c *WebsiteController) ClearLog(ctx http.Context) http.Response {
check := Check(ctx, "openresty")
if check != nil {
return check
}
id := ctx.Request().InputInt("id")
if id == 0 {
Error(ctx, http.StatusBadRequest, "参数错误")
return
return Error(ctx, http.StatusBadRequest, "参数错误")
}
website := models.Website{}
err := facades.Orm().Query().Where("id", id).Get(&website)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 获取网站信息失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
tools.RemoveFile("/www/wwwlogs/" + website.Name + ".log")
tools.Remove("/www/wwwlogs/" + website.Name + ".log")
Success(ctx, nil)
return Success(ctx, nil)
}
// UpdateRemark 更新备注
func (c *WebsiteController) UpdateRemark(ctx http.Context) {
func (c *WebsiteController) UpdateRemark(ctx http.Context) http.Response {
id := ctx.Request().InputInt("id")
if id == 0 {
Error(ctx, http.StatusBadRequest, "参数错误")
return
return Error(ctx, http.StatusBadRequest, "参数错误")
}
website := models.Website{}
err := facades.Orm().Query().Where("id", id).Get(&website)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 获取网站信息失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
website.Remark = ctx.Request().Input("remark")
err = facades.Orm().Query().Save(&website)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 保存网站备注失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
Success(ctx, nil)
return Success(ctx, nil)
}
// BackupList 备份列表
func (c *WebsiteController) BackupList(ctx http.Context) {
func (c *WebsiteController) BackupList(ctx http.Context) http.Response {
backupList, err := c.backup.WebsiteList()
if err != nil {
facades.Log().Error("[面板][WebsiteController] 获取网站备份列表失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
Success(ctx, backupList)
return Success(ctx, backupList)
}
// CreateBackup 创建备份
func (c *WebsiteController) CreateBackup(ctx http.Context) {
func (c *WebsiteController) CreateBackup(ctx http.Context) http.Response {
id := ctx.Request().InputInt("id")
if id == 0 {
Error(ctx, http.StatusBadRequest, "参数错误")
return
return Error(ctx, http.StatusBadRequest, "参数错误")
}
website := models.Website{}
err := facades.Orm().Query().Where("id", id).Get(&website)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 获取网站信息失败 ", err)
Error(ctx, http.StatusInternalServerError, "获取网站信息失败: "+err.Error())
return
return Error(ctx, http.StatusInternalServerError, "获取网站信息失败: "+err.Error())
}
err = c.backup.WebSiteBackup(website)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 备份网站失败 ", err)
Error(ctx, http.StatusInternalServerError, "备份网站失败: "+err.Error())
return
return Error(ctx, http.StatusInternalServerError, "备份网站失败: "+err.Error())
}
Success(ctx, nil)
return Success(ctx, nil)
}
// UploadBackup 上传备份
func (c *WebsiteController) UploadBackup(ctx http.Context) {
func (c *WebsiteController) UploadBackup(ctx http.Context) http.Response {
file, err := ctx.Request().File("file")
if err != nil {
Error(ctx, http.StatusBadRequest, "上传文件失败")
return
return Error(ctx, http.StatusBadRequest, "上传文件失败")
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/website"
@@ -507,50 +479,44 @@ func (c *WebsiteController) UploadBackup(ctx http.Context) {
name := file.GetClientOriginalName()
_, err = file.StoreAs(backupPath, name)
if err != nil {
Error(ctx, http.StatusBadRequest, "上传文件失败")
return
return Error(ctx, http.StatusBadRequest, "上传文件失败")
}
Success(ctx, "上传文件成功")
return Success(ctx, "上传文件成功")
}
// RestoreBackup 还原备份
func (c *WebsiteController) RestoreBackup(ctx http.Context) {
func (c *WebsiteController) RestoreBackup(ctx http.Context) http.Response {
id := ctx.Request().InputInt("id")
if id == 0 {
Error(ctx, http.StatusBadRequest, "参数错误")
return
return Error(ctx, http.StatusBadRequest, "参数错误")
}
fileName := ctx.Request().Input("name")
if len(fileName) == 0 {
Error(ctx, http.StatusBadRequest, "参数错误")
return
return Error(ctx, http.StatusBadRequest, "参数错误")
}
website := models.Website{}
err := facades.Orm().Query().Where("id", id).Get(&website)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 获取网站信息失败 ", err)
Error(ctx, http.StatusInternalServerError, "获取网站信息失败: "+err.Error())
return
return Error(ctx, http.StatusInternalServerError, "获取网站信息失败: "+err.Error())
}
err = c.backup.WebsiteRestore(website, fileName)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 还原网站失败 ", err)
Error(ctx, http.StatusInternalServerError, "还原网站失败: "+err.Error())
return
return Error(ctx, http.StatusInternalServerError, "还原网站失败: "+err.Error())
}
Success(ctx, nil)
return Success(ctx, nil)
}
// DeleteBackup 删除备份
func (c *WebsiteController) DeleteBackup(ctx http.Context) {
func (c *WebsiteController) DeleteBackup(ctx http.Context) http.Response {
fileName := ctx.Request().Input("name")
if len(fileName) == 0 {
Error(ctx, http.StatusBadRequest, "参数错误")
return
return Error(ctx, http.StatusBadRequest, "参数错误")
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/website"
@@ -558,38 +524,35 @@ func (c *WebsiteController) DeleteBackup(ctx http.Context) {
tools.Mkdir(backupPath, 0644)
}
if !tools.RemoveFile(backupPath + "/" + fileName) {
Error(ctx, http.StatusInternalServerError, "删除备份失败")
return
if !tools.Remove(backupPath + "/" + fileName) {
return Error(ctx, http.StatusInternalServerError, "删除备份失败")
}
Success(ctx, nil)
return Success(ctx, nil)
}
// ResetConfig 重置配置
func (c *WebsiteController) ResetConfig(ctx http.Context) {
if !Check(ctx, "openresty") {
return
func (c *WebsiteController) ResetConfig(ctx http.Context) http.Response {
check := Check(ctx, "openresty")
if check != nil {
return check
}
id := ctx.Request().InputInt("id")
if id == 0 {
Error(ctx, http.StatusBadRequest, "参数错误")
return
return Error(ctx, http.StatusBadRequest, "参数错误")
}
website := models.Website{}
if err := facades.Orm().Query().Where("id", id).Get(&website); err != nil {
facades.Log().Error("[面板][WebsiteController] 获取网站信息失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
website.Status = true
website.Ssl = false
if err := facades.Orm().Query().Save(&website); err != nil {
facades.Log().Error("[面板][WebsiteController] 保存网站配置失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
raw := fmt.Sprintf(`
@@ -651,39 +614,37 @@ server
`, website.Path, website.Php, website.Name, website.Name, website.Name)
tools.WriteFile("/www/server/vhost/"+website.Name+".conf", raw, 0644)
tools.WriteFile("/www/server/vhost/rewrite"+website.Name+".conf", "", 0644)
tools.ExecShell("systemctl reload openresty")
tools.Write("/www/server/vhost/"+website.Name+".conf", raw, 0644)
tools.Write("/www/server/vhost/rewrite"+website.Name+".conf", "", 0644)
tools.Exec("systemctl reload openresty")
Success(ctx, nil)
return Success(ctx, nil)
}
// Status 网站状态
func (c *WebsiteController) Status(ctx http.Context) {
if !Check(ctx, "openresty") {
return
func (c *WebsiteController) Status(ctx http.Context) http.Response {
check := Check(ctx, "openresty")
if check != nil {
return check
}
id := ctx.Request().InputInt("id")
if id == 0 {
Error(ctx, http.StatusBadRequest, "参数错误")
return
return Error(ctx, http.StatusBadRequest, "参数错误")
}
website := models.Website{}
if err := facades.Orm().Query().Where("id", id).Get(&website); err != nil {
facades.Log().Error("[面板][WebsiteController] 获取网站信息失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
website.Status = ctx.Request().InputBool("status")
if err := facades.Orm().Query().Save(&website); err != nil {
facades.Log().Error("[面板][WebsiteController] 保存网站配置失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
return Error(ctx, http.StatusInternalServerError, "系统内部错误")
}
raw := tools.ReadFile("/www/server/vhost/" + website.Name + ".conf")
raw := tools.Read("/www/server/vhost/" + website.Name + ".conf")
// 运行目录
rootConfig := tools.Cut(raw, "# root标记位开始\n", "# root标记位结束")
@@ -709,8 +670,8 @@ func (c *WebsiteController) Status(ctx http.Context) {
}
}
tools.WriteFile("/www/server/vhost/"+website.Name+".conf", raw, 0644)
tools.ExecShell("systemctl reload openresty")
tools.Write("/www/server/vhost/"+website.Name+".conf", raw, 0644)
tools.Exec("systemctl reload openresty")
Success(ctx, nil)
return Success(ctx, nil)
}
+2 -3
View File
@@ -2,16 +2,15 @@ package middleware
import (
"github.com/gin-contrib/static"
contractshttp "github.com/goravel/framework/contracts/http"
frameworkhttp "github.com/goravel/framework/http"
"github.com/goravel/gin"
"panel/app/services"
)
func Static() contractshttp.Middleware {
return func(ctx contractshttp.Context) {
static.Serve(services.NewSettingImpl().Get("entrance", "/"), static.LocalFile("/www/panel/public", false))(ctx.(*frameworkhttp.GinContext).Instance())
static.Serve(services.NewSettingImpl().Get("entrance", "/"), static.LocalFile("/www/panel/public", false))(ctx.(*gin.Context).Instance())
ctx.Request().Next()
}
+2 -2
View File
@@ -4,10 +4,10 @@ var (
Name = "MySQL-5.7"
Description = "MySQL 是最流行的关系型数据库管理系统之一,Oracle 旗下产品。"
Slug = "mysql57"
Version = "5.7.42"
Version = "5.7.43"
Requires = []string{}
Excludes = []string{"mysql80"}
Install = `bash /www/panel/scripts/mysql/install.sh 57`
Uninstall = `bash /www/panel/scripts/mysql/uninstall.sh 57`
Update = `echo "not support now"`
Update = `bash /www/panel/scripts/mysql/update.sh 57`
)
+2 -2
View File
@@ -4,10 +4,10 @@ var (
Name = "MySQL-8.0"
Description = "MySQL 是最流行的关系型数据库管理系统之一,Oracle 旗下产品。(内存 < 4G 无法安装)"
Slug = "mysql80"
Version = "8.0.33"
Version = "8.0.34"
Requires = []string{}
Excludes = []string{"mysql57"}
Install = `bash /www/panel/scripts/mysql/install.sh 80`
Uninstall = `bash /www/panel/scripts/mysql/uninstall.sh 80`
Update = `echo "not support now"`
Update = `bash /www/panel/scripts/mysql/update.sh 80`
)
+1 -1
View File
@@ -4,7 +4,7 @@ var (
Name = "PHP-8.0"
Description = "PHP 是世界上最好的语言!"
Slug = "php80"
Version = "8.0.29"
Version = "8.0.30"
Requires = []string{}
Excludes = []string{}
Install = `bash /www/panel/scripts/php/install.sh 80`
+1 -1
View File
@@ -4,7 +4,7 @@ var (
Name = "PHP-8.1"
Description = "PHP 是世界上最好的语言!"
Slug = "php81"
Version = "8.1.21"
Version = "8.1.23"
Requires = []string{}
Excludes = []string{}
Install = `bash /www/panel/scripts/php/install.sh 81`
+1 -1
View File
@@ -4,7 +4,7 @@ var (
Name = "PHP-8.2"
Description = "PHP 是世界上最好的语言!"
Slug = "php82"
Version = "8.2.8"
Version = "8.2.10"
Requires = []string{}
Excludes = []string{}
Install = `bash /www/panel/scripts/php/install.sh 82`
+3 -3
View File
@@ -2,11 +2,11 @@ package postgresql15
var (
Name = "PostgreSQL-15"
Description = "PostgreSQL 是开源的对象 - 关系数据库数据库管理系统,在类似 BSD 许可与 MIT 许可的 PostgreSQL 许可下发行。"
Description = "PostgreSQL 是世界上最先进的开源关系数据库,在类似 BSD 与 MIT 许可的 PostgreSQL 许可下发行。"
Slug = "postgresql15"
Version = "15.3"
Version = "15.4"
Requires = []string{}
Excludes = []string{}
Excludes = []string{"postgresql16"}
Install = `bash /www/panel/scripts/postgresql/install.sh 15`
Uninstall = `bash /www/panel/scripts/postgresql/uninstall.sh 15`
Update = `bash /www/panel/scripts/postgresql/update.sh 15`
+13
View File
@@ -0,0 +1,13 @@
package postgresql16
var (
Name = "PostgreSQL-16"
Description = "PostgreSQL 是世界上最先进的开源关系数据库,在类似 BSD 与 MIT 许可的 PostgreSQL 许可下发行。"
Slug = "postgresql16"
Version = "16.0"
Requires = []string{}
Excludes = []string{"postgresql15"}
Install = `bash /www/panel/scripts/postgresql/install.sh 16`
Uninstall = `bash /www/panel/scripts/postgresql/uninstall.sh 16`
Update = `bash /www/panel/scripts/postgresql/update.sh 16`
)
+13
View File
@@ -0,0 +1,13 @@
package toolbox
var (
Name = "系统工具箱"
Description = "可视化调整一些常用的配置项,如 DNS、SWAP、时区 等"
Slug = "toolbox"
Version = "1.0"
Requires = []string{}
Excludes = []string{}
Install = `panel writePlugin toolbox 1.0`
Uninstall = `panel deletePlugin toolbox`
Update = `panel writePlugin toolbox 1.0`
)
@@ -0,0 +1,22 @@
package providers
import (
"github.com/goravel/framework/contracts/database/seeder"
"github.com/goravel/framework/contracts/foundation"
"github.com/goravel/framework/facades"
"panel/database/seeders"
)
type DatabaseServiceProvider struct {
}
func (receiver *DatabaseServiceProvider) Register(app foundation.Application) {
}
func (receiver *DatabaseServiceProvider) Boot(app foundation.Application) {
facades.Seeder().Register([]seeder.Seeder{
&seeders.DatabaseSeeder{},
})
}
+4 -3
View File
@@ -12,14 +12,15 @@ type RouteServiceProvider struct {
}
func (receiver *RouteServiceProvider) Register(app foundation.Application) {
//Add HTTP middlewares
facades.Route().GlobalMiddleware(http.Kernel{}.Middleware()...)
}
func (receiver *RouteServiceProvider) Boot(app foundation.Application) {
// Add HTTP middlewares
facades.Route().GlobalMiddleware(http.Kernel{}.Middleware()...)
receiver.configureRateLimiting()
routes.Web()
routes.Api()
routes.Plugin()
}
+23 -23
View File
@@ -84,7 +84,7 @@ func (s *BackupImpl) WebSiteBackup(website models.Website) error {
}
backupFile := backupPath + "/" + website.Name + "_" + carbon.Now().ToShortDateTimeString() + ".zip"
tools.ExecShell(`cd '` + website.Path + `' && zip -r '` + backupFile + `' .`)
tools.Exec(`cd '` + website.Path + `' && zip -r '` + backupFile + `' .`)
return nil
}
@@ -106,8 +106,8 @@ func (s *BackupImpl) WebsiteRestore(website models.Website, backupFile string) e
return errors.New("备份文件不存在")
}
tools.ExecShell(`rm -rf '` + website.Path + `/*'`)
tools.ExecShell(`unzip -o '` + backupFile + `' -d '` + website.Path + `' 2>&1`)
tools.Exec(`rm -rf '` + website.Path + `/*'`)
tools.Exec(`unzip -o '` + backupFile + `' -d '` + website.Path + `' 2>&1`)
tools.Chmod(website.Path, 0755)
tools.Chown(website.Path, "www", "www")
@@ -158,9 +158,9 @@ func (s *BackupImpl) MysqlBackup(database string) error {
return err
}
tools.ExecShell("/www/server/mysql/bin/mysqldump -uroot " + database + " > " + backupPath + "/" + backupFile)
tools.ExecShell("cd " + backupPath + " && zip -r " + backupPath + "/" + backupFile + ".zip " + backupFile)
tools.RemoveFile(backupPath + "/" + backupFile)
tools.Exec("/www/server/mysql/bin/mysqldump -uroot " + database + " > " + backupPath + "/" + backupFile)
tools.Exec("cd " + backupPath + " && zip -r " + backupPath + "/" + backupFile + ".zip " + backupFile)
tools.Remove(backupPath + "/" + backupFile)
_ = os.Unsetenv("MYSQL_PWD")
return nil
@@ -183,26 +183,26 @@ func (s *BackupImpl) MysqlRestore(database string, backupFile string) error {
switch ext {
case ".zip":
tools.ExecShell("unzip -o " + backupFile + " -d " + backupPath)
tools.Exec("unzip -o " + backupFile + " -d " + backupPath)
backupFile = strings.TrimSuffix(backupFile, ext)
case ".gz":
if strings.HasSuffix(backupFile, ".tar.gz") {
// 解压.tar.gz文件
tools.ExecShell("tar -zxvf " + backupFile + " -C " + backupPath)
tools.Exec("tar -zxvf " + backupFile + " -C " + backupPath)
backupFile = strings.TrimSuffix(backupFile, ".tar.gz")
} else {
// 解压.gz文件
tools.ExecShell("gzip -d " + backupFile)
tools.Exec("gzip -d " + backupFile)
backupFile = strings.TrimSuffix(backupFile, ext)
}
case ".bz2":
tools.ExecShell("bzip2 -d " + backupFile)
tools.Exec("bzip2 -d " + backupFile)
backupFile = strings.TrimSuffix(backupFile, ext)
case ".tar":
tools.ExecShell("tar -xvf " + backupFile + " -C " + backupPath)
tools.Exec("tar -xvf " + backupFile + " -C " + backupPath)
backupFile = strings.TrimSuffix(backupFile, ext)
case ".rar":
tools.ExecShell("unrar x " + backupFile + " " + backupPath)
tools.Exec("unrar x " + backupFile + " " + backupPath)
backupFile = strings.TrimSuffix(backupFile, ext)
}
@@ -210,7 +210,7 @@ func (s *BackupImpl) MysqlRestore(database string, backupFile string) error {
return errors.New("自动解压失败,请手动解压")
}
tools.ExecShell("/www/server/mysql/bin/mysql -uroot " + database + " < " + backupFile)
tools.Exec("/www/server/mysql/bin/mysql -uroot " + database + " < " + backupFile)
_ = os.Unsetenv("MYSQL_PWD")
return nil
@@ -255,9 +255,9 @@ func (s *BackupImpl) PostgresqlBackup(database string) error {
tools.Mkdir(backupPath, 0644)
}
tools.ExecShell(`su - postgres -c "pg_dump ` + database + `" > ` + backupPath + "/" + backupFile)
tools.ExecShell("cd " + backupPath + " && zip -r " + backupPath + "/" + backupFile + ".zip " + backupFile)
tools.RemoveFile(backupPath + "/" + backupFile)
tools.Exec(`su - postgres -c "pg_dump ` + database + `" > ` + backupPath + "/" + backupFile)
tools.Exec("cd " + backupPath + " && zip -r " + backupPath + "/" + backupFile + ".zip " + backupFile)
tools.Remove(backupPath + "/" + backupFile)
return nil
}
@@ -273,26 +273,26 @@ func (s *BackupImpl) PostgresqlRestore(database string, backupFile string) error
switch ext {
case ".zip":
tools.ExecShell("unzip -o " + backupFile + " -d " + backupPath)
tools.Exec("unzip -o " + backupFile + " -d " + backupPath)
backupFile = strings.TrimSuffix(backupFile, ext)
case ".gz":
if strings.HasSuffix(backupFile, ".tar.gz") {
// 解压.tar.gz文件
tools.ExecShell("tar -zxvf " + backupFile + " -C " + backupPath)
tools.Exec("tar -zxvf " + backupFile + " -C " + backupPath)
backupFile = strings.TrimSuffix(backupFile, ".tar.gz")
} else {
// 解压.gz文件
tools.ExecShell("gzip -d " + backupFile)
tools.Exec("gzip -d " + backupFile)
backupFile = strings.TrimSuffix(backupFile, ext)
}
case ".bz2":
tools.ExecShell("bzip2 -d " + backupFile)
tools.Exec("bzip2 -d " + backupFile)
backupFile = strings.TrimSuffix(backupFile, ext)
case ".tar":
tools.ExecShell("tar -xvf " + backupFile + " -C " + backupPath)
tools.Exec("tar -xvf " + backupFile + " -C " + backupPath)
backupFile = strings.TrimSuffix(backupFile, ext)
case ".rar":
tools.ExecShell("unrar x " + backupFile + " " + backupPath)
tools.Exec("unrar x " + backupFile + " " + backupPath)
backupFile = strings.TrimSuffix(backupFile, ext)
}
@@ -300,7 +300,7 @@ func (s *BackupImpl) PostgresqlRestore(database string, backupFile string) error
return errors.New("自动解压失败,请手动解压")
}
tools.ExecShell(`su - postgres -c "psql ` + database + `" < ` + backupFile)
tools.Exec(`su - postgres -c "psql ` + database + `" < ` + backupFile)
return nil
}
+8 -8
View File
@@ -22,11 +22,11 @@ func NewCronImpl() *CronImpl {
// AddToSystem 添加到系统
func (r *CronImpl) AddToSystem(cron models.Cron) {
if tools.IsRHEL() {
tools.ExecShell("echo \"" + cron.Time + " " + cron.Shell + " >> " + cron.Log + " 2>&1\" >> /var/spool/cron/root")
tools.ExecShell("systemctl restart crond")
tools.Exec("echo \"" + cron.Time + " " + cron.Shell + " >> " + cron.Log + " 2>&1\" >> /var/spool/cron/root")
tools.Exec("systemctl restart crond")
} else {
tools.ExecShell("echo \"" + cron.Time + " " + cron.Shell + " >> " + cron.Log + " 2>&1\" >> /var/spool/cron/crontabs/root")
tools.ExecShell("systemctl restart cron")
tools.Exec("echo \"" + cron.Time + " " + cron.Shell + " >> " + cron.Log + " 2>&1\" >> /var/spool/cron/crontabs/root")
tools.Exec("systemctl restart cron")
}
}
@@ -35,10 +35,10 @@ func (r *CronImpl) DeleteFromSystem(cron models.Cron) {
// 需要转义Shell路径的/为\/
cron.Shell = strings.ReplaceAll(cron.Shell, "/", "\\/")
if tools.IsRHEL() {
tools.ExecShell("sed -i '/" + cron.Shell + "/d' /var/spool/cron/root")
tools.ExecShell("systemctl restart crond")
tools.Exec("sed -i '/" + cron.Shell + "/d' /var/spool/cron/root")
tools.Exec("systemctl restart crond")
} else {
tools.ExecShell("sed -i '/" + cron.Shell + "/d' /var/spool/cron/crontabs/root")
tools.ExecShell("systemctl restart cron")
tools.Exec("sed -i '/" + cron.Shell + "/d' /var/spool/cron/crontabs/root")
tools.Exec("systemctl restart cron")
}
}
+24
View File
@@ -15,10 +15,12 @@ import (
"panel/app/plugins/php82"
"panel/app/plugins/phpmyadmin"
"panel/app/plugins/postgresql15"
"panel/app/plugins/postgresql16"
"panel/app/plugins/pureftpd"
"panel/app/plugins/redis"
"panel/app/plugins/s3fs"
"panel/app/plugins/supervisor"
"panel/app/plugins/toolbox"
)
// PanelPlugin 插件元数据结构
@@ -106,6 +108,17 @@ func (r *PluginImpl) All() []PanelPlugin {
Uninstall: postgresql15.Uninstall,
Update: postgresql15.Update,
})
p = append(p, PanelPlugin{
Name: postgresql16.Name,
Description: postgresql16.Description,
Slug: postgresql16.Slug,
Version: postgresql16.Version,
Requires: postgresql16.Requires,
Excludes: postgresql16.Excludes,
Install: postgresql16.Install,
Uninstall: postgresql16.Uninstall,
Update: postgresql16.Update,
})
p = append(p, PanelPlugin{
Name: php74.Name,
Description: php74.Description,
@@ -216,6 +229,17 @@ func (r *PluginImpl) All() []PanelPlugin {
Uninstall: fail2ban.Uninstall,
Update: fail2ban.Update,
})
p = append(p, PanelPlugin{
Name: toolbox.Name,
Description: toolbox.Description,
Slug: toolbox.Slug,
Version: toolbox.Version,
Requires: toolbox.Requires,
Excludes: toolbox.Excludes,
Install: toolbox.Install,
Uninstall: toolbox.Uninstall,
Update: toolbox.Update,
})
return p
}
+48 -22
View File
@@ -2,6 +2,8 @@
package services
import (
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"regexp"
@@ -49,6 +51,11 @@ type WebsiteSetting struct {
Ssl bool `json:"ssl"`
SslCertificate string `json:"ssl_certificate"`
SslCertificateKey string `json:"ssl_certificate_key"`
SslNotBefore string `json:"ssl_not_before"`
SslNotAfter string `json:"ssl_not_after"`
SSlDNSNames []string `json:"ssl_dns_names"`
SslIssuer string `json:"ssl_issuer"`
SslOCSPServer []string `json:"ssl_ocsp_server"`
HttpRedirect bool `json:"http_redirect"`
Hsts bool `json:"hsts"`
Waf bool `json:"waf"`
@@ -129,7 +136,7 @@ func (r *WebsiteImpl) Add(website PanelWebsite) (models.Website, error) {
</body>
</html>
`
tools.WriteFile(website.Path+"/index.html", index, 0644)
tools.Write(website.Path+"/index.html", index, 0644)
domainArr := strings.Split(website.Domain, "\n")
portList := ""
@@ -217,24 +224,30 @@ server
}
`, portList, domainList, website.Path, website.Php, website.Name, website.Name, website.Name)
tools.WriteFile("/www/server/vhost/"+website.Name+".conf", nginxConf, 0644)
tools.WriteFile("/www/server/vhost/rewrite/"+website.Name+".conf", "", 0644)
tools.WriteFile("/www/server/vhost/ssl/"+website.Name+".pem", "", 0644)
tools.WriteFile("/www/server/vhost/ssl/"+website.Name+".key", "", 0644)
tools.Write("/www/server/vhost/"+website.Name+".conf", nginxConf, 0644)
tools.Write("/www/server/vhost/rewrite/"+website.Name+".conf", "", 0644)
tools.Write("/www/server/vhost/ssl/"+website.Name+".pem", "", 0644)
tools.Write("/www/server/vhost/ssl/"+website.Name+".key", "", 0644)
tools.Chmod(r.setting.Get(models.SettingKeyWebsitePath), 0755)
tools.Chmod(website.Path, 0755)
tools.Chown(r.setting.Get(models.SettingKeyWebsitePath), "www", "www")
tools.Chown(website.Path, "www", "www")
tools.ExecShell("systemctl reload openresty")
tools.Exec("systemctl reload openresty")
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
if website.Db && website.DbType == "mysql" {
tools.ExecShell(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "CREATE DATABASE IF NOT EXISTS ` + website.DbName + ` DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;"`)
tools.ExecShell(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "CREATE USER '` + website.DbUser + `'@'localhost' IDENTIFIED BY '` + website.DbPassword + `';"`)
tools.ExecShell(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "GRANT ALL PRIVILEGES ON ` + website.DbName + `.* TO '` + website.DbUser + `'@'localhost';"`)
tools.ExecShell(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "FLUSH PRIVILEGES;"`)
tools.Exec(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "CREATE DATABASE IF NOT EXISTS ` + website.DbName + ` DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;"`)
tools.Exec(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "CREATE USER '` + website.DbUser + `'@'localhost' IDENTIFIED BY '` + website.DbPassword + `';"`)
tools.Exec(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "GRANT ALL PRIVILEGES ON ` + website.DbName + `.* TO '` + website.DbUser + `'@'localhost';"`)
tools.Exec(`/www/server/mysql/bin/mysql -uroot -p` + rootPassword + ` -e "FLUSH PRIVILEGES;"`)
}
if website.Db && website.DbType == "postgresql" {
tools.Exec(`echo "CREATE DATABASE ` + website.DbName + `;" | su - postgres -c "psql"`)
tools.Exec(`echo "CREATE USER ` + website.DbUser + ` WITH PASSWORD '` + website.DbPassword + `';" | su - postgres -c "psql"`)
tools.Exec(`echo "ALTER DATABASE ` + website.DbName + ` OWNER TO ` + website.DbUser + `;" | su - postgres -c "psql"`)
tools.Exec(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + website.DbName + ` TO ` + website.DbUser + `;" | su - postgres -c "psql"`)
}
return w, nil
@@ -251,13 +264,13 @@ func (r *WebsiteImpl) Delete(id int) error {
return err
}
tools.RemoveFile("/www/server/vhost/" + website.Name + ".conf")
tools.RemoveFile("/www/server/vhost/rewrite/" + website.Name + ".conf")
tools.RemoveFile("/www/server/vhost/ssl/" + website.Name + ".pem")
tools.RemoveFile("/www/server/vhost/ssl/" + website.Name + ".key")
tools.RemoveFile(website.Path)
tools.Remove("/www/server/vhost/" + website.Name + ".conf")
tools.Remove("/www/server/vhost/rewrite/" + website.Name + ".conf")
tools.Remove("/www/server/vhost/ssl/" + website.Name + ".pem")
tools.Remove("/www/server/vhost/ssl/" + website.Name + ".key")
tools.Remove(website.Path)
tools.ExecShell("systemctl reload openresty")
tools.Exec("systemctl reload openresty")
return nil
}
@@ -269,7 +282,7 @@ func (r *WebsiteImpl) GetConfig(id int) (WebsiteSetting, error) {
return WebsiteSetting{}, err
}
config := tools.ReadFile("/www/server/vhost/" + website.Name + ".conf")
config := tools.Read("/www/server/vhost/" + website.Name + ".conf")
var setting WebsiteSetting
setting.Name = website.Name
@@ -303,7 +316,7 @@ func (r *WebsiteImpl) GetConfig(id int) (WebsiteSetting, error) {
}
if tools.Exists(setting.Root + "/.user.ini") {
userIni := tools.ReadFile(setting.Path + "/.user.ini")
userIni := tools.Read(setting.Path + "/.user.ini")
if strings.Contains(userIni, "open_basedir") {
setting.OpenBasedir = true
} else {
@@ -313,12 +326,25 @@ func (r *WebsiteImpl) GetConfig(id int) (WebsiteSetting, error) {
setting.OpenBasedir = false
}
setting.SslCertificate = tools.ReadFile("/www/server/vhost/ssl/" + website.Name + ".pem")
setting.SslCertificateKey = tools.ReadFile("/www/server/vhost/ssl/" + website.Name + ".key")
setting.SslCertificate = tools.Read("/www/server/vhost/ssl/" + website.Name + ".pem")
setting.SslCertificateKey = tools.Read("/www/server/vhost/ssl/" + website.Name + ".key")
if setting.Ssl {
ssl := tools.Cut(config, "# ssl标记位开始", "# ssl标记位结束")
setting.HttpRedirect = strings.Contains(ssl, "# http重定向标记位")
setting.Hsts = strings.Contains(ssl, "# hsts标记位")
certData := tools.Read("/www/server/vhost/ssl/" + website.Name + ".pem")
block, _ := pem.Decode([]byte(certData))
if block != nil {
cert, err := x509.ParseCertificate(block.Bytes)
if err == nil {
setting.SslNotBefore = cert.NotBefore.Format("2006-01-02 15:04:05")
setting.SslNotAfter = cert.NotAfter.Format("2006-01-02 15:04:05")
setting.SslIssuer = cert.Issuer.CommonName
setting.SslOCSPServer = cert.OCSPServer
setting.SSlDNSNames = cert.DNSNames
}
}
} else {
setting.HttpRedirect = false
setting.Hsts = false
@@ -339,8 +365,8 @@ func (r *WebsiteImpl) GetConfig(id int) (WebsiteSetting, error) {
setting.WafCache = match[1]
}
setting.Rewrite = tools.ReadFile("/www/server/vhost/rewrite/" + website.Name + ".conf")
setting.Log = tools.Escape(tools.ExecShell(`tail -n 100 '/www/wwwlogs/` + website.Name + `.log'`))
setting.Rewrite = tools.Read("/www/server/vhost/rewrite/" + website.Name + ".conf")
setting.Log = tools.Escape(tools.Exec(`tail -n 100 '/www/wwwlogs/` + website.Name + `.log'`))
return setting, nil
}
-4
View File
@@ -2,7 +2,6 @@ package bootstrap
import (
"github.com/goravel/framework/foundation"
"github.com/goravel/framework/support/carbon"
"panel/config"
)
@@ -15,7 +14,4 @@ func Boot() {
// Bootstrap the config.
config.Boot()
// 设置 Carbon 时区
carbon.SetTimezone(carbon.PRC)
}
+5
View File
@@ -18,7 +18,9 @@ import (
"github.com/goravel/framework/route"
"github.com/goravel/framework/schedule"
"github.com/goravel/framework/support/carbon"
"github.com/goravel/framework/testing"
"github.com/goravel/framework/validation"
"github.com/goravel/gin"
"panel/app/providers"
)
@@ -80,6 +82,7 @@ func init() {
&crypt.ServiceProvider{},
&filesystem.ServiceProvider{},
&validation.ServiceProvider{},
&testing.ServiceProvider{},
&providers.AppServiceProvider{},
&providers.AuthServiceProvider{},
&providers.RouteServiceProvider{},
@@ -87,6 +90,8 @@ func init() {
&providers.QueueServiceProvider{},
&providers.EventServiceProvider{},
&providers.ValidationServiceProvider{},
&providers.DatabaseServiceProvider{},
&gin.ServiceProvider{},
},
})
}
+2 -1
View File
@@ -14,10 +14,11 @@ func init() {
// in web browsers. You are free to adjust these settings as needed.
//
// To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
"paths": []string{"*"},
"allowed_methods": []string{"*"},
"allowed_origins": []string{"*"},
"allowed_headers": []string{"*"},
"exposed_headers": []string{"*"},
"exposed_headers": []string{""},
"max_age": 0,
"supports_credentials": false,
})
+12
View File
@@ -1,12 +1,24 @@
package config
import (
"github.com/goravel/framework/contracts/route"
"github.com/goravel/framework/facades"
ginfacades "github.com/goravel/gin/facades"
)
func init() {
config := facades.Config()
config.Add("http", map[string]any{
// HTTP Driver
"default": "gin",
// HTTP Drivers
"drivers": map[string]any{
"gin": map[string]any{
"route": func() (route.Route, error) {
return ginfacades.Route("gin"), nil
},
},
},
// HTTP URL
"url": "http://localhost",
// HTTP Host
+1 -1
View File
@@ -32,7 +32,7 @@ func init() {
// the original token being created until they must re-authenticate.
// Defaults to 2 weeks.
//
// You can also set this to null, to yield an infinite refresh time.
// You can also set this to 0, to yield an infinite refresh time.
// Some may want this instead of never expiring tokens for e.g. a mobile app.
// This is not particularly recommended, so make sure you have appropriate
// systems in place to revoke the token if necessary.
+1 -1
View File
@@ -8,6 +8,6 @@ func init() {
config := facades.Config()
config.Add("panel", map[string]any{
"name": "耗子Linux面板",
"version": "v2.0.31",
"version": "v2.0.54",
})
}
+14
View File
@@ -0,0 +1,14 @@
package seeders
type DatabaseSeeder struct {
}
// Signature The name and signature of the seeder.
func (s *DatabaseSeeder) Signature() string {
return "DatabaseSeeder"
}
// Run executes the seeder logic.
func (s *DatabaseSeeder) Run() error {
return nil
}
+49 -43
View File
@@ -3,28 +3,29 @@ module panel
go 1.18
require (
github.com/bytedance/sonic v1.9.2
github.com/bytedance/sonic v1.10.2
github.com/gertd/go-pluralize v0.2.1
github.com/gin-contrib/static v0.0.1
github.com/gookit/color v1.5.4
github.com/goravel/framework v1.12.1-0.20230809103226-03eb8ad21087
github.com/goravel/framework v1.13.2
github.com/goravel/gin v1.1.5
github.com/gorilla/websocket v1.5.0
github.com/iancoleman/strcase v0.3.0
github.com/imroc/req/v3 v3.40.1
github.com/imroc/req/v3 v3.42.1
github.com/mojocn/base64Captcha v1.3.5
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/spf13/cast v1.5.1
github.com/stretchr/testify v1.8.4
golang.org/x/crypto v0.12.0
golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819
golang.org/x/crypto v0.14.0
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
)
require (
cloud.google.com/go v0.110.0 // indirect
cloud.google.com/go/compute v1.19.1 // indirect
cloud.google.com/go v0.110.6 // indirect
cloud.google.com/go/compute v1.23.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v0.13.0 // indirect
cloud.google.com/go/pubsub v1.30.0 // indirect
cloud.google.com/go/iam v1.1.1 // indirect
cloud.google.com/go/pubsub v1.33.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.16 // indirect
@@ -37,15 +38,17 @@ require (
github.com/RichardKnop/machinery/v2 v2.0.11 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/aws/aws-sdk-go v1.37.16 // indirect
github.com/cenkalti/backoff/v4 v4.2.0 // 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-20221115062448-fe3a3abad311 // indirect
github.com/containerd/continuity v0.3.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.0 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/containerd/continuity v0.4.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/docker/cli v20.10.22+incompatible // indirect
github.com/docker/docker v20.10.24+incompatible // indirect
github.com/docker/cli v24.0.6+incompatible // indirect
github.com/docker/docker v24.0.6+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
@@ -59,7 +62,7 @@ require (
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.1 // indirect
github.com/go-playground/validator/v10 v10.15.3 // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/go-redsync/redsync/v4 v4.0.4 // indirect
github.com/go-sql-driver/mysql v1.7.1 // indirect
@@ -70,7 +73,7 @@ require (
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
github.com/golang-migrate/migrate/v4 v4.16.2 // indirect
github.com/golang-module/carbon/v2 v2.2.3 // indirect
github.com/golang-module/carbon/v2 v2.2.8 // 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
@@ -80,13 +83,13 @@ require (
github.com/golang/snappy v0.0.4 // indirect
github.com/gomodule/redigo v2.0.0+incompatible // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/pprof v0.0.0-20230808223545-4887780b67fb // indirect
github.com/google/s2a-go v0.1.3 // indirect
github.com/google/pprof v0.0.0-20230907193218-d3ddc7976beb // indirect
github.com/google/s2a-go v0.1.4 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/google/wire v0.5.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
github.com/googleapis/gax-go/v2 v2.8.0 // indirect
github.com/googleapis/gax-go/v2 v2.11.0 // indirect
github.com/gookit/filter v1.2.0 // indirect
github.com/gookit/goutil v0.6.12 // indirect
github.com/gookit/validate v1.5.1 // indirect
@@ -96,7 +99,7 @@ require (
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/imdario/mergo v0.3.16 // 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.3.1 // indirect
@@ -118,23 +121,25 @@ require (
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/onsi/ginkgo/v2 v2.11.0 // indirect
github.com/onsi/ginkgo/v2 v2.12.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/opencontainers/runc v1.1.5 // indirect
github.com/opencontainers/runc v1.1.9 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/ory/dockertest/v3 v3.10.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.3.2 // indirect
github.com/quic-go/quic-go v0.37.4 // indirect
github.com/refraction-networking/utls v1.3.3 // indirect
github.com/quic-go/qtls-go1-20 v0.3.4 // indirect
github.com/quic-go/quic-go v0.38.1 // indirect
github.com/refraction-networking/utls v1.5.3 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rotisserie/eris v0.5.4 // indirect
github.com/rs/cors v1.10.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/afero v1.9.5 // indirect
@@ -148,6 +153,7 @@ require (
github.com/tklauser/numcpus v0.6.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/unrolled/secure v1.13.0 // indirect
github.com/urfave/cli/v2 v2.25.7 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.1 // indirect
@@ -162,30 +168,30 @@ require (
go.mongodb.org/mongo-driver v1.7.5 // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
golang.org/x/arch v0.4.0 // indirect
golang.org/x/arch v0.5.0 // indirect
golang.org/x/image v0.11.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/oauth2 v0.7.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
golang.org/x/tools v0.12.0 // indirect
google.golang.org/api v0.122.0 // indirect
golang.org/x/mod v0.13.0 // indirect
golang.org/x/net v0.16.0 // indirect
golang.org/x/oauth2 v0.10.0 // indirect
golang.org/x/sync v0.4.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.14.0 // indirect
google.golang.org/api v0.126.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/grpc v1.57.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/grpc v1.58.2 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/mysql v1.5.1 // indirect
gorm.io/driver/postgres v1.5.2 // indirect
gorm.io/driver/sqlserver v1.5.1 // indirect
gorm.io/gorm v1.25.2 // indirect
gorm.io/plugin/dbresolver v1.4.2 // indirect
gorm.io/gorm v1.25.4 // indirect
gorm.io/plugin/dbresolver v1.4.7 // indirect
modernc.org/libc v1.22.5 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect
+104 -116
View File
@@ -17,31 +17,30 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY=
cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q=
cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
cloud.google.com/go/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.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY=
cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE=
cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
cloud.google.com/go/compute/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 v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k=
cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0=
cloud.google.com/go/kms v1.10.1 h1:7hm1bRqGCA1GBRQUrp831TwJ9TWhP+tvLuP497CQS2g=
cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y=
cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU=
cloud.google.com/go/kms v1.15.0 h1:xYl5WEaSekKYN5gGRyhjvZKM22GVBBCzegGNVPy+aIs=
cloud.google.com/go/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.30.0 h1:vCge8m7aUKBJYOgrZp7EsNDf6QMd2CAlXZqWTn3yq6s=
cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4=
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/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=
@@ -85,25 +84,29 @@ github.com/aws/aws-sdk-go v1.37.16 h1:Q4YOP2s00NpB9wfmTDZArdcLRuG9ijbnoAwTW3ivle
github.com/aws/aws-sdk-go v1.37.16/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/brianvoe/gofakeit/v6 v6.23.1 h1:k2gX0hQpJStvixDbbw8oJOvPBg0XmHJWbSOF5JkiUHw=
github.com/brianvoe/gofakeit/v6 v6.23.2 h1:lVde18uhad5wII/f5RMVFLtdQNE0HaGFuBUXmYKk8i8=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.2 h1:GDaNjuWSGu09guE9Oql0MSTNhNCLlWwO8y/xM5BzcbM=
github.com/bytedance/sonic v1.9.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
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=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/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/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo=
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/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/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/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=
@@ -111,15 +114,12 @@ 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/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM=
github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -128,14 +128,13 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cu
github.com/dhui/dktest v0.3.16 h1:i6gq2YQEtcrjKbeJpBkWjE8MmLZPYllcjOFbTZuPDnw=
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/docker/cli v20.10.22+incompatible h1:0E7UqWPcn4SlvLImMHyh6xwyNRUGdPxhstpHeh0bFL0=
github.com/docker/cli v20.10.22+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY=
github.com/docker/cli v24.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/docker v20.10.24+incompatible h1:Ugvxm7a8+Gz6vqQYQQ2W7GYq5EUPaAiuPgIfVyI3dYE=
github.com/docker/docker v20.10.24+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE=
github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
@@ -148,7 +147,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
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/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -189,8 +187,8 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k=
github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-playground/validator/v10 v10.15.3 h1:S+sSpunYjNPDuXkWbK+x+bA7iXiW296KG4dL3X7xUZo=
github.com/go-playground/validator/v10 v10.15.3/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
@@ -236,8 +234,6 @@ github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/V
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
@@ -248,8 +244,8 @@ github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJ
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-migrate/migrate/v4 v4.16.2 h1:8coYbMKUyInrFk1lfGfRovTLAW7PhWp8qQDT2iKfuoA=
github.com/golang-migrate/migrate/v4 v4.16.2/go.mod h1:pfcJX4nPHaVdc5nmdCikFBWtm+UBpiZjRNNsyBbp0/o=
github.com/golang-module/carbon/v2 v2.2.3 h1:WvGIc5+qzq9drNzH+Gnjh1TZ0JgDY/IA+m2Dvk7Qm4Q=
github.com/golang-module/carbon/v2 v2.2.3/go.mod h1:LdzRApgmDT/wt0eNT8MEJbHfJdSqCtT46uZhfF30dqI=
github.com/golang-module/carbon/v2 v2.2.8 h1:a1VxHHKAR7fc1ho7sYXhS1s5S4x7+oqAf2EY5p8C46A=
github.com/golang-module/carbon/v2 v2.2.8/go.mod h1:XDALX7KgqmHk95xyLeaqX9/LJGbfLATyruTziq68SZ8=
github.com/golang-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=
@@ -324,26 +320,27 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20230808223545-4887780b67fb h1:oqpb3Cwpc7EOml5PVGMYbSGmwNui2R7i8IW83gs4W0c=
github.com/google/pprof v0.0.0-20230808223545-4887780b67fb/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
github.com/google/pprof v0.0.0-20230907193218-d3ddc7976beb h1:LCMfzVg3sflxTs4UvuP4D8CkoZnfHLe2qzqgDn/4OHs=
github.com/google/pprof v0.0.0-20230907193218-d3ddc7976beb/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.3 h1:FAgZmpLl/SXurPEZyCMPBIiiYeTbqfjlbdnCNTAkbGE=
github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc=
github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4=
github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
@@ -357,8 +354,10 @@ github.com/goravel/file-rotatelogs v0.0.0-20211215053220-2ab31dd9575c h1:obhFK91
github.com/goravel/file-rotatelogs v0.0.0-20211215053220-2ab31dd9575c/go.mod h1:YSWsLXlG16u5CWFaXNZHhEQD10+NwF3xfgDV816OwLE=
github.com/goravel/file-rotatelogs/v2 v2.4.1 h1:ogkeIFcTHSBRUBpZYiyJbpul8hkVXxHPuDbOaP78O1M=
github.com/goravel/file-rotatelogs/v2 v2.4.1/go.mod h1:euk9qr52WrzM8ICs1hecFcR4CZ/ZZOPdacHfvHgbOf0=
github.com/goravel/framework v1.12.1-0.20230809103226-03eb8ad21087 h1:YOKs5GFzbWOZz3ojiX5jl082Y1BRGOCig3ldnNBTrRs=
github.com/goravel/framework v1.12.1-0.20230809103226-03eb8ad21087/go.mod h1:qc8dK2vVVVdTSashuby7+KZZ2CfGztNgMaoDGWtvtVI=
github.com/goravel/framework v1.13.2 h1:7BRNihQ/ZNeA384941yg8zRytXRXkkStksXNyyUEgQI=
github.com/goravel/framework v1.13.2/go.mod h1:BOUTIwzhOMyKNXaxSYbVB74NkmbS2xv1W1fPRv9eDws=
github.com/goravel/gin v1.1.5 h1:HWVBxSDHsfpq2lWvuagQVMJlCwznf6ifc71IYEo3sCE=
github.com/goravel/gin v1.1.5/go.mod h1:FxnhnqcMJk/Sk6jA/xcXn6tHtFzayNXeFuw7GnLORMY=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
@@ -383,10 +382,10 @@ github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSAS
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/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/imroc/req/v3 v3.40.1 h1:/7YAmkrI4HsOTO6Md5FUUWPId90bTD074BV8sA0bt18=
github.com/imroc/req/v3 v3.40.1/go.mod h1:4wMbz0QYY5jmXNWk0BsWrRTR9ItZqOxzSJdGL0M9kzY=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/imroc/req/v3 v3.42.1 h1:g82SouLvX7pqwqJjpQJYrVvuI+LOycWhyuwxtLlyQJk=
github.com/imroc/req/v3 v3.42.1/go.mod h1:W7dOrfQORA9nFoj+CafIZ6P5iyk+rWdbp2sffOAvABU=
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=
@@ -433,11 +432,11 @@ github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -464,7 +463,6 @@ github.com/microsoft/go-mssqldb v1.1.0 h1:jsV+tpvcPTbNNKW0o3kiCD69kOHICsfjZ2VcVu
github.com/microsoft/go-mssqldb v1.1.0/go.mod h1:LzkFdl4z2Ck+Hi+ycGOTbL56VEfgoyA2DvYejrNGbRk=
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/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
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=
@@ -479,7 +477,6 @@ github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -488,22 +485,20 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108
github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI=
github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ=
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.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
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.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs=
github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
github.com/opencontainers/runc v1.1.9 h1:XR0VIHTGce5eWPkaPesqTBrhW2yAcaraWfsEalNwQLM=
github.com/opencontainers/runc v1.1.9/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
@@ -512,8 +507,8 @@ github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnz
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -525,12 +520,12 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
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/qtls-go1-20 v0.3.2 h1:rRgN3WfnKbyik4dBV8A6girlJVxGand/d+jVKbQq5GI=
github.com/quic-go/qtls-go1-20 v0.3.2/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/quic-go v0.37.4 h1:ke8B73yMCWGq9MfrCCAw0Uzdm7GaViC3i39dsIdDlH4=
github.com/quic-go/quic-go v0.37.4/go.mod h1:YsbH1r4mSHPJcLF4k4zruUkLBqctEMBDR6VPvcYjIsU=
github.com/refraction-networking/utls v1.3.3 h1:f/TBLX7KBciRyFH3bwupp+CE4fzoYKCirhdRcC490sw=
github.com/refraction-networking/utls v1.3.3/go.mod h1:DlecWW1LMlMJu+9qpzzQqdHDT/C2LAe03EdpLUz/RL8=
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/quic-go v0.38.1 h1:M36YWA5dEhEeT+slOu/SwMEucbYd0YFidxG3KlGPZaE=
github.com/quic-go/quic-go v0.38.1/go.mod h1:ijnZM7JsFIkp4cRyjxJNIzdSfCLmUMg9wdyhGmg+SN4=
github.com/refraction-networking/utls v1.5.3 h1:Ds5Ocg1+MC1ahNx5iBEcHe0jHeLaA/fLey61EENm7ro=
github.com/refraction-networking/utls v1.5.3/go.mod h1:SPuDbBmgLGp8s+HLNc83FuavwZCFoMmExj+ltUHiHUw=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
@@ -543,18 +538,19 @@ github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE=
github.com/rotisserie/eris v0.5.4 h1:Il6IvLdAapsMhvuOahHWiBnl1G++Q0/L5UIkI5mARSk=
github.com/rotisserie/eris v0.5.4/go.mod h1:Z/kgYTJiJtocxCbFfvRmO+QejApzG6zpyky9G1A4g9s=
github.com/rs/cors v1.10.0 h1:62NOS1h+r8p1mW6FM0FSB0exioXLhd/sh15KpjWBZ+8=
github.com/rs/cors v1.10.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
@@ -586,14 +582,12 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM=
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
@@ -608,12 +602,10 @@ github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLY
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/unrolled/secure v1.13.0 h1:sdr3Phw2+f8Px8HE5sd1EHdj1aV3yUwed/uZXChLFsk=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/unrolled/secure v1.13.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
@@ -669,8 +661,8 @@ go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc=
golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y=
golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
@@ -688,8 +680,8 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
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=
@@ -700,8 +692,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 h1:EDuYyU/MkFXllv9QF9819VlI9a4tzGuCbhG0ExK9o1U=
golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -731,8 +723,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -776,8 +768,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos=
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -788,8 +780,8 @@ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210113205817-d3ed898aa8a3/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
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=
@@ -804,8 +796,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -819,7 +811,6 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -827,8 +818,6 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -864,9 +853,7 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -875,14 +862,14 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -896,8 +883,9 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/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=
@@ -961,8 +949,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -987,8 +975,8 @@ google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.39.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.122.0 h1:zDobeejm3E7pEG1mNHvdxvjs5XJoCMzyNH+CmwL94Es=
google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms=
google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o=
google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw=
google.golang.org/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=
@@ -1036,12 +1024,12 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 h1:9NWlQfY2ePejTmfwUH1OWwmznFa+0kKcHGPDvcPza9M=
google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk=
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 h1:m8v1xLLLzMe1m5P+gCTF8nJB9epwZQUBERm20Oy1poQ=
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g=
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8=
google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e h1:z3vDksarJxsAKM5dmEGv0GHwE2hKJ096wZra71Vs4sw=
google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
google.golang.org/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=
@@ -1061,8 +1049,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.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I=
google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
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=
@@ -1075,9 +1063,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
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.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
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=
@@ -1100,7 +1087,6 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
@@ -1112,10 +1098,11 @@ gorm.io/driver/sqlserver v1.5.1 h1:wpyW/pR26U94uaujltiFGXY7fd2Jw5hC9PB1ZF/Y5s4=
gorm.io/driver/sqlserver v1.5.1/go.mod h1:AYHzzte2msKTmYBYsSIq8ZUsznLJwBdkB2wpI+kt0nM=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho=
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/plugin/dbresolver v1.4.2 h1:IeLSH20ayxbo4rN6HMIQ0ccdsh/fkLK23pp6ivZrqBI=
gorm.io/plugin/dbresolver v1.4.2/go.mod h1:l4Cn87EHLEYuqUncpEeTC2tTJQkjngPSD+lo8hIvcT0=
gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw=
gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/plugin/dbresolver v1.4.7 h1:ZwtwmJQxTx9us7o6zEHFvH1q4OeEo1pooU7efmnunJA=
gorm.io/plugin/dbresolver v1.4.7/go.mod h1:l4Cn87EHLEYuqUncpEeTC2tTJQkjngPSD+lo8hIvcT0=
gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -1132,6 +1119,7 @@ modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+1 -1
View File
@@ -68,5 +68,5 @@ func NewSSHClient(conf *SSHClientConfig) (*ssh.Client, error) {
}
func getKey(keyPath string) (ssh.Signer, error) {
return ssh.ParsePrivateKey([]byte(tools.ReadFile(keyPath)))
return ssh.ParsePrivateKey([]byte(tools.Read(keyPath)))
}
+1 -1
View File
@@ -19,5 +19,5 @@ func IsRHEL() bool {
// IsArm 判断是否是 ARM 架构
func IsArm() bool {
return runtime.GOARCH == "arm"
return runtime.GOARCH == "arm" || runtime.GOARCH == "arm64"
}
+45 -17
View File
@@ -9,8 +9,8 @@ import (
"github.com/goravel/framework/facades"
)
// WriteFile 写入文件
func WriteFile(path string, data string, permission os.FileMode) bool {
// Write 写入文件
func Write(path string, data string, permission os.FileMode) bool {
if err := os.MkdirAll(filepath.Dir(path), permission); err != nil {
facades.Log().Errorf("[面板][Helpers] 创建目录失败: %s", err.Error())
return false
@@ -25,8 +25,8 @@ func WriteFile(path string, data string, permission os.FileMode) bool {
return true
}
// ReadFile 读取文件
func ReadFile(path string) string {
// Read 读取文件
func Read(path string) string {
data, err := os.ReadFile(path)
if err != nil {
facades.Log().Errorf("[面板][Helpers] 读取文件 %s 失败: %s", path, err.Error())
@@ -36,8 +36,8 @@ func ReadFile(path string) string {
return string(data)
}
// RemoveFile 删除文件
func RemoveFile(path string) bool {
// Remove 删除文件
func Remove(path string) bool {
if err := os.Remove(path); err != nil {
facades.Log().Errorf("[面板][Helpers] 删除文件 %s 失败: %s", path, err.Error())
return false
@@ -46,8 +46,8 @@ func RemoveFile(path string) bool {
return true
}
// ExecShell 执行 shell 命令
func ExecShell(shell string) string {
// Exec 执行 shell 命令
func Exec(shell string) string {
cmd := exec.Command("bash", "-c", shell)
output, err := cmd.CombinedOutput()
@@ -59,8 +59,8 @@ func ExecShell(shell string) string {
return strings.TrimSpace(string(output))
}
// ExecShellAsync 异步执行 shell 命令
func ExecShellAsync(shell string) {
// ExecAsync 异步执行 shell 命令
func ExecAsync(shell string) {
cmd := exec.Command("bash", "-c", shell)
err := cmd.Start()
@@ -126,27 +126,55 @@ func Empty(path string) bool {
}
// Mv 移动路径
func Mv(src, dst string) bool {
func Mv(src, dst string) (bool, error) {
cmd := exec.Command("mv", src, dst)
err := cmd.Run()
if err != nil {
facades.Log().Errorf("[面板][Helpers] 移动 %s 到 %s 失败: %s", src, dst, err.Error())
return false
return false, err
}
return true
return true, nil
}
// Cp 复制路径
func Cp(src, dst string) bool {
func Cp(src, dst string) (bool, error) {
cmd := exec.Command("cp", "-r", src, dst)
err := cmd.Run()
if err != nil {
facades.Log().Errorf("[面板][Helpers] 复制 %s 到 %s 失败: %s", src, dst, err.Error())
return false
return false, err
}
return true
return true, nil
}
// Size 获取路径大小
func Size(path string) (int64, error) {
var size int64
err := filepath.Walk(path, func(filePath string, info os.FileInfo, walkErr error) error {
if walkErr != nil {
return walkErr
}
size += info.Size()
return nil
})
if err != nil {
return 0, err
}
return size, nil
}
// FileSize 获取文件大小
func FileSize(path string) (int64, error) {
info, err := os.Stat(path)
if err != nil {
return 0, err
}
return info.Size(), nil
}
+44 -10
View File
@@ -17,45 +17,45 @@ func TestSystemHelperTestSuite(t *testing.T) {
suite.Run(t, &SystemHelperTestSuite{})
}
func (s *SystemHelperTestSuite) TestWriteFile() {
func (s *SystemHelperTestSuite) TestWrite() {
filePath := "/tmp/testfile"
defer os.Remove(filePath)
s.True(WriteFile(filePath, "test data", 0644))
s.True(Write(filePath, "test data", 0644))
s.FileExists(filePath)
content, _ := os.ReadFile(filePath)
s.Equal("test data", string(content))
}
func (s *SystemHelperTestSuite) TestReadFile() {
func (s *SystemHelperTestSuite) TestRead() {
filePath := "/tmp/testfile"
defer os.Remove(filePath)
err := os.WriteFile(filePath, []byte("test data"), 0644)
s.Nil(err)
s.Equal("test data", ReadFile(filePath))
s.Equal("test data", Read(filePath))
}
func (s *SystemHelperTestSuite) TestRemoveFile() {
func (s *SystemHelperTestSuite) TestRemove() {
filePath := "/tmp/testfile"
err := os.WriteFile(filePath, []byte("test data"), 0644)
s.Nil(err)
s.True(RemoveFile(filePath))
s.True(Remove(filePath))
}
func (s *SystemHelperTestSuite) TestExecShell() {
s.Equal("test", ExecShell("echo 'test'"))
func (s *SystemHelperTestSuite) TestExec() {
s.Equal("test", Exec("echo 'test'"))
}
func (s *SystemHelperTestSuite) TestExecShellAsync() {
func (s *SystemHelperTestSuite) TestExecAsync() {
command := "echo 'test' > /tmp/testfile"
defer os.Remove("/tmp/testfile")
ExecShellAsync(command)
ExecAsync(command)
time.Sleep(time.Second)
@@ -104,3 +104,37 @@ func (s *SystemHelperTestSuite) TestEmpty() {
s.True(Empty("/tmp/123"))
s.False(Empty("/tmp"))
}
func (s *SystemHelperTestSuite) TestMv() {
filePath := "/tmp/testfile"
defer os.Remove(filePath)
err := os.WriteFile(filePath, []byte("test data"), 0644)
s.Nil(err)
s.True(Mv(filePath, "/tmp/testfile2"))
s.False(Exists(filePath))
}
func (s *SystemHelperTestSuite) TestCp() {
filePath := "/tmp/testfile"
defer os.Remove(filePath)
err := os.WriteFile(filePath, []byte("test data"), 0644)
s.Nil(err)
s.True(Cp(filePath, "/tmp/testfile2"))
s.True(Exists(filePath))
}
func (s *SystemHelperTestSuite) TestSize() {
size, err := Size("/tmp/123")
s.Equal(int64(0), size)
s.Error(err)
}
func (s *SystemHelperTestSuite) TestFileSize() {
size, err := FileSize("/tmp/123")
s.Equal(int64(0), size)
s.Error(err)
}
+24 -11
View File
@@ -127,42 +127,55 @@ func UpdatePanel(proxy bool) error {
color.Greenln("使用代理: " + strconv.FormatBool(proxy))
color.Greenln("备份面板配置...")
ExecShell("cp -f /www/panel/database/panel.db /tmp/panel.db.bak")
ExecShell("cp -f /www/panel/panel.conf /tmp/panel.conf.bak")
Exec("cp -f /www/panel/database/panel.db /tmp/panel.db.bak")
Exec("cp -f /www/panel/panel.conf /tmp/panel.conf.bak")
if !Exists("/tmp/panel.db.bak") || !Exists("/tmp/panel.conf.bak") {
return errors.New("备份面板配置失败")
}
color.Greenln("备份完成")
color.Greenln("清理旧版本...")
ExecShell("rm -rf /www/panel/*")
Exec("rm -rf /www/panel/*")
color.Greenln("清理完成")
color.Greenln("正在下载...")
if proxy {
ExecShell("wget -O /www/panel/panel.zip https://ghproxy.com/" + panelInfo.DownloadUrl)
Exec("wget -T 120 -t 3 -O /www/panel/panel.zip https://ghproxy.com/" + panelInfo.DownloadUrl)
} else {
ExecShell("wget -O /www/panel/panel.zip " + panelInfo.DownloadUrl)
Exec("wget -T 120 -t 3 -O /www/panel/panel.zip " + panelInfo.DownloadUrl)
}
if !Exists("/www/panel/panel.zip") {
return errors.New("下载失败")
}
color.Greenln("下载完成")
color.Greenln("更新新版本...")
ExecShell("cd /www/panel && unzip -o panel.zip && rm -rf panel.zip && chmod 700 panel && bash scripts/update_panel.sh")
Exec("cd /www/panel && unzip -o panel.zip && rm -rf panel.zip && chmod 700 panel && bash scripts/update_panel.sh")
if !Exists("/www/panel/panel") {
return errors.New("更新失败,可能是下载过程中出现了问题")
}
color.Greenln("更新完成")
color.Greenln("恢复面板配置...")
ExecShell("cp -f /tmp/panel.db.bak /www/panel/database/panel.db")
ExecShell("cp -f /tmp/panel.conf.bak /www/panel/panel.conf")
Exec("cp -f /tmp/panel.db.bak /www/panel/database/panel.db")
Exec("cp -f /tmp/panel.conf.bak /www/panel/panel.conf")
if !Exists("/www/panel/database/panel.db") || !Exists("/www/panel/panel.conf") {
return errors.New("恢复面板配置失败")
}
ExecShell("/www/panel/panel --env=panel.conf artisan migrate")
Exec("/www/panel/panel --env=panel.conf artisan migrate")
color.Greenln("恢复完成")
ExecShell("panel writeSetting version " + panelInfo.Version)
Exec("panel writeSetting version " + panelInfo.Version)
Exec("rm -rf /tmp/panel.db.bak")
Exec("rm -rf /tmp/panel.conf.bak")
color.Greenln("重启面板...")
ExecShell("systemctl restart panel")
Exec("systemctl restart panel")
color.Greenln("重启完成")
return nil
+2 -2
View File
@@ -165,7 +165,7 @@ Date: 2023-06-22
</div>
<div class="layui-card-body layui-text layadmin-text">
<blockquote class="layui-elem-quote">
<p style="color: red;">开发组祝大家2023端午快乐!永无Bug,永不宕机!</p>
<p style="color: red;">开发组祝大家2023中秋国庆快乐!永无Bug,永不宕机!</p>
</blockquote>
<blockquote class="layui-elem-quote">
<p>欢迎您使用耗子Linux面板。如遇到问题/Bug,可通过 <a
@@ -206,7 +206,6 @@ Date: 2023-06-22
, admin = layui.admin
, element = layui.element
let device = layui.device()
let cpu_info
admin.req({
url: '/api/panel/info/nowMonitor'
@@ -310,6 +309,7 @@ Date: 2023-06-22
index = layer.msg('正在获取版本信息...', {
icon: 16
, time: 0
, shade: 0.3
})
admin.req(
{
+3 -12
View File
@@ -1,7 +1,6 @@
<!--
Name: 资源监控
Author: 耗子
Date: 2023-07-21
Date: 2023-08-13
-->
<title>资源监控</title>
@@ -93,18 +92,16 @@ Date: 2023-07-21
<script>
layui.use(['admin', 'view', 'form', 'element', 'carousel'], function () {
var admin = layui.admin;
var view = layui.view;
var $ = layui.jquery;
var form = layui.form;
var laydate = layui.laydate;
// 渲染时间选择器
// 默认开始时间为今天0点,结束时间为当前时间
var startDate = new Date(new Date().setHours(0, 0, 0, 0));
var endDate = new Date();
// 拼接时间字符串
var startStr = startDate.getFullYear() + '-' + (startDate.getMonth() + 1) + '-' + startDate.getDate() + ' ' + startDate.getHours() + ':' + startDate.getMinutes() + ':' + startDate.getSeconds();
var endStr = endDate.getFullYear() + '-' + (endDate.getMonth() + 1) + '-' + endDate.getDate() + ' ' + endDate.getHours() + ':' + endDate.getMinutes() + ':' + endDate.getSeconds();
var startStr = startDate.getFullYear() + '-' + startDate.getMonth() + '-' + startDate.getDate() + ' ' + startDate.getHours() + ':' + startDate.getMinutes() + ':' + startDate.getSeconds();
var endStr = endDate.getFullYear() + '-' + endDate.getMonth() + '-' + endDate.getDate() + ' ' + endDate.getHours() + ':' + endDate.getMinutes() + ':' + endDate.getSeconds();
laydate.render({
elem: '#monitor-date'
, type: 'datetime'
@@ -139,7 +136,6 @@ Date: 2023-07-21
}
}
});
// 监听switch开关:是否开启监控
form.on('switch(monitor)', function (data) {
admin.req({
url: '/api/panel/monitor/switch',
@@ -155,7 +151,6 @@ Date: 2023-07-21
}
});
});
// 监听保存天数按钮
$('#save_monitor_date').click(function () {
var days = $('input[name="monitor-save-days"]').val();
if (days == '') {
@@ -176,8 +171,6 @@ Date: 2023-07-21
}
});
});
// 监听清除监控数据按钮
$('#clear_monitor_record').click(function () {
layer.confirm('确定要清除监控数据吗?', function (index) {
admin.req({
@@ -202,12 +195,10 @@ Date: 2023-07-21
getMonitorData(startDate.getTime(), endDate.getTime());
});
// 获取监控数据
function getMonitorData(startDate, endDate) {
layui.use(['admin'], function () {
let admin = layui.admin;
// 获取监控数据
admin.req({
url: '/api/panel/monitor/list?start=' + startDate + '&end=' + endDate,
type: 'get',
@@ -0,0 +1,524 @@
<!--
Name: PostgreSQL管理器
Author: 耗子
Date: 2023-08-13
-->
<title>PostgreSQL</title>
<div class="layui-fluid" id="component-tabs">
<div class="layui-row">
<div class="layui-col-md12">
<div class="layui-card">
<div class="layui-card-header">PostgreSQL管理</div>
<div class="layui-card-body">
<div class="layui-tab">
<ul class="layui-tab-title">
<li class="layui-this">基本信息</li>
<li>管理</li>
<li>主配置</li>
<li>用户配置</li>
<li>负载状态</li>
<li>日志</li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
<legend>运行状态</legend>
</fieldset>
<blockquote id="postgresql-status" class="layui-elem-quote layui-quote-nm">
当前状态:<span
class="layui-badge layui-bg-black">获取中</span></blockquote>
<div class="layui-btn-container" style="padding-top: 30px;">
<button id="postgresql-start" class="layui-btn">启动</button>
<button id="postgresql-stop" class="layui-btn layui-btn-danger">停止</button>
<button id="postgresql-restart" class="layui-btn layui-btn-warm">重启</button>
<button id="postgresql-reload" class="layui-btn layui-btn-normal">重载</button>
</div>
</div>
<div class="layui-tab-item">
<blockquote class="layui-elem-quote">面板仅集成了部分常用功能,如需更多功能,请使用
pgAdmin 客户端。
</blockquote>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
<legend>数据库列表</legend>
</fieldset>
<table class="layui-hide" id="postgresql-database-list"
lay-filter="postgresql-database-list"></table>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
<legend>用户列表</legend>
</fieldset>
<table class="layui-hide" id="postgresql-user-list"
lay-filter="postgresql-user-list"></table>
<!-- 数据库顶部工具栏 -->
<script type="text/html" id="postgresql-database-list-bar">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="add_database">新建数据库
</button>
</div>
</script>
<!-- 用户顶部工具栏 -->
<script type="text/html" id="postgresql-user-list-bar">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="add_user">新建用户</button>
</div>
</script>
<!-- 数据库右侧管理 -->
<script type="text/html" id="postgresql-database-list-control">
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="backup">备份</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>
<!-- 用户右侧管理 -->
<script type="text/html" id="postgresql-user-list-control">
<a class="layui-btn layui-btn-normal layui-btn-xs"
lay-event="change_password">改密</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>
</div>
<div class="layui-tab-item">
<blockquote class="layui-elem-quote">此处修改的是PostgreSQL主配置文件,如果你不了解各参数的含义,请不要随意修改!<br>
提示:Ctrl+F 搜索关键字,Ctrl+S 保存,Ctrl+H 查找替换!
</blockquote>
<div id="postgresql-config-editor"
style="height: 600px;"></div>
<div class="layui-btn-container" style="padding-top: 30px;">
<button id="postgresql-config-save" class="layui-btn">保存</button>
</div>
</div>
<div class="layui-tab-item">
<blockquote class="layui-elem-quote">此处修改的是PostgreSQL用户配置文件,如果你不了解各参数的含义,请不要随意修改!<br>
提示:Ctrl+F 搜索关键字,Ctrl+S 保存,Ctrl+H 查找替换!
</blockquote>
<div id="postgresql-user-config-editor"
style="height: 600px;"></div>
<div class="layui-btn-container" style="padding-top: 30px;">
<button id="postgresql-user-config-save" class="layui-btn">保存</button>
</div>
</div>
<div class="layui-tab-item">
<table class="layui-hide" id="postgresql-load-status"></table>
</div>
<div class="layui-tab-item">
<pre id="postgresql-log" class="layui-code">
获取中...
</pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
let postgresql_config_editor;
let postgresql_user_config_editor;
layui.use(['index', 'admin', 'element', 'form', 'view', 'code', 'table'], function () {
let $ = layui.$
, admin = layui.admin
, element = layui.element
, code = layui.code
, table = layui.table
, form = layui.form
, view = layui.view;
form.render();
admin.req({
url: "/api/plugins/postgresql16/status"
, type: 'get'
, success: function (result) {
if (result.code !== 0) {
return false;
}
if (result.data) {
$('#postgresql-status').html('当前状态:<span class="layui-badge layui-bg-green">运行中</span>');
} else {
$('#postgresql-status').html('当前状态:<span class="layui-badge layui-bg-red">已停止</span>');
}
}
});
// 获取数据库列表
table.render({
elem: '#postgresql-database-list'
, url: '/api/plugins/postgresql16/database'
, toolbar: '#postgresql-database-list-bar'
, title: '数据库列表'
, cols: [[
{field: 'name', title: '库名', fixed: 'left', unresize: true, sort: true}
, {field: 'owner', title: '所有者', unresize: true, sort: true}
, {field: 'encoding', title: '编码', unresize: true, sort: true}
, {fixed: 'right', title: '操作', toolbar: '#postgresql-database-list-control', width: 150}
]]
, page: true
, text: {
none: '暂无数据'
}
, parseData: function (res) {
return {
"code": res.code,
"msg": res.message,
"count": res.data.total,
"data": res.data.items
};
}
});
// 头工具栏事件
table.on('toolbar(postgresql-database-list)', function (obj) {
if (obj.event === 'add_database') {
admin.popup({
title: '新建数据库'
, area: ['600px', '300px']
, id: 'LAY-popup-postgresql-database-add'
, success: function (layer, index) {
view(this.id).render('plugins/postgresql16/add_database', {}).done(function () {
form.render(null, 'LAY-popup-postgresql-database-add');
});
}
});
}
});
// 行工具事件
table.on('tool(postgresql-database-list)', function (obj) {
let data = obj.data;
if (obj.event === 'del') {
layer.confirm('高风险操作,确定要删除数据库 <b style="color: red;">' + data.name + '</b> 吗?', function (index) {
index = layer.msg('正在删除数据库 <b style="color: red;">' + data.name + '</b> ...', {
icon: 16
, time: 0
, shade: 0.3
});
admin.req({
url: "/api/plugins/postgresql16/deleteDatabase"
, type: 'post'
, data: {
database: data.name
}
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
obj.del();
layer.alert('数据库' + data.name + '删除成功!');
}
});
layer.close(index);
});
} else if (obj.event === 'backup') {
// 打开备份页面
admin.popup({
title: '备份管理 - ' + data.name
, area: ['70%', '80%']
, id: 'LAY-popup-postgresql-backup'
, success: function (layero, index) {
view(this.id).render('plugins/postgresql16/backup', {
data: data
}).done(function () {
form.render(null, 'LAY-popup-postgresql-backup');
});
}
});
}
});
// 获取数据库用户列表
table.render({
elem: '#postgresql-user-list'
, url: '/api/plugins/postgresql16/user'
, toolbar: '#postgresql-user-list-bar'
, title: '用户列表'
, cols: [[
{field: 'user', title: '用户名', fixed: 'left', width: 300, sort: true}
, {field: 'role', title: '权限'}
, {fixed: 'right', title: '操作', toolbar: '#postgresql-user-list-control', width: 150}
]]
, page: true
, text: {
none: '暂无数据'
}
, parseData: function (res) {
return {
"code": res.code,
"msg": res.message,
"count": res.data.total,
"data": res.data.items
};
}
});
// 头工具栏事件
table.on('toolbar(postgresql-user-list)', function (obj) {
if (obj.event === 'add_user') {
admin.popup({
title: '新建用户'
, area: ['600px', '300px']
, id: 'LAY-popup-postgresql-user-add'
, success: function (layer, index) {
view(this.id).render('plugins/postgresql16/add_user', {}).done(function () {
form.render(null, 'LAY-popup-postgresql-user-add');
});
}
});
}
});
// 行工具事件
table.on('tool(postgresql-user-list)', function (obj) {
let data = obj.data;
if (obj.event === 'del') {
layer.confirm('高风险操作,确定要删除用户 <b style="color: red;">' + data.user + '</b> 吗?', function (index) {
index = layer.msg('正在删除用户 <b style="color: red;">' + data.user + '</b> ...', {
icon: 16
, time: 0
, shade: 0.3
});
admin.req({
url: "/api/plugins/postgresql16/deleteUser"
, type: 'post'
, data: data
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
obj.del();
layer.alert('用户' + data.user + '删除成功!');
}
});
layer.close(index);
});
} else if (obj.event === 'change_password') {
// 弹出输入密码框
layer.prompt({
formType: 1
, title: '请输入新密码(建议8位以上大小写数字特殊符号混合)'
}, function (value, index) {
layer.close(index);
layer.msg('正在修改密码 ...', {
icon: 16
, time: 0
, shade: 0.3
});
// 发送请求
admin.req({
url: "/api/plugins/postgresql16/changePassword"
, type: 'post'
, data: {
user: data.user,
password: value
}
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
layer.alert('用户' + data.user + '密码修改成功!');
}
});
});
}
});
// 获取postgresql日志并渲染
admin.req({
url: "/api/plugins/postgresql16/log"
, type: 'get'
, success: function (result) {
if (result.code !== 0) {
$('#postgresql-log').text('PostgreSQL日志获取失败,请刷新重试!');
code({
elem: '#postgresql-log'
, title: 'error.log'
, encode: true
, about: false
});
return false;
}
$('#postgresql-log').text(result.data);
code({
elem: '#postgresql-log'
, title: 'postgresql.log'
, encode: true
, about: false
});
}
});
// 获取postgresql配置并渲染
admin.req({
url: "/api/plugins/postgresql16/config"
, type: 'get'
, success: function (result) {
if (result.code !== 0) {
return false;
}
$('#postgresql-config-editor').text(result.data);
postgresql_config_editor = ace.edit("postgresql-config-editor", {
mode: "ace/mode/ini",
selectionStyle: "text"
});
}
});
// 获取postgresql用户配置并渲染
admin.req({
url: "/api/plugins/postgresql16/userConfig"
, type: 'get'
, success: function (result) {
if (result.code !== 0) {
return false;
}
$('#postgresql-user-config-editor').text(result.data);
postgresql_user_config_editor = ace.edit("postgresql-user-config-editor", {
mode: "ace/mode/ini",
selectionStyle: "text"
});
}
});
// 获取postgresql负载状态并渲染
table.render({
elem: '#postgresql-load-status'
, url: '/api/plugins/postgresql16/load'
, cols: [[
{field: 'name', width: '80%', title: '属性',}
, {field: 'value', width: '20%', title: '当前值'}
]]
});
element.render();
// 事件监听
$('#postgresql-start').click(function () {
layer.confirm('确定要启动PostgreSQL吗?', {
btn: ['启动', '取消']
}, function () {
index = layer.msg('正在启动PostgreSQL...', {
icon: 16
, time: 0
, shade: 0.3
});
admin.req({
url: "/api/plugins/postgresql16/start"
, type: 'post'
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
admin.events.refresh();
layer.alert('PostgreSQL启动成功!');
}
});
});
});
$('#postgresql-stop').click(function () {
layer.confirm('停止PostgreSQL将导致使用PostgreSQL的网站无法访问,是否继续停止?', {
btn: ['停止', '取消']
}, function () {
index = layer.msg('正在停止PostgreSQL...', {
icon: 16
, time: 0
, shade: 0.3
});
admin.req({
url: "/api/plugins/postgresql16/stop"
, type: 'post'
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
admin.events.refresh();
layer.alert('PostgreSQL停止成功!');
}
});
});
});
$('#postgresql-restart').click(function () {
layer.confirm('重启PostgreSQL将导致使用PostgreSQL的网站短时间无法访问,是否继续重启?', {
btn: ['重启', '取消']
}, function () {
index = layer.msg('正在重启PostgreSQL...', {
icon: 16
, time: 0
, shade: 0.3
});
admin.req({
url: "/api/plugins/postgresql16/restart"
, type: 'post'
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
admin.events.refresh();
layer.alert('PostgreSQL重启成功!');
}
});
});
});
$('#postgresql-reload').click(function () {
index = layer.msg('正在重载PostgreSQL...', {
icon: 16
, time: 0
, shade: 0.3
});
admin.req({
url: "/api/plugins/postgresql16/reload"
, type: 'post'
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
admin.events.refresh();
layer.alert('PostgreSQL重载成功!');
}
});
});
$('#postgresql-config-save').click(function () {
index = layer.msg('正在保存配置...', {
icon: 16
, time: 0
, shade: 0.3
});
admin.req({
url: "/api/plugins/postgresql16/config"
, type: 'post'
, data: {
config: postgresql_config_editor.getValue()
}
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
layer.alert('PostgreSQL配置保存成功!');
}
});
});
$('#postgresql-user-config-save').click(function () {
index = layer.msg('正在保存配置...', {
icon: 16
, time: 0
, shade: 0.3
});
admin.req({
url: "/api/plugins/postgresql16/userConfig"
, type: 'post'
, data: {
config: postgresql_user_config_editor.getValue()
}
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
layer.alert('PostgreSQL用户配置保存成功!');
}
});
});
});
</script>
@@ -0,0 +1,77 @@
<!--
Name: PostgreSQL管理器 - 添加数据库
Author: 耗子
Date: 2023-08-13
-->
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
<form class="layui-form" action="" lay-filter="add-postgresql-database-form">
<div class="layui-form-item">
<label class="layui-form-label">数据库名</label>
<div class="layui-input-block">
<input type="text" name="database" lay-verify="required" placeholder="请输入数据库名"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">用户名</label>
<div class="layui-input-block">
<input type="text" name="user" lay-verify="required" placeholder="请输入用户名"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">密码</label>
<div class="layui-input-block">
<input type="text" name="password" lay-verify="required" placeholder="请输入密码(8位以上大小写数字特殊符号混合)"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<div class="layui-footer">
<button class="layui-btn" lay-submit="" lay-filter="add-postgresql-database-submit">立即提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</div>
</form>
</script>
<script>
layui.data.sendParams = function (params) {
layui.use(['admin', 'form', 'table'], function () {
var admin = layui.admin
, layer = layui.layer
, form = layui.form
, table = layui.table
form.render();
// 提交
form.on('submit(add-postgresql-database-submit)', function (data) {
index = layer.msg('正在提交...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/postgresql16/addDatabase"
, type: 'post'
, data: data.field
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
table.reload('postgresql-database-list');
table.reload('postgresql-user-list');
layer.alert('数据库添加成功!', {
icon: 1
, title: '提示'
, btn: ['确定']
, yes: function (index) {
layer.closeAll();
}
});
}
});
return false;
});
});
};
</script>
@@ -0,0 +1,79 @@
<!--
Name: PostgreSQL管理器 - 添加用户
Author: 耗子
Date: 2023-08-13
-->
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
<form class="layui-form" action="" lay-filter="add-postgresql-user-form">
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">用户名</label>
<div class="layui-input-block">
<input type="text" name="user" lay-verify="required" placeholder="请输入用户名"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">密码</label>
<div class="layui-input-block">
<input type="text" name="password" lay-verify="required"
placeholder="请输入密码(8位以上大小写数字特殊符号混合)"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">数据库</label>
<div class="layui-input-block">
<input type="text" name="database" lay-verify="required" placeholder="输入授权给该用户的数据库名"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<div class="layui-footer">
<button class="layui-btn" lay-submit="" lay-filter="add-postgresql-user-submit">立即提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</div>
</form>
</script>
<script>
layui.data.sendParams = function (params) {
layui.use(['admin', 'form', 'table'], function () {
var $ = layui.$
, admin = layui.admin
, layer = layui.layer
, form = layui.form
, table = layui.table
form.render();
// 提交
form.on('submit(add-postgresql-user-submit)', function (data) {
index = layer.msg('正在提交...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/postgresql16/addUser"
, type: 'post'
, data: data.field
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
table.reload('postgresql-database-list');
table.reload('postgresql-user-list');
layer.alert('用户添加成功!', {
icon: 1
, title: '提示'
, btn: ['确定']
, yes: function (index) {
layer.closeAll();
}
});
}
});
return false;
});
});
};
</script>
@@ -0,0 +1,141 @@
<!--
Name: PostgreSQL管理器 - 数据库备份
Author: 耗子
Date: 2023-08-13
-->
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
<div class="layui-row">
<div class="layui-col-xs12 layui-col-sm12 layui-col-md12">
<table class="layui-hide" id="postgresql-backup-list" lay-filter="postgresql-backup-list"></table>
</div>
</div>
</script>
<!-- 备份顶部工具栏 -->
<script type="text/html" id="postgresql-database-backup-bar">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="backup_database">备份数据库</button>
<button class="layui-btn layui-btn-sm" id="upload_postgresql_backup">上传备份</button>
</div>
</script>
<!-- 备份右侧管理 -->
<script type="text/html" id="postgresql-database-backup-control">
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="restore">恢复</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>
<script>
layui.data.sendParams = function (params) {
layui.use(['admin', 'form', 'laydate', 'code'], function () {
var admin = layui.admin
, layer = layui.layer
, table = layui.table
, upload = layui.upload;
// 渲染表格
table.render({
elem: '#postgresql-backup-list'
, url: '/api/plugins/postgresql16/backup'
, toolbar: '#postgresql-database-backup-bar'
, title: '备份列表'
, cols: [[
{field: 'name', title: '备份名称', width: 500}
, {field: 'size', title: '文件大小'}
, {field: 'right', title: '操作', width: 150, toolbar: '#postgresql-database-backup-control'}
]]
, text: {
none: '无备份数据'
}
, done: function (res, curr, count) {
upload.render({
elem: '#upload_postgresql_backup'
, url: '/api/plugins/postgresql16/uploadBackup'
, accept: 'file'
, ext: 'sql|zip|rar|tar|gz|bz2'
, before: function (obj) {
index = layer.msg('正在上传备份文件,可能需要较长时间,请勿操作...', {
icon: 16
, time: 0
});
}
, done: function (res) {
layer.close(index);
layer.msg('上传成功!', {icon: 1});
table.reload('postgresql-backup-list');
}
});
}
});
// 头工具栏事件
table.on('toolbar(postgresql-backup-list)', function (obj) {
if (obj.event === 'backup_database') {
index = layer.msg('正在备份数据库,请稍等...', {
icon: 16
, time: 0
});
admin.req({
url: '/api/plugins/postgresql16/createBackup'
, type: 'post'
, data: {
database: params.data.name
}
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
layer.alert('备份失败!');
return false;
}
table.reload('postgresql-backup-list');
layer.msg('备份成功!', {icon: 1});
}
});
}
});
// 行工具事件
table.on('tool(postgresql-backup-list)', function (obj) {
let data = obj.data;
if (obj.event === 'del') {
layer.confirm('确定要删除数据库备份 <b style="color: red;">' + data.name + '</b> 吗?', function (index) {
index = layer.msg('正在删除数据库备份,请稍等...', {
icon: 16
, time: 0
});
admin.req({
url: "/api/plugins/postgresql16/deleteBackup"
, type: 'post'
, data: data
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
layer.msg('数据库备份删除失败,请刷新重试!')
return false;
}
obj.del();
layer.alert('数据库备份' + data.name + '删除成功!');
}
});
});
} else if (obj.event === 'restore') {
layer.confirm('高风险操作,确定要恢复数据库备份 <b style="color: red;">' + data.name + '</b> 吗?', function (index) {
index = layer.msg('正在恢复数据库备份,可能需要较长时间,请勿操作...', {
icon: 16
, time: 0
});
data.database = params.data.name;
admin.req({
url: "/api/plugins/postgresql16/restoreBackup"
, type: 'post'
, data: data
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
layer.msg('数据库备份恢复失败,请刷新重试!')
return false;
}
layer.alert('数据库备份' + data.name + '恢复成功!');
}
});
});
}
});
});
};
</script>
+302
View File
@@ -0,0 +1,302 @@
<!--
Name: 系统工具箱
-->
<title>系统工具箱</title>
<div class="layui-fluid" id="component-tabs">
<div class="layui-row">
<div class="layui-col-md12">
<div class="layui-card">
<div class="layui-card-header">系统工具箱</div>
<div class="layui-card-body">
<div class="layui-tab">
<ul class="layui-tab-title">
<li class="layui-this">DNS</li>
<li>SWAP</li>
<li>Hosts</li>
<li>时区</li>
<li>Root密码</li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
<div class="layui-form" lay-filter="toolbox-dns">
<div class="layui-form-item">
<label class="layui-form-label" style="font-size: 13px;">DNS 1</label>
<div class="layui-input-inline">
<input type="text" name="dns1" value="获取中ing..."
class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">修改 DNS 1</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" style="font-size: 13px;">DNS 2</label>
<div class="layui-input-inline">
<input type="text" name="dns2" value="获取中ing..."
class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">修改 DNS 2</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-sm" lay-submit
lay-filter="toolbox-dns-submit">确认修改
</button>
</div>
</div>
</div>
</div>
<div class="layui-tab-item">
<blockquote class="layui-elem-quote layui-quote-nm">
总共:
<span class="layui-badge layui-bg-blue"
id="toolbox-swap-total">获取中...</span>
</blockquote>
<blockquote class="layui-elem-quote layui-quote-nm">
已用:
<span class="layui-badge" id="toolbox-swap-used">获取中...</span>
</blockquote>
<blockquote class="layui-elem-quote layui-quote-nm">
可用:
<span class="layui-badge layui-bg-green"
id="toolbox-swap-free">获取中...</span>
</blockquote>
<div class="layui-form" lay-filter="toolbox-swap">
<div class="layui-form-item">
<label class="layui-form-label" style="font-size: 13px;">SWAP 大小</label>
<div class="layui-input-inline">
<input type="text" name="swap" value="获取中ing..."
class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">MB</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-sm" lay-submit
lay-filter="toolbox-swap-submit">确认修改
</button>
</div>
</div>
</div>
</div>
<div class="layui-tab-item">
<blockquote class="layui-elem-quote">此处修改的是 /etc/hosts 文件。<br>
提示:Ctrl+F 搜索关键字,Ctrl+S 保存,Ctrl+H 查找替换!
</blockquote>
<div id="toolbox-hosts-editor"
style="height: 600px;"></div>
<div class="layui-btn-container" style="padding-top: 30px;">
<button id="toolbox-hosts-save" class="layui-btn">保存</button>
</div>
</div>
<div class="layui-tab-item">
<div class="layui-form" lay-filter="toolbox-timezone">
<div class="layui-form-item">
<label class="layui-form-label" style="font-size: 13px;">时区</label>
<div class="layui-input-inline">
<script type="text/html" template lay-url="/api/plugins/toolbox/timezone"
lay-done="layui.data.done(d);">
<select name="timezone">
{{# layui.each(d.data.zones, function(index, item){ }}
{{# if(item == d.data.zone){ }}
<option value="{{ item }}" selected="">{{ item }}</option>
{{# }else{ }}
<option value="{{ item }}">{{ item }}</option>
{{# } }}
{{# }); }}
</select>
</script>
</div>
<div class="layui-form-mid layui-word-aux">选择一个时区</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-sm" lay-submit
lay-filter="toolbox-timezone-submit">确认修改
</button>
</div>
</div>
</div>
</div>
<div class="layui-tab-item">
<div class="layui-form" lay-filter="toolbox-root-password">
<div class="layui-form-item">
<label class="layui-form-label" style="font-size: 13px;">root 密码</label>
<div class="layui-input-inline">
<input type="text" name="password" value=""
class="layui-input" lay-verify="required">
</div>
<div class="layui-form-mid layui-word-aux">修改 root 密码</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-sm" lay-submit
lay-filter="toolbox-root-password-submit">确认修改
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
let toolbox_hosts_editor;
layui.use(['jquery', 'admin', 'view', 'form'], function () {
let $ = layui.$
, admin = layui.admin
, form = layui.form;
form.render();
admin.req({
url: "/api/plugins/toolbox/dns"
, type: 'get'
, success: function (result) {
if (result.code !== 0) {
return false;
}
if (result.data) {
form.val('toolbox-dns', {
"dns1": result.data[0]
, "dns2": result.data[1]
});
}
}
});
form.on('submit(toolbox-dns-submit)', function (data) {
index = layer.msg('正在修改DNS...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/toolbox/dns"
, type: 'post'
, data: data.field
, success: function (result) {
layer.close(index)
if (result.code !== 0) {
return false;
}
admin.events.refresh();
layer.alert('DNS修改成功!');
}
});
return false;
});
admin.req({
url: "/api/plugins/toolbox/swap"
, type: 'get'
, success: function (result) {
if (result.code !== 0) {
return false;
}
if (result.data) {
$('#toolbox-swap-total').html(result.data.total);
$('#toolbox-swap-used').html(result.data.used);
$('#toolbox-swap-free').html(result.data.free);
form.val('toolbox-swap', {
"swap": result.data.size
});
}
}
});
form.on('submit(toolbox-swap-submit)', function (data) {
index = layer.msg('正在修改SWAP...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/toolbox/swap"
, type: 'post'
, data: {
size: data.field.swap
}
, success: function (result) {
layer.close(index)
if (result.code !== 0) {
return false;
}
admin.events.refresh();
layer.alert('SWAP修改成功!');
}
});
return false;
});
admin.req({
url: "/api/plugins/toolbox/hosts"
, type: 'get'
, success: function (result) {
if (result.code !== 0) {
return false;
}
$('#toolbox-hosts-editor').text(result.data);
toolbox_hosts_editor = ace.edit("toolbox-hosts-editor", {
mode: "ace/mode/ini",
selectionStyle: "text"
});
}
});
$('#toolbox-hosts-save').click(function () {
index = layer.msg('正在保存Hosts...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/toolbox/hosts"
, type: 'post'
, data: {
hosts: toolbox_hosts_editor.getValue()
}
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
layer.alert('Hosts保存成功!');
}
});
});
form.on('submit(toolbox-root-password-submit)', function (data) {
index = layer.msg('正在修改root密码...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/toolbox/rootPassword"
, type: 'post'
, data: data.field
, success: function (result) {
layer.close(index)
if (result.code !== 0) {
return false;
}
admin.events.refresh();
layer.alert('root密码修改成功!');
}
});
return false;
});
});
layui.data.done = function (d) {
layui.use(['form', 'admin'], function () {
let form = layui.form,
admin = layui.admin;
form.render(null, 'toolbox-timezone');
form.on('submit(toolbox-timezone-submit)', function (data) {
index = layer.msg('正在修改时区...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/toolbox/timezone"
, type: 'post'
, data: data.field
, success: function (result) {
layer.close(index)
if (result.code !== 0) {
return false;
}
admin.events.refresh();
layer.alert('时区修改成功!');
}
});
return false;
});
});
};
</script>
+5
View File
@@ -69,6 +69,11 @@ Date: 2023-07-22
$('#plugin-install-log').html('实时安装日志获取失败,请刷新重试!');
return false;
}
if (result.data.includes('完成') || result.data.includes('错误')) {
setTimeout(function () {
location.reload();
}, 2000);
}
$('#plugin-install-log').html(result.data);
}
})
+4 -3
View File
@@ -1,7 +1,7 @@
<!--
Name: 网站 - 添加
Author: 耗子
Date: 2023-06-24
Date: 2023-069-29
-->
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
<form class="layui-form" action="" lay-filter="add-website-form">
@@ -94,7 +94,7 @@ Date: 2023-06-24
</script>
<script>
layui.data.sendParams = function (params) {
layui.use(['admin', 'form', 'laydate'], function () {
layui.use(['jquery', 'admin', 'layer', 'table', 'form'], function () {
var $ = layui.$
, admin = layui.admin
, layer = layui.layer
@@ -131,13 +131,14 @@ Date: 2023-06-24
// 提交
form.on('submit(add-website-submit)', function (data) {
data.field.db = data.field.db_type !== "false";
index = layer.msg('正在提交...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: '/api/panel/website/add'
, type: 'post'
, data: data.field
, success: function (result) {
layer.close(index)
if (result.code !== 0) {
console.log('耗子Linux面板:新网站添加失败,接口返回' + result)
layer.msg('新网站添加失败!')
return false
}
+26 -6
View File
@@ -201,7 +201,27 @@ Date: 2023-07-24
<div class="layui-form-item layui-form-text">
{{# if(d.params.config.ssl){ }}
<label class="layui-form-label">证书 <span style="color: red; float: right;">剩余有效期{{ d.params.config.ssl_date }}</span></label>
<blockquote class="layui-elem-quote layui-quote-nm">
有效期:
<span class="layui-badge">{{ d.params.config.ssl_not_before }} ~ {{ d.params.config.ssl_not_after }}</span>
</blockquote>
<blockquote class="layui-elem-quote layui-quote-nm">
域名:
{{# layui.each(d.params.config.ssl_dns_names, function(index, item){ }}
<span class="layui-badge layui-bg-blue">{{ item }}</span>
{{# }); }}
</blockquote>
<blockquote class="layui-elem-quote layui-quote-nm">
签发者:
<span class="layui-badge layui-bg-orange">{{ d.params.config.ssl_issuer }}</span>
</blockquote>
<blockquote class="layui-elem-quote layui-quote-nm">
OCSP:
{{# layui.each(d.params.config.ssl_ocsp_server, function(index, item){ }}
<span class="layui-badge layui-bg-blue">{{ item }}</span>
{{# }); }}
</blockquote>
<label class="layui-form-label">证书</label>
{{# }else{ }}
<label class="layui-form-label">证书</label>
{{# } }}
@@ -311,11 +331,11 @@ Date: 2023-07-24
layer.load()
let ports = $("input[name='ports[]']").map(function () {
return $(this).val();
}).get()
}).get().join('\n')
let domains = $("input[name='domains[]']").map(function () {
return $(this).val();
}).get()
}).get().join('\n')
var reg = new RegExp(/\n443.*\n?/);
// 如果开启了https,就自动添加443端口
if ($('input[name="ssl"]').prop('checked') && !reg.test(ports)) {
@@ -323,7 +343,7 @@ Date: 2023-07-24
}
// 如果关闭了https,就自动删除443端口
if (!$('input[name="ssl"]').prop('checked') && reg.test(ports)) {
ports = ports.replace(/443.*\n?/, '');
ports = ports.replace(/\n443.*\n?/, '');
}
admin.req({
@@ -331,8 +351,8 @@ Date: 2023-07-24
, type: 'post'
, data: {
id: params.data.id,
domains: domains.join('\n'),
ports: ports.join('\n'),
domains: domains,
ports: ports,
ssl: $('input[name="ssl"]').prop('checked'),
http_redirect: $('input[name="http_redirect"]').prop('checked'),
hsts: $('input[name="hsts"]').prop('checked'),
+9 -3
View File
@@ -7,10 +7,16 @@
"🤖 Dependencies"
],
"commitMessagePrefix": "chore(deps): ",
"customEnvVariables": {
"GOPROXY": "https://goproxy.cn,direct"
},
"postUpdateOptions": [
"gomodTidy"
],
"packageRules": [
{
"groupName": "all dependencies",
"groupSlug": "all",
"matchPackagePatterns": [
"*"
]
}
]
}
View File
+14 -14
View File
@@ -9,10 +9,10 @@ import (
"panel/app/http/middleware"
)
func Web() {
func Api() {
facades.Route().StaticFile("favicon.ico", "/www/panel/public/favicon.ico")
facades.Route().Prefix("api/panel").Group(func(r route.Route) {
r.Prefix("info").Group(func(r route.Route) {
facades.Route().Prefix("api/panel").Group(func(r route.Router) {
r.Prefix("info").Group(func(r route.Router) {
infoController := controllers.NewInfoController()
r.Get("name", infoController.Name)
r.Middleware(middleware.Jwt()).Get("menu", infoController.Menu)
@@ -24,19 +24,19 @@ func Web() {
r.Middleware(middleware.Jwt()).Post("update", infoController.Update)
r.Middleware(middleware.Jwt()).Post("restart", infoController.Restart)
})
r.Prefix("user").Group(func(r route.Route) {
r.Prefix("user").Group(func(r route.Router) {
userController := controllers.NewUserController()
r.Post("login", userController.Login)
r.Middleware(middleware.Jwt()).Get("info", userController.Info)
})
r.Prefix("task").Middleware(middleware.Jwt()).Group(func(r route.Route) {
r.Prefix("task").Middleware(middleware.Jwt()).Group(func(r route.Router) {
taskController := controllers.NewTaskController()
r.Get("status", taskController.Status)
r.Get("list", taskController.List)
r.Get("log", taskController.Log)
r.Post("delete", taskController.Delete)
})
r.Prefix("website").Middleware(middleware.Jwt()).Group(func(r route.Route) {
r.Prefix("website").Middleware(middleware.Jwt()).Group(func(r route.Router) {
websiteController := controllers.NewWebsiteController()
r.Get("list", websiteController.List)
r.Post("add", websiteController.Add)
@@ -55,7 +55,7 @@ func Web() {
r.Post("resetConfig", websiteController.ResetConfig)
r.Post("status", websiteController.Status)
})
r.Prefix("plugin").Middleware(middleware.Jwt()).Group(func(r route.Route) {
r.Prefix("plugin").Middleware(middleware.Jwt()).Group(func(r route.Router) {
pluginController := controllers.NewPluginController()
r.Get("list", pluginController.List)
r.Post("install", pluginController.Install)
@@ -63,7 +63,7 @@ func Web() {
r.Post("update", pluginController.Update)
r.Post("updateShow", pluginController.UpdateShow)
})
r.Prefix("cron").Middleware(middleware.Jwt()).Group(func(r route.Route) {
r.Prefix("cron").Middleware(middleware.Jwt()).Group(func(r route.Router) {
cronController := controllers.NewCronController()
r.Get("list", cronController.List)
r.Get("script", cronController.Script)
@@ -73,7 +73,7 @@ func Web() {
r.Post("status", cronController.Status)
r.Get("log", cronController.Log)
})
r.Prefix("safe").Middleware(middleware.Jwt()).Group(func(r route.Route) {
r.Prefix("safe").Middleware(middleware.Jwt()).Group(func(r route.Router) {
safeController := controllers.NewSafeController()
r.Get("firewallStatus", safeController.GetFirewallStatus)
r.Post("firewallStatus", safeController.SetFirewallStatus)
@@ -87,7 +87,7 @@ func Web() {
r.Get("pingStatus", safeController.GetPingStatus)
r.Post("pingStatus", safeController.SetPingStatus)
})
r.Prefix("monitor").Middleware(middleware.Jwt()).Group(func(r route.Route) {
r.Prefix("monitor").Middleware(middleware.Jwt()).Group(func(r route.Router) {
monitorController := controllers.NewMonitorController()
r.Post("switch", monitorController.Switch)
r.Post("saveDays", monitorController.SaveDays)
@@ -95,20 +95,20 @@ func Web() {
r.Get("list", monitorController.List)
r.Get("switchAndDays", monitorController.SwitchAndDays)
})
r.Prefix("ssh").Middleware(middleware.Jwt()).Group(func(r route.Route) {
r.Prefix("ssh").Middleware(middleware.Jwt()).Group(func(r route.Router) {
sshController := controllers.NewSshController()
r.Get("info", sshController.GetInfo)
r.Post("info", sshController.UpdateInfo)
r.Get("session", sshController.Session)
})
r.Prefix("setting").Middleware(middleware.Jwt()).Group(func(r route.Route) {
r.Prefix("setting").Middleware(middleware.Jwt()).Group(func(r route.Router) {
settingController := controllers.NewSettingController()
r.Get("list", settingController.List)
r.Post("save", settingController.Save)
})
})
facades.Route().Fallback(func(ctx http.Context) {
ctx.Response().String(404, "not found")
facades.Route().Fallback(func(ctx http.Context) http.Response {
return ctx.Response().String(404, "not found")
})
}
+55 -14
View File
@@ -14,16 +14,18 @@ import (
"panel/app/http/controllers/plugins/php82"
"panel/app/http/controllers/plugins/phpmyadmin"
"panel/app/http/controllers/plugins/postgresql15"
"panel/app/http/controllers/plugins/postgresql16"
"panel/app/http/controllers/plugins/pureftpd"
"panel/app/http/controllers/plugins/redis"
"panel/app/http/controllers/plugins/s3fs"
"panel/app/http/controllers/plugins/supervisor"
"panel/app/http/controllers/plugins/toolbox"
"panel/app/http/middleware"
)
// Plugin 加载插件路由
func Plugin() {
facades.Route().Prefix("api/plugins/openresty").Middleware(middleware.Jwt()).Group(func(route route.Route) {
facades.Route().Prefix("api/plugins/openresty").Middleware(middleware.Jwt()).Group(func(route route.Router) {
openRestyController := openresty.NewOpenrestyController()
route.Get("status", openRestyController.Status)
route.Post("reload", openRestyController.Reload)
@@ -36,7 +38,7 @@ func Plugin() {
route.Get("errorLog", openRestyController.ErrorLog)
route.Post("clearErrorLog", openRestyController.ClearErrorLog)
})
facades.Route().Prefix("api/plugins/mysql57").Middleware(middleware.Jwt()).Group(func(route route.Route) {
facades.Route().Prefix("api/plugins/mysql57").Middleware(middleware.Jwt()).Group(func(route route.Router) {
mysql57Controller := mysql57.NewMysql57Controller()
route.Get("status", mysql57Controller.Status)
route.Post("reload", mysql57Controller.Reload)
@@ -66,7 +68,7 @@ func Plugin() {
route.Post("userPassword", mysql57Controller.SetUserPassword)
route.Post("userPrivileges", mysql57Controller.SetUserPrivileges)
})
facades.Route().Prefix("api/plugins/mysql80").Middleware(middleware.Jwt()).Group(func(route route.Route) {
facades.Route().Prefix("api/plugins/mysql80").Middleware(middleware.Jwt()).Group(func(route route.Router) {
mysql80Controller := mysql80.NewMysql80Controller()
route.Get("status", mysql80Controller.Status)
route.Post("reload", mysql80Controller.Reload)
@@ -96,7 +98,7 @@ func Plugin() {
route.Post("userPassword", mysql80Controller.SetUserPassword)
route.Post("userPrivileges", mysql80Controller.SetUserPrivileges)
})
facades.Route().Prefix("api/plugins/postgresql15").Middleware(middleware.Jwt()).Group(func(route route.Route) {
facades.Route().Prefix("api/plugins/postgresql15").Middleware(middleware.Jwt()).Group(func(route route.Router) {
postgresql15Controller := postgresql15.NewPostgresql15Controller()
route.Get("status", postgresql15Controller.Status)
route.Post("reload", postgresql15Controller.Reload)
@@ -123,7 +125,34 @@ func Plugin() {
route.Post("deleteUser", postgresql15Controller.DeleteUser)
route.Post("userPassword", postgresql15Controller.SetUserPassword)
})
facades.Route().Prefix("api/plugins/php74").Middleware(middleware.Jwt()).Group(func(route route.Route) {
facades.Route().Prefix("api/plugins/postgresql16").Middleware(middleware.Jwt()).Group(func(route route.Router) {
postgresql16Controller := postgresql16.NewPostgresql16Controller()
route.Get("status", postgresql16Controller.Status)
route.Post("reload", postgresql16Controller.Reload)
route.Post("start", postgresql16Controller.Start)
route.Post("stop", postgresql16Controller.Stop)
route.Post("restart", postgresql16Controller.Restart)
route.Get("load", postgresql16Controller.Load)
route.Get("config", postgresql16Controller.GetConfig)
route.Post("config", postgresql16Controller.SaveConfig)
route.Get("userConfig", postgresql16Controller.GetUserConfig)
route.Post("userConfig", postgresql16Controller.SaveUserConfig)
route.Get("log", postgresql16Controller.Log)
route.Post("clearLog", postgresql16Controller.ClearLog)
route.Get("database", postgresql16Controller.DatabaseList)
route.Post("addDatabase", postgresql16Controller.AddDatabase)
route.Post("deleteDatabase", postgresql16Controller.DeleteDatabase)
route.Get("backup", postgresql16Controller.BackupList)
route.Post("createBackup", postgresql16Controller.CreateBackup)
route.Post("uploadBackup", postgresql16Controller.UploadBackup)
route.Post("deleteBackup", postgresql16Controller.DeleteBackup)
route.Post("restoreBackup", postgresql16Controller.RestoreBackup)
route.Get("user", postgresql16Controller.UserList)
route.Post("addUser", postgresql16Controller.AddUser)
route.Post("deleteUser", postgresql16Controller.DeleteUser)
route.Post("userPassword", postgresql16Controller.SetUserPassword)
})
facades.Route().Prefix("api/plugins/php74").Middleware(middleware.Jwt()).Group(func(route route.Router) {
php74Controller := php74.NewPhp74Controller()
route.Get("status", php74Controller.Status)
route.Post("reload", php74Controller.Reload)
@@ -141,7 +170,7 @@ func Plugin() {
route.Post("installExtension", php74Controller.InstallExtension)
route.Post("uninstallExtension", php74Controller.UninstallExtension)
})
facades.Route().Prefix("api/plugins/php80").Middleware(middleware.Jwt()).Group(func(route route.Route) {
facades.Route().Prefix("api/plugins/php80").Middleware(middleware.Jwt()).Group(func(route route.Router) {
php80Controller := php80.NewPhp80Controller()
route.Get("status", php80Controller.Status)
route.Post("reload", php80Controller.Reload)
@@ -159,7 +188,7 @@ func Plugin() {
route.Post("installExtension", php80Controller.InstallExtension)
route.Post("uninstallExtension", php80Controller.UninstallExtension)
})
facades.Route().Prefix("api/plugins/php81").Middleware(middleware.Jwt()).Group(func(route route.Route) {
facades.Route().Prefix("api/plugins/php81").Middleware(middleware.Jwt()).Group(func(route route.Router) {
php81Controller := php81.NewPhp81Controller()
route.Get("status", php81Controller.Status)
route.Post("reload", php81Controller.Reload)
@@ -177,7 +206,7 @@ func Plugin() {
route.Post("installExtension", php81Controller.InstallExtension)
route.Post("uninstallExtension", php81Controller.UninstallExtension)
})
facades.Route().Prefix("api/plugins/php82").Middleware(middleware.Jwt()).Group(func(route route.Route) {
facades.Route().Prefix("api/plugins/php82").Middleware(middleware.Jwt()).Group(func(route route.Router) {
php82Controller := php82.NewPhp82Controller()
route.Get("status", php82Controller.Status)
route.Post("reload", php82Controller.Reload)
@@ -195,12 +224,12 @@ func Plugin() {
route.Post("installExtension", php82Controller.InstallExtension)
route.Post("uninstallExtension", php82Controller.UninstallExtension)
})
facades.Route().Prefix("api/plugins/phpmyadmin").Middleware(middleware.Jwt()).Group(func(route route.Route) {
facades.Route().Prefix("api/plugins/phpmyadmin").Middleware(middleware.Jwt()).Group(func(route route.Router) {
phpMyAdminController := phpmyadmin.NewPhpMyAdminController()
route.Get("info", phpMyAdminController.Info)
route.Post("port", phpMyAdminController.SetPort)
})
facades.Route().Prefix("api/plugins/pureftpd").Middleware(middleware.Jwt()).Group(func(route route.Route) {
facades.Route().Prefix("api/plugins/pureftpd").Middleware(middleware.Jwt()).Group(func(route route.Router) {
pureFtpdController := pureftpd.NewPureFtpdController()
route.Get("status", pureFtpdController.Status)
route.Post("reload", pureFtpdController.Reload)
@@ -214,7 +243,7 @@ func Plugin() {
route.Get("port", pureFtpdController.GetPort)
route.Post("port", pureFtpdController.SetPort)
})
facades.Route().Prefix("api/plugins/redis").Middleware(middleware.Jwt()).Group(func(route route.Route) {
facades.Route().Prefix("api/plugins/redis").Middleware(middleware.Jwt()).Group(func(route route.Router) {
redisController := redis.NewRedisController()
route.Get("status", redisController.Status)
route.Post("reload", redisController.Reload)
@@ -225,13 +254,13 @@ func Plugin() {
route.Get("config", redisController.GetConfig)
route.Post("config", redisController.SaveConfig)
})
facades.Route().Prefix("api/plugins/s3fs").Middleware(middleware.Jwt()).Group(func(route route.Route) {
facades.Route().Prefix("api/plugins/s3fs").Middleware(middleware.Jwt()).Group(func(route route.Router) {
s3fsController := s3fs.NewS3fsController()
route.Get("list", s3fsController.List)
route.Post("add", s3fsController.Add)
route.Post("delete", s3fsController.Delete)
})
facades.Route().Prefix("api/plugins/supervisor").Middleware(middleware.Jwt()).Group(func(route route.Route) {
facades.Route().Prefix("api/plugins/supervisor").Middleware(middleware.Jwt()).Group(func(route route.Router) {
supervisorController := supervisor.NewSupervisorController()
route.Get("status", supervisorController.Status)
route.Post("start", supervisorController.Start)
@@ -254,7 +283,7 @@ func Plugin() {
route.Post("addProcess", supervisorController.AddProcess)
})
facades.Route().Prefix("api/plugins/fail2ban").Middleware(middleware.Jwt()).Group(func(route route.Route) {
facades.Route().Prefix("api/plugins/fail2ban").Middleware(middleware.Jwt()).Group(func(route route.Router) {
fail2banController := fail2ban.NewFail2banController()
route.Get("status", fail2banController.Status)
route.Post("start", fail2banController.Start)
@@ -269,4 +298,16 @@ func Plugin() {
route.Post("whiteList", fail2banController.SetWhiteList)
route.Get("whiteList", fail2banController.GetWhiteList)
})
facades.Route().Prefix("api/plugins/toolbox").Middleware(middleware.Jwt()).Group(func(route route.Router) {
toolboxController := toolbox.NewToolBoxController()
route.Get("dns", toolboxController.GetDNS)
route.Post("dns", toolboxController.SetDNS)
route.Get("swap", toolboxController.GetSWAP)
route.Post("swap", toolboxController.SetSWAP)
route.Get("timezone", toolboxController.GetTimezone)
route.Post("timezone", toolboxController.SetTimezone)
route.Get("hosts", toolboxController.GetHosts)
route.Post("hosts", toolboxController.SetHosts)
route.Post("rootPassword", toolboxController.SetRootPassword)
})
}
+1 -22
View File
@@ -23,7 +23,7 @@ OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } ||
if [ "${OS}" == "centos" ]; then
dnf install -y fail2ban
elif [ "${OS}" == "debian" ]; then
apt install -y fail2ban
apt-get install -y fail2ban
else
echo -e $HR
echo "错误:不支持的操作系统"
@@ -59,18 +59,6 @@ bantime = 86400
action = %(action_mwl)s
logpath = /var/log/secure
# ssh-END
# pure-ftpd-START
[pure-ftpd]
enabled = true
filter = pure-ftpd
port = 21
maxretry = 5
findtime = 300
bantime = 86400
action = %(action_mwl)s
logpath = /var/log/messages
# pure-ftpd-END
EOF
# 替换端口
sshPort=$(cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}')
@@ -78,15 +66,6 @@ if [ "${sshPort}" == "" ]; then
sshPort="22"
fi
sed -i "s/port = 22/port = ${sshPort}/g" /etc/fail2ban/jail.local
if [ -f "/www/server/pure-ftpd/etc/pure-ftpd.conf" ]; then
ftpPort=$(cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}')
fi
if [ "${ftpPort}" == "" ]; then
ftpPort="21"
sed -i "s/port = 21/port = ${ftpPort}/g" /etc/fail2ban/jail.local
else
sed -i "s/port = 21/port = ${ftpPort}/g" /etc/fail2ban/jail.local
fi
# Debian 的特殊处理
if [ "${OS}" == "debian" ]; then
+1 -1
View File
@@ -28,7 +28,7 @@ systemctl disable fail2ban
if [ "${OS}" == "centos" ]; then
dnf remove -y fail2ban
elif [ "${OS}" == "debian" ]; then
apt install -y fail2ban
apt-get install -y fail2ban
else
echo -e $HR
echo "错误:不支持的操作系统"
+1 -1
View File
@@ -23,7 +23,7 @@ OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } ||
if [ "${OS}" == "centos" ]; then
dnf install -y fail2ban
elif [ "${OS}" == "debian" ]; then
apt install -y fail2ban
apt-get install -y fail2ban
else
echo -e $HR
echo "错误:不支持的操作系统"
+48 -24
View File
@@ -22,9 +22,9 @@ HR="+----------------------------------------------------"
download_Url=""
setup_Path="/www"
sshPort=$(cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}')
ipLocation=$(curl -s https://ip.ping0.cc/geo)
inChina=$(curl --retry 2 -m 10 -L https://www.cloudflare-cn.com/cdn-cgi/trace 2> /dev/null | grep -qx 'loc=CN' && echo "true" || echo "false")
Prepare_system() {
Prepare_System() {
if [ $(whoami) != "root" ]; then
echo -e $HR
echo "错误:请使用root用户运行安装命令。"
@@ -63,8 +63,11 @@ Prepare_system() {
useradd -s /sbin/nologin -g www www
fi
rm -rf /etc/localtime
ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
if [ ! -d ${setup_Path} ]; then
mkdir ${setup_Path}
fi
timedatectl set-timezone Asia/Shanghai
[ -s /etc/selinux/config ] && sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
setenforce 0 > /dev/null 2>&1
@@ -93,13 +96,13 @@ Prepare_system() {
fi
if [ "${OS}" == "centos" ]; then
if [[ ${ipLocation} =~ "中国" ]]; then
if ${inChina}; then
sed -e 's|^mirrorlist=|#mirrorlist=|g' \
-e 's|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.aliyun.com/rockylinux|g' \
-e 's|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.cloud.tencent.com/rocky|g' \
-i.bak \
/etc/yum.repos.d/[Rr]ocky*.repo
sed -e 's|^mirrorlist=|#mirrorlist=|g' \
-e 's|^# baseurl=https://repo.almalinux.org|baseurl=https://mirrors.aliyun.com|g' \
-e 's|^# baseurl=https://repo.almalinux.org|baseurl=https://mirrors.cloud.tencent.com|g' \
-i.bak \
/etc/yum.repos.d/[Aa]lmalinux*.repo
@@ -108,8 +111,8 @@ Prepare_system() {
dnf install dnf-plugins-core -y
dnf install epel-release -y
dnf config-manager --set-enabled epel
if [[ ${ipLocation} =~ "中国" ]]; then
sed -i 's|^#baseurl=https://download.example/pub|baseurl=https://mirrors.aliyun.com|' /etc/yum.repos.d/epel*
if ${inChina}; then
sed -i 's|^#baseurl=https://download.example/pub|baseurl=https://mirrors.cloud.tencent.com|' /etc/yum.repos.d/epel*
sed -i 's|^metalink|#metalink|' /etc/yum.repos.d/epel*
dnf makecache -y
fi
@@ -122,12 +125,12 @@ Prepare_system() {
dnf makecache -y
dnf install -y curl wget zip unzip tar git jq git-core dos2unix
elif [ "${OS}" == "debian" ]; then
if [[ ${ipLocation} =~ "中国" ]]; 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 update -y
apt install -y curl wget zip unzip tar git jq git dos2unix
apt-get update -y
apt-get install -y curl wget zip unzip tar git jq git dos2unix rsyslog
else
echo -e $HR
echo "错误:该系统不支持安装耗子Linux面板,请更换Debian12/RHEL9安装。"
@@ -148,17 +151,25 @@ Auto_Swap() {
return
fi
if [ ! -d ${setup_Path} ]; then
mkdir ${setup_Path}
fi
# 设置swap
swapFile="${setup_Path}/swap"
dd if=/dev/zero of=$swapFile bs=1M count=4096
btrfsCheck=$(df -T /www | awk '{print $2}' | tail -n 1)
if [ "${btrfsCheck}" == "btrfs" ]; then
btrfs filesystem mkswapfile --size 4G --uuid clear ${swapFile}
else
dd if=/dev/zero of=$swapFile bs=1M count=4096
fi
chmod 600 $swapFile
mkswap -f $swapFile
swapon $swapFile
echo "$swapFile swap swap defaults 0 0" >> /etc/fstab
mount -a
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:检测到系统的 /etc/fstab 文件配置有误,请检查排除后重试,问题解决前勿重启系统。"
exit 1
fi
}
Init_Panel() {
@@ -183,9 +194,19 @@ Init_Panel() {
echo "错误:获取面板下载链接失败,请截图错误信息寻求帮助。"
exit 1
fi
wget -O ${setup_Path}/panel/panel.zip "${download_Url}${panelZip}"
wget -T 120 -t 3 -O ${setup_Path}/panel/panel.zip "${download_Url}${panelZip}"
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:下载面板失败,请截图错误信息寻求帮助。"
exit 1
fi
cd ${setup_Path}/panel
unzip -o panel.zip
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:解压面板失败,请截图错误信息寻求帮助。"
exit 1
fi
rm -rf panel.zip
cp panel-example.conf panel.conf
${setup_Path}/panel/panel --env="panel.conf" artisan key:generate
@@ -207,7 +228,7 @@ Init_Panel() {
firewall-cmd --permanent --zone=public --add-port=${sshPort}/tcp > /dev/null 2>&1
firewall-cmd --reload
elif [ "${OS}" == "debian" ]; then
apt install ufw -y
apt-get install ufw -y
echo y | ufw enable
ufw allow 22/tcp
ufw allow 80/tcp
@@ -243,6 +264,11 @@ EOF
systemctl daemon-reload
systemctl enable panel.service
systemctl start panel.service
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:面板启动失败,请截图错误信息寻求帮助。"
exit 1
fi
clear
echo -e $LOGO
@@ -268,17 +294,15 @@ if [ "$proxy" == 'y' ]; then
download_Url="https://ghproxy.com/"
fi
clear
echo -e $LOGO
echo '安装面板依赖软件(如报错请检查 APT/Yum 源是否正常)'
echo -e $HR
sleep 3s
Prepare_system
sleep 2s
Prepare_System
Auto_Swap
clear
echo -e $LOGO
echo '安装面板运行环境(视网络情况可能需要较长时间)'
echo -e $HR
sleep 3s
sleep 2s
Init_Panel
+82 -8
View File
@@ -29,9 +29,9 @@ mysqlPassword=$(cat /dev/urandom | head -n 16 | md5sum | head -c 16)
cpuCore=$(cat /proc/cpuinfo | grep "processor" | wc -l)
if [[ "${1}" == "80" ]]; then
mysqlVersion="8.0.33"
mysqlVersion="8.0.34"
elif [[ "${1}" == "57" ]]; then
mysqlVersion="5.7.42"
mysqlVersion="5.7.43"
else
echo -e $HR
echo "错误:不支持的 MySQL 版本!"
@@ -49,9 +49,10 @@ 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 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 update
apt install build-essential cmake bison libncurses5-dev libtirpc-dev libssl-dev pkg-config libldap2-dev libudev-dev libsasl2-dev patchelf -y
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
else
echo -e $HR
echo "错误:耗子Linux面板不支持该系统"
@@ -70,13 +71,13 @@ mkdir -p ${mysqlPath}
cd ${mysqlPath}
# 下载源码
wget -T 120 -O ${mysqlPath}/mysql-${mysqlVersion}.tar.gz ${downloadUrl}/mysql-boost-${mysqlVersion}.tar.gz
wget -T 120 -t 3 -O ${mysqlPath}/mysql-${mysqlVersion}.tar.gz ${downloadUrl}/mysql-boost-${mysqlVersion}.tar.gz
tar -zxvf mysql-${mysqlVersion}.tar.gz
rm -f mysql-${mysqlVersion}.tar.gz
mv mysql-${mysqlVersion} src
# openssl
wget -T 120 -O ${mysqlPath}/openssl-1.1.1u.tar.gz ${downloadUrl}/openssl/openssl-1.1.1u.tar.gz
wget -T 120 -t 3 -O ${mysqlPath}/openssl-1.1.1u.tar.gz ${downloadUrl}/openssl/openssl-1.1.1u.tar.gz
tar -zxvf openssl-1.1.1u.tar.gz
rm -f openssl-1.1.1u.tar.gz
mv openssl-1.1.1u openssl
@@ -276,6 +277,7 @@ elif [[ ${memTotal} -ge 32768 ]]; then
fi
# 初始化
cd ${mysqlPath}
rm -rf ${mysqlPath}/src
rm -rf ${mysqlPath}/data
mkdir -p ${mysqlPath}/data
@@ -285,10 +287,82 @@ chmod 644 ${mysqlPath}/conf/my.cnf
${mysqlPath}/bin/mysqld --initialize-insecure --user=mysql --basedir=${mysqlPath} --datadir=${mysqlPath}/data
echo "export PATH=${mysqlPath}/bin:\$PATH" >> /etc/profile
echo "export PATH=${mysqlPath}/bin:\$PATH" >> /etc/profile.d/mysql.sh
source /etc/profile
# 启动
# 检查 systemd 文件是否存在
if [ -f "${mysqlPath}/lib/systemd/system/mysqld.service" ]; then
mkdir -p ${mysqlPath}/lib/systemd/system
cat > ${mysqlPath}/lib/systemd/system/mysqld.service << EOF
# Copyright (c) 2015, 2023, Oracle and/or its affiliates.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2.0,
# as published by the Free Software Foundation.
#
# This program is also distributed with certain software (including
# but not limited to OpenSSL) that is licensed under separate terms,
# as designated in a particular file or component or in included license
# documentation. The authors of MySQL hereby grant you an additional
# permission to link the program and your derivative works with the
# separately licensed software that they have included with MySQL.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License, version 2.0, for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# systemd service file for MySQL forking server
#
[Unit]
Description=MySQL Server
Documentation=man:mysqld(8)
Documentation=http://dev.mysql.com/doc/refman/en/using-systemd.html
After=network.target
After=syslog.target
[Install]
WantedBy=multi-user.target
[Service]
User=mysql
Group=mysql
Type=forking
PIDFile=/www/server/mysql/mysqld.pid
# Disable service start and stop timeout logic of systemd for mysqld service.
TimeoutSec=0
# Execute pre and post scripts as root
PermissionsStartOnly=true
# Needed to create system tables
ExecStartPre=/www/server/mysql/bin/mysqld_pre_systemd
# Start main service
ExecStart=/www/server/mysql/bin/mysqld --daemonize --pid-file=/www/server/mysql/mysqld.pid $MYSQLD_OPTS
# Use this to switch malloc implementation
EnvironmentFile=-/etc/sysconfig/mysql
# Sets open_files_limit
LimitNOFILE = 5000
Restart=on-failure
RestartPreventExitStatus=1
PrivateTmp=false
EOF
fi
cp ${mysqlPath}/lib/systemd/system/mysqld.service /etc/systemd/system/mysqld.service
sed -i '/ExecStartPre/d' /etc/systemd/system/mysqld.service
+1 -1
View File
@@ -33,7 +33,7 @@ rm -f /usr/lib64/libmysql*
userdel -r mysql
groupdel mysql
sed -i '/export PATH=\/www\/server\/mysql/d' /etc/profile
rm -f /etc/profile.d/mysql.sh
source /etc/profile
panel deletePlugin mysql${1}
+135
View File
@@ -0,0 +1,135 @@
#!/bin/bash
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
: '
Copyright 2022 HaoZi Technology Co., Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
'
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://dl.cdn.haozi.net/panel/mysql"
setupPath="/www"
mysqlPath="${setupPath}/server/mysql"
mysqlVersion=""
mysqlPassword=$(cat /dev/urandom | head -n 16 | md5sum | head -c 16)
cpuCore=$(cat /proc/cpuinfo | grep "processor" | wc -l)
if [[ "${1}" == "80" ]]; then
mysqlVersion="8.0.34"
elif [[ "${1}" == "57" ]]; then
mysqlVersion="5.7.43"
else
echo -e $HR
echo "错误:不支持的 MySQL 版本!"
exit 1
fi
if [[ "${memTotal}" -lt "4096" ]] && [[ "${1}" == "80" ]]; then
echo -e $HR
echo "错误:这点内存(${memTotal}M)还想装 MySQL 8.0?洗洗睡吧!"
exit 1
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 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
else
echo -e $HR
echo "错误:耗子Linux面板不支持该系统"
exit 1
fi
mysqlUserCheck=$(cat /etc/passwd | grep mysql)
if [ "${mysqlUserCheck}" == "" ]; then
groupadd mysql
useradd -s /sbin/nologin -g mysql mysql
fi
# 准备目录
cd ${mysqlPath}
# 下载源码
wget -T 120 -t 3 -O ${mysqlPath}/mysql-${mysqlVersion}.tar.gz ${downloadUrl}/mysql-boost-${mysqlVersion}.tar.gz
tar -zxvf mysql-${mysqlVersion}.tar.gz
rm -f mysql-${mysqlVersion}.tar.gz
mv mysql-${mysqlVersion} src
# openssl
wget -T 120 -t 3 -O ${mysqlPath}/openssl-1.1.1u.tar.gz ${downloadUrl}/openssl/openssl-1.1.1u.tar.gz
tar -zxvf openssl-1.1.1u.tar.gz
rm -f openssl-1.1.1u.tar.gz
mv openssl-1.1.1u openssl
cd openssl
./config --prefix=/usr/local/openssl-1.1 --openssldir=/usr/local/openssl-1.1
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
# 编译
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
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:MySQL 编译初始化失败,请截图错误信息寻求帮助。"
exit 1
fi
if [[ "${cpuCore}" -gt "1" ]]; then
make -j2
else
make
fi
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:MySQL 编译失败,请截图错误信息寻求帮助。"
exit 1
fi
# 停止已有服务
systemctl stop mysqld
# 安装
make install
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:MySQL 安装失败,请截图错误信息寻求帮助。"
exit 1
fi
# 设置权限
chown -R mysql:mysql ${mysqlPath}
chmod -R 755 ${mysqlPath}
chmod 644 ${mysqlPath}/conf/my.cnf
# 启动服务
systemctl daemon-reload
systemctl enable mysqld
panel writePlugin mysql${1} ${mysqlVersion}
echo -e "${HR}\nMySQL-${1} 升级完成\n${HR}"
+15 -22
View File
@@ -25,22 +25,15 @@ setupPath="/www"
openrestyPath="${setupPath}/server/openresty"
openrestyVersion="1.21.4.2"
cpuCore=$(cat /proc/cpuinfo | grep "processor" | wc -l)
ipLocation=$(curl -s https://ip.ping0.cc/geo)
# 安装依赖
if [ "${OS}" == "centos" ]; then
# EPEL 9 仓库中目前没有 GeoIP-devel 包,暂时使用 Remi 仓库
dnf install https://rpms.remirepo.net/enterprise/remi-release-9.rpm -y
if [[ ${ipLocation} =~ "中国" ]]; then
sed -e 's!^#mirrorlist=!mirrorlist=!g' -e 's!^mirrorlist=!#mirrorlist=!g' -e 's!^#baseurl=!baseurl=!g' -e 's!http://rpms.remirepo.net/enterprise!https://mirrors.tuna.tsinghua.edu.cn/remi/enterprise!g' -i /etc/yum.repos.d/remi*
fi
dnf makecache -y
dnf groupinstall "Development Tools" -y
dnf install tar unzip gd gd-devel git-core flex perl oniguruma oniguruma-devel libsodium-devel libxml2-devel libxslt-devel GeoIP-devel bison yajl yajl-devel curl curl-devel ncurses-devel libevent-devel readline-devel libuuid-devel brotli-devel icu libicu libicu-devel openssl openssl-devel -y
dnf install tar unzip gd gd-devel git-core flex perl oniguruma oniguruma-devel libsodium-devel libxml2-devel libxslt-devel bison yajl yajl-devel curl curl-devel ncurses-devel libevent-devel readline-devel libuuid-devel brotli-devel icu libicu libicu-devel openssl openssl-devel -y
elif [ "${OS}" == "debian" ]; then
apt update
apt install build-essential tar unzip libgd3 libgd-dev git flex perl libonig-dev libsodium-dev libxml2-dev libxslt1-dev libgeoip-dev bison libyajl-dev curl libcurl4-openssl-dev libncurses5-dev libevent-dev libreadline-dev uuid-dev libbrotli-dev icu-devtools libicu-dev openssl libssl-dev -y
apt-get update
apt-get install build-essential tar unzip libgd3 libgd-dev git flex perl libonig-dev libsodium-dev libxml2-dev libxslt1-dev bison libyajl-dev curl libcurl4-openssl-dev libncurses5-dev libevent-dev libreadline-dev uuid-dev libbrotli-dev icu-devtools libicu-dev openssl libssl-dev -y
else
echo -e $HR
echo "错误:耗子Linux面板不支持该系统"
@@ -53,56 +46,56 @@ mkdir -p ${openrestyPath}
cd ${openrestyPath}
# 下载源码
wget -T 120 -O ${openrestyPath}/openresty-${openrestyVersion}.tar.gz ${downloadUrl}/openresty-${openrestyVersion}.tar.gz
wget -T 120 -t 3 -O ${openrestyPath}/openresty-${openrestyVersion}.tar.gz ${downloadUrl}/openresty-${openrestyVersion}.tar.gz
tar -zxvf openresty-${openrestyVersion}.tar.gz
rm -f openresty-${openrestyVersion}.tar.gz
mv openresty-${openrestyVersion} src
cd src
# openssl
wget -T 120 -O openssl.tar.gz ${downloadUrl}/openssl/openssl-1.1.1u.tar.gz
wget -T 120 -t 3 -O openssl.tar.gz ${downloadUrl}/openssl/openssl-1.1.1u.tar.gz
tar -zxvf openssl.tar.gz
rm -f openssl.tar.gz
mv openssl-1.1.1u openssl
rm -f openssl.tar.gz
# pcre
wget -T 60 -O pcre-8.45.tar.gz ${downloadUrl}/pcre/pcre-8.45.tar.gz
wget -T 60 -t 3 -O pcre-8.45.tar.gz ${downloadUrl}/pcre/pcre-8.45.tar.gz
tar -zxvf pcre-8.45.tar.gz
rm -f pcre-8.45.tar.gz
mv pcre-8.45 pcre
rm -f pcre-8.45.tar.gz
# ngx_cache_purge
wget -T 20 -O ngx_cache_purge.tar.gz ${downloadUrl}/modules/ngx_cache_purge-2.3.tar.gz
wget -T 20 -t 3 -O ngx_cache_purge.tar.gz ${downloadUrl}/modules/ngx_cache_purge-2.3.tar.gz
tar -zxvf ngx_cache_purge.tar.gz
rm -f ngx_cache_purge.tar.gz
mv ngx_cache_purge-2.3 ngx_cache_purge
rm -f ngx_cache_purge.tar.gz
# nginx-sticky-module
wget -T 20 -O nginx-sticky-module.zip ${downloadUrl}/modules/nginx-sticky-module.zip
wget -T 20 -t 3 -O nginx-sticky-module.zip ${downloadUrl}/modules/nginx-sticky-module.zip
unzip -o nginx-sticky-module.zip
rm -f nginx-sticky-module.zip
# nginx-dav-ext-module
wget -T 20 -O nginx-dav-ext-module-3.0.0.tar.gz ${downloadUrl}/modules/nginx-dav-ext-module-3.0.0.tar.gz
wget -T 20 -t 3 -O nginx-dav-ext-module-3.0.0.tar.gz ${downloadUrl}/modules/nginx-dav-ext-module-3.0.0.tar.gz
tar -xvf nginx-dav-ext-module-3.0.0.tar.gz
rm -f nginx-dav-ext-module-3.0.0.tar.gz
mv nginx-dav-ext-module-3.0.0 nginx-dav-ext-module
# waf
wget -T 60 -O uthash.zip ${downloadUrl}/modules/uthash-2.3.0.zip
wget -T 60 -t 3 -O uthash.zip ${downloadUrl}/modules/uthash-2.3.0.zip
unzip -o uthash.zip
mv uthash-2.3.0 uthash
rm -f uthash.zip
cd ../
wget -T 20 -O ngx_waf.zip ${downloadUrl}/modules/ngx_waf-6.1.9.zip
wget -T 20 -t 3 -O ngx_waf.zip ${downloadUrl}/modules/ngx_waf-6.1.9.zip
unzip -o ngx_waf.zip
mv ngx_waf-6.1.9 ngx_waf
rm -f ngx_waf.zip
cd ngx_waf/inc
wget -T 60 -O libinjection.zip ${downloadUrl}/modules/libinjection-3.10.0.zip
wget -T 60 -t 3 -O libinjection.zip ${downloadUrl}/modules/libinjection-3.10.0.zip
unzip -o libinjection.zip
mv libinjection-3.10.0 libinjection
rm -f libinjection.zip
@@ -117,13 +110,13 @@ fi
cd ${openrestyPath}/src
# brotli
wget -T 20 -O ngx_brotli.zip ${downloadUrl}/modules/ngx_brotli-1.0.0rc.zip
wget -T 20 -t 3 -O ngx_brotli.zip ${downloadUrl}/modules/ngx_brotli-1.0.0rc.zip
unzip -o ngx_brotli.zip
mv ngx_brotli-1.0.0rc ngx_brotli
rm -f ngx_brotli.zip
cd ngx_brotli/deps
rm -rf brotli
wget -T 20 -O brotli.zip ${downloadUrl}/modules/brotli-1.0.9.zip
wget -T 20 -t 3 -O brotli.zip ${downloadUrl}/modules/brotli-1.0.9.zip
unzip -o brotli.zip
mv brotli-1.0.9 brotli
rm -f brotli.zip
@@ -133,7 +126,7 @@ cd ${openrestyPath}/src
export LD_LIBRARY_PATH=/usr/local/lib/:$LD_LIBRARY_PATH
export LIB_UTHASH=${openrestyPath}/src/uthash
./configure --user=www --group=www --prefix=${openrestyPath} --with-luajit --add-module=${openrestyPath}/src/ngx_cache_purge --add-module=${openrestyPath}/src/nginx-sticky-module --with-openssl=${openrestyPath}/src/openssl --with-pcre=${openrestyPath}/src/pcre --with-http_v2_module --with-http_slice_module --with-threads --with-stream --with-stream_ssl_module --with-stream_realip_module --with-stream_geoip_module --with-stream_ssl_preread_module --with-http_stub_status_module --with-http_ssl_module --with-http_image_filter_module --with-http_gzip_static_module --with-http_gunzip_module --with-ipv6 --with-http_geoip_module --with-http_sub_module --with-http_flv_module --with-http_addition_module --with-http_realip_module --with-http_mp4_module --with-ld-opt="-Wl,-E" --with-cc-opt="-std=gnu99" --with-http_dav_module --add-module=${openrestyPath}/src/nginx-dav-ext-module --add-module=${openrestyPath}/src/ngx_brotli --add-module=${openrestyPath}/ngx_waf
./configure --user=www --group=www --prefix=${openrestyPath} --with-luajit --add-module=${openrestyPath}/src/ngx_cache_purge --add-module=${openrestyPath}/src/nginx-sticky-module --with-openssl=${openrestyPath}/src/openssl --with-pcre=${openrestyPath}/src/pcre --with-http_v2_module --with-http_slice_module --with-threads --with-stream --with-stream_ssl_module --with-stream_realip_module --with-stream_ssl_preread_module --with-http_stub_status_module --with-http_ssl_module --with-http_image_filter_module --with-http_gzip_static_module --with-http_gunzip_module --with-ipv6 --with-http_sub_module --with-http_flv_module --with-http_addition_module --with-http_realip_module --with-http_mp4_module --with-ld-opt="-Wl,-E" --with-cc-opt="-std=gnu99" --with-http_dav_module --add-module=${openrestyPath}/src/nginx-dav-ext-module --add-module=${openrestyPath}/src/ngx_brotli --add-module=${openrestyPath}/ngx_waf
if [[ "${cpuCore}" -gt "1" ]]; then
make -j2
else
+18 -14
View File
@@ -40,10 +40,10 @@ if [ "${OS}" == "centos" ]; then
/usr/bin/crb enable
dnf makecache
dnf groupinstall "Development Tools" -y
dnf install autoconf glibc-headers gdbm-devel gd gd-devel perl oniguruma-devel libsodium-devel libxml2-devel sqlite-devel libzip-devel bzip2-devel xz-devel libpng-devel libjpeg-devel libwebp-devel freetype-devel gmp-devel openssl-devel readline-devel libxslt-devel libcurl-devel pkgconfig libedit-devel zlib-devel pcre-devel crontabs libicu libicu-devel c-ares -y
dnf install autoconf glibc-headers gdbm-devel gd gd-devel perl oniguruma-devel libsodium-devel libxml2-devel sqlite-devel libzip-devel bzip2-devel xz-devel libpng-devel libjpeg-devel libwebp-devel libavif-devel freetype-devel gmp-devel openssl-devel readline-devel libxslt-devel libcurl-devel pkgconfig libedit-devel zlib-devel pcre-devel crontabs libicu libicu-devel c-ares -y
elif [ "${OS}" == "debian" ]; then
apt update
apt install build-essential autoconf libc6-dev libgdbm-dev libgd-tools libgd-dev perl libonig-dev libsodium-dev libxml2-dev libsqlite3-dev libzip-dev libbz2-dev liblzma-dev libpng-dev libjpeg-dev libwebp-dev libfreetype6-dev libgmp-dev libssl-dev libreadline-dev libxslt1-dev libcurl4-openssl-dev pkg-config libedit-dev zlib1g-dev libpcre3-dev cron libicu-dev libc-ares2 libc-ares-dev -y
apt-get update
apt-get install build-essential autoconf libc6-dev libgdbm-dev libgd-tools libgd-dev perl libonig-dev libsodium-dev libxml2-dev libsqlite3-dev libzip-dev libbz2-dev liblzma-dev libpng-dev libjpeg-dev libwebp-dev libavif-dev libfreetype6-dev libgmp-dev libssl-dev libreadline-dev libxslt1-dev libcurl4-openssl-dev pkg-config libedit-dev zlib1g-dev libpcre3-dev cron libicu-dev libc-ares2 libc-ares-dev -y
else
echo -e $HR
echo "错误:耗子Linux面板不支持该系统"
@@ -59,18 +59,18 @@ cd ${phpPath}
if [ "${phpVersion}" == "74" ]; then
phpVersionCode="7.4.33"
elif [ "${phpVersion}" == "80" ]; then
phpVersionCode="8.0.29"
phpVersionCode="8.0.30"
elif [ "${phpVersion}" == "81" ]; then
phpVersionCode="8.1.21"
phpVersionCode="8.1.23"
elif [ "${phpVersion}" == "82" ]; then
phpVersionCode="8.2.8"
phpVersionCode="8.2.10"
else
echo -e $HR
echo "错误:PHP-${phpVersion}不支持,请检查版本号是否正确。"
exit 1
fi
wget -O ${phpPath}/php-${phpVersion}.tar.gz ${downloadUrl}/php-${phpVersionCode}.tar.gz
wget -T 120 -t 3 -O ${phpPath}/php-${phpVersion}.tar.gz ${downloadUrl}/php-${phpVersionCode}.tar.gz
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:PHP-${phpVersion}下载失败,请检查网络是否正常。"
@@ -82,7 +82,7 @@ rm -f php-${phpVersion}.tar.gz
mv php-* src
if [ "${phpVersion}" -le "80" ]; then
wget -T 120 -O ${phpPath}/openssl-1.1.1u.tar.gz ${downloadUrl}/openssl/openssl-1.1.1u.tar.gz
wget -T 120 -t 3 -O ${phpPath}/openssl-1.1.1u.tar.gz ${downloadUrl}/openssl/openssl-1.1.1u.tar.gz
tar -zxvf openssl-1.1.1u.tar.gz
rm -f openssl-1.1.1u.tar.gz
mv openssl-1.1.1u openssl
@@ -101,7 +101,11 @@ fi
# 配置
cd src
./configure --prefix=${phpPath} --with-config-file-path=${phpPath}/etc --enable-fpm --with-fpm-user=www --with-fpm-group=www --enable-mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-freetype --with-jpeg --with-zlib --with-libxml-dir=/usr --enable-xml --disable-rpath --enable-bcmath --enable-shmop --enable-sysvsem --enable-inline-optimization --with-curl --enable-mbregex --enable-mbstring --enable-intl --enable-pcntl --enable-ftp --enable-gd --with-openssl --with-mhash --enable-pcntl --enable-sockets --with-xmlrpc --enable-soap --with-gettext --enable-fileinfo --enable-opcache --with-sodium --with-webp
if [ "${phpVersion}" == "81" ] || [ "${phpVersion}" == "82" ]; then
./configure --prefix=${phpPath} --with-config-file-path=${phpPath}/etc --enable-fpm --with-fpm-user=www --with-fpm-group=www --enable-mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-freetype --with-jpeg --with-zlib --with-libxml-dir=/usr --enable-xml --disable-rpath --enable-bcmath --enable-shmop --enable-sysvsem --enable-inline-optimization --with-curl --enable-mbregex --enable-mbstring --enable-intl --enable-pcntl --enable-ftp --enable-gd --with-openssl --with-mhash --enable-pcntl --enable-sockets --with-xmlrpc --enable-soap --with-gettext --enable-fileinfo --enable-opcache --with-sodium --with-webp --with-avif
else
./configure --prefix=${phpPath} --with-config-file-path=${phpPath}/etc --enable-fpm --with-fpm-user=www --with-fpm-group=www --enable-mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-freetype --with-jpeg --with-zlib --with-libxml-dir=/usr --enable-xml --disable-rpath --enable-bcmath --enable-shmop --enable-sysvsem --enable-inline-optimization --with-curl --enable-mbregex --enable-mbstring --enable-intl --enable-pcntl --enable-ftp --enable-gd --with-openssl --with-mhash --enable-pcntl --enable-sockets --with-xmlrpc --enable-soap --with-gettext --enable-fileinfo --enable-opcache --with-sodium --with-webp
fi
# 编译安装
if [[ "${cpuCore}" -gt "1" ]]; then
@@ -135,10 +139,10 @@ fi
cd ../../
# 写入拓展标记位
echo ";下方标记位禁止删除,否则将导致PHP拓展无法正常安装!" >>${phpPath}/etc/php.ini
echo ";haozi" >>${phpPath}/etc/php.ini
echo ";下方标记位禁止删除,否则将导致PHP拓展无法正常安装!" >> ${phpPath}/etc/php.ini
echo ";haozi" >> ${phpPath}/etc/php.ini
# 写入zip拓展到php配置
echo "extension=zip" >>${phpPath}/etc/php.ini
echo "extension=zip" >> ${phpPath}/etc/php.ini
# 设置软链接
rm -f /usr/bin/php-${phpVersion}
@@ -152,7 +156,7 @@ ln -sf ${phpPath}/bin/pecl /usr/bin/pecl
ln -sf ${phpPath}/sbin/php-fpm /usr/bin/php-fpm-${phpVersion}
# 设置fpm
cat >${phpPath}/etc/php-fpm.conf <<EOF
cat > ${phpPath}/etc/php-fpm.conf << EOF
[global]
pid = ${phpPath}/var/run/php-fpm.pid
error_log = ${phpPath}/var/log/php-fpm.log
@@ -230,7 +234,7 @@ sed -i 's/error_reporting =.*/error_reporting = E_ALL \& \~E_NOTICE/g' ${phpPath
sed -i 's/expose_php = On/expose_php = Off/g' ${phpPath}/etc/php.ini
# 写入openresty 调用php配置文件
cat >/www/server/openresty/conf/enable-php-${phpVersion}.conf <<EOF
cat > /www/server/openresty/conf/enable-php-${phpVersion}.conf << EOF
location ~ \.php$ {
try_files \$uri =404;
fastcgi_pass unix:/tmp/php-cgi-${phpVersion}.sock;
+2 -2
View File
@@ -38,7 +38,7 @@ Install() {
if [ "${OS}" == "centos" ]; then
dnf install ImageMagick ImageMagick-devel -y
elif [ "${OS}" == "debian" ]; then
apt install imagemagick libmagickwand-dev -y
apt-get install imagemagick libmagickwand-dev -y
else
echo -e $HR
echo "错误:耗子Linux面板不支持该系统"
@@ -48,7 +48,7 @@ Install() {
cd /www/server/php/${phpVersion}/src/ext
rm -rf imagick
rm -rf imagick.tar.gz
wget -O imagick.tar.gz ${downloadUrl}/imagick-${imagickVersion}.tar.gz
wget -T 60 -t 3 -O imagick.tar.gz ${downloadUrl}/imagick-${imagickVersion}.tar.gz
tar -zxvf imagick.tar.gz
mv imagick-${imagickVersion} imagick
cd imagick
+1 -1
View File
@@ -33,7 +33,7 @@ Install() {
fi
mkdir /usr/local/ioncube
wget -O /usr/local/ioncube/ioncube_loader_lin_${phpVersion}.so ${downloadUrl}/ioncube_loader_lin_${phpVersion}.so
wget -T 60 -t 3 -O /usr/local/ioncube/ioncube_loader_lin_${phpVersion}.so ${downloadUrl}/ioncube_loader_lin_${phpVersion}.so
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:ionCube 下载失败,请检查网络是否正常。"
+1 -1
View File
@@ -33,7 +33,7 @@ Install() {
cd /www/server/php/${phpVersion}/src/ext/pdo_pgsql
/www/server/php/${phpVersion}/bin/phpize
./configure --with-php-config=/www/server/php/${phpVersion}/bin/php-config --with-pdo-pgsql=/usr/pgsql-15
./configure --with-php-config=/www/server/php/${phpVersion}/bin/php-config --with-pdo-pgsql=/www/server/postgresql
make
if [ "$?" != "0" ]; then
echo -e $HR
+1 -1
View File
@@ -36,7 +36,7 @@ Install() {
cd /www/server/php/${phpVersion}/src/ext
rm -rf phpredis
rm -rf phpredis.tar.gz
wget -O phpredis.tar.gz ${downloadUrl}/phpredis-${phpredisVersion}.tar.gz
wget -T 60 -t 3 -O phpredis.tar.gz ${downloadUrl}/phpredis-${phpredisVersion}.tar.gz
tar -zxvf phpredis.tar.gz
mv phpredis-${phpredisVersion} phpredis
cd phpredis
+1 -1
View File
@@ -31,7 +31,7 @@ rm -rf ${phpmyadminPath}
mkdir -p ${phpmyadminPath}
cd ${phpmyadminPath}
wget -O phpmyadmin.zip ${downloadUrl}/phpMyAdmin-${phpmyadminVersion}-all-languages.zip
wget -T 60 -t 3 -O phpmyadmin.zip ${downloadUrl}/phpMyAdmin-${phpmyadminVersion}-all-languages.zip
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:phpMyAdmin 下载失败"
+14 -5
View File
@@ -25,11 +25,12 @@ downloadUrl="https://dl.cdn.haozi.net/panel/postgresql"
setupPath="/www"
postgresqlPath="${setupPath}/server/postgresql"
postgresqlVersion=""
postgresqlPassword=$(cat /dev/urandom | head -n 16 | md5sum | head -c 16)
cpuCore=$(cat /proc/cpuinfo | grep "processor" | wc -l)
if [[ "${1}" == "15" ]]; then
postgresqlVersion="15.3"
postgresqlVersion="15.4"
elif [[ "${1}" == "16" ]]; then
postgresqlVersion="16.0"
else
echo -e $HR
echo "错误:不支持的 PostgreSQL 版本!"
@@ -42,8 +43,8 @@ if [ "${OS}" == "centos" ]; then
dnf groupinstall "Development Tools" -y
dnf install make gettext zlib-devel readline-devel libicu-devel libxml2-devel libxslt-devel openssl-devel systemd-devel -y
elif [ "${OS}" == "debian" ]; then
apt update
apt install build-essential make gettext zlib1g-dev libreadline-dev libicu-dev libxml2-dev libxslt-dev libssl-dev libsystemd-dev -y
apt-get update
apt-get install build-essential make gettext zlib1g-dev libreadline-dev libicu-dev libxml2-dev libxslt-dev libssl-dev libsystemd-dev -y
else
echo -e $HR
echo "错误:耗子Linux面板不支持该系统"
@@ -62,7 +63,7 @@ mkdir -p ${postgresqlPath}
cd ${postgresqlPath}
# 下载源码
wget -T 120 -O ${postgresqlPath}/postgresql-${postgresqlVersion}.tar.gz ${downloadUrl}/postgresql-${postgresqlVersion}.tar.gz
wget -T 120 -t 3 -O ${postgresqlPath}/postgresql-${postgresqlVersion}.tar.gz ${downloadUrl}/postgresql-${postgresqlVersion}.tar.gz
tar -zxvf postgresql-${postgresqlVersion}.tar.gz
rm -f postgresql-${postgresqlVersion}.tar.gz
mv postgresql-${postgresqlVersion} src
@@ -91,6 +92,7 @@ if [ "$?" != "0" ]; then
exit 1
fi
cd ${postgresqlPath}
rm -rf ${postgresqlPath}/src
# 配置
@@ -161,6 +163,13 @@ TimeoutSec=infinity
WantedBy=multi-user.target
EOF
# 在 /etc/systemd/logind.conf 设置 RemoveIPC=no,不然会删除 /dev/shm 下的共享内存文件
checkRemoveIPC=$(cat /etc/systemd/logind.conf | grep '^RemoveIPC=no.*$')
if [ "${checkRemoveIPC}" == "" ]; then
echo "RemoveIPC=no" >> /etc/systemd/logind.conf
systemctl restart systemd-logind
fi
# 启动服务
systemctl daemon-reload
systemctl enable postgresql
+100
View File
@@ -0,0 +1,100 @@
#!/bin/bash
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
: '
Copyright 2022 HaoZi Technology Co., Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
'
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://dl.cdn.haozi.net/panel/postgresql"
setupPath="/www"
postgresqlPath="${setupPath}/server/postgresql"
postgresqlVersion=""
cpuCore=$(cat /proc/cpuinfo | grep "processor" | wc -l)
if [[ "${1}" == "15" ]]; then
postgresqlVersion="15.4"
elif [[ "${1}" == "16" ]]; then
postgresqlVersion="16.0"
else
echo -e $HR
echo "错误:不支持的 PostgreSQL 版本!"
exit 1
fi
# 安装依赖
if [ "${OS}" == "centos" ]; then
dnf makecache -y
dnf groupinstall "Development Tools" -y
dnf install make gettext zlib-devel readline-devel libicu-devel libxml2-devel libxslt-devel openssl-devel systemd-devel -y
elif [ "${OS}" == "debian" ]; then
apt-get update
apt-get install build-essential make gettext zlib1g-dev libreadline-dev libicu-dev libxml2-dev libxslt-dev libssl-dev libsystemd-dev -y
else
echo -e $HR
echo "错误:耗子Linux面板不支持该系统"
exit 1
fi
# 停止已有服务
systemctl stop postgresql
# 准备目录
rm -rf ${postgresqlPath}/src
cd ${postgresqlPath}
# 下载源码
wget -T 120 -t 3 -O ${postgresqlPath}/postgresql-${postgresqlVersion}.tar.gz ${downloadUrl}/postgresql-${postgresqlVersion}.tar.gz
tar -zxvf postgresql-${postgresqlVersion}.tar.gz
rm -f postgresql-${postgresqlVersion}.tar.gz
mv postgresql-${postgresqlVersion} src
# 编译
cd src
./configure --prefix=${postgresqlPath} --enable-nls='zh_CN en' --with-icu --with-ssl=openssl --with-systemd --with-libxml --with-libxslt
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:PostgreSQL 编译初始化失败,请截图错误信息寻求帮助。"
exit 1
fi
make -j${cpuCore}
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:PostgreSQL 编译失败,请截图错误信息寻求帮助。"
exit 1
fi
make install
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:PostgreSQL 安装失败,请截图错误信息寻求帮助。"
exit 1
fi
cd ${postgresqlPath}
rm -rf ${postgresqlPath}/src
# 配置
chown -R postgres:postgres ${postgresqlPath}
chmod -R 700 ${postgresqlPath}
panel writePlugin postgresql${1} ${postgresqlVersion}
systemctl daemon-reload
systemctl restart postgresql
echo -e "${HR}\nPostgreSQL-${1} 升级完成\n${HR}"
+1 -1
View File
@@ -29,7 +29,7 @@ rm -rf ${pureftpdPath}
mkdir -p ${pureftpdPath}
cd ${pureftpdPath}
wget -O ${pureftpdPath}/pure-ftpd-${pureftpdVersion}.tar.gz ${downloadUrl}/pure-ftpd-${pureftpdVersion}.tar.gz
wget -T 120 -t 3 -O ${pureftpdPath}/pure-ftpd-${pureftpdVersion}.tar.gz ${downloadUrl}/pure-ftpd-${pureftpdVersion}.tar.gz
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:Pure-Ftpd-${pureftpdVersion}下载失败,请检查网络是否正常。"

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