mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 08:00:27 +00:00
Compare commits
345
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
001c896eed | ||
|
|
1cf3a0143c | ||
|
|
f04be521e5 | ||
|
|
2873632a40 | ||
|
|
075879b5b5 | ||
|
|
9b12930376 | ||
|
|
3339ba9bad | ||
|
|
68922676fc | ||
|
|
dcf2afb4a6 | ||
|
|
16a35141dd | ||
|
|
f6c334bdad | ||
|
|
cbb119117b | ||
|
|
cae0a6118f | ||
|
|
3c0dc7459c | ||
|
|
ca0c96cb12 | ||
|
|
ed599265e4 | ||
|
|
864bff574a | ||
|
|
3b1df05ae0 | ||
|
|
464392f7e1 | ||
|
|
b562d9f8e8 | ||
|
|
74bd997f07 | ||
|
|
12c6fe6331 | ||
|
|
eb4d550305 | ||
|
|
9a635e99b2 | ||
|
|
03b04eea6b | ||
|
|
d2740308a1 | ||
|
|
6f86dda1aa | ||
|
|
0194362a35 | ||
|
|
a5bf435d2c | ||
|
|
bcfbbb656a | ||
|
|
c0b6a06fb7 | ||
|
|
89628fbbcc | ||
|
|
e670e1f2e7 | ||
|
|
b3759cebd8 | ||
|
|
e427b2a219 | ||
|
|
b1ec7d9f04 | ||
|
|
0ccfeed10d | ||
|
|
afd4c8486a | ||
|
|
730a25fcd2 | ||
|
|
9fa191ad69 | ||
|
|
756dbe08eb | ||
|
|
5f2cfe6e20 | ||
|
|
af9aef4d34 | ||
|
|
16d26077be | ||
|
|
aef163df29 | ||
|
|
f449557281 | ||
|
|
d01e34aa40 | ||
|
|
02e7c2e974 | ||
|
|
047d49e0eb | ||
|
|
1c167e828a | ||
|
|
8d1322af37 | ||
|
|
577dfadb9a | ||
|
|
0c2653d270 | ||
|
|
0d3e834b37 | ||
|
|
2e8403347a | ||
|
|
0105da02af | ||
|
|
40d9dea96c | ||
|
|
f0fbb02495 | ||
|
|
9ca69732ca | ||
|
|
966b10fbb5 | ||
|
|
9e103baf94 | ||
|
|
9a38dcd2a7 | ||
|
|
08125b150a | ||
|
|
f370f7eacf | ||
|
|
a4accc071d | ||
|
|
fd0260f43a | ||
|
|
8301382770 | ||
|
|
0872374fe4 | ||
|
|
80f78548fd | ||
|
|
1e553fc14e | ||
|
|
5426c4ea68 | ||
|
|
a8842c806c | ||
|
|
8888c9f4ee | ||
|
|
9da5e67183 | ||
|
|
2a1ec4db75 | ||
|
|
ca803ab26a | ||
|
|
7f77a13b0c | ||
|
|
f131aae344 | ||
|
|
d71514885d | ||
|
|
433055b851 | ||
|
|
32e2e493cc | ||
|
|
b5b710f8b2 | ||
|
|
6fd9dc53e2 | ||
|
|
7f80d1455d | ||
|
|
b3605f54d6 | ||
|
|
98e73395fc | ||
|
|
65fb2bd401 | ||
|
|
ebcfcf87c8 | ||
|
|
be469333d1 | ||
|
|
9cc4ec49ef | ||
|
|
3adf1aebb8 | ||
|
|
4504a8b188 | ||
|
|
e3d87c542d | ||
|
|
a6367fd96b | ||
|
|
070484772b | ||
|
|
d00fb2163a | ||
|
|
7c7d30376f | ||
|
|
90886bda7e | ||
|
|
2e61af2c83 | ||
|
|
4482bc0b1d | ||
|
|
2fafe3dcb5 | ||
|
|
eded682ecc | ||
|
|
151f9d58d3 | ||
|
|
18719f9112 | ||
|
|
0886bcd310 | ||
|
|
8fedb04c95 | ||
|
|
15a43ef81b | ||
|
|
9121d57e7e | ||
|
|
f6cc0b272c | ||
|
|
6e191fc342 | ||
|
|
6da38ee166 | ||
|
|
064fddb2ee | ||
|
|
32e0c6fb3e | ||
|
|
419343364d | ||
|
|
97b790a092 | ||
|
|
a08ce58563 | ||
|
|
f2350d0a65 | ||
|
|
ee0aa532d7 | ||
|
|
9f3181b8f3 | ||
|
|
003c87bf00 | ||
|
|
1577581257 | ||
|
|
b6d916de6a | ||
|
|
534bbb0211 | ||
|
|
f3f08ce2d3 | ||
|
|
7f19532051 | ||
|
|
0bdeba7288 | ||
|
|
4460329d2d | ||
|
|
f59bea27e8 | ||
|
|
4c59090180 | ||
|
|
f17db1d6d5 | ||
|
|
39a5767319 | ||
|
|
7e182d32a6 | ||
|
|
e32104581b | ||
|
|
c30ea553bd | ||
|
|
345dbda400 | ||
|
|
f4103e285d | ||
|
|
dca47af92a | ||
|
|
af0ecce25b | ||
|
|
b36e2c5676 | ||
|
|
b428d818e7 | ||
|
|
25b0725570 | ||
|
|
2513c10d22 | ||
|
|
584234c4f1 | ||
|
|
8a1b0d6913 | ||
|
|
68ac4341ac | ||
|
|
4209452e0b | ||
|
|
3b7f8c2724 | ||
|
|
01588ed9b5 | ||
|
|
99fc39d401 | ||
|
|
b6f2497346 | ||
|
|
09dc15a940 | ||
|
|
9bd9700bac | ||
|
|
05e62a6f94 | ||
|
|
ff549a4793 | ||
|
|
0341587716 | ||
|
|
e2316369a7 | ||
|
|
4285fedc5d | ||
|
|
05c8370f0d | ||
|
|
b866816800 | ||
|
|
e0f8cd96f8 | ||
|
|
c81f8c1bc4 | ||
|
|
45d628838f | ||
|
|
f433895c97 | ||
|
|
1fcfbaf0a9 | ||
|
|
63305e7316 | ||
|
|
c394efa2ec | ||
|
|
859ace71df | ||
|
|
a7cf69e5a2 | ||
|
|
d0105c7863 | ||
|
|
e18102dfc9 | ||
|
|
135ac91160 | ||
|
|
f670095c08 | ||
|
|
ac384628c9 | ||
|
|
60df7786d8 | ||
|
|
0e74acbec0 | ||
|
|
37de6aae66 | ||
|
|
f03051ce43 | ||
|
|
d83402b819 | ||
|
|
97e800ee15 | ||
|
|
7f583e5678 | ||
|
|
80b7bb44f7 | ||
|
|
752e2ec17e | ||
|
|
0118fbb944 | ||
|
|
dba5302d68 | ||
|
|
546bc53f28 | ||
|
|
4883c9dd49 | ||
|
|
428743d600 | ||
|
|
33c4b8bba9 | ||
|
|
296ae1c2e1 | ||
|
|
7fb41f43cc | ||
|
|
0afc6242a2 | ||
|
|
62beecf337 | ||
|
|
b08c7ff89d | ||
|
|
daab425c78 | ||
|
|
8ec9f727bd | ||
|
|
8b781d466a | ||
|
|
0182586869 | ||
|
|
dab5434125 | ||
|
|
e638a416ff | ||
|
|
ceca7fade0 | ||
|
|
bdf8ba45ac | ||
|
|
e45f43da9a | ||
|
|
32fb7feb44 | ||
|
|
caee2159a8 | ||
|
|
6011f35f59 | ||
|
|
869d0c73bd | ||
|
|
9af00cc80c | ||
|
|
400dd79b9f | ||
|
|
8c929004e9 | ||
|
|
e044d92075 | ||
|
|
a02f160adf | ||
|
|
ac33f5461d | ||
|
|
bf7a4f3254 | ||
|
|
ea40e32991 | ||
|
|
d38d2c617d | ||
|
|
90bcf464d3 | ||
|
|
bd91c88357 | ||
|
|
60dbd75ce2 | ||
|
|
d5abd7ec3f | ||
|
|
c133f39b5e | ||
|
|
9df8f13fa7 | ||
|
|
e5bcb07cf6 | ||
|
|
c62232e648 | ||
|
|
a4a66e1e18 | ||
|
|
86d021cbfc | ||
|
|
9f3573bcb8 | ||
|
|
28f2dddcf6 | ||
|
|
3aa62f91bf | ||
|
|
fe00cb8015 | ||
|
|
312c19e3cc | ||
|
|
409e90d2c5 | ||
|
|
8fc708be97 | ||
|
|
2c7dcbfe17 | ||
|
|
c189c48d51 | ||
|
|
a08612e0a0 | ||
|
|
acda63d2f7 | ||
|
|
c39f029808 | ||
|
|
41bc8b634b | ||
|
|
b80b67b060 | ||
|
|
9c357f1ea4 | ||
|
|
99ed9be1dd | ||
|
|
3df8863904 | ||
|
|
f211b6b5ae | ||
|
|
b59c9ee9ad | ||
|
|
8798babe07 | ||
|
|
63c330054e | ||
|
|
f30b05a495 | ||
|
|
260fbb6e19 | ||
|
|
5cd59c7f39 | ||
|
|
ede664be18 | ||
|
|
0a7144f625 | ||
|
|
4dc722b83c | ||
|
|
6131a99db0 | ||
|
|
d9f9f9a629 | ||
|
|
5e951f028e | ||
|
|
65f92bf0c3 | ||
|
|
201600ea06 | ||
|
|
64ed07e4fa | ||
|
|
35c4f4edfd | ||
|
|
c31b3e9ddc | ||
|
|
2e47afef3b | ||
|
|
54e81b2de5 | ||
|
|
8870b92ceb | ||
|
|
cce60f4712 | ||
|
|
94cebfd8cc | ||
|
|
c56435970a | ||
|
|
188d9dd4c7 | ||
|
|
8b9d1d9052 | ||
|
|
5a8869e832 | ||
|
|
81835ef9a7 | ||
|
|
b3ce35d516 | ||
|
|
5979e8b5d4 | ||
|
|
79c2ba2125 | ||
|
|
95bbf4ee52 | ||
|
|
d43df6941b | ||
|
|
df53193f3f | ||
|
|
d5c75f4eee | ||
|
|
76356eaf5b | ||
|
|
5d9c09f69e | ||
|
|
e799d1cc49 | ||
|
|
1090df3703 | ||
|
|
f3267bf137 | ||
|
|
0a426f3cd8 | ||
|
|
c6e1bb5245 | ||
|
|
0fa027d01d | ||
|
|
395bc63946 | ||
|
|
7a6d0c5380 | ||
|
|
1e192c18e7 | ||
|
|
d7df881da9 | ||
|
|
a6f354fed9 | ||
|
|
be12c2397d | ||
|
|
bddb488baf | ||
|
|
75c23271df | ||
|
|
34eb20a679 | ||
|
|
b01d8949cb | ||
|
|
b489f5cf2f | ||
|
|
43ebf6eef9 | ||
|
|
736914f443 | ||
|
|
4243ccb5c2 | ||
|
|
539b600d6c | ||
|
|
e676a632b9 | ||
|
|
4f6a443b13 | ||
|
|
7684066689 | ||
|
|
9719ef2edc | ||
|
|
a5b6121411 | ||
|
|
e8fb727676 | ||
|
|
164a48c99f | ||
|
|
e47d449b1b | ||
|
|
5c46c26dd5 | ||
|
|
a40116d991 | ||
|
|
b0a3ecd3d2 | ||
|
|
29fd941d55 | ||
|
|
9851b31013 | ||
|
|
6e635c691d | ||
|
|
5532636e02 | ||
|
|
e47e2d63d1 | ||
|
|
b9dde4ebf0 | ||
|
|
6a30ccd5dc | ||
|
|
ad6a1ac465 | ||
|
|
35f311c452 | ||
|
|
c3dd6f2e0a | ||
|
|
94405d877f | ||
|
|
9cd37e8ec5 | ||
|
|
1381ef9e8e | ||
|
|
42dffe8078 | ||
|
|
92f019cf7c | ||
|
|
c23dfc374f | ||
|
|
61f7374d71 | ||
|
|
3fcb20d24c | ||
|
|
d4b561e42d | ||
|
|
b6b4bfe195 | ||
|
|
dcc0264b67 | ||
|
|
15b1f28fb9 | ||
|
|
4596f9c2c2 | ||
|
|
3156c84b19 | ||
|
|
ac400c7188 | ||
|
|
7fd672c1c9 | ||
|
|
884a169a6f | ||
|
|
e79a5f9ae2 | ||
|
|
25608e7378 | ||
|
|
dad8bac715 | ||
|
|
a4ee16cf1f | ||
|
|
7f7465b383 | ||
|
|
b7c3dc0d2a | ||
|
|
bb02991b05 |
@@ -1 +0,0 @@
|
||||
custom: ["https://afdian.net/a/1Panel"]
|
||||
@@ -13,11 +13,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '18.14'
|
||||
node-version: '20.2'
|
||||
- name: Build Web
|
||||
id: build_frontend
|
||||
run: |
|
||||
@@ -25,10 +25,10 @@ jobs:
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.20.x'
|
||||
go-version: '1.22'
|
||||
- name: Build Server
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
args: release --snapshot --clean
|
||||
@@ -11,9 +11,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.2'
|
||||
- name: Build Web
|
||||
@@ -22,15 +22,15 @@ jobs:
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21.x'
|
||||
go-version: '1.22'
|
||||
- name: Build Release
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: latest
|
||||
args: release --skip-publish --clean
|
||||
args: release --skip=publish --clean
|
||||
- name: Upload Assets
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
|
||||
+7
-2
@@ -1,11 +1,16 @@
|
||||
# Contributing
|
||||
|
||||
As a contributor, you should agree that:
|
||||
|
||||
- The producer can adjust the open-source agreement to be more strict or relaxed as deemed necessary.
|
||||
- Your contributed code may be used for commercial purposes, including but not limited to its cloud business operations.
|
||||
|
||||
## Create pull request
|
||||
PR are always welcome, even if they only contain small fixes like typos or a few lines of code. If there will be a significant effort, please document it as an issue and get a discussion going before starting to work on it.
|
||||
|
||||
Please submit a PR broken down into small changes bit by bit. A PR consisting of a lot features and code changes may be hard to review. It is recommended to submit PRs in an incremental fashion.
|
||||
Please submit a PR broken down into small changes bit by bit. A PR consisting of a lot of features and code changes may be hard to review. It is recommended to submit PRs in an incremental fashion.
|
||||
|
||||
This [development guideline](https://1panel.cn/docs/dev_manual/dev_manual/) contains information about repository structure, how to setup development environment, how to run it, and more.
|
||||
This [development guideline](https://1panel.cn/docs/dev_manual/dev_manual/) contains information about repository structure, how to set up development environment, how to run it, and more.
|
||||
|
||||
Note: If you split your pull request to small changes, please make sure any of the changes goes to master will not break anything. Otherwise, it can not be merged until this feature complete.
|
||||
|
||||
|
||||
@@ -1,38 +1,34 @@
|
||||
<p align="center"><a href="https://1panel.cn"><img src="http://1panel.oss-cn-hangzhou.aliyuncs.com/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
|
||||
<p align="center"><b>Web-based linux server management control panel</b></p>
|
||||
<p align="center"><b>现代化、开源的 Linux 服务器运维管理面板</b></p>
|
||||
<p align="center">
|
||||
<a href="https://trendshift.io/repositories/2462" target="_blank"><img src="https://trendshift.io/api/badge/repositories/2462" alt="1Panel-dev%2F1Panel | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
<a href="https://hellogithub.com/repository/71791baf930149ac9b84e1acf186573f" target="_blank"><img src="https://api.hellogithub.com/v1/widgets/recommend.svg?rid=71791baf930149ac9b84e1acf186573f&claim_uid=p8vB3kP5CMrthiL&theme=dark&theme=neutral" alt="Featured|HelloGitHub" style="width: 250px; height: 55px;" width="250" height="55" /></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF" alt="License: GPL v3"></a>
|
||||
<a href="https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard"><img src="https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef" alt="Codacy"></a>
|
||||
<a href="https://github.com/1Panel-dev/1Panel/releases"><img src="https://img.shields.io/github/v/release/1Panel-dev/1Panel" alt="GitHub release"></a>
|
||||
<a href="https://github.com/1Panel-dev/1Panel"><img src="https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square" alt="Stars"></a>
|
||||
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2F1Panel-dev%2F1Panel?ref=badge_shield"><img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2F1Panel-dev%2F1Panel.svg?type=shield" alt="FOSSA Status"></a><br>
|
||||
[<a href="docs/README_TW.md">中文(繁體)</a>] | [<a href="docs/README_EN.md">English</a>] | [<a href="docs/README_JP.md">日本語</a>]
|
||||
<a href="https://github.com/1Panel-dev/1Panel"><img src="https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square" alt="GitHub Stars"></a>
|
||||
<a href="https://gitee.com/fit2cloud-feizhiyun/1Panel"><img src="https://gitee.com/fit2cloud-feizhiyun/1Panel/badge/star.svg?theme=gvp" alt="Gitee Stars"></a><br>
|
||||
[<a href="docs/README_EN.md">English</a>] | [<a href="docs/README_TW.md">中文(繁體)</a>] | [<a href="docs/README_JP.md">日本語</a>]
|
||||
</p>
|
||||
|
||||
[](https://www.bilibili.com/video/BV1Mt421n7LZ/)
|
||||
|
||||
------------------------------
|
||||
## 什么是 1Panel?
|
||||
|
||||
1Panel 是新一代的 Linux 服务器运维管理面板。
|
||||
|
||||
- **高效管理**:用户可以通过 Web 图形界面轻松管理 Linux 服务器,实现主机监控、文件管理、数据库管理、容器管理等功能;
|
||||
- **高效管理**:用户可以通过 Web 界面轻松管理 Linux 服务器,如主机监控、文件管理、数据库管理、容器管理等;
|
||||
- **快速建站**:深度集成开源建站软件 WordPress 和 [Halo](https://github.com/halo-dev/halo/),域名绑定、SSL 证书配置等操作一键搞定;
|
||||
- **应用商店**:精选上架各类高质量的开源工具和应用软件,协助用户轻松安装并升级;
|
||||
- **安全可靠**:基于容器管理并部署应用,实现最小的漏洞暴露面,同时提供防火墙和日志审计等功能;
|
||||
- **安全可靠**:基于容器管理并部署应用,实现最小的漏洞暴露面,同时提供病毒防护、防火墙和日志审计等功能;
|
||||
- **一键备份**:支持一键备份和恢复,用户可以将数据备份到各类云端存储介质,永不丢失。
|
||||
|
||||
1Panel 三分钟速览:https://www.bilibili.com/video/BV1Mt421n7LZ/
|
||||
|
||||
## UI 展示
|
||||
|
||||

|
||||
|
||||
## 快速开始
|
||||
|
||||
**在线体验**
|
||||
|
||||
- 环境地址:<https://demo.1panel.cn/>
|
||||
- 用户名:demo
|
||||
- 密码:1panel
|
||||
|
||||
**一键安装**
|
||||
|
||||
执行如下命令一键安装 1Panel:
|
||||
@@ -41,33 +37,21 @@
|
||||
curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh
|
||||
```
|
||||
|
||||
如果是用于离线环境,推荐使用 [安装包方式](https://1panel.cn/docs/installation/package_installation/) 进行安装部署。
|
||||
|
||||
**学习资料**
|
||||
|
||||
- [在线文档](https://1panel.cn/docs/)
|
||||
- [教学视频](https://space.bilibili.com/510493147/channel/collectiondetail?sid=1199760)
|
||||
- [社区论坛](https://bbs.fit2cloud.com/c/1p/7)
|
||||
- [如何加入微信交流群?](https://bbs.fit2cloud.com/t/topic/2147)
|
||||
|
||||
## 安全说明
|
||||
## 飞致云的其他明星项目
|
||||
|
||||
如果您在使用过程中发现任何安全问题,请通过以下方式直接联系我们:
|
||||
|
||||
- 邮箱:wanghe@fit2cloud.com
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#1Panel-dev/1Panel&Date)
|
||||
|
||||
## 我们的其他明星项目
|
||||
- [MaxKB](https://github.com/1Panel-dev/MaxKB/) - 基于 LLM 大语言模型的开源知识库问答系统
|
||||
- [JumpServer](https://github.com/jumpserver/jumpserver/) - 广受欢迎的开源堡垒机
|
||||
- [DataEase](https://github.com/dataease/dataease/) - 人人可用的开源数据可视化分析工具
|
||||
- [MeterSphere](https://github.com/metersphere/metersphere/) - 一站式开源自动化测试平台
|
||||
- [Halo](https://github.com/halo-dev/halo/) - 强大易用的开源建站工具
|
||||
|
||||
|
||||
## FOSSA Status
|
||||
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2F1Panel-dev%2F1Panel?ref=badge_large)
|
||||
- [DataEase](https://github.com/dataease/dataease/) - 人人可用的开源数据可视化分析工具
|
||||
- [MeterSphere](https://github.com/metersphere/metersphere/) - 开源自动化测试平台
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ func (b *BaseApi) GetIgnoredApp(c *gin.Context) {
|
||||
// @Success 200 {object} model.AppInstall
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/install [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[{"input_column":"name","input_value":"name","isList":false,"db":"app_installs","output_column":"app_id","output_value":"appId"},{"info":"appId","isList":false,"db":"apps","output_column":"key","output_value":"appKey"}],"formatZH":"安装应用 [appKey]-[name]","formatEN":"Install app [appKey]-[name]"}
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"安装应用 [name]","formatEN":"Install app [name]"}
|
||||
func (b *BaseApi) InstallApp(c *gin.Context) {
|
||||
var req request.AppInstallCreate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
|
||||
@@ -205,14 +205,13 @@ func (b *BaseApi) GetServices(c *gin.Context) {
|
||||
// @Param appInstallId path integer true "request"
|
||||
// @Success 200 {array} dto.AppVersion
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/installed/:appInstallId/versions [get]
|
||||
// @Router /apps/installed/update/versions [post]
|
||||
func (b *BaseApi) GetUpdateVersions(c *gin.Context) {
|
||||
appInstallId, err := helper.GetIntParamByKey(c, "appInstallId")
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInternalServer, nil)
|
||||
var req request.AppUpdateVersion
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
versions, err := appInstallService.GetUpdateVersions(appInstallId)
|
||||
versions, err := appInstallService.GetUpdateVersions(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/middleware"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/captcha"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/qqwry"
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -118,7 +119,19 @@ func (b *BaseApi) CheckIsSafety(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, status)
|
||||
if status == "disable" && len(code) != 0 {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrNotFound, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if status == "unpass" {
|
||||
if middleware.LoadErrCode("err-entrance") != 200 {
|
||||
helper.ErrResponse(c, middleware.LoadErrCode("err-entrance"))
|
||||
return
|
||||
}
|
||||
helper.ErrorWithDetail(c, constant.CodeErrEntrance, constant.ErrTypeInternalServer, nil)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetResponsePage(c *gin.Context) {
|
||||
|
||||
@@ -118,11 +118,11 @@ func (b *BaseApi) LoadOneDriveInfo(c *gin.Context) {
|
||||
// @Summary Delete backup account
|
||||
// @Description 删除备份账号
|
||||
// @Accept json
|
||||
// @Param request body dto.BatchDeleteReq true "request"
|
||||
// @Param request body dto.OperateByID true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":true,"db":"backup_accounts","output_column":"type","output_value":"types"}],"formatZH":"删除备份账号 [types]","formatEN":"delete backup account [types]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"backup_accounts","output_column":"type","output_value":"types"}],"formatZH":"删除备份账号 [types]","formatEN":"delete backup account [types]"}
|
||||
func (b *BaseApi) DeleteBackup(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -323,7 +323,7 @@ func (b *BaseApi) Backup(c *gin.Context) {
|
||||
|
||||
switch req.Type {
|
||||
case "app":
|
||||
if err := backupService.AppBackup(req); err != nil {
|
||||
if _, err := backupService.AppBackup(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
@@ -343,7 +343,7 @@ func (b *BaseApi) Backup(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
case "redis":
|
||||
if err := backupService.RedisBackup(); err != nil {
|
||||
if err := backupService.RedisBackup(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags Clam
|
||||
// @Summary Create clam
|
||||
// @Description 创建扫描规则
|
||||
// @Accept json
|
||||
// @Param request body dto.ClamCreate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/clam [post]
|
||||
// @x-panel-log {"bodyKeys":["name","path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建扫描规则 [name][path]","formatEN":"create clam [name][path]"}
|
||||
func (b *BaseApi) CreateClam(c *gin.Context) {
|
||||
var req dto.ClamCreate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := clamService.Create(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Clam
|
||||
// @Summary Update clam
|
||||
// @Description 修改扫描规则
|
||||
// @Accept json
|
||||
// @Param request body dto.ClamUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/clam/update [post]
|
||||
// @x-panel-log {"bodyKeys":["name","path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改扫描规则 [name][path]","formatEN":"update clam [name][path]"}
|
||||
func (b *BaseApi) UpdateClam(c *gin.Context) {
|
||||
var req dto.ClamUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := clamService.Update(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Clam
|
||||
// @Summary Update clam status
|
||||
// @Description 修改扫描规则状态
|
||||
// @Accept json
|
||||
// @Param request body dto.ClamUpdateStatus true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/clam/status/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id","status"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"clams","output_column":"name","output_value":"name"}],"formatZH":"修改扫描规则 [name] 状态为 [status]","formatEN":"change the status of clam [name] to [status]."}
|
||||
func (b *BaseApi) UpdateClamStatus(c *gin.Context) {
|
||||
var req dto.ClamUpdateStatus
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := clamService.UpdateStatus(req.ID, req.Status); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Clam
|
||||
// @Summary Page clam
|
||||
// @Description 获取扫描规则列表分页
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchClamWithPage true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/clam/search [post]
|
||||
func (b *BaseApi) SearchClam(c *gin.Context) {
|
||||
var req dto.SearchClamWithPage
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
total, list, err := clamService.SearchWithPage(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Clam
|
||||
// @Summary Load clam base info
|
||||
// @Description 获取 Clam 基础信息
|
||||
// @Accept json
|
||||
// @Success 200 {object} dto.ClamBaseInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/clam/base [get]
|
||||
func (b *BaseApi) LoadClamBaseInfo(c *gin.Context) {
|
||||
info, err := clamService.LoadBaseInfo()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, info)
|
||||
}
|
||||
|
||||
// @Tags Clam
|
||||
// @Summary Operate Clam
|
||||
// @Description 修改 Clam 状态
|
||||
// @Accept json
|
||||
// @Param request body dto.Operate true "request"
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/clam/operate [post]
|
||||
// @x-panel-log {"bodyKeys":["operation"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operation] Clam","formatEN":"[operation] FTP"}
|
||||
func (b *BaseApi) OperateClam(c *gin.Context) {
|
||||
var req dto.Operate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := clamService.Operate(req.Operation); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Clam
|
||||
// @Summary Clean clam record
|
||||
// @Description 清空扫描报告
|
||||
// @Accept json
|
||||
// @Param request body dto.OperateByID true "request"
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/clam/record/clean [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":true,"db":"clams","output_column":"name","output_value":"name"}],"formatZH":"清空扫描报告 [name]","formatEN":"clean clam record [name]"}
|
||||
func (b *BaseApi) CleanClamRecord(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := clamService.CleanRecord(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Clam
|
||||
// @Summary Page clam record
|
||||
// @Description 获取扫描结果列表分页
|
||||
// @Accept json
|
||||
// @Param request body dto.ClamLogSearch true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/clam/record/search [post]
|
||||
func (b *BaseApi) SearchClamRecord(c *gin.Context) {
|
||||
var req dto.ClamLogSearch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
total, list, err := clamService.LoadRecords(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Clam
|
||||
// @Summary Load clam record detail
|
||||
// @Description 获取扫描结果详情
|
||||
// @Accept json
|
||||
// @Param request body dto.ClamLogReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/clam/record/log [post]
|
||||
func (b *BaseApi) LoadClamRecordLog(c *gin.Context) {
|
||||
var req dto.ClamLogReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
content, err := clamService.LoadRecordLog(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, content)
|
||||
}
|
||||
|
||||
// @Tags Clam
|
||||
// @Summary Load clam file
|
||||
// @Description 获取扫描文件
|
||||
// @Accept json
|
||||
// @Param request body dto.ClamFileReq true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/clam/file/search [post]
|
||||
func (b *BaseApi) SearchClamFile(c *gin.Context) {
|
||||
var req dto.ClamFileReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
content, err := clamService.LoadFile(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, content)
|
||||
}
|
||||
|
||||
// @Tags Clam
|
||||
// @Summary Update clam file
|
||||
// @Description 更新病毒扫描配置文件
|
||||
// @Accept json
|
||||
// @Param request body dto.UpdateByNameAndFile true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/clam/file/update [post]
|
||||
func (b *BaseApi) UpdateFile(c *gin.Context) {
|
||||
var req dto.UpdateByNameAndFile
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := clamService.UpdateFile(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Clam
|
||||
// @Summary Delete clam
|
||||
// @Description 删除扫描规则
|
||||
// @Accept json
|
||||
// @Param request body dto.ClamDelete true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/clam/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"clams","output_column":"name","output_value":"names"}],"formatZH":"删除扫描规则 [names]","formatEN":"delete clam [names]"}
|
||||
func (b *BaseApi) DeleteClam(c *gin.Context) {
|
||||
var req dto.ClamDelete
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := clamService.Delete(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Clam
|
||||
// @Summary Handle clam scan
|
||||
// @Description 执行病毒扫描
|
||||
// @Accept json
|
||||
// @Param request body dto.OperateByID true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/clam/handle [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":true,"db":"clams","output_column":"name","output_value":"name"}],"formatZH":"执行病毒扫描 [name]","formatEN":"handle clam scan [name]"}
|
||||
func (b *BaseApi) HandleClamScan(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := clamService.HandleOnce(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
@@ -29,6 +29,28 @@ func (b *BaseApi) CreateCommand(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Redis Command
|
||||
// @Summary Save redis command
|
||||
// @Description 保存 Redis 快速命令
|
||||
// @Accept json
|
||||
// @Param request body dto.RedisCommand true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/command/redis [post]
|
||||
// @x-panel-log {"bodyKeys":["name","command"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"保存 redis 快捷命令 [name][command]","formatEN":"save quick command for redis [name][command]"}
|
||||
func (b *BaseApi) SaveRedisCommand(c *gin.Context) {
|
||||
var req dto.RedisCommand
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := commandService.SaveRedisCommand(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Command
|
||||
// @Summary Page commands
|
||||
// @Description 获取快速命令列表分页
|
||||
@@ -55,6 +77,32 @@ func (b *BaseApi) SearchCommand(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Redis Command
|
||||
// @Summary Page redis commands
|
||||
// @Description 获取 redis 快速命令列表分页
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchWithPage true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/command/redis/search [post]
|
||||
func (b *BaseApi) SearchRedisCommand(c *gin.Context) {
|
||||
var req dto.SearchWithPage
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
total, list, err := commandService.SearchRedisCommandWithPage(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Command
|
||||
// @Summary Tree commands
|
||||
// @Description 获取快速命令树
|
||||
@@ -72,6 +120,22 @@ func (b *BaseApi) SearchCommandTree(c *gin.Context) {
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Redis Command
|
||||
// @Summary List redis commands
|
||||
// @Description 获取 redis 快速命令列表
|
||||
// @Success 200 {Array} dto.RedisCommand
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/command/redis [get]
|
||||
func (b *BaseApi) ListRedisCommand(c *gin.Context) {
|
||||
list, err := commandService.ListRedisCommand()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Command
|
||||
// @Summary List commands
|
||||
// @Description 获取快速命令列表
|
||||
@@ -110,6 +174,28 @@ func (b *BaseApi) DeleteCommand(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Redis Command
|
||||
// @Summary Delete redis command
|
||||
// @Description 删除 redis 快速命令
|
||||
// @Accept json
|
||||
// @Param request body dto.BatchDeleteReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/command/redis/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"redis_commands","output_column":"name","output_value":"names"}],"formatZH":"删除 redis 快捷命令 [names]","formatEN":"delete quick command of redis [names]"}
|
||||
func (b *BaseApi) DeleteRedisCommand(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := commandService.DeleteRedisCommand(req.Ids); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Command
|
||||
// @Summary Update command
|
||||
// @Description 更新快速命令
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pkg/errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// @Tags Container
|
||||
@@ -346,6 +347,26 @@ func (b *BaseApi) ContainerRename(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Commit Container
|
||||
// @Description 容器提交生成新镜像
|
||||
// @Accept json
|
||||
// @Param request body dto.ContainerCommit true "request"
|
||||
// @Success 200
|
||||
// @Router /containers/commit [post]
|
||||
func (b *BaseApi) ContainerCommit(c *gin.Context) {
|
||||
var req dto.ContainerCommit
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := containerService.ContainerCommit(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Operate Container
|
||||
// @Description 容器操作
|
||||
@@ -440,6 +461,19 @@ func (b *BaseApi) ContainerLogs(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// @Description 下载容器日志
|
||||
// @Router /containers/download/log [post]
|
||||
func (b *BaseApi) DownloadContainerLogs(c *gin.Context) {
|
||||
var req dto.ContainerLog
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
err := containerService.DownloadContainerLogs(req.ContainerType, req.Container, req.Since, strconv.Itoa(int(req.Tail)), c)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags Container Network
|
||||
// @Summary Page networks
|
||||
// @Description 获取容器网络列表分页
|
||||
|
||||
@@ -36,12 +36,12 @@ func (b *BaseApi) CreateCronjob(c *gin.Context) {
|
||||
// @Summary Page cronjobs
|
||||
// @Description 获取计划任务分页
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchWithPage true "request"
|
||||
// @Param request body dto.PageCronjob true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /cronjobs/search [post]
|
||||
func (b *BaseApi) SearchCronjob(c *gin.Context) {
|
||||
var req dto.SearchWithPage
|
||||
var req dto.PageCronjob
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -12,11 +12,17 @@ import (
|
||||
// @Tags Database Redis
|
||||
// @Summary Load redis status info
|
||||
// @Description 获取 redis 状态信息
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithName true "request"
|
||||
// @Success 200 {object} dto.RedisStatus
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/redis/status [get]
|
||||
// @Router /databases/redis/status [post]
|
||||
func (b *BaseApi) LoadRedisStatus(c *gin.Context) {
|
||||
data, err := redisService.LoadStatus()
|
||||
var req dto.OperationWithName
|
||||
if err := helper.CheckBind(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := redisService.LoadStatus(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
@@ -28,11 +34,17 @@ func (b *BaseApi) LoadRedisStatus(c *gin.Context) {
|
||||
// @Tags Database Redis
|
||||
// @Summary Load redis conf
|
||||
// @Description 获取 redis 配置信息
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithName true "request"
|
||||
// @Success 200 {object} dto.RedisConf
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/redis/conf [get]
|
||||
// @Router /databases/redis/conf [post]
|
||||
func (b *BaseApi) LoadRedisConf(c *gin.Context) {
|
||||
data, err := redisService.LoadConf()
|
||||
var req dto.OperationWithName
|
||||
if err := helper.CheckBind(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := redisService.LoadConf(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
@@ -44,11 +56,17 @@ func (b *BaseApi) LoadRedisConf(c *gin.Context) {
|
||||
// @Tags Database Redis
|
||||
// @Summary Load redis persistence conf
|
||||
// @Description 获取 redis 持久化配置
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithName true "request"
|
||||
// @Success 200 {object} dto.RedisPersistence
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/redis/persistence/conf [get]
|
||||
// @Router /databases/redis/persistence/conf [post]
|
||||
func (b *BaseApi) LoadPersistenceConf(c *gin.Context) {
|
||||
data, err := redisService.LoadPersistenceConf()
|
||||
var req dto.OperationWithName
|
||||
if err := helper.CheckBind(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := redisService.LoadPersistenceConf(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
@@ -57,6 +75,25 @@ func (b *BaseApi) LoadPersistenceConf(c *gin.Context) {
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
func (b *BaseApi) CheckHasCli(c *gin.Context) {
|
||||
helper.SuccessWithData(c, redisService.CheckHasCli())
|
||||
}
|
||||
|
||||
// @Tags Database Redis
|
||||
// @Summary Install redis-cli
|
||||
// @Description 安装 redis cli
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/redis/install/cli [post]
|
||||
func (b *BaseApi) InstallCli(c *gin.Context) {
|
||||
if err := redisService.InstallCli(); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Database Redis
|
||||
// @Summary Update redis conf
|
||||
// @Description 更新 redis 配置信息
|
||||
@@ -131,29 +168,3 @@ func (b *BaseApi) UpdateRedisPersistenceConf(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Redis
|
||||
// @Summary Page redis backups
|
||||
// @Description 获取 redis 备份记录分页
|
||||
// @Accept json
|
||||
// @Param request body dto.PageInfo true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/redis/backup/search [post]
|
||||
func (b *BaseApi) RedisBackupList(c *gin.Context) {
|
||||
var req dto.PageInfo
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
total, list, err := redisService.SearchBackupListWithPage(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ func (b *BaseApi) LoadDaemonJson(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/daemonjson/update [post]
|
||||
// @x-panel-log {"bodyKeys":["key", "value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json 配置 [key]=>[value]","formatEN":"Updated the docker daemon.json configuration [key]=>[value]"}
|
||||
// @x-panel-log {"bodyKeys":["key", "value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新配置 [key]","formatEN":"Updated configuration [key]"}
|
||||
func (b *BaseApi) UpdateDaemonJson(c *gin.Context) {
|
||||
var req dto.SettingUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -84,7 +84,7 @@ func (b *BaseApi) UpdateDaemonJson(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/logoption/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json 日志配置","formatEN":"Updated the docker daemon.json log option"}
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新日志配置","formatEN":"Updated the log option"}
|
||||
func (b *BaseApi) UpdateLogOption(c *gin.Context) {
|
||||
var req dto.LogOption
|
||||
if err := helper.CheckBind(&req, c); err != nil {
|
||||
@@ -107,7 +107,7 @@ func (b *BaseApi) UpdateLogOption(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/ipv6option/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json ipv6 配置","formatEN":"Updated the docker daemon.json ipv6 option"}
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 ipv6 配置","formatEN":"Updated the ipv6 option"}
|
||||
func (b *BaseApi) UpdateIpv6Option(c *gin.Context) {
|
||||
var req dto.Ipv6Option
|
||||
if err := helper.CheckBind(&req, c); err != nil {
|
||||
@@ -130,7 +130,7 @@ func (b *BaseApi) UpdateIpv6Option(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/daemonjson/update/byfile [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json 配置","formatEN":"Updated the docker daemon.json configuration"}
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新配置文件","formatEN":"Updated configuration file"}
|
||||
func (b *BaseApi) UpdateDaemonJsonByFile(c *gin.Context) {
|
||||
var req dto.DaemonJsonUpdateByFile
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
|
||||
@@ -37,6 +37,8 @@ var (
|
||||
|
||||
deviceService = service.NewIDeviceService()
|
||||
fail2banService = service.NewIFail2BanService()
|
||||
ftpService = service.NewIFtpService()
|
||||
clamService = service.NewIClamService()
|
||||
|
||||
settingService = service.NewISettingService()
|
||||
backupService = service.NewIBackupService()
|
||||
|
||||
+52
-11
@@ -11,6 +11,7 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
@@ -318,7 +319,7 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
||||
}
|
||||
dir := path.Dir(paths[0])
|
||||
|
||||
info, err := os.Stat(dir)
|
||||
_, err = os.Stat(dir)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
mode, err := files.GetParentMode(dir)
|
||||
if err != nil {
|
||||
@@ -330,14 +331,18 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
}
|
||||
info, err = os.Stat(dir)
|
||||
info, err := os.Stat(dir)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
mode := info.Mode()
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
stat, ok := info.Sys().(*syscall.Stat_t)
|
||||
uid, gid := -1, -1
|
||||
if ok {
|
||||
uid, gid = int(stat.Uid), int(stat.Gid)
|
||||
}
|
||||
|
||||
success := 0
|
||||
failures := make(buserr.MultiErr)
|
||||
@@ -351,6 +356,7 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
||||
global.LOG.Error(e)
|
||||
continue
|
||||
}
|
||||
_ = os.Chown(dstDir, uid, gid)
|
||||
}
|
||||
tmpFilename := dstFilename + ".tmp"
|
||||
if err := c.SaveUploadedFile(file, tmpFilename); err != nil {
|
||||
@@ -378,6 +384,9 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
||||
} else {
|
||||
_ = os.Chmod(dstFilename, mode)
|
||||
}
|
||||
if uid != -1 && gid != -1 {
|
||||
_ = os.Chown(dstFilename, uid, gid)
|
||||
}
|
||||
success++
|
||||
}
|
||||
if success == 0 {
|
||||
@@ -486,6 +495,7 @@ func (b *BaseApi) Download(c *gin.Context) {
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
}
|
||||
defer file.Close()
|
||||
info, _ := file.Stat()
|
||||
c.Header("Content-Length", strconv.FormatInt(info.Size(), 10))
|
||||
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(info.Name()))
|
||||
@@ -591,24 +601,46 @@ func (b *BaseApi) Size(c *gin.Context) {
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int) error {
|
||||
func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int, overwrite bool) error {
|
||||
defer func() {
|
||||
_ = os.RemoveAll(fileDir)
|
||||
}()
|
||||
|
||||
op := files.NewFileOp()
|
||||
dstDir = strings.TrimSpace(dstDir)
|
||||
mode, _ := files.GetParentMode(dstDir)
|
||||
if mode == 0 {
|
||||
mode = 0755
|
||||
}
|
||||
if _, err := os.Stat(dstDir); err != nil && os.IsNotExist(err) {
|
||||
if err = op.CreateDir(dstDir, mode); err != nil {
|
||||
return err
|
||||
uid, gid := -1, -1
|
||||
if info, err := os.Stat(dstDir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err = op.CreateDir(dstDir, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stat, ok := info.Sys().(*syscall.Stat_t)
|
||||
if ok {
|
||||
uid, gid = int(stat.Uid), int(stat.Gid)
|
||||
}
|
||||
}
|
||||
dstFileName := filepath.Join(dstDir, fileName)
|
||||
dstInfo, statErr := os.Stat(dstFileName)
|
||||
if statErr == nil {
|
||||
mode = dstInfo.Mode()
|
||||
} else {
|
||||
mode = 0644
|
||||
}
|
||||
|
||||
targetFile, err := os.OpenFile(filepath.Join(dstDir, fileName), os.O_RDWR|os.O_CREATE, mode)
|
||||
if overwrite {
|
||||
_ = os.Remove(dstFileName)
|
||||
}
|
||||
targetFile, err := os.OpenFile(dstFileName, os.O_RDWR|os.O_CREATE, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer targetFile.Close()
|
||||
for i := 0; i < chunkCount; i++ {
|
||||
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", fileName, i))
|
||||
chunkData, err := os.ReadFile(chunkPath)
|
||||
@@ -619,9 +651,13 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = os.Remove(chunkPath)
|
||||
}
|
||||
if uid != -1 && gid != -1 {
|
||||
_ = os.Chown(dstFileName, uid, gid)
|
||||
}
|
||||
|
||||
return files.NewFileOp().DeleteDir(fileDir)
|
||||
return nil
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
@@ -643,6 +679,7 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
defer uploadFile.Close()
|
||||
chunkIndex, err := strconv.Atoi(c.PostForm("chunkIndex"))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
@@ -702,7 +739,11 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
|
||||
}
|
||||
|
||||
if chunkIndex+1 == chunkCount {
|
||||
err = mergeChunks(filename, fileDir, c.PostForm("path"), chunkCount)
|
||||
overwrite := true
|
||||
if ow := c.PostForm("overwrite"); ow != "" {
|
||||
overwrite, _ = strconv.ParseBool(ow)
|
||||
}
|
||||
err = mergeChunks(filename, fileDir, c.PostForm("path"), chunkCount, overwrite)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, buserr.WithMap(constant.ErrFileUpload, map[string]interface{}{"name": filename, "detail": err.Error()}, err))
|
||||
return
|
||||
|
||||
@@ -94,6 +94,29 @@ func (b *BaseApi) OperatePortRule(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// OperateForwardRule
|
||||
// @Tags Firewall
|
||||
// @Summary Create group
|
||||
// @Description 更新防火墙端口转发规则
|
||||
// @Accept json
|
||||
// @Param request body dto.ForwardRuleOperate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/firewall/forward [post]
|
||||
// @x-panel-log {"bodyKeys":["source_port"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新端口转发规则 [source_port]","formatEN":"update port forward rules [source_port]"}
|
||||
func (b *BaseApi) OperateForwardRule(c *gin.Context) {
|
||||
var req dto.ForwardRuleOperate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := firewallService.OperateForwardRule(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary Create group
|
||||
// @Description 创建防火墙 IP 规则
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags FTP
|
||||
// @Summary Load FTP base info
|
||||
// @Description 获取 FTP 基础信息
|
||||
// @Success 200 {object} dto.FtpBaseInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/ftp/base [get]
|
||||
func (b *BaseApi) LoadFtpBaseInfo(c *gin.Context) {
|
||||
data, err := ftpService.LoadBaseInfo()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags FTP
|
||||
// @Summary Load FTP operation log
|
||||
// @Description 获取 FTP 操作日志
|
||||
// @Accept json
|
||||
// @Param request body dto.FtpLogSearch true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/ftp/log/search [post]
|
||||
func (b *BaseApi) LoadFtpLogInfo(c *gin.Context) {
|
||||
var req dto.FtpLogSearch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
total, list, err := ftpService.LoadLog(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags FTP
|
||||
// @Summary Operate FTP
|
||||
// @Description 修改 FTP 状态
|
||||
// @Accept json
|
||||
// @Param request body dto.Operate true "request"
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/ftp/operate [post]
|
||||
// @x-panel-log {"bodyKeys":["operation"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operation] FTP","formatEN":"[operation] FTP"}
|
||||
func (b *BaseApi) OperateFtp(c *gin.Context) {
|
||||
var req dto.Operate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := ftpService.Operate(req.Operation); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags FTP
|
||||
// @Summary Page FTP user
|
||||
// @Description 获取 FTP 账户列表分页
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchWithPage true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/ftp/search [post]
|
||||
func (b *BaseApi) SearchFtp(c *gin.Context) {
|
||||
var req dto.SearchWithPage
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
total, list, err := ftpService.SearchWithPage(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags FTP
|
||||
// @Summary Create FTP user
|
||||
// @Description 创建 FTP 账户
|
||||
// @Accept json
|
||||
// @Param request body dto.FtpCreate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/ftp [post]
|
||||
// @x-panel-log {"bodyKeys":["user", "path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建 FTP 账户 [user][path]","formatEN":"create FTP [user][path]"}
|
||||
func (b *BaseApi) CreateFtp(c *gin.Context) {
|
||||
var req dto.FtpCreate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Password) != 0 {
|
||||
pass, err := base64.StdEncoding.DecodeString(req.Password)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.Password = string(pass)
|
||||
}
|
||||
if _, err := ftpService.Create(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags FTP
|
||||
// @Summary Delete FTP user
|
||||
// @Description 删除 FTP 账户
|
||||
// @Accept json
|
||||
// @Param request body dto.BatchDeleteReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/ftp/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"ftps","output_column":"user","output_value":"users"}],"formatZH":"删除 FTP 账户 [users]","formatEN":"delete FTP users [users]"}
|
||||
func (b *BaseApi) DeleteFtp(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := ftpService.Delete(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags FTP
|
||||
// @Summary Sync FTP user
|
||||
// @Description 同步 FTP 账户
|
||||
// @Accept json
|
||||
// @Param request body dto.BatchDeleteReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/ftp/sync [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"同步 FTP 账户","formatEN":"sync FTP users"}
|
||||
func (b *BaseApi) SyncFtp(c *gin.Context) {
|
||||
if err := ftpService.Sync(); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags FTP
|
||||
// @Summary Update FTP user
|
||||
// @Description 修改 FTP 账户
|
||||
// @Accept json
|
||||
// @Param request body dto.FtpUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/ftp/update [post]
|
||||
// @x-panel-log {"bodyKeys":["user", "path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 FTP 账户 [user][path]","formatEN":"update FTP [user][path]"}
|
||||
func (b *BaseApi) UpdateFtp(c *gin.Context) {
|
||||
var req dto.FtpUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Password) != 0 {
|
||||
pass, err := base64.StdEncoding.DecodeString(req.Password)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.Password = string(pass)
|
||||
}
|
||||
if err := ftpService.Update(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
@@ -133,3 +133,8 @@ func CheckBind(req interface{}, c *gin.Context) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ErrResponse(ctx *gin.Context, code int) {
|
||||
ctx.JSON(code, nil)
|
||||
ctx.Abort()
|
||||
}
|
||||
|
||||
@@ -76,6 +76,20 @@ func (b *BaseApi) DeleteRuntime(c *gin.Context) {
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
func (b *BaseApi) DeleteRuntimeCheck(c *gin.Context) {
|
||||
runTimeId, err := helper.GetIntParamByKey(c, "runTimeId")
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInternalServer, nil)
|
||||
return
|
||||
}
|
||||
checkData, err := runtimeService.DeleteCheck(runTimeId)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, checkData)
|
||||
}
|
||||
|
||||
// @Tags Runtime
|
||||
// @Summary Update runtime
|
||||
// @Description 更新运行环境
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"os"
|
||||
"path"
|
||||
@@ -60,6 +61,37 @@ func (b *BaseApi) UpdateSetting(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Update proxy setting
|
||||
// @Description 服务器代理配置
|
||||
// @Accept json
|
||||
// @Param request body dto.ProxyUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/proxy/update [post]
|
||||
// @x-panel-log {"bodyKeys":["proxyUrl","proxyPort"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"服务器代理配置 [proxyPort]:[proxyPort]","formatEN":"set proxy [proxyPort]:[proxyPort]."}
|
||||
func (b *BaseApi) UpdateProxy(c *gin.Context) {
|
||||
var req dto.ProxyUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.ProxyPasswd) != 0 && len(req.ProxyType) != 0 {
|
||||
pass, err := base64.StdEncoding.DecodeString(req.ProxyPasswd)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.ProxyPasswd = string(pass)
|
||||
}
|
||||
|
||||
if err := settingService.UpdateProxy(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Update system setting
|
||||
// @Description 隐藏高级功能菜单
|
||||
|
||||
@@ -168,13 +168,13 @@ func (b *BaseApi) RollbackSnapshot(c *gin.Context) {
|
||||
// @Summary Delete system backup
|
||||
// @Description 删除系统快照
|
||||
// @Accept json
|
||||
// @Param request body dto.BatchDeleteReq true "request"
|
||||
// @Param request body dto.SnapshotBatchDelete true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/snapshot/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"snapshots","output_column":"name","output_value":"name"}],"formatZH":"删除系统快照 [name]","formatEN":"Delete system backup [name]"}
|
||||
func (b *BaseApi) DeleteSnapshot(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
var req dto.SnapshotBatchDelete
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/copier"
|
||||
@@ -78,6 +79,13 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
defer wsConn.Close()
|
||||
|
||||
if global.CONF.System.IsDemo {
|
||||
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
|
||||
@@ -87,22 +95,38 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||
return
|
||||
}
|
||||
redisConf, err := redisService.LoadConf()
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "load redis container failed")) {
|
||||
name := c.Query("name")
|
||||
from := c.Query("from")
|
||||
commands := []string{"redis-cli"}
|
||||
database, err := databaseService.Get(name)
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "no such database in db")) {
|
||||
return
|
||||
}
|
||||
|
||||
defer wsConn.Close()
|
||||
commands := "redis-cli"
|
||||
if len(redisConf.Requirepass) != 0 {
|
||||
commands = fmt.Sprintf("redis-cli -a %s --no-auth-warning", redisConf.Requirepass)
|
||||
if from == "local" {
|
||||
redisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: name, Type: "redis"})
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "no such database in db")) {
|
||||
return
|
||||
}
|
||||
name = redisInfo.ContainerName
|
||||
if len(database.Password) != 0 {
|
||||
commands = []string{"redis-cli", "-a", database.Password, "--no-auth-warning"}
|
||||
}
|
||||
} else {
|
||||
itemPort := fmt.Sprintf("%v", database.Port)
|
||||
commands = []string{"redis-cli", "-h", database.Address, "-p", itemPort}
|
||||
if len(database.Password) != 0 {
|
||||
commands = []string{"redis-cli", "-h", database.Address, "-p", itemPort, "-a", database.Password, "--no-auth-warning"}
|
||||
}
|
||||
name = "1Panel-redis-cli-tools"
|
||||
}
|
||||
pidMap := loadMapFromDockerTop(redisConf.ContainerName)
|
||||
slave, err := terminal.NewCommand(fmt.Sprintf("docker exec -it %s %s", redisConf.ContainerName, commands))
|
||||
|
||||
pidMap := loadMapFromDockerTop(name)
|
||||
itemCmds := append([]string{"exec", "-it", name}, commands...)
|
||||
slave, err := terminal.NewCommand(itemCmds)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(redisConf.ContainerName, pidMap)
|
||||
defer killBash(name, strings.Join(commands, " "), pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
|
||||
@@ -130,6 +154,12 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
|
||||
}
|
||||
defer wsConn.Close()
|
||||
|
||||
if global.CONF.System.IsDemo {
|
||||
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
containerID := c.Query("containerid")
|
||||
command := c.Query("command")
|
||||
user := c.Query("user")
|
||||
@@ -161,16 +191,16 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
commands := fmt.Sprintf("docker exec -it %s %s", containerID, command)
|
||||
commands := []string{"exec", "-it", containerID, command}
|
||||
if len(user) != 0 {
|
||||
commands = fmt.Sprintf("docker exec -it -u %s %s %s", user, containerID, command)
|
||||
commands = []string{"exec", "-it", "-u", user, containerID, command}
|
||||
}
|
||||
pidMap := loadMapFromDockerTop(containerID)
|
||||
slave, err := terminal.NewCommand(commands)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(containerID, pidMap)
|
||||
defer killBash(containerID, command, pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, true)
|
||||
@@ -221,26 +251,26 @@ func loadMapFromDockerTop(containerID string) map[string]string {
|
||||
lines := strings.Split(stdout, "\n")
|
||||
for _, line := range lines {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) == 0 {
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
pidMap[parts[0]] = strings.Join(parts, " ")
|
||||
pidMap[parts[0]] = strings.Join(parts[1:], " ")
|
||||
}
|
||||
return pidMap
|
||||
}
|
||||
|
||||
func killBash(containerID string, pidMap map[string]string) {
|
||||
func killBash(containerID, comm string, pidMap map[string]string) {
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
newPidMap := loadMapFromDockerTop(containerID)
|
||||
for pid, newCmd := range newPidMap {
|
||||
for pid, command := range newPidMap {
|
||||
isOld := false
|
||||
for pid2, oldCmd := range pidMap {
|
||||
if pid == pid2 && oldCmd == newCmd {
|
||||
for pid2 := range pidMap {
|
||||
if pid == pid2 {
|
||||
isOld = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isOld {
|
||||
if !isOld && command == comm {
|
||||
_, _ = cmd.Execf("%s kill -9 %s", sudo, pid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
@@ -76,6 +78,14 @@ func (b *BaseApi) CreateWebsite(c *gin.Context) {
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if len(req.FtpPassword) != 0 {
|
||||
pass, err := base64.StdEncoding.DecodeString(req.FtpPassword)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.FtpPassword = string(pass)
|
||||
}
|
||||
err := websiteService.CreateWebsite(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
@@ -768,3 +778,45 @@ func (b *BaseApi) GetDirConfig(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Get default html
|
||||
// @Description 获取默认 html
|
||||
// @Accept json
|
||||
// @Success 200 {object} response.FileInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/default/html/:type [get]
|
||||
func (b *BaseApi) GetDefaultHtml(c *gin.Context) {
|
||||
resourceType, err := helper.GetStrParamByKey(c, "type")
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInternalServer, nil)
|
||||
return
|
||||
}
|
||||
fileInfo, err := websiteService.GetDefaultHtml(resourceType)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, fileInfo)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update default html
|
||||
// @Description 更新默认 html
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteHtmlUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/default/html/update [post]
|
||||
// @x-panel-log {"bodyKeys":["type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新默认 html","formatEN":"Update default html"}
|
||||
func (b *BaseApi) UpdateDefaultHtml(c *gin.Context) {
|
||||
var req request.WebsiteHtmlUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateDefaultHtml(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// @Tags Website CA
|
||||
@@ -142,3 +145,33 @@ func (b *BaseApi) RenewWebsiteCA(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Website CA
|
||||
// @Summary Download CA file
|
||||
// @Description 下载 CA 证书文件
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteResourceReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/ca/download [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_cas","output_column":"name","output_value":"name"}],"formatZH":"下载 CA 证书文件 [name]","formatEN":"download ca file [name]"}
|
||||
func (b *BaseApi) DownloadCAFile(c *gin.Context) {
|
||||
var req request.WebsiteResourceReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
file, err := websiteCAService.DownloadFile(req.ID)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
info, err := file.Stat()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
c.Header("Content-Length", strconv.FormatInt(info.Size(), 10))
|
||||
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(info.Name()))
|
||||
http.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), file)
|
||||
}
|
||||
|
||||
@@ -236,6 +236,7 @@ func (b *BaseApi) DownloadWebsiteSSL(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
info, err := file.Stat()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
|
||||
@@ -37,8 +37,9 @@ type AppOssConfig struct {
|
||||
}
|
||||
|
||||
type AppVersion struct {
|
||||
Version string `json:"version"`
|
||||
DetailId uint `json:"detailId"`
|
||||
Version string `json:"version"`
|
||||
DetailId uint `json:"detailId"`
|
||||
DockerCompose string `json:"dockerCompose"`
|
||||
}
|
||||
|
||||
type AppList struct {
|
||||
|
||||
@@ -35,6 +35,7 @@ type CommonBackup struct {
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql"`
|
||||
Name string `json:"name"`
|
||||
DetailName string `json:"detailName"`
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
type CommonRecover struct {
|
||||
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive WebDAV"`
|
||||
@@ -42,6 +43,7 @@ type CommonRecover struct {
|
||||
Name string `json:"name"`
|
||||
DetailName string `json:"detailName"`
|
||||
File string `json:"file"`
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
type RecordSearch struct {
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type SearchClamWithPage struct {
|
||||
PageInfo
|
||||
Info string `json:"info"`
|
||||
OrderBy string `json:"orderBy" validate:"required,oneof=name status created_at"`
|
||||
Order string `json:"order" validate:"required,oneof=null ascending descending"`
|
||||
}
|
||||
|
||||
type ClamBaseInfo struct {
|
||||
Version string `json:"version"`
|
||||
IsActive bool `json:"isActive"`
|
||||
IsExist bool `json:"isExist"`
|
||||
|
||||
FreshVersion string `json:"freshVersion"`
|
||||
FreshIsActive bool `json:"freshIsActive"`
|
||||
FreshIsExist bool `json:"freshIsExist"`
|
||||
}
|
||||
|
||||
type ClamInfo struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Path string `json:"path"`
|
||||
InfectedStrategy string `json:"infectedStrategy"`
|
||||
InfectedDir string `json:"infectedDir"`
|
||||
LastHandleDate string `json:"lastHandleDate"`
|
||||
Spec string `json:"spec"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type ClamLogSearch struct {
|
||||
PageInfo
|
||||
|
||||
ClamID uint `json:"clamID"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
EndTime time.Time `json:"endTime"`
|
||||
}
|
||||
|
||||
type ClamLogReq struct {
|
||||
Tail string `json:"tail"`
|
||||
ClamName string `json:"clamName"`
|
||||
RecordName string `json:"recordName"`
|
||||
}
|
||||
|
||||
type ClamFileReq struct {
|
||||
Tail string `json:"tail"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
|
||||
type ClamLog struct {
|
||||
Name string `json:"name"`
|
||||
ScanDate string `json:"scanDate"`
|
||||
ScanTime string `json:"scanTime"`
|
||||
InfectedFiles string `json:"infectedFiles"`
|
||||
TotalError string `json:"totalError"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type ClamCreate struct {
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Path string `json:"path"`
|
||||
InfectedStrategy string `json:"infectedStrategy"`
|
||||
InfectedDir string `json:"infectedDir"`
|
||||
Spec string `json:"spec"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type ClamUpdate struct {
|
||||
ID uint `json:"id"`
|
||||
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
InfectedStrategy string `json:"infectedStrategy"`
|
||||
InfectedDir string `json:"infectedDir"`
|
||||
Spec string `json:"spec"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type ClamUpdateStatus struct {
|
||||
ID uint `json:"id"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type ClamDelete struct {
|
||||
RemoveRecord bool `json:"removeRecord"`
|
||||
RemoveInfected bool `json:"removeInfected"`
|
||||
Ids []uint `json:"ids" validate:"required"`
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
package dto
|
||||
|
||||
type SearchCommandWithPage struct {
|
||||
SearchWithPage
|
||||
OrderBy string `json:"orderBy"`
|
||||
Order string `json:"order"`
|
||||
PageInfo
|
||||
OrderBy string `json:"orderBy" validate:"required,oneof=name command created_at"`
|
||||
Order string `json:"order" validate:"required,oneof=null ascending descending"`
|
||||
GroupID uint `json:"groupID"`
|
||||
Info string `json:"info"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type CommandOperate struct {
|
||||
@@ -29,3 +30,9 @@ type CommandTree struct {
|
||||
Label string `json:"label"`
|
||||
Children []CommandInfo `json:"children"`
|
||||
}
|
||||
|
||||
type RedisCommand struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Command string `json:"command"`
|
||||
}
|
||||
|
||||
@@ -2,9 +2,7 @@ package dto
|
||||
|
||||
type SearchWithPage struct {
|
||||
PageInfo
|
||||
Info string `json:"info"`
|
||||
OrderBy string `json:"orderBy"`
|
||||
Order string `json:"order"`
|
||||
Info string `json:"info"`
|
||||
}
|
||||
|
||||
type PageInfo struct {
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
package dto
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type PageContainer struct {
|
||||
PageInfo
|
||||
Name string `json:"name"`
|
||||
State string `json:"state" validate:"required,oneof=all created running paused restarting removing exited dead"`
|
||||
OrderBy string `json:"orderBy"`
|
||||
Order string `json:"order"`
|
||||
OrderBy string `json:"orderBy" validate:"required,oneof=name state created_at"`
|
||||
Order string `json:"order" validate:"required,oneof=null ascending descending"`
|
||||
Filters string `json:"filters"`
|
||||
ExcludeAppStore bool `json:"excludeAppStore"`
|
||||
}
|
||||
@@ -122,8 +124,17 @@ type ContainerRename struct {
|
||||
NewName string `json:"newName" validate:"required"`
|
||||
}
|
||||
|
||||
type ContainerCommit struct {
|
||||
ContainerId string `json:"containerID" validate:"required"`
|
||||
ContainerName string `json:"containerName"`
|
||||
NewImageName string `json:"newImageName"`
|
||||
Comment string `json:"comment"`
|
||||
Author string `json:"author"`
|
||||
Pause bool `json:"pause"`
|
||||
}
|
||||
|
||||
type ContainerPrune struct {
|
||||
PruneType string `json:"pruneType" validate:"required,oneof=container image volume network"`
|
||||
PruneType string `json:"pruneType" validate:"required,oneof=container image volume network buildcache"`
|
||||
WithTagAll bool `json:"withTagAll"`
|
||||
}
|
||||
|
||||
@@ -214,3 +225,10 @@ type ComposeUpdate struct {
|
||||
Path string `json:"path" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
}
|
||||
|
||||
type ContainerLog struct {
|
||||
Container string `json:"container" validate:"required"`
|
||||
Since string `json:"since"`
|
||||
Tail uint `json:"tail"`
|
||||
ContainerType string `json:"containerType"`
|
||||
}
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
package dto
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type PageCronjob struct {
|
||||
PageInfo
|
||||
Info string `json:"info"`
|
||||
OrderBy string `json:"orderBy" validate:"required,oneof=name status created_at"`
|
||||
Order string `json:"order" validate:"required,oneof=null ascending descending"`
|
||||
}
|
||||
|
||||
type CronjobCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
@@ -21,6 +30,7 @@ type CronjobCreate struct {
|
||||
BackupAccounts string `json:"backupAccounts"`
|
||||
DefaultDownload string `json:"defaultDownload"`
|
||||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
type CronjobUpdate struct {
|
||||
@@ -42,6 +52,7 @@ type CronjobUpdate struct {
|
||||
BackupAccounts string `json:"backupAccounts"`
|
||||
DefaultDownload string `json:"defaultDownload"`
|
||||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
type CronjobUpdateStatus struct {
|
||||
@@ -87,6 +98,7 @@ type CronjobInfo struct {
|
||||
|
||||
LastRecordTime string `json:"lastRecordTime"`
|
||||
Status string `json:"status"`
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
type SearchRecord struct {
|
||||
@@ -98,12 +110,12 @@ type SearchRecord struct {
|
||||
}
|
||||
|
||||
type Record struct {
|
||||
ID uint `json:"id"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
Records string `json:"records"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
TargetPath string `json:"targetPath"`
|
||||
Interval int `json:"interval"`
|
||||
File string `json:"file"`
|
||||
ID uint `json:"id"`
|
||||
StartTime string `json:"startTime"`
|
||||
Records string `json:"records"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
TargetPath string `json:"targetPath"`
|
||||
Interval int `json:"interval"`
|
||||
File string `json:"file"`
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ type OsInfo struct {
|
||||
PlatformFamily string `json:"platformFamily"`
|
||||
KernelArch string `json:"kernelArch"`
|
||||
KernelVersion string `json:"kernelVersion"`
|
||||
|
||||
DiskSize int64 `json:"diskSize"`
|
||||
}
|
||||
|
||||
type DashboardCurrent struct {
|
||||
|
||||
+19
-15
@@ -27,8 +27,8 @@ type MysqlDBSearch struct {
|
||||
PageInfo
|
||||
Info string `json:"info"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
OrderBy string `json:"orderBy"`
|
||||
Order string `json:"order"`
|
||||
OrderBy string `json:"orderBy" validate:"required,oneof=name created_at"`
|
||||
Order string `json:"order" validate:"required,oneof=null ascending descending"`
|
||||
}
|
||||
|
||||
type MysqlDBInfo struct {
|
||||
@@ -131,7 +131,7 @@ type MysqlStatus struct {
|
||||
}
|
||||
|
||||
type MysqlVariables struct {
|
||||
BinlogCacheSize string `json:"binlog_cache_size"`
|
||||
BinlogCacheSize string `json:"binlog_cache_size"`
|
||||
InnodbBufferPoolSize string `json:"innodb_buffer_pool_size"`
|
||||
InnodbLogBufferSize string `json:"innodb_log_buffer_size"`
|
||||
JoinBufferSize string `json:"join_buffer_size"`
|
||||
@@ -139,7 +139,7 @@ type MysqlVariables struct {
|
||||
MaxConnections string `json:"max_connections"`
|
||||
MaxHeapTableSize string `json:"max_heap_table_size"`
|
||||
QueryCacheSize string `json:"query_cache_size"`
|
||||
QueryCache_type string `json:"query_cache_type"`
|
||||
QueryCacheType string `json:"query_cache_type"`
|
||||
ReadBufferSize string `json:"read_buffer_size"`
|
||||
ReadRndBufferSize string `json:"read_rnd_buffer_size"`
|
||||
SortBufferSize string `json:"sort_buffer_size"`
|
||||
@@ -165,26 +165,26 @@ type MysqlVariablesUpdateHelper struct {
|
||||
|
||||
// redis
|
||||
type ChangeRedisPass struct {
|
||||
Value string `json:"value" validate:"required"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type RedisConfUpdate struct {
|
||||
Database string `json:"database" validate:"required"`
|
||||
Timeout string `json:"timeout"`
|
||||
Maxclients string `json:"maxclients"`
|
||||
Maxmemory string `json:"maxmemory"`
|
||||
}
|
||||
type RedisConfPersistenceUpdate struct {
|
||||
Database string `json:"database" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=aof rbd"`
|
||||
Appendonly string `json:"appendonly"`
|
||||
Appendfsync string `json:"appendfsync"`
|
||||
Save string `json:"save"`
|
||||
}
|
||||
type RedisConfUpdateByFile struct {
|
||||
File string `json:"file" validate:"required"`
|
||||
RestartNow bool `json:"restartNow"`
|
||||
}
|
||||
|
||||
type RedisConf struct {
|
||||
Database string `json:"database" validate:"required"`
|
||||
Name string `json:"name"`
|
||||
Port int64 `json:"port"`
|
||||
ContainerName string `json:"containerName"`
|
||||
@@ -195,18 +195,20 @@ type RedisConf struct {
|
||||
}
|
||||
|
||||
type RedisPersistence struct {
|
||||
Database string `json:"database" validate:"required"`
|
||||
Appendonly string `json:"appendonly"`
|
||||
Appendfsync string `json:"appendfsync"`
|
||||
Save string `json:"save"`
|
||||
}
|
||||
|
||||
type RedisStatus struct {
|
||||
Database string `json:"database" validate:"required"`
|
||||
TcpPort string `json:"tcp_port"`
|
||||
UptimeInDays string `json:"uptime_in_days"`
|
||||
ConnectedClients string `json:"connected_clients"`
|
||||
UsedMemory string `json:"used_memory"`
|
||||
UsedMemory_rss string `json:"used_memory_rss"`
|
||||
UsedMemory_peak string `json:"used_memory_peak"`
|
||||
UsedMemoryRss string `json:"used_memory_rss"`
|
||||
UsedMemoryPeak string `json:"used_memory_peak"`
|
||||
MemFragmentationRatio string `json:"mem_fragmentation_ratio"`
|
||||
TotalConnectionsReceived string `json:"total_connections_received"`
|
||||
TotalCommandsProcessed string `json:"total_commands_processed"`
|
||||
@@ -217,12 +219,14 @@ type RedisStatus struct {
|
||||
}
|
||||
|
||||
type DatabaseFileRecords struct {
|
||||
Database string `json:"database" validate:"required"`
|
||||
FileName string `json:"fileName"`
|
||||
FileDir string `json:"fileDir"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
Size int `json:"size"`
|
||||
}
|
||||
type RedisBackupRecover struct {
|
||||
Database string `json:"database" validate:"required"`
|
||||
FileName string `json:"fileName"`
|
||||
FileDir string `json:"fileDir"`
|
||||
}
|
||||
@@ -232,8 +236,8 @@ type DatabaseSearch struct {
|
||||
PageInfo
|
||||
Info string `json:"info"`
|
||||
Type string `json:"type"`
|
||||
OrderBy string `json:"orderBy"`
|
||||
Order string `json:"order"`
|
||||
OrderBy string `json:"orderBy" validate:"required,oneof=name created_at"`
|
||||
Order string `json:"order" validate:"required,oneof=null ascending descending"`
|
||||
}
|
||||
|
||||
type DatabaseInfo struct {
|
||||
@@ -281,7 +285,7 @@ type DatabaseCreate struct {
|
||||
Address string `json:"address"`
|
||||
Port uint `json:"port"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Password string `json:"password"`
|
||||
|
||||
SSL bool `json:"ssl"`
|
||||
RootCert string `json:"rootCert"`
|
||||
@@ -299,7 +303,7 @@ type DatabaseUpdate struct {
|
||||
Address string `json:"address"`
|
||||
Port uint `json:"port"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Password string `json:"password"`
|
||||
|
||||
SSL bool `json:"ssl"`
|
||||
RootCert string `json:"rootCert"`
|
||||
|
||||
@@ -6,8 +6,8 @@ type PostgresqlDBSearch struct {
|
||||
PageInfo
|
||||
Info string `json:"info"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
OrderBy string `json:"orderBy"`
|
||||
Order string `json:"order"`
|
||||
OrderBy string `json:"orderBy" validate:"required,oneof=name created_at"`
|
||||
Order string `json:"order" validate:"required,oneof=null ascending descending"`
|
||||
}
|
||||
|
||||
type PostgresqlDBInfo struct {
|
||||
|
||||
@@ -29,6 +29,17 @@ type PortRuleOperate struct {
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type ForwardRuleOperate struct {
|
||||
Rules []struct {
|
||||
Operation string `json:"operation" validate:"required,oneof=add remove"`
|
||||
Num string `json:"num"`
|
||||
Protocol string `json:"protocol" validate:"required,oneof=tcp udp tcp/udp"`
|
||||
Port string `json:"port" validate:"required"`
|
||||
TargetIP string `json:"targetIP"`
|
||||
TargetPort string `json:"targetPort" validate:"required"`
|
||||
} `json:"rules"`
|
||||
}
|
||||
|
||||
type UpdateFirewallDescription struct {
|
||||
Type string `json:"type"`
|
||||
Address string `json:"address"`
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type FtpInfo struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
|
||||
User string `json:"user"`
|
||||
Password string `json:"password"`
|
||||
Path string `json:"path"`
|
||||
Status string `json:"status"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type FtpBaseInfo struct {
|
||||
IsActive bool `json:"isActive"`
|
||||
IsExist bool `json:"isExist"`
|
||||
}
|
||||
|
||||
type FtpLogSearch struct {
|
||||
PageInfo
|
||||
User string `json:"user"`
|
||||
Operation string `json:"operation"`
|
||||
}
|
||||
|
||||
type FtpCreate struct {
|
||||
User string `json:"user" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Path string `json:"path" validate:"required"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type FtpUpdate struct {
|
||||
ID uint `json:"id"`
|
||||
|
||||
Password string `json:"password" validate:"required"`
|
||||
Path string `json:"path" validate:"required"`
|
||||
Status string `json:"status"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
@@ -43,6 +43,7 @@ type AppInstalledSearch struct {
|
||||
Update bool `json:"update"`
|
||||
Unused bool `json:"unused"`
|
||||
All bool `json:"all"`
|
||||
Sync bool `json:"sync"`
|
||||
}
|
||||
|
||||
type AppInstalledInfo struct {
|
||||
@@ -60,15 +61,24 @@ type AppBackupDelete struct {
|
||||
}
|
||||
|
||||
type AppInstalledOperate struct {
|
||||
InstallId uint `json:"installId" validate:"required"`
|
||||
BackupId uint `json:"backupId"`
|
||||
DetailId uint `json:"detailId"`
|
||||
Operate constant.AppOperate `json:"operate" validate:"required"`
|
||||
ForceDelete bool `json:"forceDelete"`
|
||||
DeleteBackup bool `json:"deleteBackup"`
|
||||
DeleteDB bool `json:"deleteDB"`
|
||||
Backup bool `json:"backup"`
|
||||
PullImage bool `json:"pullImage"`
|
||||
InstallId uint `json:"installId" validate:"required"`
|
||||
BackupId uint `json:"backupId"`
|
||||
DetailId uint `json:"detailId"`
|
||||
Operate constant.AppOperate `json:"operate" validate:"required"`
|
||||
ForceDelete bool `json:"forceDelete"`
|
||||
DeleteBackup bool `json:"deleteBackup"`
|
||||
DeleteDB bool `json:"deleteDB"`
|
||||
Backup bool `json:"backup"`
|
||||
PullImage bool `json:"pullImage"`
|
||||
DockerCompose string `json:"dockerCompose"`
|
||||
}
|
||||
|
||||
type AppInstallUpgrade struct {
|
||||
InstallID uint `json:"installId"`
|
||||
DetailID uint `json:"detailId"`
|
||||
Backup bool `json:"backup"`
|
||||
PullImage bool `json:"pullImage"`
|
||||
DockerCompose string `json:"dockerCompose"`
|
||||
}
|
||||
|
||||
type AppInstalledUpdate struct {
|
||||
@@ -87,3 +97,8 @@ type PortUpdate struct {
|
||||
Name string `json:"name"`
|
||||
Port int64 `json:"port"`
|
||||
}
|
||||
|
||||
type AppUpdateVersion struct {
|
||||
AppInstallID uint `json:"appInstallID" validate:"required"`
|
||||
UpdateVersion string `json:"updateVersion"`
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@ type FileOption struct {
|
||||
}
|
||||
|
||||
type FileContentReq struct {
|
||||
Path string `json:"path" validate:"required"`
|
||||
Path string `json:"path" validate:"required"`
|
||||
IsDetail bool `json:"isDetail"`
|
||||
}
|
||||
|
||||
type SearchUploadWithPage struct {
|
||||
@@ -54,12 +55,14 @@ type FileCompress struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Replace bool `json:"replace"`
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
type FileDeCompress struct {
|
||||
Dst string `json:"dst" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
Path string `json:"path" validate:"required"`
|
||||
Dst string `json:"dst" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
Path string `json:"path" validate:"required"`
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
type FileEdit struct {
|
||||
@@ -124,6 +127,7 @@ type FileReadByLineReq struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
ID uint `json:"ID"`
|
||||
Name string `json:"name"`
|
||||
Latest bool `json:"latest"`
|
||||
}
|
||||
|
||||
type FileExistReq struct {
|
||||
|
||||
@@ -27,7 +27,7 @@ type NginxRewriteReq struct {
|
||||
type NginxRewriteUpdate struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type NginxProxyUpdate struct {
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
type WebsiteSearch struct {
|
||||
dto.PageInfo
|
||||
Name string `json:"name"`
|
||||
OrderBy string `json:"orderBy"`
|
||||
Order string `json:"order"`
|
||||
OrderBy string `json:"orderBy" validate:"required,oneof=primary_domain type status created_at expire_date"`
|
||||
Order string `json:"order" validate:"required,oneof=null ascending descending"`
|
||||
WebsiteGroupID uint `json:"websiteGroupId"`
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ type WebsiteCreate struct {
|
||||
AppID uint `json:"appID"`
|
||||
AppInstallID uint `json:"appInstallID"`
|
||||
|
||||
FtpUser string `json:"ftpUser"`
|
||||
FtpPassword string `json:"ftpPassword"`
|
||||
|
||||
RuntimeID uint `json:"runtimeID"`
|
||||
RuntimeConfig
|
||||
}
|
||||
@@ -52,7 +55,7 @@ type WebsiteUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
PrimaryDomain string `json:"primaryDomain" validate:"required"`
|
||||
Remark string `json:"remark"`
|
||||
WebsiteGroupID uint `json:"webSiteGroupID" validate:"required"`
|
||||
WebsiteGroupID uint `json:"webSiteGroupID"`
|
||||
ExpireDate string `json:"expireDate"`
|
||||
IPV6 bool `json:"IPV6"`
|
||||
}
|
||||
@@ -120,6 +123,7 @@ type WebsiteHTTPSOp struct {
|
||||
HttpConfig string `json:"httpConfig" validate:"oneof=HTTPSOnly HTTPAlso HTTPToHTTPS"`
|
||||
SSLProtocol []string `json:"SSLProtocol"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
Hsts bool `json:"hsts"`
|
||||
}
|
||||
|
||||
type WebsiteNginxUpdate struct {
|
||||
@@ -178,13 +182,14 @@ type WebsiteProxyConfig struct {
|
||||
CacheTime int `json:"cacheTime" `
|
||||
CacheUnit string `json:"cacheUnit"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Modifier string `json:"modifier" validate:"required"`
|
||||
Modifier string `json:"modifier"`
|
||||
Match string `json:"match" validate:"required"`
|
||||
ProxyPass string `json:"proxyPass" validate:"required"`
|
||||
ProxyHost string `json:"proxyHost" validate:"required"`
|
||||
Content string `json:"content"`
|
||||
FilePath string `json:"filePath"`
|
||||
Replaces map[string]string `json:"replaces"`
|
||||
SNI bool `json:"sni"`
|
||||
}
|
||||
|
||||
type WebsiteProxyReq struct {
|
||||
@@ -204,3 +209,12 @@ type WafWebsite struct {
|
||||
Domains []string `json:"domains"`
|
||||
Host []string `json:"host"`
|
||||
}
|
||||
|
||||
type WebsiteHtmlReq struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteHtmlUpdate struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -20,6 +20,12 @@ type WebsiteSSLCreate struct {
|
||||
Dir string `json:"dir"`
|
||||
ID uint `json:"id"`
|
||||
Description string `json:"description"`
|
||||
DisableCNAME bool `json:"disableCNAME"`
|
||||
SkipDNS bool `json:"skipDNS"`
|
||||
Nameserver1 string `json:"nameserver1"`
|
||||
Nameserver2 string `json:"nameserver2"`
|
||||
ExecShell bool `json:"execShell"`
|
||||
Shell string `json:"shell"`
|
||||
}
|
||||
|
||||
type WebsiteDNSReq struct {
|
||||
@@ -32,8 +38,9 @@ type WebsiteSSLRenew struct {
|
||||
}
|
||||
|
||||
type WebsiteSSLApply struct {
|
||||
ID uint `json:"ID" validate:"required"`
|
||||
SkipDNSCheck bool `json:"SkipDNSCheck"`
|
||||
ID uint `json:"ID" validate:"required"`
|
||||
SkipDNSCheck bool `json:"skipDNSCheck"`
|
||||
Nameservers []string `json:"nameservers"`
|
||||
}
|
||||
|
||||
type WebsiteAcmeAccountCreate struct {
|
||||
@@ -66,9 +73,24 @@ type WebsiteBatchDelReq struct {
|
||||
}
|
||||
|
||||
type WebsiteSSLUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
AutoRenew bool `json:"autoRenew"`
|
||||
Description string `json:"description"`
|
||||
ID uint `json:"id" validate:"required"`
|
||||
AutoRenew bool `json:"autoRenew"`
|
||||
Description string `json:"description"`
|
||||
PrimaryDomain string `json:"primaryDomain" validate:"required"`
|
||||
OtherDomains string `json:"otherDomains"`
|
||||
Provider string `json:"provider" validate:"required"`
|
||||
AcmeAccountID uint `json:"acmeAccountId"`
|
||||
DnsAccountID uint `json:"dnsAccountId"`
|
||||
KeyType string `json:"keyType"`
|
||||
Apply bool `json:"apply"`
|
||||
PushDir bool `json:"pushDir"`
|
||||
Dir string `json:"dir"`
|
||||
DisableCNAME bool `json:"disableCNAME"`
|
||||
SkipDNS bool `json:"skipDNS"`
|
||||
Nameserver1 string `json:"nameserver1"`
|
||||
Nameserver2 string `json:"nameserver2"`
|
||||
ExecShell bool `json:"execShell"`
|
||||
Shell string `json:"shell"`
|
||||
}
|
||||
|
||||
type WebsiteSSLUpload struct {
|
||||
@@ -108,6 +130,8 @@ type WebsiteCAObtain struct {
|
||||
Renew bool `json:"renew"`
|
||||
SSLID uint `json:"sslID"`
|
||||
Description string `json:"description"`
|
||||
ExecShell bool `json:"execShell"`
|
||||
Shell string `json:"shell"`
|
||||
}
|
||||
|
||||
type WebsiteCARenew struct {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
type AppRes struct {
|
||||
Items []*AppDTO `json:"items"`
|
||||
Items []*AppDto `json:"items"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
@@ -28,6 +28,22 @@ type AppDTO struct {
|
||||
Tags []model.Tag `json:"tags"`
|
||||
}
|
||||
|
||||
type AppDto struct {
|
||||
Name string `json:"name"`
|
||||
Key string `json:"key"`
|
||||
ID uint `json:"id"`
|
||||
ShortDescZh string `json:"shortDescZh"`
|
||||
ShortDescEn string `json:"shortDescEn"`
|
||||
Icon string `json:"icon"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
Resource string `json:"resource"`
|
||||
Installed bool `json:"installed"`
|
||||
Versions []string `json:"versions"`
|
||||
Limit int `json:"limit"`
|
||||
Tags []model.Tag `json:"tags"`
|
||||
}
|
||||
|
||||
type TagDTO struct {
|
||||
model.Tag
|
||||
}
|
||||
@@ -72,7 +88,38 @@ type AppInstalledDTO struct {
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
type AppDetail struct {
|
||||
Website string `json:"website"`
|
||||
Document string `json:"document"`
|
||||
Github string `json:"github"`
|
||||
}
|
||||
|
||||
type AppInstallDTO struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
AppID uint `json:"appID"`
|
||||
AppDetailID uint `json:"appDetailID"`
|
||||
Version string `json:"version"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
HttpPort int `json:"httpPort"`
|
||||
HttpsPort int `json:"httpsPort"`
|
||||
Path string `json:"path"`
|
||||
CanUpdate bool `json:"canUpdate"`
|
||||
Icon string `json:"icon"`
|
||||
AppName string `json:"appName"`
|
||||
Ready int `json:"ready"`
|
||||
Total int `json:"total"`
|
||||
AppKey string `json:"appKey"`
|
||||
AppType string `json:"appType"`
|
||||
AppStatus string `json:"appStatus"`
|
||||
DockerCompose string `json:"dockerCompose"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
App AppDetail `json:"app"`
|
||||
}
|
||||
|
||||
type DatabaseConn struct {
|
||||
Status string `json:"status"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
ContainerName string `json:"containerName"`
|
||||
|
||||
@@ -15,10 +15,12 @@ type UploadInfo struct {
|
||||
}
|
||||
|
||||
type FileTree struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Children []FileTree `json:"children"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
IsDir bool `json:"isDir"`
|
||||
Extension string `json:"extension"`
|
||||
Children []FileTree `json:"children"`
|
||||
}
|
||||
|
||||
type DirSizeRes struct {
|
||||
@@ -37,6 +39,7 @@ type FileLineContent struct {
|
||||
Content string `json:"content"`
|
||||
End bool `json:"end"`
|
||||
Path string `json:"path"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type FileExist struct {
|
||||
|
||||
@@ -2,6 +2,7 @@ package response
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WebsiteDTO struct {
|
||||
@@ -11,6 +12,24 @@ type WebsiteDTO struct {
|
||||
SitePath string `json:"sitePath"`
|
||||
AppName string `json:"appName"`
|
||||
RuntimeName string `json:"runtimeName"`
|
||||
SiteDir string `gorm:"type:varchar;" json:"siteDir"`
|
||||
}
|
||||
|
||||
type WebsiteRes struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Protocol string `json:"protocol"`
|
||||
PrimaryDomain string `json:"primaryDomain"`
|
||||
Type string `json:"type"`
|
||||
Alias string `json:"alias"`
|
||||
Remark string `json:"remark"`
|
||||
Status string `json:"status"`
|
||||
ExpireDate time.Time `json:"expireDate"`
|
||||
SitePath string `json:"sitePath"`
|
||||
AppName string `json:"appName"`
|
||||
RuntimeName string `json:"runtimeName"`
|
||||
SSLExpireDate time.Time `json:"sslExpireDate"`
|
||||
SSLStatus string `json:"sslStatus"`
|
||||
}
|
||||
|
||||
type WebsiteOption struct {
|
||||
@@ -37,6 +56,7 @@ type WebsiteHTTPS struct {
|
||||
SSL model.WebsiteSSL `json:"SSL"`
|
||||
SSLProtocol []string `json:"SSLProtocol"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
Hsts bool `json:"hsts"`
|
||||
}
|
||||
|
||||
type WebsiteLog struct {
|
||||
@@ -62,3 +82,7 @@ type WebsiteDirConfig struct {
|
||||
UserGroup string `json:"userGroup"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
type WebsiteHtmlRes struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ type SettingInfo struct {
|
||||
SystemIP string `json:"systemIP"`
|
||||
SystemVersion string `json:"systemVersion"`
|
||||
DockerSockPath string `json:"dockerSockPath"`
|
||||
DeveloperMode string `json:"developerMode"`
|
||||
|
||||
SessionTimeout string `json:"sessionTimeout"`
|
||||
LocalTime string `json:"localTime"`
|
||||
@@ -57,6 +58,13 @@ type SettingInfo struct {
|
||||
SnapshotIgnore string `json:"snapshotIgnore"`
|
||||
XpackHideMenu string `json:"xpackHideMenu"`
|
||||
NoAuthSetting string `json:"noAuthSetting"`
|
||||
|
||||
ProxyUrl string `json:"proxyUrl"`
|
||||
ProxyType string `json:"proxyType"`
|
||||
ProxyPort string `json:"proxyPort"`
|
||||
ProxyUser string `json:"proxyUser"`
|
||||
ProxyPasswd string `json:"proxyPasswd"`
|
||||
ProxyPasswdKeep string `json:"proxyPasswdKeep"`
|
||||
}
|
||||
|
||||
type SettingUpdate struct {
|
||||
@@ -108,11 +116,17 @@ type SnapshotCreate struct {
|
||||
From string `json:"from" validate:"required"`
|
||||
DefaultDownload string `json:"defaultDownload" validate:"required"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
type SnapshotRecover struct {
|
||||
IsNew bool `json:"isNew"`
|
||||
ReDownload bool `json:"reDownload"`
|
||||
ID uint `json:"id" validate:"required"`
|
||||
IsNew bool `json:"isNew"`
|
||||
ReDownload bool `json:"reDownload"`
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
type SnapshotBatchDelete struct {
|
||||
DeleteWithFile bool `json:"deleteWithFile"`
|
||||
Ids []uint `json:"ids" validate:"required"`
|
||||
}
|
||||
type SnapshotImport struct {
|
||||
From string `json:"from"`
|
||||
@@ -129,6 +143,7 @@ type SnapshotInfo struct {
|
||||
Message string `json:"message"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Version string `json:"version"`
|
||||
Size int64 `json:"size"`
|
||||
|
||||
InterruptStep string `json:"interruptStep"`
|
||||
RecoverStatus string `json:"recoverStatus"`
|
||||
@@ -140,6 +155,7 @@ type SnapshotInfo struct {
|
||||
}
|
||||
|
||||
type UpgradeInfo struct {
|
||||
TestVersion string `json:"testVersion"`
|
||||
NewVersion string `json:"newVersion"`
|
||||
LatestVersion string `json:"latestVersion"`
|
||||
ReleaseNote string `json:"releaseNote"`
|
||||
@@ -158,11 +174,21 @@ type Upgrade struct {
|
||||
Version string `json:"version" validate:"required"`
|
||||
}
|
||||
|
||||
type ProxyUpdate struct {
|
||||
ProxyUrl string `json:"proxyUrl"`
|
||||
ProxyType string `json:"proxyType"`
|
||||
ProxyPort string `json:"proxyPort"`
|
||||
ProxyUser string `json:"proxyUser"`
|
||||
ProxyPasswd string `json:"proxyPasswd"`
|
||||
ProxyPasswdKeep string `json:"proxyPasswdKeep"`
|
||||
}
|
||||
|
||||
type CleanData struct {
|
||||
SystemClean []CleanTree `json:"systemClean"`
|
||||
UploadClean []CleanTree `json:"uploadClean"`
|
||||
DownloadClean []CleanTree `json:"downloadClean"`
|
||||
SystemLogClean []CleanTree `json:"systemLogClean"`
|
||||
ContainerClean []CleanTree `json:"containerClean"`
|
||||
}
|
||||
|
||||
type CleanTree struct {
|
||||
@@ -183,3 +209,12 @@ type Clean struct {
|
||||
Name string `json:"name"`
|
||||
Size uint64 `json:"size"`
|
||||
}
|
||||
|
||||
type XpackHideMenu struct {
|
||||
ID string `json:"id"`
|
||||
Label string `json:"label"`
|
||||
IsCheck bool `json:"isCheck"`
|
||||
Title string `json:"title"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Children []XpackHideMenu `json:"children,omitempty"`
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ type App struct {
|
||||
Type string `json:"type" gorm:"type:varchar(64);not null"`
|
||||
Status string `json:"status" gorm:"type:varchar(64);not null"`
|
||||
Required string `json:"required" gorm:"type:varchar(64);"`
|
||||
CrossVersionUpdate bool `json:"crossVersionUpdate"`
|
||||
CrossVersionUpdate bool `json:"crossVersionUpdate" yaml:"crossVersionUpdate"`
|
||||
Limit int `json:"limit" gorm:"type:Integer;not null"`
|
||||
Website string `json:"website" gorm:"type:varchar(64);not null"`
|
||||
Github string `json:"github" gorm:"type:varchar(64);not null"`
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package model
|
||||
|
||||
type Clam struct {
|
||||
BaseModel
|
||||
|
||||
Name string `gorm:"type:varchar(64);not null" json:"name"`
|
||||
Status string `gorm:"type:varchar(64)" json:"status"`
|
||||
Path string `gorm:"type:varchar(64);not null" json:"path"`
|
||||
InfectedStrategy string `gorm:"type:varchar(64)" json:"infectedStrategy"`
|
||||
InfectedDir string `gorm:"type:varchar(64)" json:"infectedDir"`
|
||||
Spec string `gorm:"type:varchar(64)" json:"spec"`
|
||||
EntryID int `gorm:"type:varchar(64)" json:"entryID"`
|
||||
Description string `gorm:"type:varchar(64)" json:"description"`
|
||||
}
|
||||
@@ -6,3 +6,9 @@ type Command struct {
|
||||
GroupID uint `gorm:"type:decimal" json:"groupID"`
|
||||
Command string `gorm:"type:varchar(256);not null" json:"command"`
|
||||
}
|
||||
|
||||
type RedisCommand struct {
|
||||
BaseModel
|
||||
Name string `gorm:"type:varchar(64);unique;not null" json:"name"`
|
||||
Command string `gorm:"type:varchar(256);not null" json:"command"`
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Cronjob struct {
|
||||
BaseModel
|
||||
@@ -31,6 +33,7 @@ type Cronjob struct {
|
||||
Status string `gorm:"type:varchar(64)" json:"status"`
|
||||
EntryIDs string `gorm:"type:varchar(64)" json:"entryIDs"`
|
||||
Records []JobRecords `json:"records"`
|
||||
Secret string `gorm:"type:varchar(64)" json:"secret"`
|
||||
}
|
||||
|
||||
type JobRecords struct {
|
||||
|
||||
@@ -10,3 +10,12 @@ type Firewall struct {
|
||||
Strategy string `gorm:"type:varchar(64);not null" json:"strategy"`
|
||||
Description string `gorm:"type:varchar(64);not null" json:"description"`
|
||||
}
|
||||
|
||||
type Forward struct {
|
||||
BaseModel
|
||||
|
||||
Protocol string `gorm:"type:varchar(64);not null" json:"protocol"`
|
||||
Port string `gorm:"type:varchar(64);not null" json:"port"`
|
||||
TargetIP string `gorm:"type:varchar(64);not null" json:"targetIP"`
|
||||
TargetPort string `gorm:"type:varchar(64);not null" json:"targetPort"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package model
|
||||
|
||||
type Ftp struct {
|
||||
BaseModel
|
||||
|
||||
User string `gorm:"type:varchar(64);not null" json:"user"`
|
||||
Password string `gorm:"type:varchar(64);not null" json:"password"`
|
||||
Status string `gorm:"type:varchar(64);not null" json:"status"`
|
||||
Path string `gorm:"type:varchar(64);not null" json:"path"`
|
||||
Description string `gorm:"type:varchar(64);not null" json:"description"`
|
||||
}
|
||||
@@ -26,6 +26,7 @@ type Website struct {
|
||||
WebsiteSSLID uint `gorm:"type:integer" json:"webSiteSSLId"`
|
||||
RuntimeID uint `gorm:"type:integer" json:"runtimeID"`
|
||||
AppInstallID uint `gorm:"type:integer" json:"appInstallId"`
|
||||
FtpID uint `gorm:"type:integer" json:"ftpId"`
|
||||
|
||||
User string `gorm:"type:varchar;" json:"user"`
|
||||
Group string `gorm:"type:varchar;" json:"group"`
|
||||
|
||||
@@ -9,26 +9,32 @@ import (
|
||||
|
||||
type WebsiteSSL struct {
|
||||
BaseModel
|
||||
PrimaryDomain string `gorm:"type:varchar(256);not null" json:"primaryDomain"`
|
||||
PrivateKey string `gorm:"type:longtext;not null" json:"privateKey"`
|
||||
Pem string `gorm:"type:longtext;not null" json:"pem"`
|
||||
Domains string `gorm:"type:varchar(256);not null" json:"domains"`
|
||||
CertURL string `gorm:"type:varchar(256);not null" json:"certURL"`
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
Provider string `gorm:"type:varchar(64);not null" json:"provider"`
|
||||
Organization string `gorm:"type:varchar(64);not null" json:"organization"`
|
||||
DnsAccountID uint `gorm:"type:integer;not null" json:"dnsAccountId"`
|
||||
AcmeAccountID uint `gorm:"type:integer;not null" json:"acmeAccountId"`
|
||||
CaID uint `gorm:"type:integer;not null;default:0" json:"caId"`
|
||||
AutoRenew bool `gorm:"type:varchar(64);not null" json:"autoRenew"`
|
||||
PrimaryDomain string `json:"primaryDomain"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
Pem string `json:"pem"`
|
||||
Domains string `json:"domains"`
|
||||
CertURL string `json:"certURL"`
|
||||
Type string `json:"type"`
|
||||
Provider string `json:"provider"`
|
||||
Organization string `json:"organization"`
|
||||
DnsAccountID uint `json:"dnsAccountId"`
|
||||
AcmeAccountID uint `gorm:"column:acme_account_id" json:"acmeAccountId" `
|
||||
CaID uint `json:"caId"`
|
||||
AutoRenew bool `json:"autoRenew"`
|
||||
ExpireDate time.Time `json:"expireDate"`
|
||||
StartDate time.Time `json:"startDate"`
|
||||
Status string `gorm:"not null;default:ready" json:"status"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
KeyType string `gorm:"not null;default:2048" json:"keyType"`
|
||||
PushDir bool `gorm:"not null;default:0" json:"pushDir"`
|
||||
KeyType string `json:"keyType"`
|
||||
PushDir bool `json:"pushDir"`
|
||||
Dir string `json:"dir"`
|
||||
Description string `json:"description"`
|
||||
SkipDNS bool `json:"skipDNS"`
|
||||
Nameserver1 string `json:"nameserver1"`
|
||||
Nameserver2 string `json:"nameserver2"`
|
||||
DisableCNAME bool `json:"disableCNAME"`
|
||||
ExecShell bool `json:"execShell"`
|
||||
Shell string `json:"shell"`
|
||||
|
||||
AcmeAccount WebsiteAcmeAccount `json:"acmeAccount" gorm:"-:migration"`
|
||||
DnsAccount WebsiteDnsAccount `json:"dnsAccount" gorm:"-:migration"`
|
||||
|
||||
@@ -3,6 +3,7 @@ package repo
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
|
||||
"gorm.io/gorm/clause"
|
||||
@@ -160,6 +161,7 @@ func (a *AppInstallRepo) BatchUpdateBy(maps map[string]interface{}, opts ...DBOp
|
||||
type RootInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Port int64 `json:"port"`
|
||||
HttpsPort int64 `json:"httpsPort"`
|
||||
UserName string `json:"userName"`
|
||||
@@ -234,5 +236,6 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
|
||||
info.Key = app.Key
|
||||
appInstall.App = app
|
||||
info.AppPath = appInstall.GetAppPath()
|
||||
info.Status = appInstall.Status
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
)
|
||||
|
||||
type ClamRepo struct{}
|
||||
|
||||
type IClamRepo interface {
|
||||
Page(limit, offset int, opts ...DBOption) (int64, []model.Clam, error)
|
||||
Create(clam *model.Clam) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
Delete(opts ...DBOption) error
|
||||
Get(opts ...DBOption) (model.Clam, error)
|
||||
List(opts ...DBOption) ([]model.Clam, error)
|
||||
}
|
||||
|
||||
func NewIClamRepo() IClamRepo {
|
||||
return &ClamRepo{}
|
||||
}
|
||||
|
||||
func (u *ClamRepo) Get(opts ...DBOption) (model.Clam, error) {
|
||||
var clam model.Clam
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.First(&clam).Error
|
||||
return clam, err
|
||||
}
|
||||
|
||||
func (u *ClamRepo) List(opts ...DBOption) ([]model.Clam, error) {
|
||||
var clam []model.Clam
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(&clam).Error
|
||||
return clam, err
|
||||
}
|
||||
|
||||
func (u *ClamRepo) Page(page, size int, opts ...DBOption) (int64, []model.Clam, error) {
|
||||
var users []model.Clam
|
||||
db := global.DB.Model(&model.Clam{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
|
||||
return count, users, err
|
||||
}
|
||||
|
||||
func (u *ClamRepo) Create(clam *model.Clam) error {
|
||||
return global.DB.Create(clam).Error
|
||||
}
|
||||
|
||||
func (u *ClamRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.Clam{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
|
||||
func (u *ClamRepo) Delete(opts ...DBOption) error {
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
return db.Delete(&model.Clam{}).Error
|
||||
}
|
||||
@@ -16,6 +16,13 @@ type ICommandRepo interface {
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
Delete(opts ...DBOption) error
|
||||
Get(opts ...DBOption) (model.Command, error)
|
||||
WithLikeName(name string) DBOption
|
||||
|
||||
PageRedis(limit, offset int, opts ...DBOption) (int64, []model.RedisCommand, error)
|
||||
GetRedis(opts ...DBOption) (model.RedisCommand, error)
|
||||
GetRedisList(opts ...DBOption) ([]model.RedisCommand, error)
|
||||
SaveRedis(command *model.RedisCommand) error
|
||||
DeleteRedis(opts ...DBOption) error
|
||||
}
|
||||
|
||||
func NewICommandRepo() ICommandRepo {
|
||||
@@ -32,6 +39,16 @@ func (u *CommandRepo) Get(opts ...DBOption) (model.Command, error) {
|
||||
return command, err
|
||||
}
|
||||
|
||||
func (u *CommandRepo) GetRedis(opts ...DBOption) (model.RedisCommand, error) {
|
||||
var command model.RedisCommand
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.First(&command).Error
|
||||
return command, err
|
||||
}
|
||||
|
||||
func (u *CommandRepo) Page(page, size int, opts ...DBOption) (int64, []model.Command, error) {
|
||||
var users []model.Command
|
||||
db := global.DB.Model(&model.Command{})
|
||||
@@ -44,6 +61,18 @@ func (u *CommandRepo) Page(page, size int, opts ...DBOption) (int64, []model.Com
|
||||
return count, users, err
|
||||
}
|
||||
|
||||
func (u *CommandRepo) PageRedis(page, size int, opts ...DBOption) (int64, []model.RedisCommand, error) {
|
||||
var users []model.RedisCommand
|
||||
db := global.DB.Model(&model.RedisCommand{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
|
||||
return count, users, err
|
||||
}
|
||||
|
||||
func (u *CommandRepo) GetList(opts ...DBOption) ([]model.Command, error) {
|
||||
var commands []model.Command
|
||||
db := global.DB.Model(&model.Command{})
|
||||
@@ -54,6 +83,16 @@ func (u *CommandRepo) GetList(opts ...DBOption) ([]model.Command, error) {
|
||||
return commands, err
|
||||
}
|
||||
|
||||
func (u *CommandRepo) GetRedisList(opts ...DBOption) ([]model.RedisCommand, error) {
|
||||
var commands []model.RedisCommand
|
||||
db := global.DB.Model(&model.RedisCommand{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(&commands).Error
|
||||
return commands, err
|
||||
}
|
||||
|
||||
func (c *CommandRepo) WithByInfo(info string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(info) == 0 {
|
||||
@@ -68,6 +107,10 @@ func (u *CommandRepo) Create(command *model.Command) error {
|
||||
return global.DB.Create(command).Error
|
||||
}
|
||||
|
||||
func (u *CommandRepo) SaveRedis(command *model.RedisCommand) error {
|
||||
return global.DB.Save(command).Error
|
||||
}
|
||||
|
||||
func (u *CommandRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.Command{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
@@ -79,3 +122,20 @@ func (u *CommandRepo) Delete(opts ...DBOption) error {
|
||||
}
|
||||
return db.Delete(&model.Command{}).Error
|
||||
}
|
||||
|
||||
func (u *CommandRepo) DeleteRedis(opts ...DBOption) error {
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
return db.Delete(&model.RedisCommand{}).Error
|
||||
}
|
||||
|
||||
func (a CommandRepo) WithLikeName(name string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(name) == 0 {
|
||||
return g
|
||||
}
|
||||
return g.Where("name like ? or command like ?", "%"+name+"%", "%"+name+"%")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type FtpRepo struct{}
|
||||
|
||||
type IFtpRepo interface {
|
||||
Get(opts ...DBOption) (model.Ftp, error)
|
||||
GetList(opts ...DBOption) ([]model.Ftp, error)
|
||||
Page(limit, offset int, opts ...DBOption) (int64, []model.Ftp, error)
|
||||
Create(ftp *model.Ftp) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
Delete(opts ...DBOption) error
|
||||
WithByUser(user string) DBOption
|
||||
}
|
||||
|
||||
func NewIFtpRepo() IFtpRepo {
|
||||
return &FtpRepo{}
|
||||
}
|
||||
|
||||
func (u *FtpRepo) Get(opts ...DBOption) (model.Ftp, error) {
|
||||
var ftp model.Ftp
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.First(&ftp).Error
|
||||
return ftp, err
|
||||
}
|
||||
|
||||
func (h *FtpRepo) WithByUser(user string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(user) == 0 {
|
||||
return g
|
||||
}
|
||||
return g.Where("user like ?", "%"+user+"%")
|
||||
}
|
||||
}
|
||||
|
||||
func (u *FtpRepo) GetList(opts ...DBOption) ([]model.Ftp, error) {
|
||||
var ftps []model.Ftp
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(&ftps).Error
|
||||
return ftps, err
|
||||
}
|
||||
|
||||
func (h *FtpRepo) Page(page, size int, opts ...DBOption) (int64, []model.Ftp, error) {
|
||||
var users []model.Ftp
|
||||
db := global.DB.Model(&model.Ftp{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
|
||||
return count, users, err
|
||||
}
|
||||
|
||||
func (h *FtpRepo) Create(ftp *model.Ftp) error {
|
||||
return global.DB.Create(ftp).Error
|
||||
}
|
||||
|
||||
func (h *FtpRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.Ftp{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
|
||||
func (h *FtpRepo) Delete(opts ...DBOption) error {
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
return db.Delete(&model.Ftp{}).Error
|
||||
}
|
||||
@@ -96,7 +96,7 @@ func (w *WebsiteRepo) Page(page, size int, opts ...DBOption) (int64, []model.Web
|
||||
db := getDb(opts...).Model(&model.Website{})
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Preload("WebsiteSSL").Find(&websites).Error
|
||||
err := db.Debug().Limit(size).Offset(size * (page - 1)).Preload("WebsiteSSL").Find(&websites).Error
|
||||
return count, websites, err
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ type ISSLRepo interface {
|
||||
Create(ctx context.Context, ssl *model.WebsiteSSL) error
|
||||
Save(ssl *model.WebsiteSSL) error
|
||||
DeleteBy(opts ...DBOption) error
|
||||
SaveByMap(ssl *model.WebsiteSSL, params map[string]interface{}) error
|
||||
}
|
||||
|
||||
type WebsiteSSLRepo struct {
|
||||
@@ -82,7 +83,15 @@ func (w WebsiteSSLRepo) Create(ctx context.Context, ssl *model.WebsiteSSL) error
|
||||
}
|
||||
|
||||
func (w WebsiteSSLRepo) Save(ssl *model.WebsiteSSL) error {
|
||||
return getDb().Save(&ssl).Error
|
||||
return getDb().Model(&model.WebsiteSSL{BaseModel: model.BaseModel{
|
||||
ID: ssl.ID,
|
||||
}}).Save(&ssl).Error
|
||||
}
|
||||
|
||||
func (w WebsiteSSLRepo) SaveByMap(ssl *model.WebsiteSSL, params map[string]interface{}) error {
|
||||
return getDb().Model(&model.WebsiteSSL{BaseModel: model.BaseModel{
|
||||
ID: ssl.ID,
|
||||
}}).Updates(params).Error
|
||||
}
|
||||
|
||||
func (w WebsiteSSLRepo) DeleteBy(opts ...DBOption) error {
|
||||
|
||||
+44
-35
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -26,6 +25,8 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
http2 "github.com/1Panel-dev/1Panel/backend/utils/http"
|
||||
httpUtil "github.com/1Panel-dev/1Panel/backend/utils/http"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -88,14 +89,22 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var appDTOs []*response.AppDTO
|
||||
var appDTOs []*response.AppDto
|
||||
for _, ap := range apps {
|
||||
ap.ReadMe = ""
|
||||
ap.Website = ""
|
||||
ap.Document = ""
|
||||
ap.Github = ""
|
||||
appDTO := &response.AppDTO{
|
||||
App: ap,
|
||||
appDTO := &response.AppDto{
|
||||
ID: ap.ID,
|
||||
Name: ap.Name,
|
||||
Key: ap.Key,
|
||||
Type: ap.Type,
|
||||
Icon: ap.Icon,
|
||||
ShortDescZh: ap.ShortDescZh,
|
||||
ShortDescEn: ap.ShortDescEn,
|
||||
Resource: ap.Resource,
|
||||
Limit: ap.Limit,
|
||||
}
|
||||
appDTOs = append(appDTOs, appDTO)
|
||||
appTags, err := appTagRepo.GetByAppId(ap.ID)
|
||||
@@ -136,6 +145,9 @@ func (a AppService) GetAppTags() ([]response.TagDTO, error) {
|
||||
|
||||
func (a AppService) GetApp(key string) (*response.AppDTO, error) {
|
||||
var appDTO response.AppDTO
|
||||
if key == "postgres" {
|
||||
key = "postgresql"
|
||||
}
|
||||
app, err := appRepo.GetFirst(appRepo.WithKey(key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -226,20 +238,16 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
|
||||
if appDetailDTO.DockerCompose == "" {
|
||||
filename := filepath.Base(appDetailDTO.DownloadUrl)
|
||||
dockerComposeUrl := fmt.Sprintf("%s%s", strings.TrimSuffix(appDetailDTO.DownloadUrl, filename), "docker-compose.yml")
|
||||
composeRes, err := http.Get(dockerComposeUrl)
|
||||
statusCode, composeRes, err := httpUtil.HandleGet(dockerComposeUrl, http.MethodGet, constant.TimeOut20s)
|
||||
if err != nil {
|
||||
return appDetailDTO, buserr.WithDetail("ErrGetCompose", err.Error(), err)
|
||||
}
|
||||
bodyContent, err := io.ReadAll(composeRes.Body)
|
||||
if err != nil {
|
||||
return appDetailDTO, buserr.WithDetail("ErrGetCompose", err.Error(), err)
|
||||
if statusCode > 200 {
|
||||
return appDetailDTO, buserr.WithDetail("ErrGetCompose", string(composeRes), err)
|
||||
}
|
||||
if composeRes.StatusCode > 200 {
|
||||
return appDetailDTO, buserr.WithDetail("ErrGetCompose", string(bodyContent), err)
|
||||
}
|
||||
detail.DockerCompose = string(bodyContent)
|
||||
detail.DockerCompose = string(composeRes)
|
||||
_ = appDetailRepo.Update(context.Background(), detail)
|
||||
appDetailDTO.DockerCompose = string(bodyContent)
|
||||
appDetailDTO.DockerCompose = string(composeRes)
|
||||
}
|
||||
|
||||
appDetailDTO.HostMode = isHostModel(appDetailDTO.DockerCompose)
|
||||
@@ -362,7 +370,7 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
||||
}
|
||||
|
||||
value, ok := composeMap["services"]
|
||||
if !ok {
|
||||
if !ok || value == nil {
|
||||
err = buserr.New(constant.ErrFileParse)
|
||||
return
|
||||
}
|
||||
@@ -439,6 +447,7 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
||||
return
|
||||
}
|
||||
appInstall.Env = string(paramByte)
|
||||
|
||||
if err = appInstallRepo.Create(ctx, appInstall); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -448,7 +457,7 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
||||
go func() {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
appInstall.Status = constant.Error
|
||||
appInstall.Status = constant.UpErr
|
||||
appInstall.Message = err.Error()
|
||||
_ = appInstallRepo.Save(context.Background(), appInstall)
|
||||
}
|
||||
@@ -698,16 +707,11 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
|
||||
}
|
||||
|
||||
versionUrl := fmt.Sprintf("%s/%s/1panel.json.version.txt", global.CONF.System.AppRepo, global.CONF.System.Mode)
|
||||
versionRes, err := http2.GetHttpRes(versionUrl)
|
||||
_, versionRes, err := http2.HandleGet(versionUrl, http.MethodGet, constant.TimeOut20s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer versionRes.Body.Close()
|
||||
body, err := io.ReadAll(versionRes.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lastModifiedStr := string(body)
|
||||
lastModifiedStr := string(versionRes)
|
||||
lastModified, err := strconv.Atoi(lastModifiedStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -727,6 +731,14 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
|
||||
res.CanUpdate = true
|
||||
return res, err
|
||||
}
|
||||
apps, _ := appRepo.GetBy(appRepo.WithResource(constant.AppResourceRemote))
|
||||
for _, app := range apps {
|
||||
if app.Icon == "" {
|
||||
res.CanUpdate = true
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
||||
list, err := getAppList()
|
||||
if err != nil {
|
||||
return res, err
|
||||
@@ -744,10 +756,10 @@ func getAppFromRepo(downloadPath string) error {
|
||||
global.LOG.Infof("[AppStore] download file from %s", downloadUrl)
|
||||
fileOp := files.NewFileOp()
|
||||
packagePath := filepath.Join(constant.ResourceDir, filepath.Base(downloadUrl))
|
||||
if err := fileOp.DownloadFile(downloadUrl, packagePath); err != nil {
|
||||
if err := fileOp.DownloadFileWithProxy(downloadUrl, packagePath); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.Decompress(packagePath, constant.ResourceDir, files.SdkZip); err != nil {
|
||||
if err := fileOp.Decompress(packagePath, constant.ResourceDir, files.SdkZip, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
@@ -825,21 +837,22 @@ func (a AppService) SyncAppListFromRemote() (err error) {
|
||||
oldAppIds = append(oldAppIds, old.ID)
|
||||
}
|
||||
|
||||
transport := xpack.LoadRequestTransport()
|
||||
baseRemoteUrl := fmt.Sprintf("%s/%s/1panel", global.CONF.System.AppRepo, global.CONF.System.Mode)
|
||||
appsMap := getApps(oldApps, list.Apps)
|
||||
|
||||
global.LOG.Infof("Starting synchronization of application details...")
|
||||
for _, l := range list.Apps {
|
||||
app := appsMap[l.AppProperty.Key]
|
||||
iconRes, err := http.Get(l.Icon)
|
||||
_, iconRes, err := httpUtil.HandleGetWithTransport(l.Icon, http.MethodGet, transport, constant.TimeOut20s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body, err := io.ReadAll(iconRes.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
iconStr := ""
|
||||
if !strings.Contains(string(iconRes), "<xml>") {
|
||||
iconStr = base64.StdEncoding.EncodeToString(iconRes)
|
||||
}
|
||||
iconStr := base64.StdEncoding.EncodeToString(body)
|
||||
|
||||
app.Icon = iconStr
|
||||
app.TagsKey = l.AppProperty.Tags
|
||||
if l.AppProperty.Recommend > 0 {
|
||||
@@ -858,15 +871,11 @@ func (a AppService) SyncAppListFromRemote() (err error) {
|
||||
|
||||
if _, ok := InitTypes[app.Type]; ok {
|
||||
dockerComposeUrl := fmt.Sprintf("%s/%s", versionUrl, "docker-compose.yml")
|
||||
composeRes, err := http.Get(dockerComposeUrl)
|
||||
_, composeRes, err := httpUtil.HandleGetWithTransport(dockerComposeUrl, http.MethodGet, transport, constant.TimeOut20s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bodyContent, err := io.ReadAll(composeRes.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
detail.DockerCompose = string(bodyContent)
|
||||
detail.DockerCompose = string(composeRes)
|
||||
} else {
|
||||
detail.DockerCompose = ""
|
||||
}
|
||||
|
||||
@@ -5,14 +5,18 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
httpUtil "github.com/1Panel-dev/1Panel/backend/utils/http"
|
||||
"github.com/docker/docker/api/types"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/env"
|
||||
@@ -39,17 +43,17 @@ type AppInstallService struct {
|
||||
}
|
||||
|
||||
type IAppInstallService interface {
|
||||
Page(req request.AppInstalledSearch) (int64, []response.AppInstalledDTO, error)
|
||||
Page(req request.AppInstalledSearch) (int64, []response.AppInstallDTO, error)
|
||||
CheckExist(req request.AppInstalledInfo) (*response.AppInstalledCheck, error)
|
||||
LoadPort(req dto.OperationWithNameAndType) (int64, error)
|
||||
LoadConnInfo(req dto.OperationWithNameAndType) (response.DatabaseConn, error)
|
||||
SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstalledDTO, error)
|
||||
SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstallDTO, error)
|
||||
Operate(req request.AppInstalledOperate) error
|
||||
Update(req request.AppInstalledUpdate) error
|
||||
IgnoreUpgrade(req request.AppInstalledIgnoreUpgrade) error
|
||||
SyncAll(systemInit bool) error
|
||||
GetServices(key string) ([]response.AppService, error)
|
||||
GetUpdateVersions(installId uint) ([]dto.AppVersion, error)
|
||||
GetUpdateVersions(req request.AppUpdateVersion) ([]dto.AppVersion, error)
|
||||
GetParams(id uint) (*response.AppConfig, error)
|
||||
ChangeAppPort(req request.PortUpdate) error
|
||||
GetDefaultConfigByKey(key, name string) (string, error)
|
||||
@@ -74,7 +78,7 @@ func (a *AppInstallService) GetInstallList() ([]dto.AppInstallInfo, error) {
|
||||
return datas, nil
|
||||
}
|
||||
|
||||
func (a *AppInstallService) Page(req request.AppInstalledSearch) (int64, []response.AppInstalledDTO, error) {
|
||||
func (a *AppInstallService) Page(req request.AppInstalledSearch) (int64, []response.AppInstallDTO, error) {
|
||||
var (
|
||||
opts []repo.DBOption
|
||||
total int64
|
||||
@@ -118,7 +122,7 @@ func (a *AppInstallService) Page(req request.AppInstalledSearch) (int64, []respo
|
||||
}
|
||||
}
|
||||
|
||||
installDTOs, err := handleInstalled(installs, req.Update)
|
||||
installDTOs, err := handleInstalled(installs, req.Update, req.Sync)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
@@ -150,7 +154,7 @@ func (a *AppInstallService) CheckExist(req request.AppInstalledInfo) (*response.
|
||||
if reflect.DeepEqual(appInstall, model.AppInstall{}) {
|
||||
return res, nil
|
||||
}
|
||||
if err = syncAppInstallStatus(&appInstall); err != nil {
|
||||
if err = syncAppInstallStatus(&appInstall, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -182,6 +186,7 @@ func (a *AppInstallService) LoadConnInfo(req dto.OperationWithNameAndType) (resp
|
||||
if err != nil {
|
||||
return data, nil
|
||||
}
|
||||
data.Status = app.Status
|
||||
data.Username = app.UserName
|
||||
data.Password = app.Password
|
||||
data.ServiceName = app.ServiceName
|
||||
@@ -190,7 +195,7 @@ func (a *AppInstallService) LoadConnInfo(req dto.OperationWithNameAndType) (resp
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (a *AppInstallService) SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstalledDTO, error) {
|
||||
func (a *AppInstallService) SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstallDTO, error) {
|
||||
var (
|
||||
installs []model.AppInstall
|
||||
err error
|
||||
@@ -220,7 +225,7 @@ func (a *AppInstallService) SearchForWebsite(req request.AppInstalledSearch) ([]
|
||||
}
|
||||
}
|
||||
|
||||
return handleInstalled(installs, false)
|
||||
return handleInstalled(installs, false, true)
|
||||
}
|
||||
|
||||
func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
|
||||
@@ -240,28 +245,35 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
|
||||
if err != nil {
|
||||
return handleErr(install, err, out)
|
||||
}
|
||||
return syncAppInstallStatus(&install)
|
||||
return syncAppInstallStatus(&install, false)
|
||||
case constant.Stop:
|
||||
out, err := compose.Stop(dockerComposePath)
|
||||
if err != nil {
|
||||
return handleErr(install, err, out)
|
||||
}
|
||||
return syncAppInstallStatus(&install)
|
||||
return syncAppInstallStatus(&install, false)
|
||||
case constant.Restart:
|
||||
out, err := compose.Restart(dockerComposePath)
|
||||
if err != nil {
|
||||
return handleErr(install, err, out)
|
||||
}
|
||||
return syncAppInstallStatus(&install)
|
||||
return syncAppInstallStatus(&install, false)
|
||||
case constant.Delete:
|
||||
if err := deleteAppInstall(install, req.DeleteBackup, req.ForceDelete, req.DeleteDB); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case constant.Sync:
|
||||
return syncAppInstallStatus(&install)
|
||||
return syncAppInstallStatus(&install, true)
|
||||
case constant.Upgrade:
|
||||
return upgradeInstall(install.ID, req.DetailId, req.Backup, req.PullImage)
|
||||
upgradeReq := request.AppInstallUpgrade{
|
||||
InstallID: install.ID,
|
||||
DetailID: req.DetailId,
|
||||
Backup: req.Backup,
|
||||
PullImage: req.PullImage,
|
||||
DockerCompose: req.DockerCompose,
|
||||
}
|
||||
return upgradeInstall(upgradeReq)
|
||||
case constant.Reload:
|
||||
return opNginx(install.ContainerName, constant.NginxReload)
|
||||
default:
|
||||
@@ -420,7 +432,7 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
|
||||
continue
|
||||
}
|
||||
if !systemInit {
|
||||
if err = syncAppInstallStatus(&i); err != nil {
|
||||
if err = syncAppInstallStatus(&i, false); err != nil {
|
||||
global.LOG.Errorf("sync install app[%s] error,mgs: %s", i.Name, err.Error())
|
||||
}
|
||||
}
|
||||
@@ -483,8 +495,8 @@ func (a *AppInstallService) GetServices(key string) ([]response.AppService, erro
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (a *AppInstallService) GetUpdateVersions(installId uint) ([]dto.AppVersion, error) {
|
||||
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId))
|
||||
func (a *AppInstallService) GetUpdateVersions(req request.AppUpdateVersion) ([]dto.AppVersion, error) {
|
||||
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(req.AppInstallID))
|
||||
var versions []dto.AppVersion
|
||||
if err != nil {
|
||||
return versions, err
|
||||
@@ -505,9 +517,28 @@ func (a *AppInstallService) GetUpdateVersions(installId uint) ([]dto.AppVersion,
|
||||
continue
|
||||
}
|
||||
if common.CompareVersion(detail.Version, install.Version) {
|
||||
var newCompose string
|
||||
if req.UpdateVersion != "" && req.UpdateVersion == detail.Version && detail.DockerCompose == "" && !app.IsLocalApp() {
|
||||
filename := filepath.Base(detail.DownloadUrl)
|
||||
dockerComposeUrl := fmt.Sprintf("%s%s", strings.TrimSuffix(detail.DownloadUrl, filename), "docker-compose.yml")
|
||||
statusCode, composeRes, err := httpUtil.HandleGet(dockerComposeUrl, http.MethodGet, constant.TimeOut20s)
|
||||
if err != nil {
|
||||
return versions, err
|
||||
}
|
||||
if statusCode > 200 {
|
||||
return versions, err
|
||||
}
|
||||
detail.DockerCompose = string(composeRes)
|
||||
_ = appDetailRepo.Update(context.Background(), detail)
|
||||
}
|
||||
newCompose, err = getUpgradeCompose(install, detail)
|
||||
if err != nil {
|
||||
return versions, err
|
||||
}
|
||||
versions = append(versions, dto.AppVersion{
|
||||
Version: detail.Version,
|
||||
DetailId: detail.ID,
|
||||
Version: detail.Version,
|
||||
DetailId: detail.ID,
|
||||
DockerCompose: newCompose,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -700,81 +731,30 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func syncAppInstallStatus(appInstall *model.AppInstall) error {
|
||||
func syncAppInstallStatus(appInstall *model.AppInstall, force bool) error {
|
||||
if appInstall.Status == constant.Installing || appInstall.Status == constant.Rebuilding || appInstall.Status == constant.Upgrading {
|
||||
return nil
|
||||
}
|
||||
containerNames, err := getContainerNames(*appInstall)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cli, err := docker.NewClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
containers, err := cli.ListContainersByName(containerNames)
|
||||
defer cli.Close()
|
||||
|
||||
var (
|
||||
containers []types.Container
|
||||
containersMap map[string]types.Container
|
||||
containerNames = strings.Split(appInstall.ContainerName, ",")
|
||||
)
|
||||
containers, err = cli.ListContainersByName(containerNames)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
runningCount int
|
||||
exitedCount int
|
||||
pausedCount int
|
||||
exitedContainerNames []string
|
||||
total = len(containerNames)
|
||||
count = len(containers)
|
||||
)
|
||||
|
||||
foundNames := make(map[string]bool)
|
||||
containersMap = make(map[string]types.Container)
|
||||
for _, con := range containers {
|
||||
foundNames[con.Names[0]] = true
|
||||
switch con.State {
|
||||
case "exited":
|
||||
exitedContainerNames = append(exitedContainerNames, strings.TrimPrefix(con.Names[0], "/"))
|
||||
exitedCount++
|
||||
case "running":
|
||||
runningCount++
|
||||
case "paused":
|
||||
pausedCount++
|
||||
}
|
||||
containersMap[con.Names[0]] = con
|
||||
}
|
||||
|
||||
var notFoundNames []string
|
||||
for _, name := range containerNames {
|
||||
if !foundNames["/"+name] {
|
||||
notFoundNames = append(notFoundNames, name)
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case count == 0:
|
||||
if appInstall.Status != constant.Error {
|
||||
appInstall.Status = constant.SyncErr
|
||||
appInstall.Message = buserr.WithName("ErrContainerNotFound", strings.Join(containerNames, ",")).Error()
|
||||
}
|
||||
case exitedCount == total:
|
||||
appInstall.Status = constant.Stopped
|
||||
case runningCount == total:
|
||||
appInstall.Status = constant.Running
|
||||
case pausedCount == total:
|
||||
appInstall.Status = constant.Paused
|
||||
default:
|
||||
var msg string
|
||||
if exitedCount > 0 {
|
||||
msg = buserr.WithName("ErrContainerMsg", strings.Join(exitedContainerNames, ",")).Error()
|
||||
}
|
||||
if len(notFoundNames) > 0 {
|
||||
msg += buserr.WithName("ErrContainerNotFound", strings.Join(notFoundNames, ",")).Error()
|
||||
}
|
||||
if msg == "" {
|
||||
msg = buserr.New("ErrAppWarn").Error()
|
||||
}
|
||||
appInstall.Message = msg
|
||||
appInstall.Status = constant.UnHealthy
|
||||
}
|
||||
_ = appInstallRepo.Save(context.Background(), appInstall)
|
||||
|
||||
synAppInstall(containersMap, appInstall, force)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -802,7 +782,7 @@ func updateInstallInfoInDB(appKey, appName, param string, value interface{}) err
|
||||
}
|
||||
case "port":
|
||||
envKey = "PANEL_APP_PORT_HTTP="
|
||||
case "user-password":
|
||||
default:
|
||||
envKey = "PANEL_DB_USER_PASSWORD="
|
||||
}
|
||||
files := strings.Split(string(lineBytes), "\n")
|
||||
|
||||
+279
-103
@@ -5,8 +5,6 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"math"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -16,6 +14,13 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
|
||||
httpUtil "github.com/1Panel-dev/1Panel/backend/utils/http"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
|
||||
@@ -37,6 +42,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
composeV2 "github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/pkg/errors"
|
||||
@@ -435,12 +441,65 @@ func deleteLink(ctx context.Context, install *model.AppInstall, deleteDB bool, f
|
||||
return appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||
}
|
||||
|
||||
func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error {
|
||||
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installID))
|
||||
func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string, error) {
|
||||
if detail.DockerCompose == "" {
|
||||
return "", nil
|
||||
}
|
||||
composeMap := make(map[string]interface{})
|
||||
if err := yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); err != nil {
|
||||
return "", err
|
||||
}
|
||||
value, ok := composeMap["services"]
|
||||
if !ok || value == nil {
|
||||
return "", buserr.New(constant.ErrFileParse)
|
||||
}
|
||||
servicesMap := value.(map[string]interface{})
|
||||
if len(servicesMap) == 1 {
|
||||
index := 0
|
||||
oldServiceName := ""
|
||||
for k := range servicesMap {
|
||||
oldServiceName = k
|
||||
index++
|
||||
if index > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
servicesMap[install.ServiceName] = servicesMap[oldServiceName]
|
||||
if install.ServiceName != oldServiceName {
|
||||
delete(servicesMap, oldServiceName)
|
||||
}
|
||||
}
|
||||
envs := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(install.Env), &envs); err != nil {
|
||||
return "", err
|
||||
}
|
||||
config := getAppCommonConfig(envs)
|
||||
if config.ContainerName == "" {
|
||||
config.ContainerName = install.ContainerName
|
||||
envs[constant.ContainerName] = install.ContainerName
|
||||
}
|
||||
config.Advanced = true
|
||||
if err := addDockerComposeCommonParam(composeMap, install.ServiceName, config, envs); err != nil {
|
||||
return "", err
|
||||
}
|
||||
paramByte, err := json.Marshal(envs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
install.Env = string(paramByte)
|
||||
composeByte, err := yaml.Marshal(composeMap)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(composeByte), nil
|
||||
}
|
||||
|
||||
func upgradeInstall(req request.AppInstallUpgrade) error {
|
||||
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(req.InstallID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
detail, err := appDetailRepo.GetFirst(commonRepo.WithByID(detailID))
|
||||
detail, err := appDetailRepo.GetFirst(commonRepo.WithByID(req.DetailID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -450,43 +509,70 @@ func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error
|
||||
install.Status = constant.Upgrading
|
||||
|
||||
go func() {
|
||||
if backup {
|
||||
if err = NewIBackupService().AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name}); err != nil {
|
||||
global.LOG.Errorf(i18n.GetMsgWithMap("ErrAppBackup", map[string]interface{}{"name": install.Name, "err": err.Error()}))
|
||||
var (
|
||||
upErr error
|
||||
backupFile string
|
||||
preErr error
|
||||
)
|
||||
global.LOG.Infof(i18n.GetMsgWithName("UpgradeAppStart", install.Name, nil))
|
||||
if req.Backup {
|
||||
backupRecord, err := NewIBackupService().AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name})
|
||||
if err == nil {
|
||||
localDir, err := loadLocalDir()
|
||||
if err == nil {
|
||||
backupFile = path.Join(localDir, backupRecord.FileDir, backupRecord.FileName)
|
||||
} else {
|
||||
global.LOG.Errorf(i18n.GetMsgWithName("ErrAppBackup", install.Name, err))
|
||||
}
|
||||
} else {
|
||||
global.LOG.Errorf(i18n.GetMsgWithName("ErrAppBackup", install.Name, err))
|
||||
}
|
||||
}
|
||||
var upErr error
|
||||
|
||||
defer func() {
|
||||
if upErr != nil {
|
||||
if backup {
|
||||
if err := NewIBackupService().AppRecover(dto.CommonRecover{Name: install.App.Key, DetailName: install.Name, Type: "app", Source: constant.ResourceLocal}); err != nil {
|
||||
global.LOG.Infof(i18n.GetMsgWithName("ErrAppUpgrade", install.Name, upErr))
|
||||
if req.Backup {
|
||||
global.LOG.Infof(i18n.GetMsgWithName("AppRecover", install.Name, nil))
|
||||
if err := NewIBackupService().AppRecover(dto.CommonRecover{Name: install.App.Key, DetailName: install.Name, Type: "app", Source: constant.ResourceLocal, File: backupFile}); err != nil {
|
||||
global.LOG.Errorf("recover app [%s] [%s] failed %v", install.App.Key, install.Name, err)
|
||||
}
|
||||
}
|
||||
install.Status = constant.UpgradeErr
|
||||
install.Message = upErr.Error()
|
||||
_ = appInstallRepo.Save(context.Background(), &install)
|
||||
existInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(req.InstallID))
|
||||
if existInstall.ID > 0 {
|
||||
existInstall.Status = constant.UpgradeErr
|
||||
existInstall.Message = upErr.Error()
|
||||
_ = appInstallRepo.Save(context.Background(), &existInstall)
|
||||
}
|
||||
}
|
||||
if preErr != nil {
|
||||
global.LOG.Infof(i18n.GetMsgWithName("ErrAppUpgrade", install.Name, preErr))
|
||||
existInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(req.InstallID))
|
||||
if existInstall.ID > 0 {
|
||||
existInstall.Status = constant.UpgradeErr
|
||||
existInstall.Message = preErr.Error()
|
||||
_ = appInstallRepo.Save(context.Background(), &existInstall)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Resource, install.App.Key, detail.Version)
|
||||
if install.App.Resource == constant.AppResourceRemote {
|
||||
if upErr = downloadApp(install.App, detail, &install); upErr != nil {
|
||||
if preErr = downloadApp(install.App, detail, &install); preErr != nil {
|
||||
return
|
||||
}
|
||||
if detail.DockerCompose == "" {
|
||||
composeDetail, err := fileOp.GetContent(path.Join(detailDir, "docker-compose.yml"))
|
||||
if err != nil {
|
||||
upErr = err
|
||||
preErr = err
|
||||
return
|
||||
}
|
||||
detail.DockerCompose = string(composeDetail)
|
||||
_ = appDetailRepo.Update(context.Background(), detail)
|
||||
}
|
||||
go func() {
|
||||
_, _ = http.Get(detail.DownloadCallBackUrl)
|
||||
_, _, _ = httpUtil.HandleGet(detail.DownloadCallBackUrl, http.MethodGet, constant.TimeOut5s)
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -494,6 +580,32 @@ func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error
|
||||
detailDir = path.Join(constant.ResourceDir, "apps", "local", strings.TrimPrefix(install.App.Key, "local"), detail.Version)
|
||||
}
|
||||
|
||||
content, err := fileOp.GetContent(install.GetEnvPath())
|
||||
if err != nil {
|
||||
preErr = err
|
||||
return
|
||||
}
|
||||
if req.PullImage {
|
||||
projectName := strings.ToLower(install.Name)
|
||||
images, err := composeV2.GetDockerComposeImages(projectName, content, []byte(detail.DockerCompose))
|
||||
if err != nil {
|
||||
preErr = err
|
||||
return
|
||||
}
|
||||
for _, image := range images {
|
||||
global.LOG.Infof(i18n.GetMsgWithName("PullImageStart", image, nil))
|
||||
if out, err := cmd.ExecWithTimeOut("docker pull "+image, 20*time.Minute); err != nil {
|
||||
if out != "" {
|
||||
err = errors.New(out)
|
||||
}
|
||||
preErr = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
|
||||
return
|
||||
} else {
|
||||
global.LOG.Infof(i18n.GetMsgByKey("PullImageSuccess"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
command := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rn %s/* %s || true", detailDir, install.GetPath()))
|
||||
stdout, _ := command.CombinedOutput()
|
||||
if stdout != nil {
|
||||
@@ -508,100 +620,42 @@ func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error
|
||||
_, _ = scriptCmd.CombinedOutput()
|
||||
}
|
||||
|
||||
composeMap := make(map[string]interface{})
|
||||
if upErr = yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); upErr != nil {
|
||||
return
|
||||
}
|
||||
value, ok := composeMap["services"]
|
||||
if !ok {
|
||||
upErr = buserr.New(constant.ErrFileParse)
|
||||
return
|
||||
}
|
||||
servicesMap := value.(map[string]interface{})
|
||||
if len(servicesMap) == 1 {
|
||||
index := 0
|
||||
oldServiceName := ""
|
||||
for k := range servicesMap {
|
||||
oldServiceName = k
|
||||
index++
|
||||
if index > 0 {
|
||||
break
|
||||
}
|
||||
var newCompose string
|
||||
if req.DockerCompose == "" {
|
||||
newCompose, upErr = getUpgradeCompose(install, detail)
|
||||
if upErr != nil {
|
||||
return
|
||||
}
|
||||
servicesMap[install.ServiceName] = servicesMap[oldServiceName]
|
||||
if install.ServiceName != oldServiceName {
|
||||
delete(servicesMap, oldServiceName)
|
||||
}
|
||||
}
|
||||
envs := make(map[string]interface{})
|
||||
if upErr = json.Unmarshal([]byte(install.Env), &envs); upErr != nil {
|
||||
return
|
||||
}
|
||||
config := getAppCommonConfig(envs)
|
||||
if config.ContainerName == "" {
|
||||
config.ContainerName = install.ContainerName
|
||||
envs[constant.ContainerName] = install.ContainerName
|
||||
}
|
||||
config.Advanced = true
|
||||
if upErr = addDockerComposeCommonParam(composeMap, install.ServiceName, config, envs); upErr != nil {
|
||||
return
|
||||
}
|
||||
paramByte, upErr := json.Marshal(envs)
|
||||
if upErr != nil {
|
||||
return
|
||||
}
|
||||
install.Env = string(paramByte)
|
||||
composeByte, upErr := yaml.Marshal(composeMap)
|
||||
if upErr != nil {
|
||||
return
|
||||
} else {
|
||||
newCompose = req.DockerCompose
|
||||
}
|
||||
|
||||
install.DockerCompose = string(composeByte)
|
||||
install.DockerCompose = newCompose
|
||||
install.Version = detail.Version
|
||||
install.AppDetailId = detailID
|
||||
install.AppDetailId = req.DetailID
|
||||
|
||||
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
upErr = errors.New(out)
|
||||
return
|
||||
}
|
||||
upErr = err
|
||||
return
|
||||
}
|
||||
envs := make(map[string]interface{})
|
||||
if upErr = json.Unmarshal([]byte(install.Env), &envs); upErr != nil {
|
||||
return
|
||||
}
|
||||
envParams := make(map[string]string, len(envs))
|
||||
handleMap(envs, envParams)
|
||||
if err = env.Write(envParams, install.GetEnvPath()); err != nil {
|
||||
if upErr = env.Write(envParams, install.GetEnvPath()); upErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = runScript(&install, "upgrade"); err != nil {
|
||||
if upErr = runScript(&install, "upgrade"); upErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
content, err := fileOp.GetContent(install.GetEnvPath())
|
||||
if err != nil {
|
||||
upErr = err
|
||||
return
|
||||
}
|
||||
|
||||
if pullImage {
|
||||
images, err := composeV2.GetDockerComposeImages(install.Name, content, []byte(detail.DockerCompose))
|
||||
if err != nil {
|
||||
upErr = err
|
||||
return
|
||||
}
|
||||
dockerCli, err := composeV2.NewClient()
|
||||
if err != nil {
|
||||
upErr = err
|
||||
return
|
||||
}
|
||||
for _, image := range images {
|
||||
if err = dockerCli.PullImage(image, true); err != nil {
|
||||
upErr = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if upErr = fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); upErr != nil {
|
||||
return
|
||||
}
|
||||
@@ -615,6 +669,7 @@ func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error
|
||||
}
|
||||
install.Status = constant.Running
|
||||
_ = appInstallRepo.Save(context.Background(), &install)
|
||||
global.LOG.Infof(i18n.GetMsgWithName("UpgradeAppSuccess", install.Name, nil))
|
||||
}()
|
||||
|
||||
return appInstallRepo.Save(context.Background(), &install)
|
||||
@@ -731,11 +786,11 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App
|
||||
}
|
||||
}()
|
||||
|
||||
if err = fileOp.DownloadFile(appDetail.DownloadUrl, filePath); err != nil {
|
||||
if err = fileOp.DownloadFileWithProxy(appDetail.DownloadUrl, filePath); err != nil {
|
||||
global.LOG.Errorf("download app[%s] error %v", app.Name, err)
|
||||
return
|
||||
}
|
||||
if err = fileOp.Decompress(filePath, appResourceDir, files.SdkTarGz); err != nil {
|
||||
if err = fileOp.Decompress(filePath, appResourceDir, files.SdkTarGz, ""); err != nil {
|
||||
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err)
|
||||
return
|
||||
}
|
||||
@@ -755,7 +810,7 @@ func copyData(app model.App, appDetail model.AppDetail, appInstall *model.AppIns
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
_, _ = http.Get(appDetail.DownloadCallBackUrl)
|
||||
_, _, _ = httpUtil.HandleGet(appDetail.DownloadCallBackUrl, http.MethodGet, constant.TimeOut5s)
|
||||
}()
|
||||
}
|
||||
appKey := app.Key
|
||||
@@ -829,6 +884,7 @@ func checkContainerNameIsExist(containerName, appDir string) (bool, error) {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer client.Close()
|
||||
var options container.ListOptions
|
||||
list, err := client.ContainerList(context.Background(), options)
|
||||
if err != nil {
|
||||
@@ -881,12 +937,19 @@ func upApp(appInstall *model.AppInstall, pullImages bool) {
|
||||
return
|
||||
}
|
||||
if err := upProject(appInstall); err != nil {
|
||||
appInstall.Status = constant.Error
|
||||
appInstall.Status = constant.UpErr
|
||||
} else {
|
||||
appInstall.Status = constant.Running
|
||||
}
|
||||
exist, _ := appInstallRepo.GetFirst(commonRepo.WithByID(appInstall.ID))
|
||||
if exist.ID > 0 {
|
||||
containerNames, err := getContainerNames(*appInstall)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(containerNames) > 0 {
|
||||
appInstall.ContainerName = strings.Join(containerNames, ",")
|
||||
}
|
||||
_ = appInstallRepo.Save(context.Background(), appInstall)
|
||||
}
|
||||
}
|
||||
@@ -906,6 +969,12 @@ func rebuildApp(appInstall model.AppInstall) error {
|
||||
_ = handleErr(appInstall, err, out)
|
||||
return
|
||||
}
|
||||
containerNames, err := getContainerNames(appInstall)
|
||||
if err != nil {
|
||||
_ = handleErr(appInstall, err, out)
|
||||
return
|
||||
}
|
||||
appInstall.ContainerName = strings.Join(containerNames, ",")
|
||||
|
||||
appInstall.Status = constant.Running
|
||||
_ = appInstallRepo.Save(context.Background(), &appInstall)
|
||||
@@ -1083,24 +1152,131 @@ func handleErr(install model.AppInstall, err error, out string) error {
|
||||
install.Message = out
|
||||
reErr = errors.New(out)
|
||||
}
|
||||
install.Status = constant.Error
|
||||
install.Status = constant.UpErr
|
||||
_ = appInstallRepo.Save(context.Background(), &install)
|
||||
return reErr
|
||||
}
|
||||
|
||||
func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]response.AppInstalledDTO, error) {
|
||||
var res []response.AppInstalledDTO
|
||||
func doNotNeedSync(installed model.AppInstall) bool {
|
||||
return installed.Status == constant.Installing || installed.Status == constant.Rebuilding || installed.Status == constant.Upgrading ||
|
||||
installed.Status == constant.Syncing
|
||||
}
|
||||
|
||||
func synAppInstall(containers map[string]types.Container, appInstall *model.AppInstall, force bool) {
|
||||
containerNames := strings.Split(appInstall.ContainerName, ",")
|
||||
if len(containers) == 0 {
|
||||
if appInstall.Status == constant.UpErr && !force {
|
||||
return
|
||||
}
|
||||
appInstall.Status = constant.Error
|
||||
appInstall.Message = buserr.WithName("ErrContainerNotFound", strings.Join(containerNames, ",")).Error()
|
||||
_ = appInstallRepo.Save(context.Background(), appInstall)
|
||||
return
|
||||
}
|
||||
notFoundNames := make([]string, 0)
|
||||
exitNames := make([]string, 0)
|
||||
exitedCount := 0
|
||||
pausedCount := 0
|
||||
runningCount := 0
|
||||
total := len(containerNames)
|
||||
for _, name := range containerNames {
|
||||
if con, ok := containers["/"+name]; ok {
|
||||
switch con.State {
|
||||
case "exited":
|
||||
exitedCount++
|
||||
exitNames = append(exitNames, name)
|
||||
case "running":
|
||||
runningCount++
|
||||
case "paused":
|
||||
pausedCount++
|
||||
}
|
||||
} else {
|
||||
notFoundNames = append(notFoundNames, name)
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case exitedCount == total:
|
||||
appInstall.Status = constant.Stopped
|
||||
case runningCount == total:
|
||||
appInstall.Status = constant.Running
|
||||
case pausedCount == total:
|
||||
appInstall.Status = constant.Paused
|
||||
case len(notFoundNames) == total:
|
||||
if appInstall.Status == constant.UpErr && !force {
|
||||
return
|
||||
}
|
||||
appInstall.Status = constant.Error
|
||||
appInstall.Message = buserr.WithName("ErrContainerNotFound", strings.Join(notFoundNames, ",")).Error()
|
||||
default:
|
||||
var msg string
|
||||
if exitedCount > 0 {
|
||||
msg = buserr.WithName("ErrContainerMsg", strings.Join(exitNames, ",")).Error()
|
||||
}
|
||||
if len(notFoundNames) > 0 {
|
||||
msg += buserr.WithName("ErrContainerNotFound", strings.Join(notFoundNames, ",")).Error()
|
||||
}
|
||||
if msg == "" {
|
||||
msg = buserr.New("ErrAppWarn").Error()
|
||||
}
|
||||
appInstall.Message = msg
|
||||
appInstall.Status = constant.UnHealthy
|
||||
}
|
||||
_ = appInstallRepo.Save(context.Background(), appInstall)
|
||||
}
|
||||
|
||||
func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool) ([]response.AppInstallDTO, error) {
|
||||
var (
|
||||
res []response.AppInstallDTO
|
||||
containersMap map[string]types.Container
|
||||
)
|
||||
if sync {
|
||||
cli, err := docker.NewClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cli.Close()
|
||||
containers, err := cli.ListAllContainers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containersMap = make(map[string]types.Container, len(containers))
|
||||
for _, contain := range containers {
|
||||
containersMap[contain.Names[0]] = contain
|
||||
}
|
||||
}
|
||||
|
||||
for _, installed := range appInstallList {
|
||||
if updated && (installed.App.Type == "php" || installed.Status == constant.Installing || (installed.App.Key == constant.AppMysql && installed.Version == "5.6.51")) {
|
||||
continue
|
||||
}
|
||||
if err := syncAppInstallStatus(&installed); err != nil {
|
||||
global.LOG.Error("sync app install status error : ", err)
|
||||
if sync && !doNotNeedSync(installed) {
|
||||
synAppInstall(containersMap, &installed, false)
|
||||
}
|
||||
|
||||
installDTO := response.AppInstalledDTO{
|
||||
AppInstall: installed,
|
||||
Path: installed.GetPath(),
|
||||
installDTO := response.AppInstallDTO{
|
||||
ID: installed.ID,
|
||||
Name: installed.Name,
|
||||
AppID: installed.AppId,
|
||||
AppDetailID: installed.AppDetailId,
|
||||
Version: installed.Version,
|
||||
Status: installed.Status,
|
||||
Message: installed.Message,
|
||||
HttpPort: installed.HttpPort,
|
||||
HttpsPort: installed.HttpsPort,
|
||||
Icon: installed.App.Icon,
|
||||
AppName: installed.App.Name,
|
||||
AppKey: installed.App.Key,
|
||||
AppType: installed.App.Type,
|
||||
Path: installed.GetPath(),
|
||||
CreatedAt: installed.CreatedAt,
|
||||
App: response.AppDetail{
|
||||
Github: installed.App.Github,
|
||||
Website: installed.App.Website,
|
||||
Document: installed.App.Document,
|
||||
},
|
||||
}
|
||||
if updated {
|
||||
installDTO.DockerCompose = installed.DockerCompose
|
||||
}
|
||||
app, err := appRepo.GetFirst(commonRepo.WithByID(installed.AppId))
|
||||
if err != nil {
|
||||
|
||||
@@ -37,6 +37,7 @@ type IBackupService interface {
|
||||
GetBuckets(backupDto dto.ForBuckets) ([]interface{}, error)
|
||||
Update(ireq dto.BackupOperate) error
|
||||
Delete(id uint) error
|
||||
DeleteRecordByName(backupType, name, detailName string, withDeleteFile bool) error
|
||||
BatchDeleteRecord(ids []uint) error
|
||||
NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error)
|
||||
|
||||
@@ -49,13 +50,13 @@ type IBackupService interface {
|
||||
MysqlRecoverByUpload(req dto.CommonRecover) error
|
||||
PostgresqlRecoverByUpload(req dto.CommonRecover) error
|
||||
|
||||
RedisBackup() error
|
||||
RedisBackup(db dto.CommonBackup) error
|
||||
RedisRecover(db dto.CommonRecover) error
|
||||
|
||||
WebsiteBackup(db dto.CommonBackup) error
|
||||
WebsiteRecover(req dto.CommonRecover) error
|
||||
|
||||
AppBackup(db dto.CommonBackup) error
|
||||
AppBackup(db dto.CommonBackup) (*model.BackupRecord, error)
|
||||
AppRecover(req dto.CommonRecover) error
|
||||
|
||||
Run()
|
||||
@@ -260,6 +261,35 @@ func (u *BackupService) Delete(id uint) error {
|
||||
return backupRepo.Delete(commonRepo.WithByID(id))
|
||||
}
|
||||
|
||||
func (u *BackupService) DeleteRecordByName(backupType, name, detailName string, withDeleteFile bool) error {
|
||||
if !withDeleteFile {
|
||||
return backupRepo.DeleteRecord(context.Background(), commonRepo.WithByType(backupType), commonRepo.WithByName(name), backupRepo.WithByDetailName(detailName))
|
||||
}
|
||||
|
||||
records, err := backupRepo.ListRecord(commonRepo.WithByType(backupType), commonRepo.WithByName(name), backupRepo.WithByDetailName(detailName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, record := range records {
|
||||
backupAccount, err := backupRepo.Get(commonRepo.WithByType(record.Source))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load backup account %s info from db failed, err: %v", record.Source, err)
|
||||
continue
|
||||
}
|
||||
client, err := u.NewClient(&backupAccount)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("new client for backup account %s failed, err: %v", record.Source, err)
|
||||
continue
|
||||
}
|
||||
if _, err = client.Delete(path.Join(record.FileDir, record.FileName)); err != nil {
|
||||
global.LOG.Errorf("remove file %s from %s failed, err: %v", path.Join(record.FileDir, record.FileName), record.Source, err)
|
||||
}
|
||||
_ = backupRepo.DeleteRecord(context.Background(), commonRepo.WithByID(record.ID))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *BackupService) BatchDeleteRecord(ids []uint) error {
|
||||
records, err := backupRepo.ListRecord(commonRepo.WithIdsIn(ids))
|
||||
if err != nil {
|
||||
@@ -334,7 +364,7 @@ func (u *BackupService) Update(req dto.BackupOperate) error {
|
||||
dirStr = dirStr[:strings.LastIndex(dirStr, "/")]
|
||||
}
|
||||
if err := copyDir(oldDir, dirStr); err != nil {
|
||||
_ = backupRepo.Update(req.ID, (map[string]interface{}{"vars": oldVars}))
|
||||
_ = backupRepo.Update(req.ID, map[string]interface{}{"vars": oldVars})
|
||||
return err
|
||||
}
|
||||
global.CONF.System.Backup = dirStr
|
||||
@@ -384,8 +414,6 @@ func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.Cl
|
||||
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
|
||||
varMap["accessKey"] = backup.AccessKey
|
||||
varMap["secretKey"] = backup.Credential
|
||||
case constant.OneDrive:
|
||||
varMap["accessToken"] = backup.Credential
|
||||
}
|
||||
|
||||
backClient, err := cloud_storage.NewCloudStorageClient(backup.Type, varMap)
|
||||
@@ -423,14 +451,13 @@ func (u *BackupService) loadAccessToken(backup *model.BackupAccount) error {
|
||||
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
|
||||
return fmt.Errorf("unmarshal backup vars failed, err: %v", err)
|
||||
}
|
||||
token, refreshToken, err := client.RefreshToken("authorization_code", varMap)
|
||||
refreshToken, err := client.RefreshToken("authorization_code", "refreshToken", varMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delete(varMap, "code")
|
||||
backup.Credential = token
|
||||
varMap["refresh_status"] = constant.StatusSuccess
|
||||
varMap["refresh_time"] = time.Now().Format("2006-01-02 15:04:05")
|
||||
varMap["refresh_time"] = time.Now().Format(constant.DateTimeLayout)
|
||||
varMap["refresh_token"] = refreshToken
|
||||
itemVars, err := json.Marshal(varMap)
|
||||
if err != nil {
|
||||
@@ -557,10 +584,10 @@ func (u *BackupService) checkBackupConn(backup *model.BackupAccount) (bool, erro
|
||||
}
|
||||
defer file.Close()
|
||||
write := bufio.NewWriter(file)
|
||||
_, _ = write.WriteString(string("1Panel 备份账号测试文件。\n"))
|
||||
_, _ = write.WriteString(string("1Panel 備份賬號測試文件。\n"))
|
||||
_, _ = write.WriteString(string("1Panel Backs up account test files.\n"))
|
||||
_, _ = write.WriteString(string("1Panelアカウントのテストファイルをバックアップします。\n"))
|
||||
_, _ = write.WriteString("1Panel 备份账号测试文件。\n")
|
||||
_, _ = write.WriteString("1Panel 備份賬號測試文件。\n")
|
||||
_, _ = write.WriteString("1Panel Backs up account test files.\n")
|
||||
_, _ = write.WriteString("1Panelアカウントのテストファイルをバックアップします。\n")
|
||||
write.Flush()
|
||||
|
||||
targetPath := strings.TrimPrefix(path.Join(backup.BackupPath, "test/1panel"), "/")
|
||||
@@ -569,7 +596,7 @@ func (u *BackupService) checkBackupConn(backup *model.BackupAccount) (bool, erro
|
||||
|
||||
func StartRefreshOneDriveToken() {
|
||||
service := NewIBackupService()
|
||||
oneDriveCronID, err := global.Cron.AddJob("0 * * * *", service)
|
||||
oneDriveCronID, err := global.Cron.AddJob("0 3 */31 * *", service)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("can not add OneDrive corn job: %s", err.Error())
|
||||
return
|
||||
@@ -583,19 +610,15 @@ func (u *BackupService) Run() {
|
||||
if backupItem.ID == 0 {
|
||||
return
|
||||
}
|
||||
if len(backupItem.Credential) == 0 {
|
||||
global.LOG.Error("OneDrive configuration lacks token information, please rebind.")
|
||||
return
|
||||
}
|
||||
global.LOG.Info("start to refresh token of OneDrive ...")
|
||||
varMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(backupItem.Vars), &varMap); err != nil {
|
||||
global.LOG.Errorf("Failed to refresh OneDrive token, please retry, err: %v", err)
|
||||
return
|
||||
}
|
||||
token, refreshToken, err := client.RefreshToken("refresh_token", varMap)
|
||||
refreshToken, err := client.RefreshToken("refresh_token", "refreshToken", varMap)
|
||||
varMap["refresh_status"] = constant.StatusSuccess
|
||||
varMap["refresh_time"] = time.Now().Format("2006-01-02 15:04:05")
|
||||
varMap["refresh_time"] = time.Now().Format(constant.DateTimeLayout)
|
||||
if err != nil {
|
||||
varMap["refresh_status"] = constant.StatusFailed
|
||||
varMap["refresh_msg"] = err.Error()
|
||||
@@ -608,8 +631,7 @@ func (u *BackupService) Run() {
|
||||
_ = global.DB.Model(&model.BackupAccount{}).
|
||||
Where("id = ?", backupItem.ID).
|
||||
Updates(map[string]interface{}{
|
||||
"credential": token,
|
||||
"vars": varsItem,
|
||||
"vars": varsItem,
|
||||
}).Error
|
||||
global.LOG.Info("Successfully refreshed OneDrive token.")
|
||||
}
|
||||
|
||||
@@ -22,26 +22,26 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (u *BackupService) AppBackup(req dto.CommonBackup) error {
|
||||
func (u *BackupService) AppBackup(req dto.CommonBackup) (*model.BackupRecord, error) {
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
app, err := appRepo.GetFirst(appRepo.WithKey(req.Name))
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
install, err := appInstallRepo.GetFirst(commonRepo.WithByName(req.DetailName), appInstallRepo.WithAppId(app.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
timeNow := time.Now().Format(constant.DateTimeSlimLayout)
|
||||
itemDir := fmt.Sprintf("app/%s/%s", req.Name, req.DetailName)
|
||||
backupDir := path.Join(localDir, itemDir)
|
||||
|
||||
fileName := fmt.Sprintf("%s_%s.tar.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
|
||||
if err := handleAppBackup(&install, backupDir, fileName); err != nil {
|
||||
return err
|
||||
if err := handleAppBackup(&install, backupDir, fileName, "", req.Secret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
record := &model.BackupRecord{
|
||||
@@ -56,9 +56,9 @@ func (u *BackupService) AppBackup(req dto.CommonBackup) error {
|
||||
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
return nil
|
||||
return record, nil
|
||||
}
|
||||
|
||||
func (u *BackupService) AppRecover(req dto.CommonRecover) error {
|
||||
@@ -78,13 +78,13 @@ func (u *BackupService) AppRecover(req dto.CommonRecover) error {
|
||||
if _, err := compose.Down(install.GetComposePath()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handleAppRecover(&install, req.File, false); err != nil {
|
||||
if err := handleAppRecover(&install, req.File, false, req.Secret); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleAppBackup(install *model.AppInstall, backupDir, fileName string) error {
|
||||
func handleAppBackup(install *model.AppInstall, backupDir, fileName string, excludes string, secret string) error {
|
||||
fileOp := files.NewFileOp()
|
||||
tmpDir := fmt.Sprintf("%s/%s", backupDir, strings.ReplaceAll(fileName, ".tar.gz", ""))
|
||||
if !fileOp.Stat(tmpDir) {
|
||||
@@ -103,7 +103,7 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
|
||||
}
|
||||
|
||||
appPath := install.GetPath()
|
||||
if err := handleTar(appPath, tmpDir, "app.tar.gz", ""); err != nil {
|
||||
if err := handleTar(appPath, tmpDir, "app.tar.gz", excludes, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -129,16 +129,16 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
|
||||
}
|
||||
}
|
||||
|
||||
if err := handleTar(tmpDir, backupDir, fileName, ""); err != nil {
|
||||
if err := handleTar(tmpDir, backupDir, fileName, "", secret); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback bool) error {
|
||||
func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback bool, secret string) error {
|
||||
isOk := false
|
||||
fileOp := files.NewFileOp()
|
||||
if err := handleUnTar(recoverFile, path.Dir(recoverFile)); err != nil {
|
||||
if err := handleUnTar(recoverFile, path.Dir(recoverFile), secret); err != nil {
|
||||
return err
|
||||
}
|
||||
tmpPath := strings.ReplaceAll(recoverFile, ".tar.gz", "")
|
||||
@@ -163,14 +163,14 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
}
|
||||
|
||||
if !isRollback {
|
||||
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("app/%s_%s.tar.gz", install.Name, time.Now().Format("20060102150405")))
|
||||
if err := handleAppBackup(install, path.Dir(rollbackFile), path.Base(rollbackFile)); err != nil {
|
||||
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("app/%s_%s.tar.gz", install.Name, time.Now().Format(constant.DateTimeSlimLayout)))
|
||||
if err := handleAppBackup(install, path.Dir(rollbackFile), path.Base(rollbackFile), "", ""); err != nil {
|
||||
return fmt.Errorf("backup app %s for rollback before recover failed, err: %v", install.Name, err)
|
||||
}
|
||||
defer func() {
|
||||
if !isOk {
|
||||
global.LOG.Info("recover failed, start to rollback now")
|
||||
if err := handleAppRecover(install, rollbackFile, true); err != nil {
|
||||
if err := handleAppRecover(install, rollbackFile, true, secret); err != nil {
|
||||
global.LOG.Errorf("rollback app %s from %s failed, err: %v", install.Name, rollbackFile, err)
|
||||
return
|
||||
}
|
||||
@@ -251,7 +251,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
_ = fileOp.Rename(appDir, backPath)
|
||||
_ = fileOp.CreateDir(appDir, 0755)
|
||||
|
||||
if err := handleUnTar(tmpPath+"/app.tar.gz", install.GetAppPath()); err != nil {
|
||||
if err := handleUnTar(tmpPath+"/app.tar.gz", install.GetAppPath(), ""); err != nil {
|
||||
global.LOG.Errorf("handle recover from app.tar.gz failed, err: %v", err)
|
||||
_ = fileOp.DeleteDir(appDir)
|
||||
_ = fileOp.Rename(backPath, appDir)
|
||||
|
||||
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -24,7 +25,7 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
|
||||
return err
|
||||
}
|
||||
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
timeNow := time.Now().Format(constant.DateTimeSlimLayout)
|
||||
itemDir := fmt.Sprintf("database/%s/%s/%s", req.Type, req.Name, req.DetailName)
|
||||
targetDir := path.Join(localDir, itemDir)
|
||||
fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
|
||||
@@ -59,14 +60,14 @@ func (u *BackupService) MysqlRecoverByUpload(req dto.CommonRecover) error {
|
||||
file := req.File
|
||||
fileName := path.Base(req.File)
|
||||
if strings.HasSuffix(fileName, ".tar.gz") {
|
||||
fileNameItem := time.Now().Format("20060102150405")
|
||||
fileNameItem := time.Now().Format(constant.DateTimeSlimLayout)
|
||||
dstDir := fmt.Sprintf("%s/%s", path.Dir(req.File), fileNameItem)
|
||||
if _, err := os.Stat(dstDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dstDir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("mkdir %s failed, err: %v", dstDir, err)
|
||||
}
|
||||
}
|
||||
if err := handleUnTar(req.File, dstDir); err != nil {
|
||||
if err := handleUnTar(req.File, dstDir, ""); err != nil {
|
||||
_ = os.RemoveAll(dstDir)
|
||||
return err
|
||||
}
|
||||
@@ -142,7 +143,7 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
|
||||
}
|
||||
|
||||
if !isRollback {
|
||||
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("database/%s/%s_%s.sql.gz", req.Type, req.DetailName, time.Now().Format("20060102150405")))
|
||||
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("database/%s/%s_%s.sql.gz", req.Type, req.DetailName, time.Now().Format(constant.DateTimeSlimLayout)))
|
||||
if err := cli.Backup(client.BackupInfo{
|
||||
Name: req.DetailName,
|
||||
Type: req.Type,
|
||||
|
||||
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -25,7 +26,7 @@ func (u *BackupService) PostgresqlBackup(req dto.CommonBackup) error {
|
||||
return err
|
||||
}
|
||||
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
timeNow := time.Now().Format(constant.DateTimeSlimLayout)
|
||||
itemDir := fmt.Sprintf("database/%s/%s/%s", req.Type, req.Name, req.DetailName)
|
||||
targetDir := path.Join(localDir, itemDir)
|
||||
fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
|
||||
@@ -59,14 +60,14 @@ func (u *BackupService) PostgresqlRecoverByUpload(req dto.CommonRecover) error {
|
||||
file := req.File
|
||||
fileName := path.Base(req.File)
|
||||
if strings.HasSuffix(fileName, ".tar.gz") {
|
||||
fileNameItem := time.Now().Format("20060102150405")
|
||||
fileNameItem := time.Now().Format(constant.DateTimeSlimLayout)
|
||||
dstDir := fmt.Sprintf("%s/%s", path.Dir(req.File), fileNameItem)
|
||||
if _, err := os.Stat(dstDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dstDir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("mkdir %s failed, err: %v", dstDir, err)
|
||||
}
|
||||
}
|
||||
if err := handleUnTar(req.File, dstDir); err != nil {
|
||||
if err := handleUnTar(req.File, dstDir, ""); err != nil {
|
||||
_ = os.RemoveAll(dstDir)
|
||||
return err
|
||||
}
|
||||
@@ -136,7 +137,7 @@ func handlePostgresqlRecover(req dto.CommonRecover, isRollback bool) error {
|
||||
defer cli.Close()
|
||||
|
||||
if !isRollback {
|
||||
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("database/%s/%s_%s.sql.gz", req.Type, req.DetailName, time.Now().Format("20060102150405")))
|
||||
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("database/%s/%s_%s.sql.gz", req.Type, req.DetailName, time.Now().Format(constant.DateTimeSlimLayout)))
|
||||
if err := cli.Backup(client.BackupInfo{
|
||||
Name: req.DetailName,
|
||||
TargetDir: path.Dir(rollbackFile),
|
||||
|
||||
@@ -20,12 +20,12 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (u *BackupService) RedisBackup() error {
|
||||
func (u *BackupService) RedisBackup(db dto.CommonBackup) error {
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", db.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -35,7 +35,7 @@ func (u *BackupService) RedisBackup() error {
|
||||
}
|
||||
global.LOG.Infof("appendonly in redis conf is %s", appendonly)
|
||||
|
||||
timeNow := time.Now().Format("20060102150405") + common.RandStrAndNum(5)
|
||||
timeNow := time.Now().Format(constant.DateTimeSlimLayout) + common.RandStrAndNum(5)
|
||||
fileName := fmt.Sprintf("%s.rdb", timeNow)
|
||||
if appendonly == "yes" {
|
||||
if strings.HasPrefix(redisInfo.Version, "6.") {
|
||||
@@ -46,11 +46,12 @@ func (u *BackupService) RedisBackup() error {
|
||||
}
|
||||
itemDir := fmt.Sprintf("database/redis/%s", redisInfo.Name)
|
||||
backupDir := path.Join(localDir, itemDir)
|
||||
if err := handleRedisBackup(redisInfo, backupDir, fileName); err != nil {
|
||||
if err := handleRedisBackup(redisInfo, backupDir, fileName, db.Secret); err != nil {
|
||||
return err
|
||||
}
|
||||
record := &model.BackupRecord{
|
||||
Type: "redis",
|
||||
Name: db.Name,
|
||||
Source: "LOCAL",
|
||||
BackupType: "LOCAL",
|
||||
FileDir: itemDir,
|
||||
@@ -64,18 +65,18 @@ func (u *BackupService) RedisBackup() error {
|
||||
}
|
||||
|
||||
func (u *BackupService) RedisRecover(req dto.CommonRecover) error {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Infof("recover redis from backup file %s", req.File)
|
||||
if err := handleRedisRecover(redisInfo, req.File, false); err != nil {
|
||||
if err := handleRedisRecover(redisInfo, req.File, false, req.Secret); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleRedisBackup(redisInfo *repo.RootInfo, backupDir, fileName string) error {
|
||||
func handleRedisBackup(redisInfo *repo.RootInfo, backupDir, fileName string, secret string) error {
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(backupDir) {
|
||||
if err := os.MkdirAll(backupDir, os.ModePerm); err != nil {
|
||||
@@ -90,7 +91,7 @@ func handleRedisBackup(redisInfo *repo.RootInfo, backupDir, fileName string) err
|
||||
|
||||
if strings.HasSuffix(fileName, ".tar.gz") {
|
||||
redisDataDir := fmt.Sprintf("%s/%s/%s/data/appendonlydir", constant.AppInstallDir, "redis", redisInfo.Name)
|
||||
if err := handleTar(redisDataDir, backupDir, fileName, ""); err != nil {
|
||||
if err := handleTar(redisDataDir, backupDir, fileName, "", secret); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -110,7 +111,7 @@ func handleRedisBackup(redisInfo *repo.RootInfo, backupDir, fileName string) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback bool) error {
|
||||
func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback bool, secret string) error {
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(recoverFile) {
|
||||
return buserr.WithName("ErrFileNotFound", recoverFile)
|
||||
@@ -145,14 +146,14 @@ func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback
|
||||
suffix = "tar.gz"
|
||||
}
|
||||
}
|
||||
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("database/redis/%s_%s.%s", redisInfo.Name, time.Now().Format("20060102150405"), suffix))
|
||||
if err := handleRedisBackup(redisInfo, path.Dir(rollbackFile), path.Base(rollbackFile)); err != nil {
|
||||
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("database/redis/%s_%s.%s", redisInfo.Name, time.Now().Format(constant.DateTimeSlimLayout), suffix))
|
||||
if err := handleRedisBackup(redisInfo, path.Dir(rollbackFile), path.Base(rollbackFile), secret); err != nil {
|
||||
return fmt.Errorf("backup database %s for rollback before recover failed, err: %v", redisInfo.Name, err)
|
||||
}
|
||||
defer func() {
|
||||
if !isOk {
|
||||
global.LOG.Info("recover failed, start to rollback now")
|
||||
if err := handleRedisRecover(redisInfo, rollbackFile, true); err != nil {
|
||||
if err := handleRedisRecover(redisInfo, rollbackFile, true, secret); err != nil {
|
||||
global.LOG.Errorf("rollback redis from %s failed, err: %v", rollbackFile, err)
|
||||
return
|
||||
}
|
||||
@@ -169,7 +170,7 @@ func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback
|
||||
}
|
||||
if appendonly == "yes" && strings.HasPrefix(redisInfo.Version, "7.") {
|
||||
redisDataDir := fmt.Sprintf("%s/%s/%s/data", constant.AppInstallDir, "redis", redisInfo.Name)
|
||||
if err := handleUnTar(recoverFile, redisDataDir); err != nil {
|
||||
if err := handleUnTar(recoverFile, redisDataDir, secret); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -4,18 +4,19 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
)
|
||||
|
||||
func handleRuntimeBackup(runtime *model.Runtime, backupDir, fileName string) error {
|
||||
func handleRuntimeBackup(runtime *model.Runtime, backupDir, fileName string, excludes string, secret string) error {
|
||||
fileOp := files.NewFileOp()
|
||||
tmpDir := fmt.Sprintf("%s/%s", backupDir, strings.ReplaceAll(fileName, ".tar.gz", ""))
|
||||
if !fileOp.Stat(tmpDir) {
|
||||
@@ -34,19 +35,19 @@ func handleRuntimeBackup(runtime *model.Runtime, backupDir, fileName string) err
|
||||
}
|
||||
|
||||
appPath := runtime.GetPath()
|
||||
if err := handleTar(appPath, tmpDir, "runtime.tar.gz", ""); err != nil {
|
||||
if err := handleTar(appPath, tmpDir, "runtime.tar.gz", excludes, secret); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handleTar(tmpDir, backupDir, fileName, ""); err != nil {
|
||||
if err := handleTar(tmpDir, backupDir, fileName, "", secret); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleRuntimeRecover(runtime *model.Runtime, recoverFile string, isRollback bool) error {
|
||||
func handleRuntimeRecover(runtime *model.Runtime, recoverFile string, isRollback bool, secret string) error {
|
||||
isOk := false
|
||||
fileOp := files.NewFileOp()
|
||||
if err := handleUnTar(recoverFile, path.Dir(recoverFile)); err != nil {
|
||||
if err := handleUnTar(recoverFile, path.Dir(recoverFile), secret); err != nil {
|
||||
return err
|
||||
}
|
||||
tmpPath := strings.ReplaceAll(recoverFile, ".tar.gz", "")
|
||||
@@ -71,14 +72,14 @@ func handleRuntimeRecover(runtime *model.Runtime, recoverFile string, isRollback
|
||||
}
|
||||
|
||||
if !isRollback {
|
||||
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("runtime/%s_%s.tar.gz", runtime.Name, time.Now().Format("20060102150405")))
|
||||
if err := handleRuntimeBackup(runtime, path.Dir(rollbackFile), path.Base(rollbackFile)); err != nil {
|
||||
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("runtime/%s_%s.tar.gz", runtime.Name, time.Now().Format(constant.DateTimeSlimLayout)))
|
||||
if err := handleRuntimeBackup(runtime, path.Dir(rollbackFile), path.Base(rollbackFile), "", secret); err != nil {
|
||||
return fmt.Errorf("backup runtime %s for rollback before recover failed, err: %v", runtime.Name, err)
|
||||
}
|
||||
defer func() {
|
||||
if !isOk {
|
||||
global.LOG.Info("recover failed, start to rollback now")
|
||||
if err := handleRuntimeRecover(runtime, rollbackFile, true); err != nil {
|
||||
if err := handleRuntimeRecover(runtime, rollbackFile, true, secret); err != nil {
|
||||
global.LOG.Errorf("rollback runtime %s from %s failed, err: %v", runtime.Name, rollbackFile, err)
|
||||
return
|
||||
}
|
||||
@@ -99,7 +100,7 @@ func handleRuntimeRecover(runtime *model.Runtime, recoverFile string, isRollback
|
||||
_ = fileOp.Rename(runtimeDir, backPath)
|
||||
_ = fileOp.CreateDir(runtimeDir, 0755)
|
||||
|
||||
if err := handleUnTar(tmpPath+"/runtime.tar.gz", fmt.Sprintf("%s/%s", constant.RuntimeDir, runtime.Type)); err != nil {
|
||||
if err := handleUnTar(tmpPath+"/runtime.tar.gz", fmt.Sprintf("%s/%s", constant.RuntimeDir, runtime.Type), secret); err != nil {
|
||||
global.LOG.Errorf("handle recover from runtime.tar.gz failed, err: %v", err)
|
||||
_ = fileOp.DeleteDir(runtimeDir)
|
||||
_ = fileOp.Rename(backPath, runtimeDir)
|
||||
|
||||
@@ -31,11 +31,11 @@ func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
|
||||
return err
|
||||
}
|
||||
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
timeNow := time.Now().Format(constant.DateTimeSlimLayout)
|
||||
itemDir := fmt.Sprintf("website/%s", req.Name)
|
||||
backupDir := path.Join(localDir, itemDir)
|
||||
fileName := fmt.Sprintf("%s_%s.tar.gz", website.PrimaryDomain, timeNow+common.RandStrAndNum(5))
|
||||
if err := handleWebsiteBackup(&website, backupDir, fileName); err != nil {
|
||||
if err := handleWebsiteBackup(&website, backupDir, fileName, "", req.Secret); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -65,16 +65,16 @@ func (u *BackupService) WebsiteRecover(req dto.CommonRecover) error {
|
||||
return err
|
||||
}
|
||||
global.LOG.Infof("recover website %s from backup file %s", req.Name, req.File)
|
||||
if err := handleWebsiteRecover(&website, req.File, false); err != nil {
|
||||
if err := handleWebsiteRecover(&website, req.File, false, req.Secret); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback bool) error {
|
||||
func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback bool, secret string) error {
|
||||
fileOp := files.NewFileOp()
|
||||
tmpPath := strings.ReplaceAll(recoverFile, ".tar.gz", "")
|
||||
if err := handleUnTar(recoverFile, path.Dir(recoverFile)); err != nil {
|
||||
if err := handleUnTar(recoverFile, path.Dir(recoverFile), secret); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
@@ -106,14 +106,14 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
|
||||
|
||||
isOk := false
|
||||
if !isRollback {
|
||||
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("website/%s_%s.tar.gz", website.Alias, time.Now().Format("20060102150405")))
|
||||
if err := handleWebsiteBackup(website, path.Dir(rollbackFile), path.Base(rollbackFile)); err != nil {
|
||||
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("website/%s_%s.tar.gz", website.Alias, time.Now().Format(constant.DateTimeSlimLayout)))
|
||||
if err := handleWebsiteBackup(website, path.Dir(rollbackFile), path.Base(rollbackFile), "", ""); err != nil {
|
||||
return fmt.Errorf("backup website %s for rollback before recover failed, err: %v", website.Alias, err)
|
||||
}
|
||||
defer func() {
|
||||
if !isOk {
|
||||
global.LOG.Info("recover failed, start to rollback now")
|
||||
if err := handleWebsiteRecover(website, rollbackFile, true); err != nil {
|
||||
if err := handleWebsiteRecover(website, rollbackFile, true, ""); err != nil {
|
||||
global.LOG.Errorf("rollback website %s from %s failed, err: %v", website.Alias, rollbackFile, err)
|
||||
return
|
||||
}
|
||||
@@ -141,7 +141,7 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handleAppRecover(&app, fmt.Sprintf("%s/%s.app.tar.gz", tmpPath, website.Alias), true); err != nil {
|
||||
if err := handleAppRecover(&app, fmt.Sprintf("%s/%s.app.tar.gz", tmpPath, website.Alias), true, ""); err != nil {
|
||||
global.LOG.Errorf("handle recover from app.tar.gz failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
@@ -154,8 +154,8 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if runtime.Type == constant.RuntimeNode {
|
||||
if err := handleRuntimeRecover(runtime, fmt.Sprintf("%s/%s.runtime.tar.gz", tmpPath, website.Alias), true); err != nil {
|
||||
if runtime.Type == constant.RuntimeNode || runtime.Type == constant.RuntimeJava || runtime.Type == constant.RuntimeGo {
|
||||
if err := handleRuntimeRecover(runtime, fmt.Sprintf("%s/%s.runtime.tar.gz", tmpPath, website.Alias), true, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Info("put runtime.tar.gz into tmp dir successful")
|
||||
@@ -163,7 +163,7 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
|
||||
}
|
||||
|
||||
siteDir := fmt.Sprintf("%s/openresty/%s/www/sites", constant.AppInstallDir, nginxInfo.Name)
|
||||
if err := handleUnTar(fmt.Sprintf("%s/%s.web.tar.gz", tmpPath, website.Alias), siteDir); err != nil {
|
||||
if err := handleUnTar(fmt.Sprintf("%s/%s.web.tar.gz", tmpPath, website.Alias), siteDir, ""); err != nil {
|
||||
global.LOG.Errorf("handle recover from web.tar.gz failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
@@ -182,7 +182,7 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleWebsiteBackup(website *model.Website, backupDir, fileName string) error {
|
||||
func handleWebsiteBackup(website *model.Website, backupDir, fileName string, excludes string, secret string) error {
|
||||
fileOp := files.NewFileOp()
|
||||
tmpDir := fmt.Sprintf("%s/%s", backupDir, strings.ReplaceAll(fileName, ".tar.gz", ""))
|
||||
if !fileOp.Stat(tmpDir) {
|
||||
@@ -216,7 +216,7 @@ func handleWebsiteBackup(website *model.Website, backupDir, fileName string) err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handleAppBackup(&app, tmpDir, fmt.Sprintf("%s.app.tar.gz", website.Alias)); err != nil {
|
||||
if err := handleAppBackup(&app, tmpDir, fmt.Sprintf("%s.app.tar.gz", website.Alias), excludes, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Info("put app.tar.gz into tmp dir successful")
|
||||
@@ -225,8 +225,8 @@ func handleWebsiteBackup(website *model.Website, backupDir, fileName string) err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if runtime.Type == constant.RuntimeNode {
|
||||
if err := handleRuntimeBackup(runtime, tmpDir, fmt.Sprintf("%s.runtime.tar.gz", website.Alias)); err != nil {
|
||||
if runtime.Type == constant.RuntimeNode || runtime.Type == constant.RuntimeJava || runtime.Type == constant.RuntimeGo {
|
||||
if err := handleRuntimeBackup(runtime, tmpDir, fmt.Sprintf("%s.runtime.tar.gz", website.Alias), excludes, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Info("put runtime.tar.gz into tmp dir successful")
|
||||
@@ -234,11 +234,11 @@ func handleWebsiteBackup(website *model.Website, backupDir, fileName string) err
|
||||
}
|
||||
|
||||
websiteDir := fmt.Sprintf("%s/openresty/%s/www/sites/%s", constant.AppInstallDir, nginxInfo.Name, website.Alias)
|
||||
if err := handleTar(websiteDir, tmpDir, fmt.Sprintf("%s.web.tar.gz", website.Alias), ""); err != nil {
|
||||
if err := handleTar(websiteDir, tmpDir, fmt.Sprintf("%s.web.tar.gz", website.Alias), excludes, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Info("put web.tar.gz into tmp dir successful, now start to tar tmp dir")
|
||||
if err := handleTar(tmpDir, backupDir, fileName, ""); err != nil {
|
||||
if err := handleTar(tmpDir, backupDir, fileName, "", secret); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -250,18 +250,18 @@ func checkValidOfWebsite(oldWebsite, website *model.Website) error {
|
||||
return buserr.WithDetail(constant.ErrBackupMatch, fmt.Sprintf("oldName: %s, oldType: %v", oldWebsite.Alias, oldWebsite.Type), nil)
|
||||
}
|
||||
if oldWebsite.AppInstallID != 0 {
|
||||
_, err := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
|
||||
_, err := appInstallRepo.GetFirst(commonRepo.WithByID(oldWebsite.AppInstallID))
|
||||
if err != nil {
|
||||
return buserr.WithDetail(constant.ErrBackupMatch, "app", nil)
|
||||
}
|
||||
}
|
||||
if oldWebsite.RuntimeID != 0 {
|
||||
if _, err := runtimeRepo.GetFirst(commonRepo.WithByID(website.RuntimeID)); err != nil {
|
||||
if _, err := runtimeRepo.GetFirst(commonRepo.WithByID(oldWebsite.RuntimeID)); err != nil {
|
||||
return buserr.WithDetail(constant.ErrBackupMatch, "runtime", nil)
|
||||
}
|
||||
}
|
||||
if oldWebsite.WebsiteSSLID != 0 {
|
||||
if _, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(website.WebsiteSSLID)); err != nil {
|
||||
if _, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(oldWebsite.WebsiteSSLID)); err != nil {
|
||||
return buserr.WithDetail(constant.ErrBackupMatch, "ssl", nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,562 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/systemctl"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/robfig/cron/v3"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
clamServiceNameCentOs = "clamd@scan.service"
|
||||
clamServiceNameUbuntu = "clamav-daemon.service"
|
||||
freshClamService = "clamav-freshclam.service"
|
||||
resultDir = "clamav"
|
||||
)
|
||||
|
||||
type ClamService struct {
|
||||
serviceName string
|
||||
}
|
||||
|
||||
type IClamService interface {
|
||||
LoadBaseInfo() (dto.ClamBaseInfo, error)
|
||||
Operate(operate string) error
|
||||
SearchWithPage(search dto.SearchClamWithPage) (int64, interface{}, error)
|
||||
Create(req dto.ClamCreate) error
|
||||
Update(req dto.ClamUpdate) error
|
||||
UpdateStatus(id uint, status string) error
|
||||
Delete(req dto.ClamDelete) error
|
||||
HandleOnce(req dto.OperateByID) error
|
||||
LoadFile(req dto.ClamFileReq) (string, error)
|
||||
UpdateFile(req dto.UpdateByNameAndFile) error
|
||||
LoadRecords(req dto.ClamLogSearch) (int64, interface{}, error)
|
||||
CleanRecord(req dto.OperateByID) error
|
||||
|
||||
LoadRecordLog(req dto.ClamLogReq) (string, error)
|
||||
}
|
||||
|
||||
func NewIClamService() IClamService {
|
||||
return &ClamService{}
|
||||
}
|
||||
|
||||
func (c *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) {
|
||||
var baseInfo dto.ClamBaseInfo
|
||||
baseInfo.Version = "-"
|
||||
baseInfo.FreshVersion = "-"
|
||||
exist1, _ := systemctl.IsExist(clamServiceNameCentOs)
|
||||
if exist1 {
|
||||
c.serviceName = clamServiceNameCentOs
|
||||
baseInfo.IsExist = true
|
||||
baseInfo.IsActive, _ = systemctl.IsActive(clamServiceNameCentOs)
|
||||
}
|
||||
exist2, _ := systemctl.IsExist(clamServiceNameUbuntu)
|
||||
if exist2 {
|
||||
c.serviceName = clamServiceNameUbuntu
|
||||
baseInfo.IsExist = true
|
||||
baseInfo.IsActive, _ = systemctl.IsActive(clamServiceNameUbuntu)
|
||||
}
|
||||
freshExist, _ := systemctl.IsExist(freshClamService)
|
||||
if freshExist {
|
||||
baseInfo.FreshIsExist = true
|
||||
baseInfo.FreshIsActive, _ = systemctl.IsActive(freshClamService)
|
||||
}
|
||||
if !cmd.Which("clamdscan") {
|
||||
baseInfo.IsActive = false
|
||||
}
|
||||
|
||||
if baseInfo.IsActive {
|
||||
version, err := cmd.Exec("clamdscan --version")
|
||||
if err == nil {
|
||||
if strings.Contains(version, "/") {
|
||||
baseInfo.Version = strings.TrimPrefix(strings.Split(version, "/")[0], "ClamAV ")
|
||||
} else {
|
||||
baseInfo.Version = strings.TrimPrefix(version, "ClamAV ")
|
||||
}
|
||||
}
|
||||
}
|
||||
if baseInfo.FreshIsActive {
|
||||
version, err := cmd.Exec("freshclam --version")
|
||||
if err == nil {
|
||||
if strings.Contains(version, "/") {
|
||||
baseInfo.FreshVersion = strings.TrimPrefix(strings.Split(version, "/")[0], "ClamAV ")
|
||||
} else {
|
||||
baseInfo.FreshVersion = strings.TrimPrefix(version, "ClamAV ")
|
||||
}
|
||||
}
|
||||
}
|
||||
return baseInfo, nil
|
||||
}
|
||||
|
||||
func (c *ClamService) Operate(operate string) error {
|
||||
switch operate {
|
||||
case "start", "restart", "stop":
|
||||
stdout, err := cmd.Execf("systemctl %s %s", operate, c.serviceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s the %s failed, err: %s", operate, c.serviceName, stdout)
|
||||
}
|
||||
return nil
|
||||
case "fresh-start", "fresh-restart", "fresh-stop":
|
||||
stdout, err := cmd.Execf("systemctl %s %s", strings.TrimPrefix(operate, "fresh-"), freshClamService)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s the %s failed, err: %s", operate, c.serviceName, stdout)
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("not support such operation: %v", operate)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClamService) SearchWithPage(req dto.SearchClamWithPage) (int64, interface{}, error) {
|
||||
total, commands, err := clamRepo.Page(req.Page, req.PageSize, commonRepo.WithLikeName(req.Info), commonRepo.WithOrderRuleBy(req.OrderBy, req.Order))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
var datas []dto.ClamInfo
|
||||
for _, command := range commands {
|
||||
var item dto.ClamInfo
|
||||
if err := copier.Copy(&item, &command); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
item.LastHandleDate = "-"
|
||||
datas = append(datas, item)
|
||||
}
|
||||
nyc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||
for i := 0; i < len(datas); i++ {
|
||||
logPaths := loadFileByName(datas[i].Name)
|
||||
sort.Slice(logPaths, func(i, j int) bool {
|
||||
return logPaths[i] > logPaths[j]
|
||||
})
|
||||
if len(logPaths) != 0 {
|
||||
t1, err := time.ParseInLocation(constant.DateTimeSlimLayout, logPaths[0], nyc)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
datas[i].LastHandleDate = t1.Format(constant.DateTimeLayout)
|
||||
}
|
||||
}
|
||||
return total, datas, err
|
||||
}
|
||||
|
||||
func (c *ClamService) Create(req dto.ClamCreate) error {
|
||||
clam, _ := clamRepo.Get(commonRepo.WithByName(req.Name))
|
||||
if clam.ID != 0 {
|
||||
return constant.ErrRecordExist
|
||||
}
|
||||
if err := copier.Copy(&clam, &req); err != nil {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
if clam.InfectedStrategy == "none" || clam.InfectedStrategy == "remove" {
|
||||
clam.InfectedDir = ""
|
||||
}
|
||||
if len(req.Spec) != 0 {
|
||||
entryID, err := xpack.StartClam(clam, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clam.EntryID = entryID
|
||||
clam.Status = constant.StatusEnable
|
||||
}
|
||||
if err := clamRepo.Create(&clam); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClamService) Update(req dto.ClamUpdate) error {
|
||||
clam, _ := clamRepo.Get(commonRepo.WithByName(req.Name))
|
||||
if clam.ID == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
if req.InfectedStrategy == "none" || req.InfectedStrategy == "remove" {
|
||||
req.InfectedDir = ""
|
||||
}
|
||||
var clamItem model.Clam
|
||||
if err := copier.Copy(&clamItem, &req); err != nil {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
clamItem.EntryID = clam.EntryID
|
||||
upMap := map[string]interface{}{}
|
||||
if len(clam.Spec) != 0 && clam.EntryID != 0 {
|
||||
global.Cron.Remove(cron.EntryID(clamItem.EntryID))
|
||||
upMap["entry_id"] = 0
|
||||
}
|
||||
if len(req.Spec) == 0 {
|
||||
upMap["status"] = ""
|
||||
upMap["entry_id"] = 0
|
||||
}
|
||||
if len(req.Spec) != 0 && clam.Status != constant.StatusDisable {
|
||||
newEntryID, err := xpack.StartClam(clamItem, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
upMap["entry_id"] = newEntryID
|
||||
}
|
||||
if len(clam.Spec) == 0 && len(req.Spec) != 0 {
|
||||
upMap["status"] = constant.StatusEnable
|
||||
}
|
||||
|
||||
upMap["name"] = req.Name
|
||||
upMap["path"] = req.Path
|
||||
upMap["infected_dir"] = req.InfectedDir
|
||||
upMap["infected_strategy"] = req.InfectedStrategy
|
||||
upMap["spec"] = req.Spec
|
||||
upMap["description"] = req.Description
|
||||
if err := clamRepo.Update(req.ID, upMap); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClamService) UpdateStatus(id uint, status string) error {
|
||||
clam, _ := clamRepo.Get(commonRepo.WithByID(id))
|
||||
if clam.ID == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
var (
|
||||
entryID int
|
||||
err error
|
||||
)
|
||||
if status == constant.StatusEnable {
|
||||
entryID, err = xpack.StartClam(clam, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
global.Cron.Remove(cron.EntryID(clam.EntryID))
|
||||
global.LOG.Infof("stop cronjob entryID: %v", clam.EntryID)
|
||||
}
|
||||
|
||||
return clamRepo.Update(clam.ID, map[string]interface{}{"status": status, "entry_id": entryID})
|
||||
}
|
||||
|
||||
func (c *ClamService) Delete(req dto.ClamDelete) error {
|
||||
for _, id := range req.Ids {
|
||||
clam, _ := clamRepo.Get(commonRepo.WithByID(id))
|
||||
if clam.ID == 0 {
|
||||
continue
|
||||
}
|
||||
if req.RemoveRecord {
|
||||
_ = os.RemoveAll(path.Join(global.CONF.System.DataDir, resultDir, clam.Name))
|
||||
}
|
||||
if req.RemoveInfected {
|
||||
_ = os.RemoveAll(path.Join(clam.InfectedDir, "1panel-infected", clam.Name))
|
||||
}
|
||||
if err := clamRepo.Delete(commonRepo.WithByID(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClamService) HandleOnce(req dto.OperateByID) error {
|
||||
if cmd.Which("clamdscan") == false {
|
||||
return buserr.New("ErrClamdscanNotFound")
|
||||
}
|
||||
clam, _ := clamRepo.Get(commonRepo.WithByID(req.ID))
|
||||
if clam.ID == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
if cmd.CheckIllegal(clam.Path) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
timeNow := time.Now().Format(constant.DateTimeSlimLayout)
|
||||
logFile := path.Join(global.CONF.System.DataDir, resultDir, clam.Name, timeNow)
|
||||
if _, err := os.Stat(path.Dir(logFile)); err != nil {
|
||||
_ = os.MkdirAll(path.Dir(logFile), os.ModePerm)
|
||||
}
|
||||
go func() {
|
||||
strategy := ""
|
||||
switch clam.InfectedStrategy {
|
||||
case "remove":
|
||||
strategy = "--remove"
|
||||
case "move":
|
||||
dir := path.Join(clam.InfectedDir, "1panel-infected", clam.Name, timeNow)
|
||||
strategy = "--move=" + dir
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
_ = os.MkdirAll(dir, os.ModePerm)
|
||||
}
|
||||
case "copy":
|
||||
dir := path.Join(clam.InfectedDir, "1panel-infected", clam.Name, timeNow)
|
||||
strategy = "--copy=" + dir
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
_ = os.MkdirAll(dir, os.ModePerm)
|
||||
}
|
||||
}
|
||||
global.LOG.Debugf("clamdscan --fdpass %s %s -l %s", strategy, clam.Path, logFile)
|
||||
stdout, err := cmd.Execf("clamdscan --fdpass %s %s -l %s", strategy, clam.Path, logFile)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("clamdscan failed, stdout: %v, err: %v", stdout, err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClamService) LoadRecords(req dto.ClamLogSearch) (int64, interface{}, error) {
|
||||
clam, _ := clamRepo.Get(commonRepo.WithByID(req.ClamID))
|
||||
if clam.ID == 0 {
|
||||
return 0, nil, constant.ErrRecordNotFound
|
||||
}
|
||||
logPaths := loadFileByName(clam.Name)
|
||||
if len(logPaths) == 0 {
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
var filterFiles []string
|
||||
nyc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||
for _, item := range logPaths {
|
||||
t1, err := time.ParseInLocation(constant.DateTimeSlimLayout, item, nyc)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if t1.After(req.StartTime) && t1.Before(req.EndTime) {
|
||||
filterFiles = append(filterFiles, item)
|
||||
}
|
||||
}
|
||||
if len(filterFiles) == 0 {
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
sort.Slice(filterFiles, func(i, j int) bool {
|
||||
return filterFiles[i] > filterFiles[j]
|
||||
})
|
||||
|
||||
var records []string
|
||||
total, start, end := len(filterFiles), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||
if start > total {
|
||||
records = make([]string, 0)
|
||||
} else {
|
||||
if end >= total {
|
||||
end = total
|
||||
}
|
||||
records = filterFiles[start:end]
|
||||
}
|
||||
|
||||
var datas []dto.ClamLog
|
||||
for i := 0; i < len(records); i++ {
|
||||
item := loadResultFromLog(path.Join(global.CONF.System.DataDir, resultDir, clam.Name, records[i]))
|
||||
datas = append(datas, item)
|
||||
}
|
||||
return int64(total), datas, nil
|
||||
}
|
||||
func (c *ClamService) LoadRecordLog(req dto.ClamLogReq) (string, error) {
|
||||
logPath := path.Join(global.CONF.System.DataDir, resultDir, req.ClamName, req.RecordName)
|
||||
var tail string
|
||||
if req.Tail != "0" {
|
||||
tail = req.Tail
|
||||
} else {
|
||||
tail = "+1"
|
||||
}
|
||||
cmd := exec.Command("tail", "-n", tail, logPath)
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("tail -n %v failed, err: %v", req.Tail, err)
|
||||
}
|
||||
return string(stdout), nil
|
||||
}
|
||||
|
||||
func (c *ClamService) CleanRecord(req dto.OperateByID) error {
|
||||
clam, _ := clamRepo.Get(commonRepo.WithByID(req.ID))
|
||||
if clam.ID == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
pathItem := path.Join(global.CONF.System.DataDir, resultDir, clam.Name)
|
||||
_ = os.RemoveAll(pathItem)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClamService) LoadFile(req dto.ClamFileReq) (string, error) {
|
||||
filePath := ""
|
||||
switch req.Name {
|
||||
case "clamd":
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
filePath = "/etc/clamav/clamd.conf"
|
||||
} else {
|
||||
filePath = "/etc/clamd.d/scan.conf"
|
||||
}
|
||||
case "clamd-log":
|
||||
filePath = c.loadLogPath("clamd-log")
|
||||
if len(filePath) != 0 {
|
||||
break
|
||||
}
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
filePath = "/var/log/clamav/clamav.log"
|
||||
} else {
|
||||
filePath = "/var/log/clamd.scan"
|
||||
}
|
||||
case "freshclam":
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
filePath = "/etc/clamav/freshclam.conf"
|
||||
} else {
|
||||
filePath = "/etc/freshclam.conf"
|
||||
}
|
||||
case "freshclam-log":
|
||||
filePath = c.loadLogPath("freshclam-log")
|
||||
if len(filePath) != 0 {
|
||||
break
|
||||
}
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
filePath = "/var/log/clamav/freshclam.log"
|
||||
} else {
|
||||
filePath = "/var/log/freshclam.log"
|
||||
}
|
||||
default:
|
||||
return "", fmt.Errorf("not support such type")
|
||||
}
|
||||
if _, err := os.Stat(filePath); err != nil {
|
||||
return "", buserr.New("ErrHttpReqNotFound")
|
||||
}
|
||||
var tail string
|
||||
if req.Tail != "0" {
|
||||
tail = req.Tail
|
||||
} else {
|
||||
tail = "+1"
|
||||
}
|
||||
cmd := exec.Command("tail", "-n", tail, filePath)
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("tail -n %v failed, err: %v", req.Tail, err)
|
||||
}
|
||||
return string(stdout), nil
|
||||
}
|
||||
|
||||
func (c *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error {
|
||||
filePath := ""
|
||||
service := ""
|
||||
switch req.Name {
|
||||
case "clamd":
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
service = clamServiceNameUbuntu
|
||||
filePath = "/etc/clamav/clamd.conf"
|
||||
} else {
|
||||
service = clamServiceNameCentOs
|
||||
filePath = "/etc/clamd.d/scan.conf"
|
||||
}
|
||||
case "freshclam":
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
filePath = "/etc/clamav/freshclam.conf"
|
||||
} else {
|
||||
filePath = "/etc/freshclam.conf"
|
||||
}
|
||||
service = "clamav-freshclam.service"
|
||||
default:
|
||||
return fmt.Errorf("not support such type")
|
||||
}
|
||||
file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
write := bufio.NewWriter(file)
|
||||
_, _ = write.WriteString(req.File)
|
||||
write.Flush()
|
||||
|
||||
_ = systemctl.Restart(service)
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadFileByName(name string) []string {
|
||||
var logPaths []string
|
||||
pathItem := path.Join(global.CONF.System.DataDir, resultDir, name)
|
||||
_ = filepath.Walk(pathItem, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if info.IsDir() || info.Name() == name {
|
||||
return nil
|
||||
}
|
||||
logPaths = append(logPaths, info.Name())
|
||||
return nil
|
||||
})
|
||||
return logPaths
|
||||
}
|
||||
func loadResultFromLog(pathItem string) dto.ClamLog {
|
||||
var data dto.ClamLog
|
||||
data.Name = path.Base(pathItem)
|
||||
data.Status = constant.StatusWaiting
|
||||
file, err := os.ReadFile(pathItem)
|
||||
if err != nil {
|
||||
return data
|
||||
}
|
||||
lines := strings.Split(string(file), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "- SCAN SUMMARY -") {
|
||||
data.Status = constant.StatusDone
|
||||
}
|
||||
if data.Status != constant.StatusDone {
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case strings.HasPrefix(line, "Infected files:"):
|
||||
data.InfectedFiles = strings.TrimPrefix(line, "Infected files:")
|
||||
case strings.HasPrefix(line, "Total errors:"):
|
||||
data.TotalError = strings.TrimPrefix(line, "Total errors:")
|
||||
case strings.HasPrefix(line, "Time:"):
|
||||
if strings.Contains(line, "(") {
|
||||
data.ScanTime = strings.ReplaceAll(strings.Split(line, "(")[1], ")", "")
|
||||
continue
|
||||
}
|
||||
data.ScanTime = strings.TrimPrefix(line, "Time:")
|
||||
case strings.HasPrefix(line, "Start Date:"):
|
||||
data.ScanDate = strings.TrimPrefix(line, "Start Date:")
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
func (c *ClamService) loadLogPath(name string) string {
|
||||
confPath := ""
|
||||
if name == "clamd-log" {
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
confPath = "/etc/clamav/clamd.conf"
|
||||
} else {
|
||||
confPath = "/etc/clamd.d/scan.conf"
|
||||
}
|
||||
} else {
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
confPath = "/etc/clamav/freshclam.conf"
|
||||
} else {
|
||||
confPath = "/etc/freshclam.conf"
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(confPath); err != nil {
|
||||
return ""
|
||||
}
|
||||
content, err := os.ReadFile(confPath)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
lines := strings.Split(string(content), "\n")
|
||||
if name == "clamd-log" {
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "LogFile ") {
|
||||
return strings.Trim(strings.ReplaceAll(line, "LogFile ", ""), " ")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "UpdateLogFile ") {
|
||||
return strings.Trim(strings.ReplaceAll(line, "UpdateLogFile ", ""), " ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
@@ -16,6 +17,11 @@ type ICommandService interface {
|
||||
Create(commandDto dto.CommandOperate) error
|
||||
Update(id uint, upMap map[string]interface{}) error
|
||||
Delete(ids []uint) error
|
||||
|
||||
SearchRedisCommandWithPage(search dto.SearchWithPage) (int64, interface{}, error)
|
||||
ListRedisCommand() ([]dto.RedisCommand, error)
|
||||
SaveRedisCommand(commandDto dto.RedisCommand) error
|
||||
DeleteRedisCommand(ids []uint) error
|
||||
}
|
||||
|
||||
func NewICommandService() ICommandService {
|
||||
@@ -65,7 +71,7 @@ func (u *CommandService) SearchForTree() ([]dto.CommandTree, error) {
|
||||
}
|
||||
|
||||
func (u *CommandService) SearchWithPage(search dto.SearchCommandWithPage) (int64, interface{}, error) {
|
||||
total, commands, err := commandRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info), commonRepo.WithByGroupID(search.GroupID), commonRepo.WithOrderRuleBy(search.OrderBy, search.Order))
|
||||
total, commands, err := commandRepo.Page(search.Page, search.PageSize, commandRepo.WithLikeName(search.Name), commonRepo.WithLikeName(search.Info), commonRepo.WithByGroupID(search.GroupID), commonRepo.WithOrderRuleBy(search.OrderBy, search.Order))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
@@ -115,3 +121,63 @@ func (u *CommandService) Delete(ids []uint) error {
|
||||
func (u *CommandService) Update(id uint, upMap map[string]interface{}) error {
|
||||
return commandRepo.Update(id, upMap)
|
||||
}
|
||||
|
||||
func (u *CommandService) SearchRedisCommandWithPage(search dto.SearchWithPage) (int64, interface{}, error) {
|
||||
total, commands, err := commandRepo.PageRedis(search.Page, search.PageSize, commandRepo.WithLikeName(search.Info))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
var dtoCommands []dto.RedisCommand
|
||||
for _, command := range commands {
|
||||
var item dto.RedisCommand
|
||||
if err := copier.Copy(&item, &command); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
dtoCommands = append(dtoCommands, item)
|
||||
}
|
||||
return total, dtoCommands, err
|
||||
}
|
||||
|
||||
func (u *CommandService) ListRedisCommand() ([]dto.RedisCommand, error) {
|
||||
commands, err := commandRepo.GetRedisList()
|
||||
if err != nil {
|
||||
return nil, constant.ErrRecordNotFound
|
||||
}
|
||||
var dtoCommands []dto.RedisCommand
|
||||
for _, command := range commands {
|
||||
var item dto.RedisCommand
|
||||
if err := copier.Copy(&item, &command); err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
dtoCommands = append(dtoCommands, item)
|
||||
}
|
||||
return dtoCommands, err
|
||||
}
|
||||
|
||||
func (u *CommandService) SaveRedisCommand(req dto.RedisCommand) error {
|
||||
if req.ID == 0 {
|
||||
command, _ := commandRepo.GetRedis(commonRepo.WithByName(req.Name))
|
||||
if command.ID != 0 {
|
||||
return constant.ErrRecordExist
|
||||
}
|
||||
}
|
||||
var command model.RedisCommand
|
||||
if err := copier.Copy(&command, &req); err != nil {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
if err := commandRepo.SaveRedis(&command); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CommandService) DeleteRedisCommand(ids []uint) error {
|
||||
if len(ids) == 1 {
|
||||
command, _ := commandRepo.GetRedis(commonRepo.WithByID(ids[0]))
|
||||
if command.ID == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
return commandRepo.DeleteRedis(commonRepo.WithByID(ids[0]))
|
||||
}
|
||||
return commandRepo.DeleteRedis(commonRepo.WithIdsIn(ids))
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -17,6 +20,8 @@ import (
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
@@ -29,6 +34,7 @@ import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
@@ -59,9 +65,11 @@ type IContainerService interface {
|
||||
ContainerListStats() ([]dto.ContainerListStats, error)
|
||||
LoadResourceLimit() (*dto.ResourceLimit, error)
|
||||
ContainerRename(req dto.ContainerRename) error
|
||||
ContainerCommit(req dto.ContainerCommit) error
|
||||
ContainerLogClean(req dto.OperationWithName) error
|
||||
ContainerOperation(req dto.ContainerOperation) error
|
||||
ContainerLogs(wsConn *websocket.Conn, containerType, container, since, tail string, follow bool) error
|
||||
DownloadContainerLogs(containerType, container, since, tail string, c *gin.Context) error
|
||||
ContainerStats(id string) (*dto.ContainerStats, error)
|
||||
Inspect(req dto.InspectReq) (string, error)
|
||||
DeleteNetwork(req dto.BatchDelete) error
|
||||
@@ -88,6 +96,7 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer client.Close()
|
||||
options := container.ListOptions{
|
||||
All: true,
|
||||
}
|
||||
@@ -181,7 +190,7 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
ports := loadContainerPort(item.Ports)
|
||||
info := dto.ContainerInfo{
|
||||
ContainerID: item.ID,
|
||||
CreateTime: time.Unix(item.Created, 0).Format("2006-01-02 15:04:05"),
|
||||
CreateTime: time.Unix(item.Created, 0).Format(constant.DateTimeLayout),
|
||||
Name: item.Names[0][1:],
|
||||
ImageId: strings.Split(item.ImageID, ":")[1],
|
||||
ImageName: item.Image,
|
||||
@@ -219,6 +228,7 @@ func (u *ContainerService) List() ([]string, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer client.Close()
|
||||
containers, err := client.ContainerList(context.Background(), container.ListOptions{All: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -240,6 +250,7 @@ func (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer client.Close()
|
||||
list, err := client.ContainerList(context.Background(), container.ListOptions{All: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -262,6 +273,7 @@ func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer client.Close()
|
||||
var inspectInfo interface{}
|
||||
switch req.Type {
|
||||
case "container":
|
||||
@@ -289,6 +301,7 @@ func (u *ContainerService) Prune(req dto.ContainerPrune) (dto.ContainerPruneRepo
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
defer client.Close()
|
||||
pruneFilters := filters.NewArgs()
|
||||
if req.WithTagAll {
|
||||
pruneFilters.Add("dangling", "false")
|
||||
@@ -318,12 +331,28 @@ func (u *ContainerService) Prune(req dto.ContainerPrune) (dto.ContainerPruneRepo
|
||||
}
|
||||
report.DeletedNumber = len(rep.NetworksDeleted)
|
||||
case "volume":
|
||||
versions, err := client.ServerVersion(context.Background())
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
if common.ComparePanelVersion(versions.APIVersion, "1.42") {
|
||||
pruneFilters.Add("all", "true")
|
||||
}
|
||||
rep, err := client.VolumesPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
report.DeletedNumber = len(rep.VolumesDeleted)
|
||||
report.SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||
case "buildcache":
|
||||
opts := types.BuildCachePruneOptions{}
|
||||
opts.All = true
|
||||
rep, err := client.BuildCachePrune(context.Background(), opts)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
report.DeletedNumber = len(rep.CachesDeleted)
|
||||
report.SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||
}
|
||||
return report, nil
|
||||
}
|
||||
@@ -350,6 +379,7 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerOperate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
ctx := context.Background()
|
||||
newContainer, _ := client.ContainerInspect(ctx, req.Name)
|
||||
if newContainer.ContainerJSONBase != nil {
|
||||
@@ -397,6 +427,7 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer client.Close()
|
||||
ctx := context.Background()
|
||||
oldContainer, err := client.ContainerInspect(ctx, req.Name)
|
||||
if err != nil {
|
||||
@@ -467,6 +498,7 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
ctx := context.Background()
|
||||
newContainer, _ := client.ContainerInspect(ctx, req.Name)
|
||||
if newContainer.ContainerJSONBase != nil && newContainer.ID != req.ContainerID {
|
||||
@@ -515,6 +547,7 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
ctx := context.Background()
|
||||
oldContainer, err := client.ContainerInspect(ctx, req.Name)
|
||||
if err != nil {
|
||||
@@ -562,6 +595,7 @@ func (u *ContainerService) ContainerRename(req dto.ContainerRename) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
newContainer, _ := client.ContainerInspect(ctx, req.NewName)
|
||||
if newContainer.ContainerJSONBase != nil {
|
||||
@@ -570,6 +604,28 @@ func (u *ContainerService) ContainerRename(req dto.ContainerRename) error {
|
||||
return client.ContainerRename(ctx, req.Name, req.NewName)
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerCommit(req dto.ContainerCommit) error {
|
||||
ctx := context.Background()
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
options := container.CommitOptions{
|
||||
Reference: req.NewImageName,
|
||||
Comment: req.Comment,
|
||||
Author: req.Author,
|
||||
Changes: nil,
|
||||
Pause: req.Pause,
|
||||
Config: nil,
|
||||
}
|
||||
_, err = client.ContainerCommit(ctx, req.ContainerId, options)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to commit container, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error {
|
||||
var err error
|
||||
ctx := context.Background()
|
||||
@@ -577,6 +633,7 @@ func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
for _, item := range req.Names {
|
||||
global.LOG.Infof("start container %s operation %s", item, req.Operation)
|
||||
switch req.Operation {
|
||||
@@ -604,6 +661,7 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
ctx := context.Background()
|
||||
containerItem, err := client.ContainerInspect(ctx, req.Name)
|
||||
if err != nil {
|
||||
@@ -701,7 +759,7 @@ func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, containerType,
|
||||
return
|
||||
}
|
||||
global.LOG.Errorf("read bytes from log failed, err: %v", err)
|
||||
continue
|
||||
return
|
||||
}
|
||||
if !utf8.Valid(buffer[:n]) {
|
||||
continue
|
||||
@@ -717,11 +775,85 @@ func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, containerType,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) DownloadContainerLogs(containerType, container, since, tail string, c *gin.Context) error {
|
||||
if cmd.CheckIllegal(container, since, tail) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
commandName := "docker"
|
||||
commandArg := []string{"logs", container}
|
||||
if containerType == "compose" {
|
||||
commandName = "docker-compose"
|
||||
commandArg = []string{"-f", container, "logs"}
|
||||
}
|
||||
if tail != "0" {
|
||||
commandArg = append(commandArg, "--tail")
|
||||
commandArg = append(commandArg, tail)
|
||||
}
|
||||
if since != "all" {
|
||||
commandArg = append(commandArg, "--since")
|
||||
commandArg = append(commandArg, since)
|
||||
}
|
||||
|
||||
cmd := exec.Command(commandName, commandArg...)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
_ = cmd.Process.Signal(syscall.SIGTERM)
|
||||
return err
|
||||
}
|
||||
cmd.Stderr = cmd.Stdout
|
||||
if err := cmd.Start(); err != nil {
|
||||
_ = cmd.Process.Signal(syscall.SIGTERM)
|
||||
return err
|
||||
}
|
||||
|
||||
tempFile, err := os.CreateTemp("", "cmd_output_*.txt")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tempFile.Close()
|
||||
defer func() {
|
||||
if err := os.Remove(tempFile.Name()); err != nil {
|
||||
global.LOG.Errorf("os.Remove() failed: %v", err)
|
||||
}
|
||||
}()
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if _, err := tempFile.WriteString(line + "\n"); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
errCh <- nil
|
||||
}()
|
||||
select {
|
||||
case err := <-errCh:
|
||||
if err != nil {
|
||||
global.LOG.Errorf("Error: %v", err)
|
||||
}
|
||||
case <-time.After(3 * time.Second):
|
||||
global.LOG.Errorf("Timeout reached")
|
||||
}
|
||||
info, _ := tempFile.Stat()
|
||||
|
||||
c.Header("Content-Length", strconv.FormatInt(info.Size(), 10))
|
||||
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(info.Name()))
|
||||
http.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), tempFile)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error) {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer client.Close()
|
||||
res, err := client.ContainerStats(context.TODO(), id, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -757,6 +889,7 @@ func (u *ContainerService) LoadContainerLogs(req dto.OperationWithNameAndType) s
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer cli.Close()
|
||||
options := container.ListOptions{All: true}
|
||||
options.Filters = filters.NewArgs()
|
||||
options.Filters.Add("label", fmt.Sprintf("%s=%s", composeProjectLabel, req.Name))
|
||||
@@ -840,16 +973,15 @@ func calculateNetwork(network map[string]types.NetworkStats) (float64, float64)
|
||||
return rx, tx
|
||||
}
|
||||
|
||||
func checkImageExist(client *client.Client, image string) bool {
|
||||
images, err := client.ImageList(context.Background(), types.ImageListOptions{})
|
||||
func checkImageExist(client *client.Client, imageItem string) bool {
|
||||
images, err := client.ImageList(context.Background(), image.ListOptions{})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return false
|
||||
}
|
||||
|
||||
for _, img := range images {
|
||||
for _, tag := range img.RepoTags {
|
||||
if tag == image || tag == image+":latest" {
|
||||
if tag == imageItem || tag == imageItem+":latest" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -857,12 +989,12 @@ func checkImageExist(client *client.Client, image string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func pullImages(ctx context.Context, client *client.Client, image string) error {
|
||||
options := types.ImagePullOptions{}
|
||||
func pullImages(ctx context.Context, client *client.Client, imageName string) error {
|
||||
options := image.PullOptions{}
|
||||
repos, _ := imageRepoRepo.List()
|
||||
if len(repos) != 0 {
|
||||
for _, repo := range repos {
|
||||
if strings.HasPrefix(image, repo.DownloadUrl) && repo.Auth {
|
||||
if strings.HasPrefix(imageName, repo.DownloadUrl) && repo.Auth {
|
||||
authConfig := registry.AuthConfig{
|
||||
Username: repo.Username,
|
||||
Password: repo.Password,
|
||||
@@ -875,8 +1007,13 @@ func pullImages(ctx context.Context, client *client.Client, image string) error
|
||||
options.RegistryAuth = authStr
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hasAuth, authStr := loadAuthInfo(imageName)
|
||||
if hasAuth {
|
||||
options.RegistryAuth = authStr
|
||||
}
|
||||
}
|
||||
out, err := client.ImagePull(ctx, image, options)
|
||||
out, err := client.ImagePull(ctx, imageName, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -897,12 +1034,11 @@ func loadCpuAndMem(client *client.Client, container string) dto.ContainerListSta
|
||||
return data
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
res.Body.Close()
|
||||
return data
|
||||
}
|
||||
res.Body.Close()
|
||||
var stats *types.StatsJSON
|
||||
if err := json.Unmarshal(body, &stats); err != nil {
|
||||
return data
|
||||
@@ -1011,7 +1147,7 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
|
||||
}
|
||||
} else {
|
||||
if req.Ipv4 != "" || req.Ipv6 != "" {
|
||||
return nil, nil, nil, fmt.Errorf("Please set up the network")
|
||||
return nil, nil, nil, fmt.Errorf("please set up the network")
|
||||
}
|
||||
networkConf = network.NetworkingConfig{}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -13,6 +12,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
@@ -39,6 +40,7 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
options := container.ListOptions{All: true}
|
||||
options.Filters = filters.NewArgs()
|
||||
@@ -57,7 +59,7 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
||||
ContainerID: container.ID,
|
||||
Name: container.Names[0][1:],
|
||||
State: container.State,
|
||||
CreateTime: time.Unix(container.Created, 0).Format("2006-01-02 15:04:05"),
|
||||
CreateTime: time.Unix(container.Created, 0).Format(constant.DateTimeLayout),
|
||||
}
|
||||
if compose, has := composeMap[name]; has {
|
||||
compose.ContainerNumber++
|
||||
@@ -68,7 +70,7 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
||||
workdir := container.Labels[composeWorkdirLabel]
|
||||
composeItem := dto.ComposeInfo{
|
||||
ContainerNumber: 1,
|
||||
CreatedAt: time.Unix(container.Created, 0).Format("2006-01-02 15:04:05"),
|
||||
CreatedAt: time.Unix(container.Created, 0).Format(constant.DateTimeLayout),
|
||||
ConfigFile: config,
|
||||
Workdir: workdir,
|
||||
Containers: []dto.ComposeContainer{containerItem},
|
||||
@@ -166,7 +168,7 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error)
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
logItem := fmt.Sprintf("%s/compose_create_%s_%s.log", dockerLogDir, req.Name, time.Now().Format("20060102150405"))
|
||||
logItem := fmt.Sprintf("%s/compose_create_%s_%s.log", dockerLogDir, req.Name, time.Now().Format(constant.DateTimeSlimLayout))
|
||||
file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -230,12 +232,6 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
|
||||
write.Flush()
|
||||
|
||||
global.LOG.Infof("docker-compose.yml %s has been replaced, now start to docker-compose restart", req.Path)
|
||||
if stdout, err := compose.Down(req.Path); err != nil {
|
||||
if err := recreateCompose(string(oldFile), req.Path); err != nil {
|
||||
return fmt.Errorf("update failed when handle compose down, err: %s, recreate failed: %v", string(stdout), err)
|
||||
}
|
||||
return fmt.Errorf("update failed when handle compose down, err: %s", string(stdout))
|
||||
}
|
||||
if stdout, err := compose.Up(req.Path); err != nil {
|
||||
if err := recreateCompose(string(oldFile), req.Path); err != nil {
|
||||
return fmt.Errorf("update failed when handle compose up, err: %s, recreate failed: %v", string(stdout), err)
|
||||
|
||||
@@ -19,6 +19,7 @@ func (u *ContainerService) PageNetwork(req dto.SearchWithPage) (int64, interface
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer client.Close()
|
||||
list, err := client.NetworkList(context.TODO(), types.NetworkListOptions{})
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
@@ -81,6 +82,7 @@ func (u *ContainerService) ListNetwork() ([]dto.Options, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer client.Close()
|
||||
list, err := client.NetworkList(context.TODO(), types.NetworkListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -100,6 +102,7 @@ func (u *ContainerService) DeleteNetwork(req dto.BatchDelete) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
for _, id := range req.Names {
|
||||
if err := client.NetworkRemove(context.TODO(), id); err != nil {
|
||||
if strings.Contains(err.Error(), "has active endpoints") {
|
||||
@@ -115,6 +118,7 @@ func (u *ContainerService) CreateNetwork(req dto.NetworkCreate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
var (
|
||||
ipams []network.IPAMConfig
|
||||
enableV6 bool
|
||||
|
||||
@@ -82,6 +82,7 @@ func (u *ContainerService) ListVolume() ([]dto.Options, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer client.Close()
|
||||
list, err := client.VolumeList(context.TODO(), volume.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -102,6 +103,7 @@ func (u *ContainerService) DeleteVolume(req dto.BatchDelete) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
for _, id := range req.Names {
|
||||
if err := client.VolumeRemove(context.TODO(), id, true); err != nil {
|
||||
if strings.Contains(err.Error(), "volume is in use") {
|
||||
@@ -117,6 +119,7 @@ func (u *ContainerService) CreateVolume(req dto.VolumeCreate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
arg := filters.NewArgs()
|
||||
arg.Add("name", req.Name)
|
||||
vos, _ := client.VolumeList(context.TODO(), volume.ListOptions{Filters: arg})
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
type CronjobService struct{}
|
||||
|
||||
type ICronjobService interface {
|
||||
SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error)
|
||||
SearchWithPage(search dto.PageCronjob) (int64, interface{}, error)
|
||||
SearchRecords(search dto.SearchRecord) (int64, interface{}, error)
|
||||
Create(cronjobDto dto.CronjobCreate) error
|
||||
HandleOnce(id uint) error
|
||||
@@ -29,7 +29,7 @@ type ICronjobService interface {
|
||||
UpdateStatus(id uint, status string) error
|
||||
Delete(req dto.CronjobBatchDelete) error
|
||||
Download(down dto.CronjobDownload) (string, error)
|
||||
StartJob(cronjob *model.Cronjob) (string, error)
|
||||
StartJob(cronjob *model.Cronjob, isUpdate bool) (string, error)
|
||||
CleanRecord(req dto.CronjobClean) error
|
||||
|
||||
LoadRecordLog(req dto.OperateByID) string
|
||||
@@ -39,7 +39,7 @@ func NewICronjobService() ICronjobService {
|
||||
return &CronjobService{}
|
||||
}
|
||||
|
||||
func (u *CronjobService) SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error) {
|
||||
func (u *CronjobService) SearchWithPage(search dto.PageCronjob) (int64, interface{}, error) {
|
||||
total, cronjobs, err := cronjobRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info), commonRepo.WithOrderRuleBy(search.OrderBy, search.Order))
|
||||
var dtoCronjobs []dto.CronjobInfo
|
||||
for _, cronjob := range cronjobs {
|
||||
@@ -49,7 +49,7 @@ func (u *CronjobService) SearchWithPage(search dto.SearchWithPage) (int64, inter
|
||||
}
|
||||
record, _ := cronjobRepo.RecordFirst(cronjob.ID)
|
||||
if record.ID != 0 {
|
||||
item.LastRecordTime = record.StartTime.Format("2006-01-02 15:04:05")
|
||||
item.LastRecordTime = record.StartTime.Format(constant.DateTimeLayout)
|
||||
} else {
|
||||
item.LastRecordTime = "-"
|
||||
}
|
||||
@@ -71,6 +71,7 @@ func (u *CronjobService) SearchRecords(search dto.SearchRecord) (int64, interfac
|
||||
if err := copier.Copy(&item, &record); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
item.StartTime = record.StartTime.Format(constant.DateTimeLayout)
|
||||
dtoCronjobs = append(dtoCronjobs, item)
|
||||
}
|
||||
return total, dtoCronjobs, err
|
||||
@@ -172,6 +173,7 @@ func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
|
||||
if cronjob.ID != 0 {
|
||||
return constant.ErrRecordExist
|
||||
}
|
||||
cronjob.Secret = cronjobDto.Secret
|
||||
if err := copier.Copy(&cronjob, &cronjobDto); err != nil {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
@@ -179,7 +181,7 @@ func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
|
||||
|
||||
global.LOG.Infof("create cronjob %s successful, spec: %s", cronjob.Name, cronjob.Spec)
|
||||
spec := cronjob.Spec
|
||||
entryIDs, err := u.StartJob(&cronjob)
|
||||
entryIDs, err := u.StartJob(&cronjob, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -191,8 +193,8 @@ func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) StartJob(cronjob *model.Cronjob) (string, error) {
|
||||
if len(cronjob.EntryIDs) != 0 {
|
||||
func (u *CronjobService) StartJob(cronjob *model.Cronjob, isUpdate bool) (string, error) {
|
||||
if len(cronjob.EntryIDs) != 0 && isUpdate {
|
||||
ids := strings.Split(cronjob.EntryIDs, ",")
|
||||
for _, id := range ids {
|
||||
idItem, _ := strconv.Atoi(id)
|
||||
@@ -249,7 +251,7 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
|
||||
cronjob.Type = cronModel.Type
|
||||
spec := cronjob.Spec
|
||||
if cronModel.Status == constant.StatusEnable {
|
||||
newEntryIDs, err := u.StartJob(&cronjob)
|
||||
newEntryIDs, err := u.StartJob(&cronjob, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -278,6 +280,7 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
|
||||
upMap["backup_accounts"] = req.BackupAccounts
|
||||
upMap["default_download"] = req.DefaultDownload
|
||||
upMap["retain_copies"] = req.RetainCopies
|
||||
upMap["secret"] = req.Secret
|
||||
return cronjobRepo.Update(id, upMap)
|
||||
}
|
||||
|
||||
@@ -291,7 +294,7 @@ func (u *CronjobService) UpdateStatus(id uint, status string) error {
|
||||
err error
|
||||
)
|
||||
if status == constant.StatusEnable {
|
||||
entryIDs, err = u.StartJob(&cronjob)
|
||||
entryIDs, err = u.StartJob(&cronjob, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -327,7 +330,7 @@ func mkdirAndWriteFile(cronjob *model.Cronjob, startTime time.Time, msg []byte)
|
||||
}
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("%s/%s.log", dir, startTime.Format("20060102150405"))
|
||||
path := fmt.Sprintf("%s/%s.log", dir, startTime.Format(constant.DateTimeSlimLayout))
|
||||
global.LOG.Infof("cronjob %s has generated some logs %s", cronjob.Name, path)
|
||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
@@ -17,15 +17,20 @@ import (
|
||||
|
||||
func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time) error {
|
||||
var apps []model.AppInstall
|
||||
if cronjob.AppID == "all" {
|
||||
if strings.Contains(cronjob.AppID, "all") {
|
||||
apps, _ = appInstallRepo.ListBy()
|
||||
} else {
|
||||
itemID, _ := (strconv.Atoi(cronjob.AppID))
|
||||
app, err := appInstallRepo.GetFirst(commonRepo.WithByID(uint(itemID)))
|
||||
appIds := strings.Split(cronjob.AppID, ",")
|
||||
var idItems []uint
|
||||
for i := 0; i < len(appIds); i++ {
|
||||
itemID, _ := strconv.Atoi(appIds[i])
|
||||
idItems = append(idItems, uint(itemID))
|
||||
}
|
||||
appItems, err := appInstallRepo.ListBy(commonRepo.WithIdsIn(idItems))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
apps = append(apps, app)
|
||||
apps = appItems
|
||||
}
|
||||
accountMap, err := loadClientMap(cronjob.BackupAccounts)
|
||||
if err != nil {
|
||||
@@ -40,8 +45,8 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time) e
|
||||
record.DetailName = app.Name
|
||||
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
|
||||
backupDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("app/%s/%s", app.App.Key, app.Name))
|
||||
record.FileName = fmt.Sprintf("app_%s_%s.tar.gz", app.Name, startTime.Format("20060102150405")+common.RandStrAndNum(5))
|
||||
if err := handleAppBackup(&app, backupDir, record.FileName); err != nil {
|
||||
record.FileName = fmt.Sprintf("app_%s_%s.tar.gz", app.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||
if err := handleAppBackup(&app, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
|
||||
return err
|
||||
}
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
|
||||
@@ -73,8 +78,8 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
|
||||
record.DetailName = web.Alias
|
||||
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
|
||||
backupDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("website/%s", web.PrimaryDomain))
|
||||
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", web.PrimaryDomain, startTime.Format("20060102150405")+common.RandStrAndNum(5))
|
||||
if err := handleWebsiteBackup(&web, backupDir, record.FileName); err != nil {
|
||||
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", web.PrimaryDomain, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||
if err := handleWebsiteBackup(&web, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
|
||||
return err
|
||||
}
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
|
||||
@@ -107,7 +112,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
|
||||
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
|
||||
|
||||
backupDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("database/%s/%s/%s", dbInfo.DBType, record.Name, dbInfo.Name))
|
||||
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format("20060102150405")+common.RandStrAndNum(5))
|
||||
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
|
||||
if err := handleMysqlBackup(dbInfo.Database, dbInfo.DBType, dbInfo.Name, backupDir, record.FileName); err != nil {
|
||||
return err
|
||||
@@ -136,9 +141,9 @@ func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.T
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileName := fmt.Sprintf("directory%s_%s.tar.gz", strings.ReplaceAll(cronjob.SourceDir, "/", "_"), startTime.Format("20060102150405")+common.RandStrAndNum(5))
|
||||
fileName := fmt.Sprintf("directory%s_%s.tar.gz", strings.ReplaceAll(cronjob.SourceDir, "/", "_"), startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||
backupDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("%s/%s", cronjob.Type, cronjob.Name))
|
||||
if err := handleTar(cronjob.SourceDir, backupDir, fileName, cronjob.ExclusionRules); err != nil {
|
||||
if err := handleTar(cronjob.SourceDir, backupDir, fileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
|
||||
return err
|
||||
}
|
||||
var record model.BackupRecord
|
||||
@@ -166,10 +171,10 @@ func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.T
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nameItem := startTime.Format("20060102150405") + common.RandStrAndNum(5)
|
||||
nameItem := startTime.Format(constant.DateTimeSlimLayout) + common.RandStrAndNum(5)
|
||||
fileName := fmt.Sprintf("system_log_%s.tar.gz", nameItem)
|
||||
backupDir := path.Join(global.CONF.System.TmpDir, "log", nameItem)
|
||||
if err := handleBackupLogs(backupDir, fileName); err != nil {
|
||||
if err := handleBackupLogs(backupDir, fileName, cronjob.Secret); err != nil {
|
||||
return err
|
||||
}
|
||||
var record model.BackupRecord
|
||||
@@ -210,7 +215,7 @@ func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, startTime time.Ti
|
||||
From: record.BackupType,
|
||||
DefaultDownload: cronjob.DefaultDownload,
|
||||
}
|
||||
name, err := NewISnapshotService().HandleSnapshot(true, logPath, req, startTime.Format("20060102150405")+common.RandStrAndNum(5))
|
||||
name, err := NewISnapshotService().HandleSnapshot(true, logPath, req, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5), cronjob.Secret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -232,7 +237,7 @@ type databaseHelper struct {
|
||||
|
||||
func loadDbsForJob(cronjob model.Cronjob) []databaseHelper {
|
||||
var dbs []databaseHelper
|
||||
if cronjob.DBName == "all" {
|
||||
if strings.Contains(cronjob.DBName, "all") {
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
|
||||
mysqlItems, _ := mysqlRepo.List()
|
||||
for _, mysql := range mysqlItems {
|
||||
@@ -254,36 +259,42 @@ func loadDbsForJob(cronjob model.Cronjob) []databaseHelper {
|
||||
}
|
||||
return dbs
|
||||
}
|
||||
itemID, _ := (strconv.Atoi(cronjob.DBName))
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
|
||||
mysqlItem, _ := mysqlRepo.Get(commonRepo.WithByID(uint(itemID)))
|
||||
dbs = append(dbs, databaseHelper{
|
||||
DBType: cronjob.DBType,
|
||||
Database: mysqlItem.MysqlName,
|
||||
Name: mysqlItem.Name,
|
||||
})
|
||||
} else {
|
||||
pgItem, _ := postgresqlRepo.Get(commonRepo.WithByID(uint(itemID)))
|
||||
dbs = append(dbs, databaseHelper{
|
||||
DBType: cronjob.DBType,
|
||||
Database: pgItem.PostgresqlName,
|
||||
Name: pgItem.Name,
|
||||
})
|
||||
|
||||
dbNames := strings.Split(cronjob.DBName, ",")
|
||||
for _, name := range dbNames {
|
||||
itemID, _ := strconv.Atoi(name)
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
|
||||
mysqlItem, _ := mysqlRepo.Get(commonRepo.WithByID(uint(itemID)))
|
||||
dbs = append(dbs, databaseHelper{
|
||||
DBType: cronjob.DBType,
|
||||
Database: mysqlItem.MysqlName,
|
||||
Name: mysqlItem.Name,
|
||||
})
|
||||
} else {
|
||||
pgItem, _ := postgresqlRepo.Get(commonRepo.WithByID(uint(itemID)))
|
||||
dbs = append(dbs, databaseHelper{
|
||||
DBType: cronjob.DBType,
|
||||
Database: pgItem.PostgresqlName,
|
||||
Name: pgItem.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
return dbs
|
||||
}
|
||||
|
||||
func loadWebsForJob(cronjob model.Cronjob) []model.Website {
|
||||
var weblist []model.Website
|
||||
if cronjob.Website == "all" {
|
||||
if strings.Contains(cronjob.Website, "all") {
|
||||
weblist, _ = websiteRepo.List()
|
||||
return weblist
|
||||
}
|
||||
itemID, _ := (strconv.Atoi(cronjob.Website))
|
||||
webItem, _ := websiteRepo.GetFirst(commonRepo.WithByID(uint(itemID)))
|
||||
if webItem.ID != 0 {
|
||||
weblist = append(weblist, webItem)
|
||||
websites := strings.Split(cronjob.Website, ",")
|
||||
var idItems []uint
|
||||
for i := 0; i < len(websites); i++ {
|
||||
itemID, _ := strconv.Atoi(websites[i])
|
||||
idItems = append(idItems, uint(itemID))
|
||||
}
|
||||
weblist, _ = websiteRepo.List(commonRepo.WithIdsIn(idItems))
|
||||
return weblist
|
||||
}
|
||||
|
||||
@@ -303,7 +314,7 @@ func loadRecordPath(cronjob model.Cronjob, accountMap map[string]cronjobUploadHe
|
||||
return source, backupType
|
||||
}
|
||||
|
||||
func handleBackupLogs(targetDir, fileName string) error {
|
||||
func handleBackupLogs(targetDir, fileName string, secret string) error {
|
||||
websites, err := websiteRepo.List()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -376,7 +387,7 @@ func handleBackupLogs(targetDir, fileName string) error {
|
||||
}
|
||||
global.LOG.Debug("backup ssh log successful!")
|
||||
|
||||
if err := handleTar(targetDir, path.Dir(targetDir), fileName, ""); err != nil {
|
||||
if err := handleTar(targetDir, path.Dir(targetDir), fileName, "", secret); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
|
||||
@@ -2,7 +2,6 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
@@ -125,13 +124,13 @@ func (u *CronjobService) handleNtpSync() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ntp.UpdateSystemTime(ntime.Format("2006-01-02 15:04:05")); err != nil {
|
||||
if err := ntp.UpdateSystemTime(ntime.Format(constant.DateTimeLayout)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
func handleTar(sourceDir, targetDir, name, exclusionRules string, secret string) error {
|
||||
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
@@ -159,8 +158,16 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
path = sourceDir
|
||||
}
|
||||
|
||||
commands := fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read -zcf %s %s %s", targetDir+"/"+name, excludeRules, path)
|
||||
global.LOG.Debug(commands)
|
||||
commands := ""
|
||||
|
||||
if len(secret) != 0 {
|
||||
extraCmd := "| openssl enc -aes-256-cbc -salt -k '" + secret + "' -out"
|
||||
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read -zcf %s %s %s %s", " -"+excludeRules, path, extraCmd, targetDir+"/"+name)
|
||||
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" %s ", secret), "******"))
|
||||
} else {
|
||||
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read -zcf %s %s %s", targetDir+"/"+name, excludeRules, path)
|
||||
global.LOG.Debug(commands)
|
||||
}
|
||||
stdout, err := cmd.ExecWithTimeOut(commands, 24*time.Hour)
|
||||
if err != nil {
|
||||
if len(stdout) != 0 {
|
||||
@@ -171,15 +178,22 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleUnTar(sourceFile, targetDir string) error {
|
||||
func handleUnTar(sourceFile, targetDir string, secret string) error {
|
||||
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
commands := ""
|
||||
if len(secret) != 0 {
|
||||
extraCmd := "openssl enc -d -aes-256-cbc -k '" + secret + "' -in " + sourceFile + " | "
|
||||
commands = fmt.Sprintf("%s tar -zxvf - -C %s", extraCmd, targetDir+" > /dev/null 2>&1")
|
||||
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" %s ", secret), "******"))
|
||||
} else {
|
||||
commands = fmt.Sprintf("tar zxvfC %s %s", sourceFile, targetDir)
|
||||
global.LOG.Debug(commands)
|
||||
}
|
||||
|
||||
commands := fmt.Sprintf("tar zxvfC %s %s", sourceFile, targetDir)
|
||||
global.LOG.Debug(commands)
|
||||
stdout, err := cmd.ExecWithTimeOut(commands, 24*time.Hour)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do handle untar failed, stdout: %s, err: %v", stdout, err)
|
||||
@@ -210,7 +224,7 @@ func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime t
|
||||
_ = os.MkdirAll(dstLogDir, 0755)
|
||||
}
|
||||
|
||||
dstName := fmt.Sprintf("%s_log_%s.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
|
||||
dstName := fmt.Sprintf("%s_log_%s.gz", website.PrimaryDomain, startTime.Format(constant.DateTimeSlimLayout))
|
||||
dstFilePath := path.Join(dstLogDir, dstName)
|
||||
filePaths = append(filePaths, dstFilePath)
|
||||
|
||||
@@ -276,21 +290,6 @@ func loadClientMap(backupAccounts string) (map[string]cronjobUploadHelper, error
|
||||
if account.BackupPath != "/" {
|
||||
pathItem = strings.TrimPrefix(account.BackupPath, "/")
|
||||
}
|
||||
if target == constant.Local {
|
||||
varMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(account.Vars), &varMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := varMap["dir"]; !ok {
|
||||
return nil, errors.New("load local backup dir failed")
|
||||
}
|
||||
baseDir, ok := varMap["dir"].(string)
|
||||
if ok {
|
||||
pathItem = baseDir
|
||||
} else {
|
||||
return nil, fmt.Errorf("error type dir: %T", varMap["dir"])
|
||||
}
|
||||
}
|
||||
clients[target] = cronjobUploadHelper{
|
||||
client: client,
|
||||
backupPath: pathItem,
|
||||
@@ -385,7 +384,7 @@ func (u *CronjobService) generateLogsPath(cronjob model.Cronjob, startTime time.
|
||||
_ = os.MkdirAll(dir, os.ModePerm)
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("%s/%s.log", dir, startTime.Format("20060102150405"))
|
||||
path := fmt.Sprintf("%s/%s.log", dir, startTime.Format(constant.DateTimeSlimLayout))
|
||||
return path
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/copier"
|
||||
@@ -64,6 +65,11 @@ func (u *DashboardService) LoadOsInfo() (*dto.OsInfo, error) {
|
||||
baseInfo.KernelArch = hostInfo.KernelArch
|
||||
baseInfo.KernelVersion = hostInfo.KernelVersion
|
||||
|
||||
diskInfo, err := disk.Usage(global.CONF.System.BaseDir)
|
||||
if err == nil {
|
||||
baseInfo.DiskSize = int64(diskInfo.Free)
|
||||
}
|
||||
|
||||
if baseInfo.KernelArch == "armv7l" {
|
||||
baseInfo.KernelArch = "armv7"
|
||||
}
|
||||
@@ -94,11 +100,15 @@ func (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto
|
||||
return nil, err
|
||||
}
|
||||
baseInfo.AppInstalledNumber = len(appInstall)
|
||||
dbs, err := mysqlRepo.List()
|
||||
postgresqlDbs, err := postgresqlRepo.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
baseInfo.DatabaseNumber = len(dbs)
|
||||
mysqlDbs, err := mysqlRepo.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
baseInfo.DatabaseNumber = len(mysqlDbs) + len(postgresqlDbs)
|
||||
website, err := websiteRepo.GetBy()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -126,7 +136,7 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
|
||||
var currentInfo dto.DashboardCurrent
|
||||
hostInfo, _ := host.Info()
|
||||
currentInfo.Uptime = hostInfo.Uptime
|
||||
currentInfo.TimeSinceUptime = time.Now().Add(-time.Duration(hostInfo.Uptime) * time.Second).Format("2006-01-02 15:04:05")
|
||||
currentInfo.TimeSinceUptime = time.Now().Add(-time.Duration(hostInfo.Uptime) * time.Second).Format(constant.DateTimeLayout)
|
||||
currentInfo.Procs = hostInfo.Procs
|
||||
|
||||
currentInfo.CPUTotal, _ = cpu.Counts(true)
|
||||
@@ -222,10 +232,13 @@ func loadDiskInfo() []dto.DiskInfo {
|
||||
if len(fields) < 7 {
|
||||
continue
|
||||
}
|
||||
if fields[1] == "tmpfs" {
|
||||
if strings.HasPrefix(fields[6], "/snap") || len(strings.Split(fields[6], "/")) > 10 {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(fields[2], "M") || strings.Contains(fields[2], "K") {
|
||||
if strings.TrimSpace(fields[1]) == "tmpfs" {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(fields[2], "K") {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(fields[6], "docker") {
|
||||
@@ -240,7 +253,7 @@ func loadDiskInfo() []dto.DiskInfo {
|
||||
if isExclude {
|
||||
continue
|
||||
}
|
||||
mounts = append(mounts, diskInfo{Type: fields[1], Device: fields[0], Mount: fields[6]})
|
||||
mounts = append(mounts, diskInfo{Type: fields[1], Device: fields[0], Mount: strings.Join(fields[6:], " ")})
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -7,7 +7,8 @@ import (
|
||||
"path"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/postgresql"
|
||||
client2 "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
|
||||
pgclient "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
|
||||
redisclient "github.com/1Panel-dev/1Panel/backend/utils/redis"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
@@ -42,6 +43,7 @@ func (u *DatabaseService) SearchWithPage(search dto.DatabaseSearch) (int64, inte
|
||||
total, dbs, err := databaseRepo.Page(search.Page, search.PageSize,
|
||||
databaseRepo.WithTypeList(search.Type),
|
||||
commonRepo.WithLikeName(search.Info),
|
||||
commonRepo.WithOrderRuleBy(search.OrderBy, search.Order),
|
||||
databaseRepo.WithoutByFrom("local"),
|
||||
)
|
||||
var datas []dto.DatabaseInfo
|
||||
@@ -69,6 +71,9 @@ func (u *DatabaseService) Get(name string) (dto.DatabaseInfo, error) {
|
||||
|
||||
func (u *DatabaseService) List(dbType string) ([]dto.DatabaseOption, error) {
|
||||
dbs, err := databaseRepo.GetList(databaseRepo.WithTypeList(dbType))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var datas []dto.DatabaseOption
|
||||
for _, db := range dbs {
|
||||
var item dto.DatabaseOption
|
||||
@@ -113,7 +118,7 @@ func (u *DatabaseService) LoadItems(dbType string) ([]dto.DatabaseItem, error) {
|
||||
func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
||||
switch req.Type {
|
||||
case constant.AppPostgresql:
|
||||
_, err := postgresql.NewPostgresqlClient(client2.DBInfo{
|
||||
_, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
@@ -122,6 +127,13 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
||||
Timeout: 6,
|
||||
})
|
||||
return err == nil
|
||||
case constant.AppRedis:
|
||||
_, err := redisclient.NewRedisClient(redisclient.DBInfo{
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Password: req.Password,
|
||||
})
|
||||
return err == nil
|
||||
case "mysql", "mariadb":
|
||||
_, err := mysql.NewMysqlClient(client.DBInfo{
|
||||
From: "remote",
|
||||
@@ -153,7 +165,7 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
|
||||
}
|
||||
switch req.Type {
|
||||
case constant.AppPostgresql:
|
||||
if _, err := postgresql.NewPostgresqlClient(client2.DBInfo{
|
||||
if _, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
@@ -163,6 +175,14 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
case constant.AppRedis:
|
||||
if _, err := redisclient.NewRedisClient(redisclient.DBInfo{
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Password: req.Password,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
case "mysql", "mariadb":
|
||||
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
||||
From: "remote",
|
||||
@@ -249,7 +269,7 @@ func (u *DatabaseService) Delete(req dto.DatabaseDelete) error {
|
||||
func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
||||
switch req.Type {
|
||||
case constant.AppPostgresql:
|
||||
if _, err := postgresql.NewPostgresqlClient(client2.DBInfo{
|
||||
if _, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
@@ -259,6 +279,14 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
case constant.AppRedis:
|
||||
if _, err := redisclient.NewRedisClient(redisclient.DBInfo{
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Password: req.Password,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
case "mysql", "mariadb":
|
||||
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
||||
From: "remote",
|
||||
|
||||
@@ -196,6 +196,9 @@ func (u *MysqlService) LoadFromRemote(req dto.MysqlLoadDB) error {
|
||||
for i := 0; i < len(databases); i++ {
|
||||
if strings.EqualFold(databases[i].Name, data.Name) && strings.EqualFold(databases[i].MysqlName, data.MysqlName) {
|
||||
hasOld = true
|
||||
if databases[i].IsDelete {
|
||||
_ = mysqlRepo.Update(databases[i].ID, map[string]interface{}{"is_delete": false})
|
||||
}
|
||||
deleteList = append(deleteList[:i], deleteList[i+1:]...)
|
||||
break
|
||||
}
|
||||
@@ -240,7 +243,7 @@ func (u *MysqlService) DeleteCheck(req dto.MysqlDBDeleteCheck) ([]string, error)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(db.ID))
|
||||
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(db.ID), appRepo.WithKey(req.Type))
|
||||
for _, app := range apps {
|
||||
appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId))
|
||||
if appInstall.ID != 0 {
|
||||
@@ -363,6 +366,17 @@ func (u *MysqlService) ChangePassword(req dto.ChangeDBInfo) error {
|
||||
if err := updateInstallInfoInDB(req.Type, req.Database, "password", req.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.From == "local" {
|
||||
remote, err := databaseRepo.Get(commonRepo.WithByName(req.Database))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pass, err := encrypt.StringEncrypt(req.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decrypt database password failed, err: %v", err)
|
||||
}
|
||||
_ = databaseRepo.Update(remote.ID, map[string]interface{}{"password": pass})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -456,7 +470,7 @@ func (u *MysqlService) LoadRemoteAccess(req dto.OperationWithNameAndType) (bool,
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
hosts, err := executeSqlForRows(app.ContainerName, app.Password, "select host from mysql.user where user='root';")
|
||||
hosts, err := executeSqlForRows(app.ContainerName, app.Key, app.Password, "select host from mysql.user where user='root';")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -474,7 +488,7 @@ func (u *MysqlService) LoadVariables(req dto.OperationWithNameAndType) (*dto.Mys
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
variableMap, err := executeSqlForMaps(app.ContainerName, app.Password, "show global variables;")
|
||||
variableMap, err := executeSqlForMaps(app.ContainerName, app.Key, app.Password, "show global variables;")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -493,7 +507,7 @@ func (u *MysqlService) LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlS
|
||||
return nil, err
|
||||
}
|
||||
|
||||
statusMap, err := executeSqlForMaps(app.ContainerName, app.Password, "show global status;")
|
||||
statusMap, err := executeSqlForMaps(app.ContainerName, app.Key, app.Password, "show global status;")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -507,17 +521,17 @@ func (u *MysqlService) LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlS
|
||||
|
||||
if value, ok := statusMap["Run"]; ok {
|
||||
uptime, _ := strconv.Atoi(value)
|
||||
info.Run = time.Unix(time.Now().Unix()-int64(uptime), 0).Format("2006-01-02 15:04:05")
|
||||
info.Run = time.Unix(time.Now().Unix()-int64(uptime), 0).Format(constant.DateTimeLayout)
|
||||
} else {
|
||||
if value, ok := statusMap["Uptime"]; ok {
|
||||
uptime, _ := strconv.Atoi(value)
|
||||
info.Run = time.Unix(time.Now().Unix()-int64(uptime), 0).Format("2006-01-02 15:04:05")
|
||||
info.Run = time.Unix(time.Now().Unix()-int64(uptime), 0).Format(constant.DateTimeLayout)
|
||||
}
|
||||
}
|
||||
|
||||
info.File = "OFF"
|
||||
info.Position = "OFF"
|
||||
rows, err := executeSqlForRows(app.ContainerName, app.Password, "show master status;")
|
||||
rows, err := executeSqlForRows(app.ContainerName, app.Key, app.Password, "show master status;")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -532,8 +546,8 @@ func (u *MysqlService) LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlS
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func executeSqlForMaps(containerName, password, command string) (map[string]string, error) {
|
||||
cmd := exec.Command("docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
|
||||
func executeSqlForMaps(containerName, dbType, password, command string) (map[string]string, error) {
|
||||
cmd := exec.Command("docker", "exec", containerName, dbType, "-uroot", "-p"+password, "-e", command)
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
stdStr := strings.ReplaceAll(string(stdout), "mysql: [Warning] Using a password on the command line interface can be insecure.\n", "")
|
||||
if err != nil || strings.HasPrefix(string(stdStr), "ERROR ") {
|
||||
@@ -551,8 +565,8 @@ func executeSqlForMaps(containerName, password, command string) (map[string]stri
|
||||
return rowMap, nil
|
||||
}
|
||||
|
||||
func executeSqlForRows(containerName, password, command string) ([]string, error) {
|
||||
cmd := exec.Command("docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
|
||||
func executeSqlForRows(containerName, dbType, password, command string) ([]string, error) {
|
||||
cmd := exec.Command("docker", "exec", containerName, dbType, "-uroot", "-p"+password, "-e", command)
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
stdStr := strings.ReplaceAll(string(stdout), "mysql: [Warning] Using a password on the command line interface can be insecure.\n", "")
|
||||
if err != nil || strings.HasPrefix(string(stdStr), "ERROR ") {
|
||||
@@ -614,6 +628,7 @@ func LoadMysqlClientByFrom(database string) (mysql.MysqlClient, string, error) {
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
dbInfo.Type = databaseItem.Type
|
||||
dbInfo.From = databaseItem.From
|
||||
dbInfo.Database = database
|
||||
if dbInfo.From != "local" {
|
||||
|
||||
@@ -222,6 +222,9 @@ func (u *PostgresqlService) LoadFromRemote(database string) error {
|
||||
for i := 0; i < len(databases); i++ {
|
||||
if strings.EqualFold(databases[i].Name, data.Name) && strings.EqualFold(databases[i].PostgresqlName, data.PostgresqlName) {
|
||||
hasOld = true
|
||||
if databases[i].IsDelete {
|
||||
_ = postgresqlRepo.Update(databases[i].ID, map[string]interface{}{"is_delete": false})
|
||||
}
|
||||
deleteList = append(deleteList[:i], deleteList[i+1:]...)
|
||||
break
|
||||
}
|
||||
@@ -266,7 +269,7 @@ func (u *PostgresqlService) DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]stri
|
||||
}
|
||||
}
|
||||
} else {
|
||||
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(db.ID))
|
||||
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(db.ID), appRepo.WithKey(req.Type))
|
||||
for _, app := range apps {
|
||||
appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId))
|
||||
if appInstall.ID != 0 {
|
||||
@@ -414,5 +417,16 @@ func (u *PostgresqlService) ChangePassword(req dto.ChangeDBInfo) error {
|
||||
if err := updateInstallInfoInDB(req.Type, req.Database, "password", req.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.From == "local" {
|
||||
remote, err := databaseRepo.Get(commonRepo.WithByName(req.Database))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pass, err := encrypt.StringEncrypt(req.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decrypt database password failed, err: %v", err)
|
||||
}
|
||||
_ = databaseRepo.Update(remote.ID, map[string]interface{}{"password": pass})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
@@ -23,11 +25,12 @@ type IRedisService interface {
|
||||
UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error
|
||||
ChangePassword(info dto.ChangeRedisPass) error
|
||||
|
||||
LoadStatus() (*dto.RedisStatus, error)
|
||||
LoadConf() (*dto.RedisConf, error)
|
||||
LoadPersistenceConf() (*dto.RedisPersistence, error)
|
||||
LoadStatus(req dto.OperationWithName) (*dto.RedisStatus, error)
|
||||
LoadConf(req dto.OperationWithName) (*dto.RedisConf, error)
|
||||
LoadPersistenceConf(req dto.OperationWithName) (*dto.RedisPersistence, error)
|
||||
|
||||
SearchBackupListWithPage(req dto.PageInfo) (int64, interface{}, error)
|
||||
CheckHasCli() bool
|
||||
InstallCli() error
|
||||
}
|
||||
|
||||
func NewIRedisService() IRedisService {
|
||||
@@ -35,7 +38,7 @@ func NewIRedisService() IRedisService {
|
||||
}
|
||||
|
||||
func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -54,19 +57,54 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *RedisService) CheckHasCli() bool {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer client.Close()
|
||||
containerLists, err := client.ContainerList(context.Background(), container.ListOptions{})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, item := range containerLists {
|
||||
if strings.ReplaceAll(item.Names[0], "/", "") == "1Panel-redis-cli-tools" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *RedisService) InstallCli() error {
|
||||
item := dto.ContainerOperate{
|
||||
Name: "1Panel-redis-cli-tools",
|
||||
Image: "redis:7.2.4",
|
||||
Network: "1panel-network",
|
||||
}
|
||||
return NewIContainerService().ContainerCreate(item)
|
||||
}
|
||||
|
||||
func (u *RedisService) ChangePassword(req dto.ChangeRedisPass) error {
|
||||
if err := updateInstallInfoInDB("redis", "", "password", req.Value); err != nil {
|
||||
if err := updateInstallInfoInDB("redis", req.Database, "password", req.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := updateInstallInfoInDB("redis-commander", "", "password", req.Value); err != nil {
|
||||
remote, err := databaseRepo.Get(commonRepo.WithByName(req.Database))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if remote.From == "local" {
|
||||
pass, err := encrypt.StringEncrypt(req.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decrypt database password failed, err: %v", err)
|
||||
}
|
||||
_ = databaseRepo.Update(remote.ID, map[string]interface{}{"password": pass})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *RedisService) UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -88,8 +126,8 @@ func (u *RedisService) UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *RedisService) LoadStatus() (*dto.RedisStatus, error) {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
|
||||
func (u *RedisService) LoadStatus(req dto.OperationWithName) (*dto.RedisStatus, error) {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -116,8 +154,8 @@ func (u *RedisService) LoadStatus() (*dto.RedisStatus, error) {
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func (u *RedisService) LoadConf() (*dto.RedisConf, error) {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
|
||||
func (u *RedisService) LoadConf(req dto.OperationWithName) (*dto.RedisConf, error) {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -133,8 +171,8 @@ func (u *RedisService) LoadConf() (*dto.RedisConf, error) {
|
||||
return &item, nil
|
||||
}
|
||||
|
||||
func (u *RedisService) LoadPersistenceConf() (*dto.RedisPersistence, error) {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
|
||||
func (u *RedisService) LoadPersistenceConf(req dto.OperationWithName) (*dto.RedisPersistence, error) {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -151,46 +189,6 @@ func (u *RedisService) LoadPersistenceConf() (*dto.RedisPersistence, error) {
|
||||
return &item, nil
|
||||
}
|
||||
|
||||
func (u *RedisService) SearchBackupListWithPage(req dto.PageInfo) (int64, interface{}, error) {
|
||||
var (
|
||||
list []dto.DatabaseFileRecords
|
||||
backDatas []dto.DatabaseFileRecords
|
||||
)
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
backupDir := path.Join(localDir, fmt.Sprintf("database/redis/%s", redisInfo.Name))
|
||||
_ = filepath.Walk(backupDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if !info.IsDir() {
|
||||
list = append(list, dto.DatabaseFileRecords{
|
||||
CreatedAt: info.ModTime().Format("2006-01-02 15:04:05"),
|
||||
Size: int(info.Size()),
|
||||
FileDir: backupDir,
|
||||
FileName: info.Name(),
|
||||
})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
total, start, end := len(list), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||
if start > total {
|
||||
backDatas = make([]dto.DatabaseFileRecords, 0)
|
||||
} else {
|
||||
if end >= total {
|
||||
end = total
|
||||
}
|
||||
backDatas = list[start:end]
|
||||
}
|
||||
return int64(total), backDatas, nil
|
||||
}
|
||||
|
||||
func configGetStr(containerName, password, param string) (string, error) {
|
||||
commands := append(redisExec(containerName, password), []string{"config", "get", param}...)
|
||||
cmd := exec.Command("docker", commands...)
|
||||
|
||||
@@ -158,7 +158,7 @@ func (u *DeviceService) Update(key, value string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ts := ntime.Format("2006-01-02 15:04:05")
|
||||
ts := ntime.Format(constant.DateTimeLayout)
|
||||
if err := ntp.UpdateSystemTime(ts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
@@ -8,6 +9,11 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
@@ -67,7 +73,7 @@ func (u *DeviceService) Scan() dto.CleanData {
|
||||
snapTree := loadSnapshotTree(fileOp)
|
||||
snapSize := uint64(0)
|
||||
for _, snap := range snapTree {
|
||||
snapSize += uint64(snap.Size)
|
||||
snapSize += snap.Size
|
||||
}
|
||||
treeData = append(treeData, dto.CleanTree{
|
||||
ID: uuid.NewString(),
|
||||
@@ -82,7 +88,7 @@ func (u *DeviceService) Scan() dto.CleanData {
|
||||
rollBackTree := loadRollBackTree(fileOp)
|
||||
rollbackSize := uint64(0)
|
||||
for _, rollback := range rollBackTree {
|
||||
rollbackSize += uint64(rollback.Size)
|
||||
rollbackSize += rollback.Size
|
||||
}
|
||||
treeData = append(treeData, dto.CleanTree{
|
||||
ID: uuid.NewString(),
|
||||
@@ -108,7 +114,7 @@ func (u *DeviceService) Scan() dto.CleanData {
|
||||
unusedTree := loadUnusedFile(fileOp)
|
||||
unusedSize := uint64(0)
|
||||
for _, unused := range unusedTree {
|
||||
unusedSize += uint64(unused.Size)
|
||||
unusedSize += unused.Size
|
||||
}
|
||||
treeData = append(treeData, dto.CleanTree{
|
||||
ID: uuid.NewString(),
|
||||
@@ -130,6 +136,9 @@ func (u *DeviceService) Scan() dto.CleanData {
|
||||
logTree := loadLogTree(fileOp)
|
||||
SystemClean.SystemLogClean = append(SystemClean.SystemLogClean, logTree...)
|
||||
|
||||
containerTree := loadContainerTree()
|
||||
SystemClean.ContainerClean = append(SystemClean.ContainerClean, containerTree...)
|
||||
|
||||
return SystemClean
|
||||
}
|
||||
|
||||
@@ -259,10 +268,18 @@ func (u *DeviceService) Clean(req []dto.Clean) {
|
||||
} else {
|
||||
_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByRecordFile(pathItem))
|
||||
}
|
||||
case "images":
|
||||
dropImages()
|
||||
case "containers":
|
||||
dropContainers()
|
||||
case "volumes":
|
||||
dropVolumes()
|
||||
case "build_cache":
|
||||
dropBuildCache()
|
||||
}
|
||||
}
|
||||
|
||||
_ = settingRepo.Update("LastCleanTime", time.Now().Format("2006-01-02 15:04:05"))
|
||||
_ = settingRepo.Update("LastCleanTime", time.Now().Format(constant.DateTimeLayout))
|
||||
_ = settingRepo.Update("LastCleanSize", fmt.Sprintf("%v", size))
|
||||
_ = settingRepo.Update("LastCleanData", fmt.Sprintf("%v", len(req)))
|
||||
|
||||
@@ -326,7 +343,7 @@ func (u *DeviceService) CleanForCronjob() (string, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
timeNow := time.Now().Format("2006-01-02 15:04:05")
|
||||
timeNow := time.Now().Format(constant.DateTimeLayout)
|
||||
dropFileOrDirWithLog(path.Join(global.CONF.System.BaseDir, dockerLogPath), &logs, &size, &fileCount)
|
||||
logs += fmt.Sprintf("\n%s: total clean: %s, total count: %d", timeNow, common.LoadSizeUnit2F(float64(size)), fileCount)
|
||||
|
||||
@@ -496,6 +513,50 @@ func loadLogTree(fileOp fileUtils.FileOp) []dto.CleanTree {
|
||||
return treeData
|
||||
}
|
||||
|
||||
func loadContainerTree() []dto.CleanTree {
|
||||
var treeData []dto.CleanTree
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return treeData
|
||||
}
|
||||
diskUsage, err := client.DiskUsage(context.Background(), types.DiskUsageOptions{})
|
||||
if err != nil {
|
||||
return treeData
|
||||
}
|
||||
imageSize := uint64(0)
|
||||
for _, file := range diskUsage.Images {
|
||||
if file.Containers == 0 {
|
||||
imageSize += uint64(file.Size)
|
||||
}
|
||||
}
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "container_images", Size: imageSize, Children: nil, Type: "images", IsRecommend: true})
|
||||
|
||||
containerSize := uint64(0)
|
||||
for _, file := range diskUsage.Containers {
|
||||
if file.State != "running" {
|
||||
containerSize += uint64(file.SizeRw)
|
||||
}
|
||||
}
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "container_containers", Size: containerSize, Children: nil, Type: "containers", IsRecommend: true})
|
||||
|
||||
volumeSize := uint64(0)
|
||||
for _, file := range diskUsage.Volumes {
|
||||
if file.UsageData.RefCount <= 0 {
|
||||
volumeSize += uint64(file.UsageData.Size)
|
||||
}
|
||||
}
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "container_volumes", Size: volumeSize, Children: nil, Type: "volumes", IsRecommend: true})
|
||||
|
||||
var buildCacheTotalSize int64
|
||||
for _, cache := range diskUsage.BuildCache {
|
||||
if cache.Type == "source.local" {
|
||||
buildCacheTotalSize += cache.Size
|
||||
}
|
||||
}
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "build_cache", Size: uint64(buildCacheTotalSize), Type: "build_cache", IsRecommend: true})
|
||||
return treeData
|
||||
}
|
||||
|
||||
func loadTreeWithDir(isCheck bool, treeType, pathItem string, fileOp fileUtils.FileOp) []dto.CleanTree {
|
||||
var lists []dto.CleanTree
|
||||
files, err := os.ReadDir(pathItem)
|
||||
@@ -586,6 +647,63 @@ func dropFileOrDir(itemPath string) {
|
||||
}
|
||||
}
|
||||
|
||||
func dropBuildCache() {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do not get docker client")
|
||||
}
|
||||
opts := types.BuildCachePruneOptions{}
|
||||
opts.All = true
|
||||
_, err = client.BuildCachePrune(context.Background(), opts)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("drop build cache failed, err %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func dropImages() {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do not get docker client")
|
||||
}
|
||||
pruneFilters := filters.NewArgs()
|
||||
pruneFilters.Add("dangling", "false")
|
||||
_, err = client.ImagesPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("drop images failed, err %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func dropContainers() {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do not get docker client")
|
||||
}
|
||||
pruneFilters := filters.NewArgs()
|
||||
_, err = client.ContainersPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("drop containers failed, err %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func dropVolumes() {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do not get docker client")
|
||||
}
|
||||
pruneFilters := filters.NewArgs()
|
||||
versions, err := client.ServerVersion(context.Background())
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do not get docker api versions")
|
||||
}
|
||||
if common.ComparePanelVersion(versions.APIVersion, "1.42") {
|
||||
pruneFilters.Add("all", "true")
|
||||
}
|
||||
_, err = client.VolumesPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("drop volumes failed, err %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func dropFileOrDirWithLog(itemPath string, log *string, size *int64, count *int) {
|
||||
itemSize := int64(0)
|
||||
itemCount := 0
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
@@ -55,6 +54,7 @@ func (u *DockerService) LoadDockerStatus() string {
|
||||
if err != nil {
|
||||
return constant.Stopped
|
||||
}
|
||||
defer client.Close()
|
||||
if _, err := client.Ping(context.Background()); err != nil {
|
||||
return constant.Stopped
|
||||
}
|
||||
@@ -72,6 +72,7 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
|
||||
if err != nil {
|
||||
data.Status = constant.Stopped
|
||||
} else {
|
||||
defer client.Close()
|
||||
if _, err := client.Ping(ctx); err != nil {
|
||||
data.Status = constant.Stopped
|
||||
}
|
||||
@@ -102,7 +103,6 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
|
||||
return &data
|
||||
}
|
||||
if err := json.Unmarshal(arr, &conf); err != nil {
|
||||
fmt.Println(err)
|
||||
return &data
|
||||
}
|
||||
if _, ok := daemonMap["iptables"]; !ok {
|
||||
@@ -129,13 +129,10 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
|
||||
}
|
||||
|
||||
func (u *DockerService) UpdateConf(req dto.SettingUpdate) error {
|
||||
if _, err := os.Stat(constant.DaemonJsonPath); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(path.Dir(constant.DaemonJsonPath), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = os.Create(constant.DaemonJsonPath)
|
||||
err := createIfNotExistDaemonJsonFile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := os.ReadFile(constant.DaemonJsonPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -217,15 +214,26 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *DockerService) UpdateLogOption(req dto.LogOption) error {
|
||||
func createIfNotExistDaemonJsonFile() error {
|
||||
if _, err := os.Stat(constant.DaemonJsonPath); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(path.Dir(constant.DaemonJsonPath), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = os.Create(constant.DaemonJsonPath)
|
||||
var daemonFile *os.File
|
||||
daemonFile, err = os.Create(constant.DaemonJsonPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer daemonFile.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *DockerService) UpdateLogOption(req dto.LogOption) error {
|
||||
err := createIfNotExistDaemonJsonFile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file, err := os.ReadFile(constant.DaemonJsonPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -254,11 +262,9 @@ func (u *DockerService) UpdateLogOption(req dto.LogOption) error {
|
||||
}
|
||||
|
||||
func (u *DockerService) UpdateIpv6Option(req dto.Ipv6Option) error {
|
||||
if _, err := os.Stat(constant.DaemonJsonPath); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(path.Dir(constant.DaemonJsonPath), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = os.Create(constant.DaemonJsonPath)
|
||||
err := createIfNotExistDaemonJsonFile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := os.ReadFile(constant.DaemonJsonPath)
|
||||
@@ -300,11 +306,9 @@ func (u *DockerService) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error {
|
||||
_ = os.Remove(constant.DaemonJsonPath)
|
||||
return nil
|
||||
}
|
||||
if _, err := os.Stat(constant.DaemonJsonPath); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(path.Dir(constant.DaemonJsonPath), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = os.Create(constant.DaemonJsonPath)
|
||||
err := createIfNotExistDaemonJsonFile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file, err := os.OpenFile(constant.DaemonJsonPath, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||
if err != nil {
|
||||
|
||||
@@ -24,6 +24,8 @@ var (
|
||||
hostRepo = repo.NewIHostRepo()
|
||||
groupRepo = repo.NewIGroupRepo()
|
||||
commandRepo = repo.NewICommandRepo()
|
||||
ftpRepo = repo.NewIFtpRepo()
|
||||
clamRepo = repo.NewIClamRepo()
|
||||
|
||||
settingRepo = repo.NewISettingRepo()
|
||||
backupRepo = repo.NewIBackupRepo()
|
||||
|
||||
+127
-23
@@ -2,17 +2,24 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"golang.org/x/net/html/charset"
|
||||
"golang.org/x/sys/unix"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/transform"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
@@ -45,6 +52,10 @@ type IFileService interface {
|
||||
ReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error)
|
||||
}
|
||||
|
||||
var filteredPaths = []string{
|
||||
"/.1panel_clash",
|
||||
}
|
||||
|
||||
func NewIFileService() IFileService {
|
||||
return &FileService{}
|
||||
}
|
||||
@@ -73,7 +84,7 @@ func (f *FileService) SearchUploadWithPage(req request.SearchUploadWithPage) (in
|
||||
}
|
||||
if !info.IsDir() {
|
||||
files = append(files, response.UploadInfo{
|
||||
CreatedAt: info.ModTime().Format("2006-01-02 15:04:05"),
|
||||
CreatedAt: info.ModTime().Format(constant.DateTimeLayout),
|
||||
Size: int(info.Size()),
|
||||
Name: info.Name(),
|
||||
})
|
||||
@@ -94,34 +105,85 @@ func (f *FileService) SearchUploadWithPage(req request.SearchUploadWithPage) (in
|
||||
|
||||
func (f *FileService) GetFileTree(op request.FileOption) ([]response.FileTree, error) {
|
||||
var treeArray []response.FileTree
|
||||
if _, err := os.Stat(op.Path); err != nil && os.IsNotExist(err) {
|
||||
return treeArray, nil
|
||||
}
|
||||
info, err := files.NewFileInfo(op.FileOption)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
node := response.FileTree{
|
||||
ID: common.GetUuid(),
|
||||
Name: info.Name,
|
||||
Path: info.Path,
|
||||
ID: common.GetUuid(),
|
||||
Name: info.Name,
|
||||
Path: info.Path,
|
||||
IsDir: info.IsDir,
|
||||
Extension: info.Extension,
|
||||
}
|
||||
for _, v := range info.Items {
|
||||
if v.IsDir {
|
||||
node.Children = append(node.Children, response.FileTree{
|
||||
ID: common.GetUuid(),
|
||||
Name: v.Name,
|
||||
Path: v.Path,
|
||||
})
|
||||
}
|
||||
err = f.buildFileTree(&node, info.Items, op, 2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append(treeArray, node), nil
|
||||
}
|
||||
|
||||
func shouldFilterPath(path string) bool {
|
||||
cleanedPath := filepath.Clean(path)
|
||||
for _, filteredPath := range filteredPaths {
|
||||
cleanedFilteredPath := filepath.Clean(filteredPath)
|
||||
if cleanedFilteredPath == cleanedPath || strings.HasPrefix(cleanedPath, cleanedFilteredPath+"/") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 递归构建文件树(只取当前目录以及当前目录下的第一层子节点)
|
||||
func (f *FileService) buildFileTree(node *response.FileTree, items []*files.FileInfo, op request.FileOption, level int) error {
|
||||
for _, v := range items {
|
||||
if shouldFilterPath(v.Path) {
|
||||
global.LOG.Info("File Tree: Skipping %s due to filter\n", v.Path)
|
||||
continue
|
||||
}
|
||||
childNode := response.FileTree{
|
||||
ID: common.GetUuid(),
|
||||
Name: v.Name,
|
||||
Path: v.Path,
|
||||
IsDir: v.IsDir,
|
||||
Extension: v.Extension,
|
||||
}
|
||||
if level > 1 && v.IsDir {
|
||||
if err := f.buildChildNode(&childNode, v, op, level); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
node.Children = append(node.Children, childNode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FileService) buildChildNode(childNode *response.FileTree, fileInfo *files.FileInfo, op request.FileOption, level int) error {
|
||||
op.Path = fileInfo.Path
|
||||
subInfo, err := files.NewFileInfo(op.FileOption)
|
||||
if err != nil {
|
||||
if os.IsPermission(err) || errors.Is(err, unix.EACCES) {
|
||||
global.LOG.Info("File Tree: Skipping %s due to permission denied\n", fileInfo.Path)
|
||||
return nil
|
||||
}
|
||||
global.LOG.Errorf("File Tree: Skipping %s due to error: %s\n", fileInfo.Path, err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
return f.buildFileTree(childNode, subInfo.Items, op, level-1)
|
||||
}
|
||||
|
||||
func (f *FileService) Create(op request.FileCreate) error {
|
||||
if files.IsInvalidChar(op.Path) {
|
||||
return buserr.New("ErrInvalidChar")
|
||||
}
|
||||
fo := files.NewFileOp()
|
||||
if fo.Stat(op.Path) {
|
||||
return buserr.New(constant.ErrFileIsExit)
|
||||
return buserr.New(constant.ErrFileIsExist)
|
||||
}
|
||||
mode := op.Mode
|
||||
if mode == 0 {
|
||||
@@ -133,7 +195,7 @@ func (f *FileService) Create(op request.FileCreate) error {
|
||||
}
|
||||
}
|
||||
if op.IsDir {
|
||||
return fo.CreateDir(op.Path, fs.FileMode(mode))
|
||||
return fo.CreateDirWithMode(op.Path, fs.FileMode(mode))
|
||||
}
|
||||
if op.IsLink {
|
||||
if !fo.Stat(op.LinkPath) {
|
||||
@@ -211,24 +273,45 @@ func (f *FileService) ChangeOwner(req request.FileRoleUpdate) error {
|
||||
func (f *FileService) Compress(c request.FileCompress) error {
|
||||
fo := files.NewFileOp()
|
||||
if !c.Replace && fo.Stat(filepath.Join(c.Dst, c.Name)) {
|
||||
return buserr.New(constant.ErrFileIsExit)
|
||||
return buserr.New(constant.ErrFileIsExist)
|
||||
}
|
||||
return fo.Compress(c.Files, c.Dst, c.Name, files.CompressType(c.Type))
|
||||
return fo.Compress(c.Files, c.Dst, c.Name, files.CompressType(c.Type), c.Secret)
|
||||
}
|
||||
|
||||
func (f *FileService) DeCompress(c request.FileDeCompress) error {
|
||||
fo := files.NewFileOp()
|
||||
return fo.Decompress(c.Path, c.Dst, files.CompressType(c.Type))
|
||||
if c.Type == "tar" && len(c.Secret) != 0 {
|
||||
c.Type = "tar.gz"
|
||||
}
|
||||
return fo.Decompress(c.Path, c.Dst, files.CompressType(c.Type), c.Secret)
|
||||
}
|
||||
|
||||
func (f *FileService) GetContent(op request.FileContentReq) (response.FileInfo, error) {
|
||||
info, err := files.NewFileInfo(files.FileOption{
|
||||
Path: op.Path,
|
||||
Expand: true,
|
||||
Path: op.Path,
|
||||
Expand: true,
|
||||
IsDetail: op.IsDetail,
|
||||
})
|
||||
if err != nil {
|
||||
return response.FileInfo{}, err
|
||||
}
|
||||
|
||||
content := []byte(info.Content)
|
||||
if len(content) > 1024 {
|
||||
content = content[:1024]
|
||||
}
|
||||
if !utf8.Valid(content) {
|
||||
_, decodeName, _ := charset.DetermineEncoding(content, "")
|
||||
if decodeName == "windows-1252" {
|
||||
reader := strings.NewReader(info.Content)
|
||||
item := transform.NewReader(reader, simplifiedchinese.GBK.NewDecoder())
|
||||
contents, err := io.ReadAll(item)
|
||||
if err != nil {
|
||||
return response.FileInfo{}, err
|
||||
}
|
||||
info.Content = string(contents)
|
||||
}
|
||||
}
|
||||
return response.FileInfo{FileInfo: *info}, nil
|
||||
}
|
||||
|
||||
@@ -303,7 +386,7 @@ func (f *FileService) FileDownload(d request.FileDownload) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
fo := files.NewFileOp()
|
||||
if err := fo.Compress(d.Paths, tempPath, d.Name, files.CompressType(d.Type)); err != nil {
|
||||
if err := fo.Compress(d.Paths, tempPath, d.Name, files.CompressType(d.Type), ""); err != nil {
|
||||
return "", err
|
||||
}
|
||||
filePath = filepath.Join(tempPath, d.Name)
|
||||
@@ -312,12 +395,32 @@ func (f *FileService) FileDownload(d request.FileDownload) (string, error) {
|
||||
}
|
||||
|
||||
func (f *FileService) DirSize(req request.DirSizeReq) (response.DirSizeRes, error) {
|
||||
var (
|
||||
res response.DirSizeRes
|
||||
)
|
||||
if req.Path == "/proc" {
|
||||
return res, nil
|
||||
}
|
||||
cmd := exec.Command("du", "-s", req.Path)
|
||||
output, err := cmd.Output()
|
||||
if err == nil {
|
||||
fields := strings.Fields(string(output))
|
||||
if len(fields) == 2 {
|
||||
var cmdSize int64
|
||||
_, err = fmt.Sscanf(fields[0], "%d", &cmdSize)
|
||||
if err == nil {
|
||||
res.Size = float64(cmdSize * 1024)
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
fo := files.NewFileOp()
|
||||
size, err := fo.GetDirSize(req.Path)
|
||||
if err != nil {
|
||||
return response.DirSizeRes{}, err
|
||||
return res, err
|
||||
}
|
||||
return response.DirSizeRes{Size: size}, nil
|
||||
res.Size = size
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error) {
|
||||
@@ -367,7 +470,7 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
|
||||
logFilePath = path.Join(global.CONF.System.TmpDir, fmt.Sprintf("docker_logs/%s", req.Name))
|
||||
}
|
||||
|
||||
lines, isEndOfFile, err := files.ReadFileByLine(logFilePath, req.Page, req.PageSize)
|
||||
lines, isEndOfFile, total, err := files.ReadFileByLine(logFilePath, req.Page, req.PageSize, req.Latest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -375,6 +478,7 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
|
||||
Content: strings.Join(lines, "\n"),
|
||||
End: isEndOfFile,
|
||||
Path: logFilePath,
|
||||
Total: total,
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package service
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -28,6 +29,7 @@ type IFirewallService interface {
|
||||
SearchWithPage(search dto.RuleSearch) (int64, interface{}, error)
|
||||
OperateFirewall(operation string) error
|
||||
OperatePortRule(req dto.PortRuleOperate, reload bool) error
|
||||
OperateForwardRule(req dto.ForwardRuleOperate) error
|
||||
OperateAddressRule(req dto.AddrRuleOperate, reload bool) error
|
||||
UpdatePortRule(req dto.PortRuleUpdate) error
|
||||
UpdateAddrRule(req dto.AddrRuleUpdate) error
|
||||
@@ -78,42 +80,39 @@ func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if req.Type == "port" {
|
||||
ports, err := client.ListPort()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if len(req.Info) != 0 {
|
||||
for _, port := range ports {
|
||||
if strings.Contains(port.Port, req.Info) {
|
||||
datas = append(datas, port)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
datas = ports
|
||||
}
|
||||
} else {
|
||||
addrs, err := client.ListAddress()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if len(req.Info) != 0 {
|
||||
for _, addr := range addrs {
|
||||
if strings.Contains(addr.Address, req.Info) {
|
||||
datas = append(datas, addr)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
datas = addrs
|
||||
}
|
||||
|
||||
var rules []fireClient.FireInfo
|
||||
switch req.Type {
|
||||
case "port":
|
||||
rules, err = client.ListPort()
|
||||
case "forward":
|
||||
rules, err = client.ListForward()
|
||||
case "address":
|
||||
rules, err = client.ListAddress()
|
||||
}
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
if len(req.Info) != 0 {
|
||||
for _, addr := range rules {
|
||||
if strings.Contains(addr.Address, req.Info) ||
|
||||
strings.Contains(addr.Port, req.Info) ||
|
||||
strings.Contains(addr.TargetPort, req.Info) ||
|
||||
strings.Contains(addr.TargetIP, req.Info) {
|
||||
datas = append(datas, addr)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
datas = rules
|
||||
}
|
||||
if req.Type == "port" {
|
||||
apps := u.loadPortByApp()
|
||||
for i := 0; i < len(datas); i++ {
|
||||
datas[i].UsedStatus = checkPortUsed(datas[i].Port, datas[i].Protocol, apps)
|
||||
}
|
||||
}
|
||||
|
||||
var datasFilterStatus []fireClient.FireInfo
|
||||
if len(req.Status) != 0 {
|
||||
for _, data := range datas {
|
||||
@@ -127,6 +126,7 @@ func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}
|
||||
} else {
|
||||
datasFilterStatus = datas
|
||||
}
|
||||
|
||||
var datasFilterStrategy []fireClient.FireInfo
|
||||
if len(req.Strategy) != 0 {
|
||||
for _, data := range datasFilterStatus {
|
||||
@@ -300,6 +300,52 @@ func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate, reload bool)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {
|
||||
client, err := firewall.NewFirewallClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rules, _ := client.ListForward()
|
||||
for _, rule := range rules {
|
||||
for _, reqRule := range req.Rules {
|
||||
if reqRule.Operation == "remove" {
|
||||
continue
|
||||
}
|
||||
if reqRule.TargetIP == "" {
|
||||
reqRule.TargetIP = "127.0.0.1"
|
||||
}
|
||||
if reqRule.Port == rule.Port && reqRule.TargetPort == rule.TargetPort && reqRule.TargetIP == rule.TargetIP {
|
||||
return constant.ErrRecordExist
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.SliceStable(req.Rules, func(i, j int) bool {
|
||||
n1, _ := strconv.Atoi(req.Rules[i].Num)
|
||||
n2, _ := strconv.Atoi(req.Rules[j].Num)
|
||||
return n1 > n2
|
||||
})
|
||||
|
||||
for _, r := range req.Rules {
|
||||
for _, p := range strings.Split(r.Protocol, "/") {
|
||||
if r.TargetIP == "" {
|
||||
r.TargetIP = "127.0.0.1"
|
||||
}
|
||||
if err = client.PortForward(fireClient.Forward{
|
||||
Num: r.Num,
|
||||
Protocol: p,
|
||||
Port: r.Port,
|
||||
TargetIP: r.TargetIP,
|
||||
TargetPort: r.TargetPort,
|
||||
}, r.Operation); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *FirewallService) OperateAddressRule(req dto.AddrRuleOperate, reload bool) error {
|
||||
client, err := firewall.NewFirewallClient()
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/toolbox"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type FtpService struct{}
|
||||
|
||||
type IFtpService interface {
|
||||
LoadBaseInfo() (dto.FtpBaseInfo, error)
|
||||
SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error)
|
||||
Operate(operation string) error
|
||||
Create(req dto.FtpCreate) (uint, error)
|
||||
Delete(req dto.BatchDeleteReq) error
|
||||
Update(req dto.FtpUpdate) error
|
||||
Sync() error
|
||||
LoadLog(req dto.FtpLogSearch) (int64, interface{}, error)
|
||||
}
|
||||
|
||||
func NewIFtpService() IFtpService {
|
||||
return &FtpService{}
|
||||
}
|
||||
|
||||
func (f *FtpService) LoadBaseInfo() (dto.FtpBaseInfo, error) {
|
||||
var baseInfo dto.FtpBaseInfo
|
||||
client, err := toolbox.NewFtpClient()
|
||||
if err != nil {
|
||||
return baseInfo, err
|
||||
}
|
||||
baseInfo.IsActive, baseInfo.IsExist = client.Status()
|
||||
return baseInfo, nil
|
||||
}
|
||||
|
||||
func (f *FtpService) LoadLog(req dto.FtpLogSearch) (int64, interface{}, error) {
|
||||
client, err := toolbox.NewFtpClient()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
logItem, err := client.LoadLogs(req.User, req.Operation)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
sort.Slice(logItem, func(i, j int) bool {
|
||||
return logItem[i].Time > logItem[j].Time
|
||||
})
|
||||
var logs []toolbox.FtpLog
|
||||
total, start, end := len(logItem), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||
if start > total {
|
||||
logs = make([]toolbox.FtpLog, 0)
|
||||
} else {
|
||||
if end >= total {
|
||||
end = total
|
||||
}
|
||||
logs = logItem[start:end]
|
||||
}
|
||||
return int64(total), logs, nil
|
||||
}
|
||||
|
||||
func (u *FtpService) Operate(operation string) error {
|
||||
client, err := toolbox.NewFtpClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return client.Operate(operation)
|
||||
}
|
||||
|
||||
func (f *FtpService) SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error) {
|
||||
total, lists, err := ftpRepo.Page(req.Page, req.PageSize, ftpRepo.WithByUser(req.Info), commonRepo.WithOrderBy("created_at desc"))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
var users []dto.FtpInfo
|
||||
for _, user := range lists {
|
||||
var item dto.FtpInfo
|
||||
if err := copier.Copy(&item, &user); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
item.Password, _ = encrypt.StringDecrypt(item.Password)
|
||||
users = append(users, item)
|
||||
}
|
||||
return total, users, err
|
||||
}
|
||||
|
||||
func (f *FtpService) Sync() error {
|
||||
client, err := toolbox.NewFtpClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lists, err := client.LoadList()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
listsInDB, err := ftpRepo.GetList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sameData := make(map[string]struct{})
|
||||
for _, item := range lists {
|
||||
for _, itemInDB := range listsInDB {
|
||||
if item.User == itemInDB.User {
|
||||
sameData[item.User] = struct{}{}
|
||||
if item.Path != itemInDB.Path || item.Status != itemInDB.Status {
|
||||
_ = ftpRepo.Update(itemInDB.ID, map[string]interface{}{"path": item.Path, "status": item.Status})
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, item := range lists {
|
||||
if _, ok := sameData[item.User]; !ok {
|
||||
_ = ftpRepo.Create(&model.Ftp{User: item.User, Path: item.Path, Status: item.Status})
|
||||
}
|
||||
}
|
||||
for _, item := range listsInDB {
|
||||
if _, ok := sameData[item.User]; !ok {
|
||||
_ = ftpRepo.Update(item.ID, map[string]interface{}{"status": "deleted"})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FtpService) Create(req dto.FtpCreate) (uint, error) {
|
||||
if _, err := os.Stat(req.Path); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(req.Path, os.ModePerm); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
pass, err := encrypt.StringEncrypt(req.Password)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
userInDB, _ := ftpRepo.Get(hostRepo.WithByUser(req.User))
|
||||
if userInDB.ID != 0 {
|
||||
return 0, constant.ErrRecordExist
|
||||
}
|
||||
client, err := toolbox.NewFtpClient()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := client.UserAdd(req.User, req.Password, req.Path); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var ftp model.Ftp
|
||||
if err := copier.Copy(&ftp, &req); err != nil {
|
||||
return 0, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
ftp.Status = constant.StatusEnable
|
||||
ftp.Password = pass
|
||||
if err := ftpRepo.Create(&ftp); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return ftp.ID, nil
|
||||
}
|
||||
|
||||
func (f *FtpService) Delete(req dto.BatchDeleteReq) error {
|
||||
client, err := toolbox.NewFtpClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, id := range req.Ids {
|
||||
ftpItem, err := ftpRepo.Get(commonRepo.WithByID(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = client.UserDel(ftpItem.User)
|
||||
_ = ftpRepo.Delete(commonRepo.WithByID(id))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FtpService) Update(req dto.FtpUpdate) error {
|
||||
if _, err := os.Stat(req.Path); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(req.Path, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
pass, err := encrypt.StringEncrypt(req.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ftpItem, _ := ftpRepo.Get(commonRepo.WithByID(req.ID))
|
||||
if ftpItem.ID == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
passItem, err := encrypt.StringDecrypt(ftpItem.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := toolbox.NewFtpClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
needReload := false
|
||||
updates := make(map[string]interface{})
|
||||
if req.Password != passItem {
|
||||
if err := client.SetPasswd(ftpItem.User, req.Password); err != nil {
|
||||
return err
|
||||
}
|
||||
updates["password"] = pass
|
||||
needReload = true
|
||||
}
|
||||
if req.Status != ftpItem.Status {
|
||||
if err := client.SetStatus(ftpItem.User, req.Status); err != nil {
|
||||
return err
|
||||
}
|
||||
updates["status"] = req.Status
|
||||
needReload = true
|
||||
}
|
||||
if req.Path != ftpItem.Path {
|
||||
if err := client.SetPath(ftpItem.User, req.Path); err != nil {
|
||||
return err
|
||||
}
|
||||
updates["path"] = req.Path
|
||||
needReload = true
|
||||
}
|
||||
if req.Description != ftpItem.Description {
|
||||
updates["description"] = req.Description
|
||||
}
|
||||
if needReload {
|
||||
_ = client.Reload()
|
||||
}
|
||||
if len(updates) != 0 {
|
||||
return ftpRepo.Update(ftpItem.ID, updates)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -307,7 +307,7 @@ func (h *HostToolService) OperateSupervisorProcess(req request.SupervisorProcess
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = section.NewKey("command", req.Command)
|
||||
_, _ = section.NewKey("command", strings.TrimSpace(req.Command))
|
||||
_, _ = section.NewKey("directory", req.Dir)
|
||||
_, _ = section.NewKey("autorestart", "true")
|
||||
_, _ = section.NewKey("startsecs", "3")
|
||||
@@ -338,7 +338,7 @@ func (h *HostToolService) OperateSupervisorProcess(req request.SupervisorProcess
|
||||
}
|
||||
|
||||
commandKey := section.Key("command")
|
||||
commandKey.SetValue(req.Command)
|
||||
commandKey.SetValue(strings.TrimSpace(req.Command))
|
||||
directoryKey := section.Key("directory")
|
||||
directoryKey.SetValue(req.Dir)
|
||||
userKey := section.Key("user")
|
||||
|
||||
@@ -7,14 +7,15 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
@@ -23,6 +24,7 @@ import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/homedir"
|
||||
)
|
||||
|
||||
type ImageService struct{}
|
||||
@@ -53,6 +55,7 @@ func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer client.Close()
|
||||
list, err = client.ImageList(context.Background(), image.ListOptions{})
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
@@ -106,6 +109,7 @@ func (u *ImageService) ListAll() ([]dto.ImageInfo, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer client.Close()
|
||||
list, err := client.ImageList(context.Background(), image.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -133,6 +137,7 @@ func (u *ImageService) List() ([]dto.Options, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer client.Close()
|
||||
list, err = client.ImageList(context.Background(), image.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -152,6 +157,7 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer client.Close()
|
||||
fileName := "Dockerfile"
|
||||
if req.From == "edit" {
|
||||
dir := fmt.Sprintf("%s/docker/build/%s", constant.DataDir, strings.ReplaceAll(req.Name, ":", "_"))
|
||||
@@ -193,7 +199,7 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
logItem := fmt.Sprintf("%s/image_build_%s_%s.log", dockerLogDir, strings.ReplaceAll(req.Name, ":", "_"), time.Now().Format("20060102150405"))
|
||||
logItem := fmt.Sprintf("%s/image_build_%s_%s.log", dockerLogDir, strings.ReplaceAll(req.Name, ":", "_"), time.Now().Format(constant.DateTimeSlimLayout))
|
||||
file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -235,6 +241,7 @@ func (u *ImageService) ImagePull(req dto.ImagePull) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer client.Close()
|
||||
dockerLogDir := path.Join(global.CONF.System.TmpDir, "docker_logs")
|
||||
if _, err := os.Stat(dockerLogDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dockerLogDir, os.ModePerm); err != nil {
|
||||
@@ -242,15 +249,20 @@ func (u *ImageService) ImagePull(req dto.ImagePull) (string, error) {
|
||||
}
|
||||
}
|
||||
imageItemName := strings.ReplaceAll(path.Base(req.ImageName), ":", "_")
|
||||
logItem := fmt.Sprintf("%s/image_pull_%s_%s.log", dockerLogDir, imageItemName, time.Now().Format("20060102150405"))
|
||||
logItem := fmt.Sprintf("%s/image_pull_%s_%s.log", dockerLogDir, imageItemName, time.Now().Format(constant.DateTimeSlimLayout))
|
||||
file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
options := image.PullOptions{}
|
||||
if req.RepoID == 0 {
|
||||
hasAuth, authStr := loadAuthInfo(req.ImageName)
|
||||
if hasAuth {
|
||||
options.RegistryAuth = authStr
|
||||
}
|
||||
go func() {
|
||||
defer file.Close()
|
||||
out, err := client.ImagePull(context.TODO(), req.ImageName, types.ImagePullOptions{})
|
||||
out, err := client.ImagePull(context.TODO(), req.ImageName, options)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("image %s pull failed, err: %v", req.ImageName, err)
|
||||
return
|
||||
@@ -265,7 +277,6 @@ func (u *ImageService) ImagePull(req dto.ImagePull) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
options := types.ImagePullOptions{}
|
||||
if repo.Auth {
|
||||
authConfig := registry.AuthConfig{
|
||||
Username: repo.Username,
|
||||
@@ -305,10 +316,12 @@ func (u *ImageService) ImageLoad(req dto.ImageLoad) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
res, err := client.ImageLoad(context.TODO(), file, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
content, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -324,6 +337,7 @@ func (u *ImageService) ImageSave(req dto.ImageSave) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
out, err := client.ImageSave(context.TODO(), []string{req.TagName})
|
||||
if err != nil {
|
||||
@@ -346,6 +360,7 @@ func (u *ImageService) ImageTag(req dto.ImageTag) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
if err := client.ImageTag(context.TODO(), req.SourceID, req.TargetName); err != nil {
|
||||
return err
|
||||
@@ -358,23 +373,22 @@ func (u *ImageService) ImagePush(req dto.ImagePush) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer client.Close()
|
||||
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.RepoID))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
options := types.ImagePushOptions{}
|
||||
if repo.Auth {
|
||||
authConfig := registry.AuthConfig{
|
||||
Username: repo.Username,
|
||||
Password: repo.Password,
|
||||
}
|
||||
encodedJSON, err := json.Marshal(authConfig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
|
||||
options.RegistryAuth = authStr
|
||||
options := image.PushOptions{All: true}
|
||||
authConfig := registry.AuthConfig{
|
||||
Username: repo.Username,
|
||||
Password: repo.Password,
|
||||
}
|
||||
encodedJSON, err := json.Marshal(authConfig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
|
||||
options.RegistryAuth = authStr
|
||||
newName := fmt.Sprintf("%s/%s", repo.DownloadUrl, req.Name)
|
||||
if newName != req.TagName {
|
||||
if err := client.ImageTag(context.TODO(), req.TagName, newName); err != nil {
|
||||
@@ -389,7 +403,7 @@ func (u *ImageService) ImagePush(req dto.ImagePush) (string, error) {
|
||||
}
|
||||
}
|
||||
imageItemName := strings.ReplaceAll(path.Base(req.Name), ":", "_")
|
||||
logItem := fmt.Sprintf("%s/image_push_%s_%s.log", dockerLogDir, imageItemName, time.Now().Format("20060102150405"))
|
||||
logItem := fmt.Sprintf("%s/image_push_%s_%s.log", dockerLogDir, imageItemName, time.Now().Format(constant.DateTimeSlimLayout))
|
||||
file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -416,6 +430,7 @@ func (u *ImageService) ImageRemove(req dto.BatchDelete) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
for _, id := range req.Names {
|
||||
if _, err := client.ImageRemove(context.TODO(), id, types.ImageRemoveOptions{Force: req.Force, PruneChildren: true}); err != nil {
|
||||
if strings.Contains(err.Error(), "image is being used") || strings.Contains(err.Error(), "is using") {
|
||||
@@ -454,3 +469,49 @@ func checkUsed(imageID string, containers []types.Container) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func loadAuthInfo(image string) (bool, string) {
|
||||
if !strings.Contains(image, "/") {
|
||||
return false, ""
|
||||
}
|
||||
homeDir := homedir.Get()
|
||||
confPath := path.Join(homeDir, ".docker/config.json")
|
||||
configFileBytes, err := os.ReadFile(confPath)
|
||||
if err != nil {
|
||||
return false, ""
|
||||
}
|
||||
var config dockerConfig
|
||||
if err = json.Unmarshal(configFileBytes, &config); err != nil {
|
||||
return false, ""
|
||||
}
|
||||
var (
|
||||
user string
|
||||
passwd string
|
||||
)
|
||||
imagePrefix := strings.Split(image, "/")[0]
|
||||
if val, ok := config.Auths[imagePrefix]; ok {
|
||||
itemByte, _ := base64.StdEncoding.DecodeString(val.Auth)
|
||||
itemStr := string(itemByte)
|
||||
if strings.Contains(itemStr, ":") {
|
||||
user = strings.Split(itemStr, ":")[0]
|
||||
passwd = strings.Split(itemStr, ":")[1]
|
||||
}
|
||||
}
|
||||
authConfig := registry.AuthConfig{
|
||||
Username: user,
|
||||
Password: passwd,
|
||||
}
|
||||
encodedJSON, err := json.Marshal(authConfig)
|
||||
if err != nil {
|
||||
return false, ""
|
||||
}
|
||||
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
|
||||
return true, authStr
|
||||
}
|
||||
|
||||
type dockerConfig struct {
|
||||
Auths map[string]authConfig `json:"auths"`
|
||||
}
|
||||
type authConfig struct {
|
||||
Auth string `json:"auth"`
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -92,6 +91,7 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
|
||||
return errors.New(string(stdout))
|
||||
}
|
||||
ticker := time.NewTicker(3 * time.Second)
|
||||
defer ticker.Stop()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
if err := func() error {
|
||||
for range ticker.C {
|
||||
@@ -195,13 +195,10 @@ func (u *ImageRepoService) CheckConn(host, user, password string) error {
|
||||
}
|
||||
|
||||
func (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) error {
|
||||
if _, err := os.Stat(constant.DaemonJsonPath); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(path.Dir(constant.DaemonJsonPath), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = os.Create(constant.DaemonJsonPath)
|
||||
err := createIfNotExistDaemonJsonFile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
daemonMap := make(map[string]interface{})
|
||||
file, err := os.ReadFile(constant.DaemonJsonPath)
|
||||
if err != nil {
|
||||
|
||||
@@ -2,7 +2,6 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -10,6 +9,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||
|
||||
@@ -76,6 +77,7 @@ func (n NginxService) GetStatus() (response.NginxStatus, error) {
|
||||
if err != nil {
|
||||
return response.NginxStatus{}, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
content, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return response.NginxStatus{}, err
|
||||
|
||||
@@ -4,6 +4,14 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||
@@ -19,13 +27,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/subosito/gotenv"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RuntimeService struct {
|
||||
@@ -43,6 +44,7 @@ type IRuntimeService interface {
|
||||
OperateNodeModules(req request.NodeModuleOperateReq) error
|
||||
SyncForRestart() error
|
||||
SyncRuntimeStatus() error
|
||||
DeleteCheck(installID uint) ([]dto.AppResource, error)
|
||||
}
|
||||
|
||||
func NewRuntimeService() IRuntimeService {
|
||||
@@ -81,7 +83,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e
|
||||
if exist != nil {
|
||||
return nil, buserr.New(constant.ErrImageExist)
|
||||
}
|
||||
case constant.RuntimeNode:
|
||||
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
|
||||
if !fileOp.Stat(create.CodeDir) {
|
||||
return nil, buserr.New(constant.ErrPathNotFound)
|
||||
}
|
||||
@@ -131,9 +133,9 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e
|
||||
if err = handlePHP(create, runtime, fileOp, appVersionDir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case constant.RuntimeNode:
|
||||
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
|
||||
runtime.Port = create.Port
|
||||
if err = handleNode(create, runtime, fileOp, appVersionDir); err != nil {
|
||||
if err = handleNodeAndJava(create, runtime, fileOp, appVersionDir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -176,6 +178,18 @@ func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.Runt
|
||||
return total, res, nil
|
||||
}
|
||||
|
||||
func (r *RuntimeService) DeleteCheck(runTimeId uint) ([]dto.AppResource, error) {
|
||||
var res []dto.AppResource
|
||||
websites, _ := websiteRepo.GetBy(websiteRepo.WithRuntimeID(runTimeId))
|
||||
for _, website := range websites {
|
||||
res = append(res, dto.AppResource{
|
||||
Type: "website",
|
||||
Name: website.PrimaryDomain,
|
||||
})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (r *RuntimeService) Delete(runtimeDelete request.RuntimeDelete) error {
|
||||
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(runtimeDelete.ID))
|
||||
if err != nil {
|
||||
@@ -193,6 +207,7 @@ func (r *RuntimeService) Delete(runtimeDelete request.RuntimeDelete) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
imageID, err := client.GetImageIDByName(runtime.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -202,7 +217,7 @@ func (r *RuntimeService) Delete(runtimeDelete request.RuntimeDelete) error {
|
||||
global.LOG.Errorf("delete image id [%s] error %v", imageID, err)
|
||||
}
|
||||
}
|
||||
case constant.RuntimeNode:
|
||||
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
|
||||
if out, err := compose.Down(runtime.GetComposePath()); err != nil && !runtimeDelete.ForceDelete {
|
||||
if out != "" {
|
||||
return errors.New(out)
|
||||
@@ -285,7 +300,7 @@ func (r *RuntimeService) Get(id uint) (*response.RuntimeDTO, error) {
|
||||
}
|
||||
}
|
||||
res.AppParams = appParams
|
||||
case constant.RuntimeNode:
|
||||
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
|
||||
res.Params = make(map[string]interface{})
|
||||
envs, err := gotenv.Unmarshal(runtime.Env)
|
||||
if err != nil {
|
||||
@@ -293,7 +308,7 @@ func (r *RuntimeService) Get(id uint) (*response.RuntimeDTO, error) {
|
||||
}
|
||||
for k, v := range envs {
|
||||
switch k {
|
||||
case "NODE_APP_PORT", "PANEL_APP_PORT_HTTP":
|
||||
case "NODE_APP_PORT", "PANEL_APP_PORT_HTTP", "JAVA_APP_PORT", "GO_APP_PORT":
|
||||
port, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -346,7 +361,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
||||
if exist != nil {
|
||||
return buserr.New(constant.ErrImageExist)
|
||||
}
|
||||
case constant.RuntimeNode:
|
||||
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
|
||||
if runtime.Port != req.Port {
|
||||
if err = checkPortExist(req.Port); err != nil {
|
||||
return err
|
||||
@@ -420,12 +435,13 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
imageID, err := client.GetImageIDByName(oldImage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go buildRuntime(runtime, imageID, req.Rebuild)
|
||||
case constant.RuntimeNode:
|
||||
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
|
||||
runtime.Version = req.Version
|
||||
runtime.CodeDir = req.CodeDir
|
||||
runtime.Port = req.Port
|
||||
@@ -594,7 +610,7 @@ func (r *RuntimeService) SyncRuntimeStatus() error {
|
||||
return err
|
||||
}
|
||||
for _, runtime := range runtimes {
|
||||
if runtime.Type == constant.RuntimeNode {
|
||||
if runtime.Type == constant.RuntimeNode || runtime.Type == constant.RuntimeJava || runtime.Type == constant.RuntimeGo {
|
||||
_ = SyncRuntimeContainerStatus(&runtime)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,6 @@ package service
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/subosito/gotenv"
|
||||
"gopkg.in/yaml.v3"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -21,9 +11,21 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
httpUtil "github.com/1Panel-dev/1Panel/backend/utils/http"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/subosito/gotenv"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func handleNode(create request.RuntimeCreate, runtime *model.Runtime, fileOp files.FileOp, appVersionDir string) (err error) {
|
||||
func handleNodeAndJava(create request.RuntimeCreate, runtime *model.Runtime, fileOp files.FileOp, appVersionDir string) (err error) {
|
||||
runtimeDir := path.Join(constant.RuntimeDir, create.Type)
|
||||
if err = fileOp.CopyDir(appVersionDir, runtimeDir); err != nil {
|
||||
return
|
||||
@@ -53,7 +55,10 @@ func handleNode(create request.RuntimeCreate, runtime *model.Runtime, fileOp fil
|
||||
}
|
||||
|
||||
go func() {
|
||||
_, _ = http.Get(nodeDetail.DownloadCallBackUrl)
|
||||
if _, _, err := httpUtil.HandleGet(nodeDetail.DownloadCallBackUrl, http.MethodGet, constant.TimeOut5s); err != nil {
|
||||
global.LOG.Errorf("http request failed(handleNode), err: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
go startRuntime(runtime)
|
||||
|
||||
@@ -144,6 +149,7 @@ func runComposeCmdWithLog(operate string, composePath string, logPath string) er
|
||||
global.LOG.Errorf("Failed to open log file: %v", err)
|
||||
return err
|
||||
}
|
||||
defer logFile.Close()
|
||||
multiWriterStdout := io.MultiWriter(os.Stdout, logFile)
|
||||
cmd.Stdout = multiWriterStdout
|
||||
var stderrBuf bytes.Buffer
|
||||
@@ -172,6 +178,7 @@ func SyncRuntimeContainerStatus(runtime *model.Runtime) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
containers, err := cli.ListContainersByName(containerNames)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -228,6 +235,7 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) {
|
||||
if oldImageID != "" {
|
||||
client, err := docker.NewClient()
|
||||
if err == nil {
|
||||
defer client.Close()
|
||||
newImageID, err := client.GetImageIDByName(runtime.Image)
|
||||
if err == nil && newImageID != oldImageID {
|
||||
global.LOG.Infof("delete imageID [%s] ", oldImageID)
|
||||
@@ -310,7 +318,23 @@ func handleParams(create request.RuntimeCreate, projectDir string) (composeConte
|
||||
}
|
||||
create.Params["CONTAINER_PACKAGE_URL"] = create.Source
|
||||
|
||||
composeContent, err = handleNodeCompose(env, composeContent, create, projectDir)
|
||||
composeContent, err = handleCompose(env, composeContent, create, projectDir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case constant.RuntimeJava:
|
||||
create.Params["CODE_DIR"] = create.CodeDir
|
||||
create.Params["JAVA_VERSION"] = create.Version
|
||||
create.Params["PANEL_APP_PORT_HTTP"] = create.Port
|
||||
composeContent, err = handleCompose(env, composeContent, create, projectDir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case constant.RuntimeGo:
|
||||
create.Params["CODE_DIR"] = create.CodeDir
|
||||
create.Params["GO_VERSION"] = create.Version
|
||||
create.Params["PANEL_APP_PORT_HTTP"] = create.Port
|
||||
composeContent, err = handleCompose(env, composeContent, create, projectDir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -333,7 +357,7 @@ func handleParams(create request.RuntimeCreate, projectDir string) (composeConte
|
||||
return
|
||||
}
|
||||
|
||||
func handleNodeCompose(env gotenv.Env, composeContent []byte, create request.RuntimeCreate, projectDir string) (composeByte []byte, err error) {
|
||||
func handleCompose(env gotenv.Env, composeContent []byte, create request.RuntimeCreate, projectDir string) (composeByte []byte, err error) {
|
||||
existMap := make(map[string]interface{})
|
||||
composeMap := make(map[string]interface{})
|
||||
if err = yaml.Unmarshal(composeContent, &composeMap); err != nil {
|
||||
@@ -352,7 +376,17 @@ func handleNodeCompose(env gotenv.Env, composeContent []byte, create request.Run
|
||||
_, ok := serviceValue["ports"].([]interface{})
|
||||
if ok {
|
||||
var ports []interface{}
|
||||
ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${NODE_APP_PORT}")
|
||||
|
||||
switch create.Type {
|
||||
case constant.RuntimeNode:
|
||||
ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${NODE_APP_PORT}")
|
||||
case constant.RuntimeJava:
|
||||
ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${JAVA_APP_PORT}")
|
||||
case constant.RuntimeGo:
|
||||
ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${GO_APP_PORT}")
|
||||
|
||||
}
|
||||
|
||||
for i, port := range create.ExposedPorts {
|
||||
containerPortStr := fmt.Sprintf("CONTAINER_PORT_%d", i)
|
||||
hostPortStr := fmt.Sprintf("HOST_PORT_%d", i)
|
||||
@@ -390,6 +424,7 @@ func checkContainerName(name string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dockerCli.Close()
|
||||
names, err := dockerCli.ListContainersByName([]string{name})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -33,6 +33,7 @@ type ISettingService interface {
|
||||
GetSettingInfo() (*dto.SettingInfo, error)
|
||||
LoadInterfaceAddr() ([]string, error)
|
||||
Update(key, value string) error
|
||||
UpdateProxy(req dto.ProxyUpdate) error
|
||||
UpdatePassword(c *gin.Context, old, new string) error
|
||||
UpdatePort(port uint) error
|
||||
UpdateBindInfo(req dto.BindInfo) error
|
||||
@@ -62,6 +63,12 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
|
||||
if err := json.Unmarshal(arr, &info); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.ProxyPasswdKeep != constant.StatusEnable {
|
||||
info.ProxyPasswd = ""
|
||||
} else {
|
||||
info.ProxyPasswd, _ = encrypt.StringDecrypt(info.ProxyPasswd)
|
||||
}
|
||||
|
||||
info.LocalTime = time.Now().Format("2006-01-02 15:04:05 MST -0700")
|
||||
return &info, err
|
||||
}
|
||||
@@ -111,7 +118,7 @@ func (u *SettingService) Update(key, value string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006-01-02 15:04:05")); err != nil {
|
||||
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format(constant.DateTimeLayout)); err != nil {
|
||||
return err
|
||||
}
|
||||
case "BindDomain":
|
||||
@@ -162,6 +169,29 @@ func (u *SettingService) UpdateBindInfo(req dto.BindInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SettingService) UpdateProxy(req dto.ProxyUpdate) error {
|
||||
if err := settingRepo.Update("ProxyUrl", req.ProxyUrl); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("ProxyType", req.ProxyType); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("ProxyPort", req.ProxyPort); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("ProxyUser", req.ProxyUser); err != nil {
|
||||
return err
|
||||
}
|
||||
pass, _ := encrypt.StringEncrypt(req.ProxyPasswd)
|
||||
if err := settingRepo.Update("ProxyPasswd", pass); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("ProxyPasswdKeep", req.ProxyPasswdKeep); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SettingService) UpdatePort(port uint) error {
|
||||
if common.ScanPort(int(port)) {
|
||||
return buserr.WithDetail(constant.ErrPortInUsed, port, nil)
|
||||
@@ -317,12 +347,18 @@ func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := loadInfoFromCert()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data dto.SSLInfo
|
||||
switch sslType.Value {
|
||||
case "self":
|
||||
data, err = loadInfoFromCert()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "import":
|
||||
data, err = loadInfoFromCert()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := os.Stat(path.Join(global.CONF.System.BaseDir, "1panel/secret/server.crt")); err != nil {
|
||||
return nil, fmt.Errorf("load server.crt file failed, err: %v", err)
|
||||
}
|
||||
@@ -340,9 +376,15 @@ func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) {
|
||||
return nil, err
|
||||
}
|
||||
id, _ := strconv.Atoi(sslID.Value)
|
||||
ssl, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(uint(id)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data.Domain = ssl.Domains
|
||||
data.SSLID = uint(id)
|
||||
data.Timeout = ssl.ExpireDate.Format(constant.DateTimeLayout)
|
||||
}
|
||||
return data, nil
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func (u *SettingService) HandlePasswordExpired(c *gin.Context, old, new string) error {
|
||||
@@ -368,7 +410,7 @@ func (u *SettingService) HandlePasswordExpired(c *gin.Context, old, new string)
|
||||
return err
|
||||
}
|
||||
timeout, _ := strconv.Atoi(expiredSetting.Value)
|
||||
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006-01-02 15:04:05")); err != nil {
|
||||
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format(constant.DateTimeLayout)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -384,23 +426,23 @@ func (u *SettingService) UpdatePassword(c *gin.Context, old, new string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadInfoFromCert() (*dto.SSLInfo, error) {
|
||||
func loadInfoFromCert() (dto.SSLInfo, error) {
|
||||
var info dto.SSLInfo
|
||||
certFile := path.Join(global.CONF.System.BaseDir, "1panel/secret/server.crt")
|
||||
if _, err := os.Stat(certFile); err != nil {
|
||||
return &info, err
|
||||
return info, err
|
||||
}
|
||||
certData, err := os.ReadFile(certFile)
|
||||
if err != nil {
|
||||
return &info, err
|
||||
return info, err
|
||||
}
|
||||
certBlock, _ := pem.Decode(certData)
|
||||
if certBlock == nil {
|
||||
return &info, err
|
||||
return info, err
|
||||
}
|
||||
certObj, err := x509.ParseCertificate(certBlock.Bytes)
|
||||
if err != nil {
|
||||
return &info, err
|
||||
return info, err
|
||||
}
|
||||
var domains []string
|
||||
if len(certObj.IPAddresses) != 0 {
|
||||
@@ -411,9 +453,9 @@ func loadInfoFromCert() (*dto.SSLInfo, error) {
|
||||
if len(certObj.DNSNames) != 0 {
|
||||
domains = append(domains, certObj.DNSNames...)
|
||||
}
|
||||
return &dto.SSLInfo{
|
||||
return dto.SSLInfo{
|
||||
Domain: strings.Join(domains, ","),
|
||||
Timeout: certObj.NotAfter.Format("2006-01-02 15:04:05"),
|
||||
Timeout: certObj.NotAfter.Format(constant.DateTimeLayout),
|
||||
RootPath: path.Join(global.CONF.System.BaseDir, "1panel/secret/server.crt"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user