mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 08:00:27 +00:00
Compare commits
399
Commits
v1.9.2
...
v1.10.6-lts
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c826525e3 | ||
|
|
3f0fd48cf3 | ||
|
|
211dbfdead | ||
|
|
aafec142cc | ||
|
|
384ff57df7 | ||
|
|
01ddd203ea | ||
|
|
29aed616ac | ||
|
|
83f0df9d84 | ||
|
|
7aa16c8142 | ||
|
|
3bd505954f | ||
|
|
275c0522a3 | ||
|
|
85dec6815b | ||
|
|
5e4e9f762e | ||
|
|
7ae3722997 | ||
|
|
884a169a6f | ||
|
|
e79a5f9ae2 | ||
|
|
25608e7378 | ||
|
|
dad8bac715 | ||
|
|
a4ee16cf1f | ||
|
|
7f7465b383 | ||
|
|
b7c3dc0d2a | ||
|
|
bb02991b05 | ||
|
|
a5fdff61ff | ||
|
|
4832081b82 | ||
|
|
ef5a13f87f | ||
|
|
e2bc8e800a | ||
|
|
85a397949e | ||
|
|
5cfdcedf9a | ||
|
|
c5bb8dab8c | ||
|
|
f1421ba7f9 | ||
|
|
9e44db53a7 | ||
|
|
4814a76c47 | ||
|
|
2ec4b2561c | ||
|
|
4efe7240ad | ||
|
|
ed13136221 | ||
|
|
735fc75e35 | ||
|
|
b8a52ae707 | ||
|
|
2501a9f78a | ||
|
|
5a8d6db43e | ||
|
|
a3312331d2 | ||
|
|
fbd0e836d7 | ||
|
|
4935d88301 | ||
|
|
8625979cc3 | ||
|
|
c77742e72e | ||
|
|
43a6b4b735 | ||
|
|
f4ee534101 | ||
|
|
c055a07e57 | ||
|
|
62bead5c50 | ||
|
|
9edafc13a3 | ||
|
|
e6ccf8be1f | ||
|
|
222089d56a | ||
|
|
96e48847d0 | ||
|
|
38b0c42555 | ||
|
|
2eae8274f6 | ||
|
|
0de79e537e | ||
|
|
e37b87d63e | ||
|
|
37a0329d4a | ||
|
|
e037b69f52 | ||
|
|
34498392dc | ||
|
|
1687b99526 | ||
|
|
0bf6db1cf8 | ||
|
|
0cd06803e2 | ||
|
|
45f6a415de | ||
|
|
3e3f0d9452 | ||
|
|
35785f1e40 | ||
|
|
437d6eb0fd | ||
|
|
1e2c85e771 | ||
|
|
ef075df3fe | ||
|
|
1e22769c8b | ||
|
|
0751326b46 | ||
|
|
db4973cfde | ||
|
|
0b47d692b8 | ||
|
|
672ef66be8 | ||
|
|
7ab005540b | ||
|
|
7c928735cd | ||
|
|
9ec1610f34 | ||
|
|
4f2a340212 | ||
|
|
e69fd3b388 | ||
|
|
6991c773da | ||
|
|
9b88979b69 | ||
|
|
7c9608727d | ||
|
|
62828c464e | ||
|
|
f1ed051135 | ||
|
|
33a0c2ba30 | ||
|
|
494da3c209 | ||
|
|
646fc4e678 | ||
|
|
cdbe24a523 | ||
|
|
10012c6148 | ||
|
|
bb4fd69f3a | ||
|
|
6ff0f14841 | ||
|
|
1088034a5c | ||
|
|
8a011b1cf9 | ||
|
|
e3eebc938a | ||
|
|
08da7802c6 | ||
|
|
79ca1b4d6b | ||
|
|
47119e508b | ||
|
|
13bb5e0d30 | ||
|
|
218c165c26 | ||
|
|
d6006d34c0 | ||
|
|
810ea03ac1 | ||
|
|
e046315616 | ||
|
|
0a7f1eb286 | ||
|
|
b0411b2df6 | ||
|
|
471b7d9657 | ||
|
|
1527c63d37 | ||
|
|
0ebeeaebd6 | ||
|
|
1ae5bef40d | ||
|
|
9b9fe5444d | ||
|
|
a5d853c61a | ||
|
|
6610a3debe | ||
|
|
6b244b2b03 | ||
|
|
0b26d70927 | ||
|
|
18faed79f3 | ||
|
|
b0cc1248f0 | ||
|
|
8013c7fd96 | ||
|
|
306082d625 | ||
|
|
821b8dfe63 | ||
|
|
8d88ed812c | ||
|
|
aa93a6d373 | ||
|
|
b4e8c8503c | ||
|
|
4de733e084 | ||
|
|
6c67dc6ed4 | ||
|
|
ba6991bfb6 | ||
|
|
33309d2d93 | ||
|
|
1b2208a4d0 | ||
|
|
e3fb97fc8c | ||
|
|
1ff5bf85e9 | ||
|
|
062b2f2ae6 | ||
|
|
44445aca93 | ||
|
|
94de830637 | ||
|
|
9160726127 | ||
|
|
3cd0c89c4c | ||
|
|
c27fcf18fc | ||
|
|
b1f20d125e | ||
|
|
cd43bff44d | ||
|
|
8cf8375911 | ||
|
|
ebc3195df4 | ||
|
|
ce69f6a142 | ||
|
|
f6777d51b5 | ||
|
|
d916f2ba6f | ||
|
|
0e159f944a | ||
|
|
329bb94d3c | ||
|
|
7f2f7fa195 | ||
|
|
67c79bcde4 | ||
|
|
d7d7c7fd73 | ||
|
|
94028d97cf | ||
|
|
cbfedb5d5e | ||
|
|
7784ee9575 | ||
|
|
29d5d82fa5 | ||
|
|
46f01032b8 | ||
|
|
341f45b4ef | ||
|
|
a0907592b2 | ||
|
|
825ebd19fb | ||
|
|
8f24432a01 | ||
|
|
6ce35b723b | ||
|
|
a33eabb4f0 | ||
|
|
34a6587ebc | ||
|
|
17020ffe9f | ||
|
|
0f9b0d5d82 | ||
|
|
be36103475 | ||
|
|
a3b5869c2b | ||
|
|
0807da41a5 | ||
|
|
ea6870e3c9 | ||
|
|
70b7daf23a | ||
|
|
1d01fa1e14 | ||
|
|
b643fa2ca3 | ||
|
|
77f4f64f0d | ||
|
|
3b697c7520 | ||
|
|
8ca6b9b6fa | ||
|
|
0b66be7a3a | ||
|
|
f9f68a975c | ||
|
|
ba63907a65 | ||
|
|
2687c91e65 | ||
|
|
eb82c168c5 | ||
|
|
21b1242a94 | ||
|
|
a6784772b9 | ||
|
|
cf4fcaa07f | ||
|
|
62b1bb3191 | ||
|
|
8ce066a9d4 | ||
|
|
7d8da406cf | ||
|
|
b3d6b2b0de | ||
|
|
21f24f2e10 | ||
|
|
fe717d993b | ||
|
|
703da8a09a | ||
|
|
a99f19471b | ||
|
|
ca3be03ae7 | ||
|
|
71ca8016e1 | ||
|
|
5633bdd41e | ||
|
|
ab11d7257c | ||
|
|
39b8de7ada | ||
|
|
c49c4b7863 | ||
|
|
d42df7184c | ||
|
|
e7f172520a | ||
|
|
dca29174ea | ||
|
|
94181538a9 | ||
|
|
e9cc7bf1cf | ||
|
|
fa1e945b8d | ||
|
|
94702a79a4 | ||
|
|
24f573f9fd | ||
|
|
7e4606eda5 | ||
|
|
73e118a3e5 | ||
|
|
514d68d395 | ||
|
|
d0bb07796b | ||
|
|
66a451fc1f | ||
|
|
c13e2a36cc | ||
|
|
2affd2bc79 | ||
|
|
c510a028db | ||
|
|
121a1ff5da | ||
|
|
1ecc53dad3 | ||
|
|
279b3942b4 | ||
|
|
a0af04bbf7 | ||
|
|
d5d1125660 | ||
|
|
08801ebfcd | ||
|
|
262cbb9405 | ||
|
|
96233ee9b2 | ||
|
|
49dcb094dc | ||
|
|
f0d5aca657 | ||
|
|
58f58f0fac | ||
|
|
3bd6f46fe2 | ||
|
|
e427ccf70c | ||
|
|
1b47e8dd63 | ||
|
|
b668f55222 | ||
|
|
9a127d9afe | ||
|
|
63484601b5 | ||
|
|
ef5e42cf0b | ||
|
|
50a83e7e11 | ||
|
|
2475c82a63 | ||
|
|
61ff55e157 | ||
|
|
dbc7799e89 | ||
|
|
6275e06806 | ||
|
|
e4f9ec32cd | ||
|
|
319c21fc36 | ||
|
|
e346442d36 | ||
|
|
1781362342 | ||
|
|
3035bb2bf6 | ||
|
|
b896a5be3b | ||
|
|
d51abd529a | ||
|
|
550eed7962 | ||
|
|
51d09b7164 | ||
|
|
046d8f526b | ||
|
|
5bf121b687 | ||
|
|
e6a43d22c8 | ||
|
|
3fd7aa17a8 | ||
|
|
6a903b7fdf | ||
|
|
958f9a1aad | ||
|
|
e48830cd99 | ||
|
|
ece2cb433d | ||
|
|
fb06e29aa3 | ||
|
|
4fc26a3061 | ||
|
|
d8669b90bf | ||
|
|
9e980e8e0c | ||
|
|
140cd564da | ||
|
|
b40087c88c | ||
|
|
8e620172e6 | ||
|
|
9b8b9f505b | ||
|
|
1169648162 | ||
|
|
c169f211a0 | ||
|
|
eef445c1eb | ||
|
|
bedb8ce286 | ||
|
|
3652c46ac9 | ||
|
|
0449868d3b | ||
|
|
fdf9046430 | ||
|
|
b3852db39e | ||
|
|
2f7bb6d784 | ||
|
|
e5d3c1bb86 | ||
|
|
c758c1ce87 | ||
|
|
d82086ce4a | ||
|
|
edc3540e67 | ||
|
|
13a69fa1ed | ||
|
|
948653213e | ||
|
|
a4cfb587d6 | ||
|
|
6aa9a95971 | ||
|
|
e3666d17a3 | ||
|
|
7219566273 | ||
|
|
08a45e9723 | ||
|
|
be53945f0e | ||
|
|
725ad3f6d9 | ||
|
|
42828d0a48 | ||
|
|
3804942ae4 | ||
|
|
83dda25591 | ||
|
|
f91d7c6170 | ||
|
|
2cf3b27402 | ||
|
|
0e4f82c434 | ||
|
|
51d7a84062 | ||
|
|
70863f411d | ||
|
|
5c9315ff1c | ||
|
|
3846dcef86 | ||
|
|
8b0d73b5d5 | ||
|
|
e309b9c030 | ||
|
|
a5304b66e3 | ||
|
|
10eef260d4 | ||
|
|
045dbaef91 | ||
|
|
7a45db71d5 | ||
|
|
0ee942cbef | ||
|
|
aa8429a836 | ||
|
|
9f2227cf19 | ||
|
|
338ade095a | ||
|
|
d5432c8e59 | ||
|
|
4eb34f24d1 | ||
|
|
527c8143a1 | ||
|
|
a6891711b7 | ||
|
|
36a253ce03 | ||
|
|
bbe1161dc2 | ||
|
|
e85ed7427a | ||
|
|
97f8a872c0 | ||
|
|
48187b508d | ||
|
|
ca1710cabc | ||
|
|
df2a705713 | ||
|
|
d917008f47 | ||
|
|
61078a24fd | ||
|
|
e35998a4be | ||
|
|
d1fde34a14 | ||
|
|
45394598d9 | ||
|
|
edd6b52f05 | ||
|
|
0f53de56a7 | ||
|
|
4ce50328ba | ||
|
|
115b9c58b0 | ||
|
|
64def5bebd | ||
|
|
e46f931989 | ||
|
|
1a78aecfb1 | ||
|
|
e8c94450f4 | ||
|
|
6bc4cfc8ac | ||
|
|
24579edad6 | ||
|
|
6499d7f462 | ||
|
|
1e5cd23bbe | ||
|
|
fce0ac2c7d | ||
|
|
f5bf111203 | ||
|
|
5152e7b8d5 | ||
|
|
d30be34def | ||
|
|
1a9182bfde | ||
|
|
aeb0549a9a | ||
|
|
64f3bbb13b | ||
|
|
bc0fbc4461 | ||
|
|
1e5ea024ad | ||
|
|
f89365106a | ||
|
|
4891de0a8a | ||
|
|
be6b58b0a1 | ||
|
|
33910f7993 | ||
|
|
7036ba0d5c | ||
|
|
204a9fac87 | ||
|
|
6879b92db3 | ||
|
|
54242ae220 | ||
|
|
6f0f282b7e | ||
|
|
817fd123da | ||
|
|
ad4aac51d0 | ||
|
|
cb85860ff6 | ||
|
|
48713b51f1 | ||
|
|
ce3b841ca7 | ||
|
|
d95ec29de3 | ||
|
|
54d841787e | ||
|
|
714de55727 | ||
|
|
3c39415d84 | ||
|
|
2c04d24108 | ||
|
|
4591e44a62 | ||
|
|
3ffae2a9d4 | ||
|
|
009919f927 | ||
|
|
1c1bc872a8 | ||
|
|
3dca8e3a9c | ||
|
|
c23c3db150 | ||
|
|
4f482ad891 | ||
|
|
625189ce85 | ||
|
|
6d03042e26 | ||
|
|
45a58b3b4b | ||
|
|
c4b7dd70cb | ||
|
|
47524dc49b | ||
|
|
9a905750af | ||
|
|
51077b0597 | ||
|
|
3c2472ab6c | ||
|
|
39818b8424 | ||
|
|
2dc63e152b | ||
|
|
6130d4e026 | ||
|
|
9713b32ba8 | ||
|
|
040a0663c6 | ||
|
|
d626b2f4bd | ||
|
|
b8a89d86a0 | ||
|
|
a8e10d0d71 | ||
|
|
e9f876853b | ||
|
|
9561336934 | ||
|
|
4d19e3904b | ||
|
|
d9005bc937 | ||
|
|
b6948b6550 | ||
|
|
3acd449787 | ||
|
|
8d3fd42197 | ||
|
|
d05d7bbcc2 | ||
|
|
ebfaf77c8f | ||
|
|
9ae5592d2b | ||
|
|
46e7431c4c | ||
|
|
14e2ced3da | ||
|
|
9716e3754e | ||
|
|
aaafeb039e | ||
|
|
ee2fa70bb0 | ||
|
|
b547955965 | ||
|
|
a6c681a429 | ||
|
|
0b94524994 | ||
|
|
85336bc73e | ||
|
|
33484a9436 | ||
|
|
57f496fe12 | ||
|
|
326bcb5b3c | ||
|
|
74dd3ce6c3 |
+5
-1
@@ -1 +1,5 @@
|
||||
*.ts linguist-language=Go
|
||||
*.ts linguist-language=Go
|
||||
*.js linguist-language=Go
|
||||
*.css linguist-language=Go
|
||||
*.scss linguist-language=Go
|
||||
*.html linguist-language=Go
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 问题与讨论 / Questions & Discussions
|
||||
url: https://github.com/1Panel-dev/1Panel/discussions
|
||||
about: 提出针对本项目安装部署、使用及其他方面的相关问题 / Raise questions about the installation, deployment, use and other aspects of the project.
|
||||
- name: 对 1Panel 项目有其他问题
|
||||
url: https://1panel.cn
|
||||
about: 如果想要进一步了解 1Panel 项目,欢迎发送邮件到 support@fit2cloud.com 进行提问。
|
||||
- name: 反馈安全问题 (Report Security Bug)
|
||||
url: mailto://support@fit2cloud.com
|
||||
about: 请通过邮箱 support@fit2cloud.com 反馈安全问题 (Please report security vulnerabilities to support@fit2cloud.com)
|
||||
url: https://bbs.fit2cloud.com/c/1p/7
|
||||
about: 如果你对 1Panel 有其他想要提问的,我们欢迎到我们的官方社区进行提问。
|
||||
@@ -1,12 +0,0 @@
|
||||
name: 问题咨询
|
||||
description: 提出针对本项目安装部署、使用及其他方面的相关问题
|
||||
title: "[QUESTION]"
|
||||
labels: question
|
||||
assignees: wanghe-fit2cloud
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: "请描述您的问题"
|
||||
validations:
|
||||
required: true
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
name: 为 PR 添加标签
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUBTOKEN }}
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18.14'
|
||||
node-version: '20.2'
|
||||
- name: Build Web
|
||||
run: |
|
||||
cd frontend && npm install && npm run build:pro
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.20.x'
|
||||
go-version: '1.21.x'
|
||||
- name: Build Release
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
with:
|
||||
|
||||
@@ -9,7 +9,7 @@ jobs:
|
||||
uses: Yikun/hub-mirror-action@master
|
||||
with:
|
||||
src: 'github/1Panel-dev'
|
||||
dst: 'gitee/fit2cloud-xlab'
|
||||
dst: 'gitee/fit2cloud-feizhiyun'
|
||||
dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}
|
||||
dst_token: ${{ secrets.GITEE_TOKEN }}
|
||||
static_list: "1Panel"
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
name: Typos Check
|
||||
on: pull_request
|
||||
|
||||
jobs:
|
||||
run:
|
||||
name: Spell Check with Typos
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Actions Repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Check spelling
|
||||
uses: crate-ci/typos@master
|
||||
+8
-8
@@ -16,18 +16,12 @@ build/1panel
|
||||
*.factorypath
|
||||
|
||||
# IntelliJ IDEA
|
||||
.idea
|
||||
.idea/*
|
||||
!.idea/icon.png
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
# NetBeans
|
||||
nbproject/private/
|
||||
build/
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
.nb-gradle/
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
@@ -44,6 +38,11 @@ cmd/server/web/monacoeditorwork
|
||||
cmd/server/web/index.html
|
||||
frontend/auto-imports.d.ts
|
||||
frontend/components.d.ts
|
||||
frontend/src/xpack
|
||||
backend/xpack
|
||||
backend/router/entry_xpack.go
|
||||
backend/server/init_xpack.go
|
||||
backend/utils/xpack/xpack_xpack.go
|
||||
|
||||
.history/
|
||||
dist/
|
||||
@@ -53,3 +52,4 @@ install.sh
|
||||
quick_start.sh
|
||||
cmd/server/fileList.txt
|
||||
.fileList.txt
|
||||
1Panel.code-workspace
|
||||
|
||||
@@ -13,6 +13,7 @@ builds:
|
||||
- main: ./cmd/server/main.go
|
||||
binary: 1panel
|
||||
flags:
|
||||
- -tags=xpack
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -w -s
|
||||
|
||||
Generated
BIN
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
+2
-2
@@ -5,7 +5,7 @@ PR are always welcome, even if they only contain small fixes like typos or a few
|
||||
|
||||
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.
|
||||
|
||||
This [development guideline](https://kubeoperator.io/docs/dev/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 setup 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.
|
||||
|
||||
@@ -22,4 +22,4 @@ When reporting issues, always include:
|
||||
* Snapshots or log files if needed
|
||||
|
||||
Because the issues are open to the public, when submitting files, be sure to remove any sensitive information, e.g. user name, password, IP address, and company name. You can
|
||||
replace those parts with "REDACTED" or other strings like "****".
|
||||
replace those parts with "REDACTED" or other strings like "****".
|
||||
|
||||
@@ -11,13 +11,15 @@
|
||||
|
||||
------------------------------
|
||||
|
||||
1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。1Panel 的功能和优势包括:
|
||||
1Panel 是新一代的 Linux 服务器运维管理面板。
|
||||
|
||||
- **快速建站**:深度集成 Wordpress 和 [Halo](https://github.com/halo-dev/halo/),域名绑定、SSL 证书配置等一键搞定;
|
||||
- **高效管理**:通过 Web 端轻松管理 Linux 服务器,包括主机监控、文件管理、数据库管理、容器管理等;
|
||||
- **安全可靠**:基于容器来管理和部署应用,最小漏洞暴露面,提供防火墙和日志审计等功能;
|
||||
- **一键备份**:支持一键备份和恢复,备份数据到各类云端存储,永不丢失;
|
||||
- **应用商店**:精选各类高质量开源工具和应用软件,助力您轻松安装和升级。
|
||||
- **高效管理**:用户可以通过 Web 图形界面轻松管理 Linux 服务器,实现主机监控、文件管理、数据库管理、容器管理等功能;
|
||||
- **快速建站**:深度集成开源建站软件 WordPress 和 [Halo](https://github.com/halo-dev/halo/),域名绑定、SSL 证书配置等操作一键搞定;
|
||||
- **应用商店**:精选上架各类高质量的开源工具和应用软件,协助用户轻松安装并升级;
|
||||
- **安全可靠**:基于容器管理并部署应用,实现最小的漏洞暴露面,同时提供防火墙和日志审计等功能;
|
||||
- **一键备份**:支持一键备份和恢复,用户可以将数据备份到各类云端存储介质,永不丢失。
|
||||
|
||||
1Panel 三分钟速览:https://www.bilibili.com/video/BV1Mt421n7LZ/
|
||||
|
||||
## UI 展示
|
||||
|
||||
@@ -50,20 +52,26 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
|
||||
|
||||
如果您在使用过程中发现任何安全问题,请通过以下方式直接联系我们:
|
||||
|
||||
- 邮箱:support@fit2cloud.com
|
||||
- 电话:400-052-0755
|
||||
- 邮箱:wanghe@fit2cloud.com
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#1Panel-dev/1Panel&Date)
|
||||
|
||||
## 我们的其他明星项目
|
||||
- [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)
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2014-2023 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.
|
||||
Copyright (c) 2014-2024 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.
|
||||
|
||||
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
|
||||
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
如果您发现安全问题,请直接联系我们:
|
||||
|
||||
- wanghe@fit2cloud.com
|
||||
- zhengkun@fit2cloud.com
|
||||
- support@fit2cloud.com
|
||||
- 400-052-0755
|
||||
|
||||
感谢您的支持!
|
||||
|
||||
@@ -14,8 +11,5 @@
|
||||
All security bugs should be reported to the contact as below:
|
||||
|
||||
- wanghe@fit2cloud.com
|
||||
- zhengkun@fit2cloud.com
|
||||
- support@fit2cloud.com
|
||||
- +86 400-052-0755
|
||||
|
||||
Thanks for your support!
|
||||
|
||||
@@ -137,7 +137,7 @@ func (b *BaseApi) GetAppDetailByID(c *gin.Context) {
|
||||
// @Accept json
|
||||
// @Success 200 {object} response.IgnoredApp
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/ingored [get]
|
||||
// @Router /apps/ignored [get]
|
||||
func (b *BaseApi) GetIgnoredApp(c *gin.Context) {
|
||||
res, err := appService.GetIgnoredApp()
|
||||
if err != nil {
|
||||
|
||||
@@ -121,6 +121,15 @@ func (b *BaseApi) CheckIsSafety(c *gin.Context) {
|
||||
helper.SuccessWithData(c, status)
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetResponsePage(c *gin.Context) {
|
||||
pageCode, err := authService.GetResponsePage()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, pageCode)
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
// @Summary Check System isDemo
|
||||
// @Description 判断是否为demo环境
|
||||
|
||||
@@ -49,6 +49,17 @@ func (b *BaseApi) CreateBackup(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Refresh OneDrive token
|
||||
// @Description 刷新 OneDrive token
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/refresh/onedrive [post]
|
||||
func (b *BaseApi) RefreshOneDriveToken(c *gin.Context) {
|
||||
backupService.Run()
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary List buckets
|
||||
// @Description 获取 bucket 列表
|
||||
@@ -91,16 +102,16 @@ func (b *BaseApi) ListBuckets(c *gin.Context) {
|
||||
// @Summary Load OneDrive info
|
||||
// @Description 获取 OneDrive 信息
|
||||
// @Accept json
|
||||
// @Success 200 string clientID
|
||||
// @Success 200 {object} dto.OneDriveInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/onedrive [get]
|
||||
func (b *BaseApi) LoadOneDriveInfo(c *gin.Context) {
|
||||
clientID, err := backupService.LoadOneDriveInfo()
|
||||
data, err := backupService.LoadOneDriveInfo()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, clientID)
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
@@ -151,6 +162,32 @@ func (b *BaseApi) SearchBackupRecords(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Page backup records by cronjob
|
||||
// @Description 通过计划任务获取备份记录列表分页
|
||||
// @Accept json
|
||||
// @Param request body dto.RecordSearchByCronjob true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/record/search/bycronjob [post]
|
||||
func (b *BaseApi) SearchBackupRecordsByCronjob(c *gin.Context) {
|
||||
var req dto.RecordSearchByCronjob
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
total, list, err := backupService.SearchRecordsByCronjobWithPage(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Download backup record
|
||||
// @Description 下载备份记录
|
||||
@@ -265,12 +302,7 @@ func (b *BaseApi) LoadFilesFromBackup(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := backupService.ListFiles(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
data := backupService.ListFiles(req)
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
@@ -281,7 +313,7 @@ func (b *BaseApi) LoadFilesFromBackup(c *gin.Context) {
|
||||
// @Param request body dto.CommonBackup true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/ [post]
|
||||
// @Router /settings/backup/backup [post]
|
||||
// @x-panel-log {"bodyKeys":["type","name","detailName"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"备份 [type] 数据 [name][detailName]","formatEN":"backup [type] data [name][detailName]"}
|
||||
func (b *BaseApi) Backup(c *gin.Context) {
|
||||
var req dto.CommonBackup
|
||||
@@ -300,6 +332,11 @@ func (b *BaseApi) Backup(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
case constant.AppPostgresql:
|
||||
if err := backupService.PostgresqlBackup(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
case "website":
|
||||
if err := backupService.WebsiteBackup(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
@@ -329,20 +366,23 @@ func (b *BaseApi) Recover(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if req.Source != "LOCAL" {
|
||||
downloadPath, err := backupService.DownloadRecord(dto.DownloadRecord{Source: req.Source, FileDir: path.Dir(req.File), FileName: path.Base(req.File)})
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, fmt.Errorf("download file failed, err: %v", err))
|
||||
return
|
||||
}
|
||||
req.File = downloadPath
|
||||
downloadPath, err := backupService.DownloadRecord(dto.DownloadRecord{Source: req.Source, FileDir: path.Dir(req.File), FileName: path.Base(req.File)})
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, fmt.Errorf("download file failed, err: %v", err))
|
||||
return
|
||||
}
|
||||
req.File = downloadPath
|
||||
switch req.Type {
|
||||
case "mysql", "mariadb":
|
||||
if err := backupService.MysqlRecover(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
case constant.AppPostgresql:
|
||||
if err := backupService.PostgresqlRecover(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
case "website":
|
||||
if err := backupService.WebsiteRecover(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
@@ -383,6 +423,11 @@ func (b *BaseApi) RecoverByUpload(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
case constant.AppPostgresql:
|
||||
if err := backupService.PostgresqlRecoverByUpload(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
case "app":
|
||||
if err := backupService.AppRecover(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
|
||||
@@ -95,17 +95,13 @@ func (b *BaseApi) SearchJobRecords(c *gin.Context) {
|
||||
// @Param request body dto.OperateByID true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /cronjob/record/log [post]
|
||||
// @Router /cronjobs/records/log [post]
|
||||
func (b *BaseApi) LoadRecordLog(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
content, err := cronjobService.LoadRecordLog(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
content := cronjobService.LoadRecordLog(req)
|
||||
helper.SuccessWithData(c, content)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,22 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags Dashboard
|
||||
// @Summary Load os info
|
||||
// @Description 获取服务器基础数据
|
||||
// @Accept json
|
||||
// @Success 200 {object} dto.OsInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /dashboard/base/os [get]
|
||||
func (b *BaseApi) LoadDashboardOsInfo(c *gin.Context) {
|
||||
data, err := dashboardService.LoadOsInfo()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Dashboard
|
||||
// @Summary Load dashboard base info
|
||||
// @Description 获取首页基础数据
|
||||
|
||||
@@ -112,6 +112,27 @@ func (b *BaseApi) ListDatabase(c *gin.Context) {
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Database
|
||||
// @Summary List databases
|
||||
// @Description 获取数据库列表
|
||||
// @Success 200 {array} dto.DatabaseItem
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/db/item/:type [get]
|
||||
func (b *BaseApi) LoadDatabaseItems(c *gin.Context) {
|
||||
dbType, err := helper.GetStrParamByKey(c, "type")
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
list, err := databaseService.LoadItems(dbType)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Database
|
||||
// @Summary Get databases
|
||||
// @Description 获取远程数据库
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
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 Database Common
|
||||
// @Summary Load base info
|
||||
// @Description 获取数据库基础信息
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithNameAndType true "request"
|
||||
// @Success 200 {object} dto.DBBaseInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/common/info [post]
|
||||
func (b *BaseApi) LoadDBBaseInfo(c *gin.Context) {
|
||||
var req dto.OperationWithNameAndType
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := dbCommonService.LoadBaseInfo(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Database Common
|
||||
// @Summary Load Database conf
|
||||
// @Description 获取数据库配置文件
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithNameAndType true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/common/load/file [post]
|
||||
func (b *BaseApi) LoadDBFile(c *gin.Context) {
|
||||
var req dto.OperationWithNameAndType
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
content, err := dbCommonService.LoadDatabaseFile(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, content)
|
||||
}
|
||||
|
||||
// @Tags Database Common
|
||||
// @Summary Update conf by upload file
|
||||
// @Description 上传替换配置文件
|
||||
// @Accept json
|
||||
// @Param request body dto.DBConfUpdateByFile true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/common/update/conf [post]
|
||||
// @x-panel-log {"bodyKeys":["type","database"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 [type] 数据库 [database] 配置信息","formatEN":"update the [type] [database] database configuration information"}
|
||||
func (b *BaseApi) UpdateDBConfByFile(c *gin.Context) {
|
||||
var req dto.DBConfUpdateByFile
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := dbCommonService.UpdateConfByFile(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
@@ -41,6 +41,37 @@ func (b *BaseApi) CreateMysql(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Mysql
|
||||
// @Summary Bind user of mysql database
|
||||
// @Description 绑定 mysql 数据库用户
|
||||
// @Accept json
|
||||
// @Param request body dto.BindUser true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/bind [post]
|
||||
// @x-panel-log {"bodyKeys":["database", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"绑定 mysql 数据库名 [database] [username]","formatEN":"bind mysql database [database] [username]"}
|
||||
func (b *BaseApi) BindUser(c *gin.Context) {
|
||||
var req dto.BindUser
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Password) != 0 {
|
||||
password, err := base64.StdEncoding.DecodeString(req.Password)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.Password = string(password)
|
||||
}
|
||||
|
||||
if err := mysqlService.BindUser(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Mysql
|
||||
// @Summary Update mysql database description
|
||||
// @Description 更新 mysql 数据库库描述信息
|
||||
@@ -138,29 +169,6 @@ func (b *BaseApi) UpdateMysqlVariables(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Mysql
|
||||
// @Summary Update mysql conf by upload file
|
||||
// @Description 上传替换 mysql 配置文件
|
||||
// @Accept json
|
||||
// @Param request body dto.MysqlConfUpdateByFile true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/conffile/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 mysql 数据库配置信息","formatEN":"update the mysql database configuration information"}
|
||||
func (b *BaseApi) UpdateMysqlConfByFile(c *gin.Context) {
|
||||
var req dto.MysqlConfUpdateByFile
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := mysqlService.UpdateConfByFile(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Mysql
|
||||
// @Summary Page mysql databases
|
||||
// @Description 获取 mysql 数据库列表分页
|
||||
@@ -273,51 +281,6 @@ func (b *BaseApi) DeleteMysql(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Mysql
|
||||
// @Summary Load mysql base info
|
||||
// @Description 获取 mysql 基础信息
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithNameAndType true "request"
|
||||
// @Success 200 {object} dto.DBBaseInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/baseinfo [post]
|
||||
func (b *BaseApi) LoadBaseinfo(c *gin.Context) {
|
||||
var req dto.OperationWithNameAndType
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := mysqlService.LoadBaseInfo(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Database
|
||||
// @Summary Load Database file
|
||||
// @Description 获取数据库文件
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithNameAndType true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/load/file [post]
|
||||
func (b *BaseApi) LoadDatabaseFile(c *gin.Context) {
|
||||
var req dto.OperationWithNameAndType
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
content, err := mysqlService.LoadDatabaseFile(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, content)
|
||||
}
|
||||
|
||||
// @Tags Database Mysql
|
||||
// @Summary Load mysql remote access
|
||||
// @Description 获取 mysql 远程访问权限
|
||||
@@ -331,7 +294,6 @@ func (b *BaseApi) LoadRemoteAccess(c *gin.Context) {
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
isRemote, err := mysqlService.LoadRemoteAccess(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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 Database Postgresql
|
||||
// @Summary Create postgresql database
|
||||
// @Description 创建 postgresql 数据库
|
||||
// @Accept json
|
||||
// @Param request body dto.PostgresqlDBCreate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建 postgresql 数据库 [name]","formatEN":"create postgresql database [name]"}
|
||||
func (b *BaseApi) CreatePostgresql(c *gin.Context) {
|
||||
var req dto.PostgresqlDBCreate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Password) != 0 {
|
||||
password, err := base64.StdEncoding.DecodeString(req.Password)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.Password = string(password)
|
||||
}
|
||||
|
||||
if _, err := postgresqlService.Create(context.Background(), req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Postgresql
|
||||
// @Summary Bind postgresql user
|
||||
// @Description 绑定 postgresql 数据库用户
|
||||
// @Accept json
|
||||
// @Param request body dto.PostgresqlBindUser true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg/bind [post]
|
||||
// @x-panel-log {"bodyKeys":["name", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"绑定 postgresql 数据库 [name] 用户 [username]","formatEN":"bind postgresql database [name] user [username]"}
|
||||
func (b *BaseApi) BindPostgresqlUser(c *gin.Context) {
|
||||
var req dto.PostgresqlBindUser
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := postgresqlService.BindUser(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Postgresql
|
||||
// @Summary Update postgresql database description
|
||||
// @Description 更新 postgresql 数据库库描述信息
|
||||
// @Accept json
|
||||
// @Param request body dto.UpdateDescription true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg/description [post]
|
||||
// @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_postgresqls","output_column":"name","output_value":"name"}],"formatZH":"postgresql 数据库 [name] 描述信息修改 [description]","formatEN":"The description of the postgresql database [name] is modified => [description]"}
|
||||
func (b *BaseApi) UpdatePostgresqlDescription(c *gin.Context) {
|
||||
var req dto.UpdateDescription
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := postgresqlService.UpdateDescription(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Postgresql
|
||||
// @Summary Change postgresql privileges
|
||||
// @Description 修改 postgresql 用户权限
|
||||
// @Accept json
|
||||
// @Param request body dto.ChangeDBInfo true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg/privileges [post]
|
||||
// @x-panel-log {"bodyKeys":["database", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新数据库 [database] 用户 [username] 权限","formatEN":"Update [user] privileges of database [database]"}
|
||||
func (b *BaseApi) ChangePostgresqlPrivileges(c *gin.Context) {
|
||||
var req dto.PostgresqlPrivileges
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := postgresqlService.ChangePrivileges(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Postgresql
|
||||
// @Summary Change postgresql password
|
||||
// @Description 修改 postgresql 密码
|
||||
// @Accept json
|
||||
// @Param request body dto.ChangeDBInfo true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg/password [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_postgresqls","output_column":"name","output_value":"name"}],"formatZH":"更新数据库 [name] 密码","formatEN":"Update database [name] password"}
|
||||
func (b *BaseApi) ChangePostgresqlPassword(c *gin.Context) {
|
||||
var req dto.ChangeDBInfo
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Value) != 0 {
|
||||
value, err := base64.StdEncoding.DecodeString(req.Value)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.Value = string(value)
|
||||
}
|
||||
|
||||
if err := postgresqlService.ChangePassword(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Postgresql
|
||||
// @Summary Page postgresql databases
|
||||
// @Description 获取 postgresql 数据库列表分页
|
||||
// @Accept json
|
||||
// @Param request body dto.PostgresqlDBSearch true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg/search [post]
|
||||
func (b *BaseApi) SearchPostgresql(c *gin.Context) {
|
||||
var req dto.PostgresqlDBSearch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
total, list, err := postgresqlService.SearchWithPage(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Database Postgresql
|
||||
// @Summary Load postgresql database from remote
|
||||
// @Description 从服务器获取
|
||||
// @Accept json
|
||||
// @Param request body dto.PostgresqlLoadDB true "request"
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg/:database/load [post]
|
||||
func (b *BaseApi) LoadPostgresqlDBFromRemote(c *gin.Context) {
|
||||
database, err := helper.GetStrParamByKey(c, "database")
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := postgresqlService.LoadFromRemote(database); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Postgresql
|
||||
// @Summary Check before delete postgresql database
|
||||
// @Description Postgresql 数据库删除前检查
|
||||
// @Accept json
|
||||
// @Param request body dto.PostgresqlDBDeleteCheck true "request"
|
||||
// @Success 200 {array} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg/del/check [post]
|
||||
func (b *BaseApi) DeleteCheckPostgresql(c *gin.Context) {
|
||||
var req dto.PostgresqlDBDeleteCheck
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
apps, err := postgresqlService.DeleteCheck(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, apps)
|
||||
}
|
||||
|
||||
// @Tags Database Postgresql
|
||||
// @Summary Delete postgresql database
|
||||
// @Description 删除 postgresql 数据库
|
||||
// @Accept json
|
||||
// @Param request body dto.PostgresqlDBDelete true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_postgresqls","output_column":"name","output_value":"name"}],"formatZH":"删除 postgresql 数据库 [name]","formatEN":"delete postgresql database [name]"}
|
||||
func (b *BaseApi) DeletePostgresql(c *gin.Context) {
|
||||
var req dto.PostgresqlDBDelete
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
tx, ctx := helper.GetTxAndContext()
|
||||
if err := postgresqlService.Delete(ctx, req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
tx.Commit()
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
@@ -1,15 +1,11 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"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/1Panel-dev/1Panel/backend/utils/compose"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -161,45 +157,3 @@ func (b *BaseApi) RedisBackupList(c *gin.Context) {
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Database Redis
|
||||
// @Summary Update redis conf by file
|
||||
// @Description 上传更新 redis 配置信息
|
||||
// @Accept json
|
||||
// @Param request body dto.RedisConfUpdateByFile true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/redis/conffile/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 redis 数据库配置信息","formatEN":"update the redis database configuration information"}
|
||||
func (b *BaseApi) UpdateRedisConfByFile(c *gin.Context) {
|
||||
var req dto.RedisConfUpdateByFile
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
redisInfo, err := redisService.LoadConf()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
path := fmt.Sprintf("%s/redis/%s/conf/redis.conf", constant.AppInstallDir, redisInfo.Name)
|
||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
write := bufio.NewWriter(file)
|
||||
_, _ = write.WriteString(req.File)
|
||||
write.Flush()
|
||||
|
||||
if req.RestartNow {
|
||||
composeDir := fmt.Sprintf("%s/redis/%s/docker-compose.yml", constant.AppInstallDir, redisInfo.Name)
|
||||
if _, err := compose.Restart(composeDir); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -21,9 +21,11 @@ var (
|
||||
imageService = service.NewIImageService()
|
||||
dockerService = service.NewIDockerService()
|
||||
|
||||
mysqlService = service.NewIMysqlService()
|
||||
databaseService = service.NewIDatabaseService()
|
||||
redisService = service.NewIRedisService()
|
||||
dbCommonService = service.NewIDBCommonService()
|
||||
mysqlService = service.NewIMysqlService()
|
||||
postgresqlService = service.NewIPostgresqlService()
|
||||
databaseService = service.NewIDatabaseService()
|
||||
redisService = service.NewIRedisService()
|
||||
|
||||
cronjobService = service.NewICronjobService()
|
||||
|
||||
@@ -52,8 +54,9 @@ var (
|
||||
snapshotService = service.NewISnapshotService()
|
||||
upgradeService = service.NewIUpgradeService()
|
||||
|
||||
runtimeService = service.NewRuntimeService()
|
||||
processService = service.NewIProcessService()
|
||||
runtimeService = service.NewRuntimeService()
|
||||
processService = service.NewIProcessService()
|
||||
phpExtensionsService = service.NewIPHPExtensionsService()
|
||||
|
||||
hostToolService = service.NewIHostToolService()
|
||||
|
||||
|
||||
+69
-12
@@ -301,28 +301,83 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
files := form.File["file"]
|
||||
uploadFiles := form.File["file"]
|
||||
paths := form.Value["path"]
|
||||
|
||||
overwrite := true
|
||||
if ow, ok := form.Value["overwrite"]; ok {
|
||||
if len(ow) != 0 {
|
||||
parseBool, _ := strconv.ParseBool(ow[0])
|
||||
overwrite = parseBool
|
||||
}
|
||||
}
|
||||
|
||||
if len(paths) == 0 || !strings.Contains(paths[0], "/") {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error paths in request"))
|
||||
return
|
||||
}
|
||||
dir := path.Dir(paths[0])
|
||||
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
|
||||
info, err := os.Stat(dir)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
mode, err := files.GetParentMode(dir)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if err = os.MkdirAll(dir, mode); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, fmt.Errorf("mkdir %s failed, err: %v", dir, err))
|
||||
return
|
||||
}
|
||||
}
|
||||
info, err = os.Stat(dir)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
mode := info.Mode()
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
|
||||
success := 0
|
||||
failures := make(buserr.MultiErr)
|
||||
for _, file := range files {
|
||||
if err := c.SaveUploadedFile(file, path.Join(paths[0], file.Filename)); err != nil {
|
||||
for _, file := range uploadFiles {
|
||||
dstFilename := path.Join(paths[0], file.Filename)
|
||||
dstDir := path.Dir(dstFilename)
|
||||
if !fileOp.Stat(dstDir) {
|
||||
if err = fileOp.CreateDir(dstDir, mode); err != nil {
|
||||
e := fmt.Errorf("create dir [%s] failed, err: %v", path.Dir(dstFilename), err)
|
||||
failures[file.Filename] = e
|
||||
global.LOG.Error(e)
|
||||
continue
|
||||
}
|
||||
}
|
||||
tmpFilename := dstFilename + ".tmp"
|
||||
if err := c.SaveUploadedFile(file, tmpFilename); err != nil {
|
||||
_ = os.Remove(tmpFilename)
|
||||
e := fmt.Errorf("upload [%s] file failed, err: %v", file.Filename, err)
|
||||
failures[file.Filename] = e
|
||||
global.LOG.Error(e)
|
||||
continue
|
||||
}
|
||||
dstInfo, statErr := os.Stat(dstFilename)
|
||||
if overwrite {
|
||||
_ = os.Remove(dstFilename)
|
||||
}
|
||||
|
||||
err = os.Rename(tmpFilename, dstFilename)
|
||||
if err != nil {
|
||||
_ = os.Remove(tmpFilename)
|
||||
e := fmt.Errorf("upload [%s] file failed, err: %v", file.Filename, err)
|
||||
failures[file.Filename] = e
|
||||
global.LOG.Error(e)
|
||||
continue
|
||||
}
|
||||
if statErr == nil {
|
||||
_ = os.Chmod(dstFilename, dstInfo.Mode())
|
||||
} else {
|
||||
_ = os.Chmod(dstFilename, mode)
|
||||
}
|
||||
success++
|
||||
}
|
||||
if success == 0 {
|
||||
@@ -422,11 +477,9 @@ func (b *BaseApi) MoveFile(c *gin.Context) {
|
||||
// @Summary Download file
|
||||
// @Description 下载文件
|
||||
// @Accept json
|
||||
// @Param request body request.FileDownload true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /files/download [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"下载文件 [name]","formatEN":"Download file [name]"}
|
||||
// @Router /files/download [get]
|
||||
func (b *BaseApi) Download(c *gin.Context) {
|
||||
filePath := c.Query("path")
|
||||
file, err := os.Open(filePath)
|
||||
@@ -541,16 +594,20 @@ func (b *BaseApi) Size(c *gin.Context) {
|
||||
func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int) error {
|
||||
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, os.ModePerm); err != nil {
|
||||
if err = op.CreateDir(dstDir, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
targetFile, err := os.Create(filepath.Join(dstDir, fileName))
|
||||
|
||||
targetFile, err := os.OpenFile(filepath.Join(dstDir, fileName), 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))
|
||||
@@ -689,7 +746,7 @@ func (b *BaseApi) Keys(c *gin.Context) {
|
||||
// @Param request body request.FileReadByLineReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /files/log/read [post]
|
||||
// @Router /files/read [post]
|
||||
func (b *BaseApi) ReadFileByLine(c *gin.Context) {
|
||||
var req request.FileReadByLineReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
|
||||
@@ -35,8 +35,24 @@ func (b *BaseApi) SearchImage(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags Container Image
|
||||
// @Summary List images
|
||||
// @Description 获取镜像列表
|
||||
// @Summary List all images
|
||||
// @Description 获取所有镜像列表
|
||||
// @Produce json
|
||||
// @Success 200 {array} dto.ImageInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/image/all [get]
|
||||
func (b *BaseApi) ListAllImage(c *gin.Context) {
|
||||
list, err := imageService.ListAll()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Container Image
|
||||
// @Summary load images options
|
||||
// @Description 获取镜像名称列表
|
||||
// @Produce json
|
||||
// @Success 200 {array} dto.Options
|
||||
// @Security ApiKeyAuth
|
||||
|
||||
@@ -15,6 +15,13 @@ import (
|
||||
"github.com/shirou/gopsutil/v3/net"
|
||||
)
|
||||
|
||||
// @Tags Monitor
|
||||
// @Summary Load monitor datas
|
||||
// @Description 获取监控数据
|
||||
// @Param request body dto.MonitorSearch true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/monitor/search [post]
|
||||
func (b *BaseApi) LoadMonitor(c *gin.Context) {
|
||||
var req dto.MonitorSearch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -28,7 +35,7 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
|
||||
var backdatas []dto.MonitorData
|
||||
if req.Param == "all" || req.Param == "cpu" || req.Param == "memory" || req.Param == "load" {
|
||||
var bases []model.MonitorBase
|
||||
if err := global.DB.
|
||||
if err := global.MonitorDB.
|
||||
Where("created_at > ? AND created_at < ?", req.StartTime, req.EndTime).
|
||||
Find(&bases).Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
@@ -45,7 +52,7 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
|
||||
}
|
||||
if req.Param == "all" || req.Param == "io" {
|
||||
var bases []model.MonitorIO
|
||||
if err := global.DB.
|
||||
if err := global.MonitorDB.
|
||||
Where("created_at > ? AND created_at < ?", req.StartTime, req.EndTime).
|
||||
Find(&bases).Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
@@ -62,7 +69,7 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
|
||||
}
|
||||
if req.Param == "all" || req.Param == "network" {
|
||||
var bases []model.MonitorNetwork
|
||||
if err := global.DB.
|
||||
if err := global.MonitorDB.
|
||||
Where("name = ? AND created_at > ? AND created_at < ?", req.Info, req.StartTime, req.EndTime).
|
||||
Find(&bases).Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
@@ -80,6 +87,30 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
|
||||
helper.SuccessWithData(c, backdatas)
|
||||
}
|
||||
|
||||
// @Tags Monitor
|
||||
// @Summary Clean monitor datas
|
||||
// @Description 清空监控数据
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/monitor/clean [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清空监控数据","formatEN":"clean monitor datas"}
|
||||
func (b *BaseApi) CleanMonitor(c *gin.Context) {
|
||||
if err := global.MonitorDB.Exec("DELETE FROM monitor_bases").Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if err := global.MonitorDB.Exec("DELETE FROM monitor_ios").Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if err := global.MonitorDB.Exec("DELETE FROM monitor_networks").Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetNetworkOptions(c *gin.Context) {
|
||||
netStat, _ := net.IOCounters(true)
|
||||
var options []string
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
// @Description 获取 OpenResty 配置信息
|
||||
// @Success 200 {object} response.FileInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /openResty [get]
|
||||
// @Router /openresty [get]
|
||||
func (b *BaseApi) GetNginx(c *gin.Context) {
|
||||
fileInfo, err := nginxService.GetNginxConfig()
|
||||
if err != nil {
|
||||
@@ -29,7 +29,7 @@ func (b *BaseApi) GetNginx(c *gin.Context) {
|
||||
// @Param request body request.NginxScopeReq true "request"
|
||||
// @Success 200 {array} response.NginxParam
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /openResty/scope [post]
|
||||
// @Router /openresty/scope [post]
|
||||
func (b *BaseApi) GetNginxConfigByScope(c *gin.Context) {
|
||||
var req request.NginxScopeReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -51,7 +51,7 @@ func (b *BaseApi) GetNginxConfigByScope(c *gin.Context) {
|
||||
// @Param request body request.NginxConfigUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /openResty/update [post]
|
||||
// @Router /openresty/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新 nginx 配置 [domain]","formatEN":"Update nginx conf [domain]"}
|
||||
func (b *BaseApi) UpdateNginxConfigByScope(c *gin.Context) {
|
||||
var req request.NginxConfigUpdate
|
||||
@@ -70,7 +70,7 @@ func (b *BaseApi) UpdateNginxConfigByScope(c *gin.Context) {
|
||||
// @Description 获取 OpenResty 状态信息
|
||||
// @Success 200 {object} response.NginxStatus
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /openResty/status [get]
|
||||
// @Router /openresty/status [get]
|
||||
func (b *BaseApi) GetNginxStatus(c *gin.Context) {
|
||||
res, err := nginxService.GetStatus()
|
||||
if err != nil {
|
||||
@@ -87,7 +87,7 @@ func (b *BaseApi) GetNginxStatus(c *gin.Context) {
|
||||
// @Param request body request.NginxConfigFileUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /openResty/file [post]
|
||||
// @Router /openresty/file [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 nginx 配置","formatEN":"Update nginx conf"}
|
||||
func (b *BaseApi) UpdateNginxFile(c *gin.Context) {
|
||||
var req request.NginxConfigFileUpdate
|
||||
@@ -101,3 +101,18 @@ func (b *BaseApi) UpdateNginxFile(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags OpenResty
|
||||
// @Summary Clear OpenResty proxy cache
|
||||
// @Description 清理 OpenResty 代理缓存
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /openresty/clear [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清理 Openresty 代理缓存","formatEN":"Clear nginx proxy cache"}
|
||||
func (b *BaseApi) ClearNginxProxyCache(c *gin.Context) {
|
||||
if err := nginxService.ClearProxyCache(); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
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/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags PHP Extensions
|
||||
// @Summary Page Extensions
|
||||
// @Description Page Extensions
|
||||
// @Accept json
|
||||
// @Param request body request.PHPExtensionsSearch true "request"
|
||||
// @Success 200 {array} response.PHPExtensionsDTO
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /runtimes/php/extensions/search [post]
|
||||
func (b *BaseApi) PagePHPExtensions(c *gin.Context) {
|
||||
var req request.PHPExtensionsSearch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if req.All {
|
||||
list, err := phpExtensionsService.List()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
} else {
|
||||
total, list, err := phpExtensionsService.Page(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Total: total,
|
||||
Items: list,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// @Tags PHP Extensions
|
||||
// @Summary Create Extensions
|
||||
// @Description Create Extensions
|
||||
// @Accept json
|
||||
// @Param request body request.PHPExtensionsCreate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /runtimes/php/extensions [post]
|
||||
func (b *BaseApi) CreatePHPExtensions(c *gin.Context) {
|
||||
var req request.PHPExtensionsCreate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := phpExtensionsService.Create(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags PHP Extensions
|
||||
// @Summary Update Extensions
|
||||
// @Description Update Extensions
|
||||
// @Accept json
|
||||
// @Param request body request.PHPExtensionsUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /runtimes/php/extensions/update [post]
|
||||
func (b *BaseApi) UpdatePHPExtensions(c *gin.Context) {
|
||||
var req request.PHPExtensionsUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := phpExtensionsService.Update(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags PHP Extensions
|
||||
// @Summary Delete Extensions
|
||||
// @Description Delete Extensions
|
||||
// @Accept json
|
||||
// @Param request body request.PHPExtensionsDelete true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /runtimes/php/extensions/del [post]
|
||||
func (b *BaseApi) DeletePHPExtensions(c *gin.Context) {
|
||||
var req request.PHPExtensionsDelete
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := phpExtensionsService.Delete(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
@@ -203,3 +203,19 @@ func (b *BaseApi) OperateNodeModules(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Runtime
|
||||
// @Summary Sync runtime status
|
||||
// @Description 同步运行环境状态
|
||||
// @Accept json
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /runtimes/sync [post]
|
||||
func (b *BaseApi) SyncStatus(c *gin.Context) {
|
||||
err := runtimeService.SyncRuntimeStatus()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
@@ -60,6 +60,28 @@ func (b *BaseApi) UpdateSetting(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Update system setting
|
||||
// @Description 隐藏高级功能菜单
|
||||
// @Accept json
|
||||
// @Param request body dto.SettingUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/menu/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"隐藏高级功能菜单","formatEN":"Hide advanced feature menu."}
|
||||
func (b *BaseApi) UpdateMenu(c *gin.Context) {
|
||||
var req dto.SettingUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := settingService.Update(req.Key, req.Value); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Update system password
|
||||
// @Description 更新系统登录密码
|
||||
@@ -227,30 +249,6 @@ func (b *BaseApi) LoadBaseDir(c *gin.Context) {
|
||||
helper.SuccessWithData(c, global.CONF.System.DataDir)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Clean monitor datas
|
||||
// @Description 清空监控数据
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/monitor/clean [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清空监控数据","formatEN":"clean monitor datas"}
|
||||
func (b *BaseApi) CleanMonitor(c *gin.Context) {
|
||||
if err := global.DB.Exec("DELETE FROM monitor_bases").Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if err := global.DB.Exec("DELETE FROM monitor_ios").Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if err := global.DB.Exec("DELETE FROM monitor_networks").Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Load mfa info
|
||||
// @Description 获取 mfa 信息
|
||||
|
||||
@@ -55,7 +55,6 @@ func (b *BaseApi) WsSsh(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
sws, err := terminal.NewLogicSshWsSession(cols, rows, true, connInfo.Client, wsConn)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
@@ -103,10 +102,10 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(redisConf.ContainerName, commands, pidMap)
|
||||
defer killBash(redisConf.ContainerName, pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave)
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
@@ -171,10 +170,10 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(containerID, command, pidMap)
|
||||
defer killBash(containerID, pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave)
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, true)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
@@ -222,26 +221,26 @@ func loadMapFromDockerTop(containerID string) map[string]string {
|
||||
lines := strings.Split(stdout, "\n")
|
||||
for _, line := range lines {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) != 2 {
|
||||
if len(parts) == 0 {
|
||||
continue
|
||||
}
|
||||
pidMap[parts[0]] = parts[1]
|
||||
pidMap[parts[0]] = strings.Join(parts, " ")
|
||||
}
|
||||
return pidMap
|
||||
}
|
||||
|
||||
func killBash(containerID, comm string, pidMap map[string]string) {
|
||||
func killBash(containerID string, pidMap map[string]string) {
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
newPidMap := loadMapFromDockerTop(containerID)
|
||||
for pid, command := range newPidMap {
|
||||
for pid, newCmd := range newPidMap {
|
||||
isOld := false
|
||||
for pid2 := range pidMap {
|
||||
if pid == pid2 {
|
||||
for pid2, oldCmd := range pidMap {
|
||||
if pid == pid2 && oldCmd == newCmd {
|
||||
isOld = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isOld && command == comm {
|
||||
if !isOld {
|
||||
_, _ = cmd.Execf("%s kill -9 %s", sudo, pid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ func (b *BaseApi) GetNotesByVersion(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/upgrade [post]
|
||||
// @x-panel-log {"bodyKeys":["version"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新系统 => [version]","formatEN":"upgrade service => [version]"}
|
||||
// @x-panel-log {"bodyKeys":["version"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新系统 => [version]","formatEN":"upgrade system => [version]"}
|
||||
func (b *BaseApi) Upgrade(c *gin.Context) {
|
||||
var req dto.Upgrade
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
|
||||
@@ -305,69 +305,6 @@ func (b *BaseApi) CreateWebsiteCheck(c *gin.Context) {
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Website WAF
|
||||
// @Summary Load websit waf conf
|
||||
// @Description 获取网站 waf 配置
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteWafReq true "request"
|
||||
// @Success 200 {object} response.WebsiteWafConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/waf/config [post]
|
||||
func (b *BaseApi) GetWebsiteWafConfig(c *gin.Context) {
|
||||
var req request.WebsiteWafReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := websiteService.GetWafConfig(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Website WAF
|
||||
// @Summary Update website waf conf
|
||||
// @Description 更新 网站 waf 配置
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteWafUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/waf/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"WAF 配置修改 [domain]","formatEN":"WAF conf update [domain]"}
|
||||
func (b *BaseApi) UpdateWebsiteWafConfig(c *gin.Context) {
|
||||
var req request.WebsiteWafUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateWafConfig(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Website WAF
|
||||
// @Summary Update website waf file
|
||||
// @Description 更新 网站 waf 配置文件
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteWafUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/waf/file/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"WAF 配置文件修改 [domain]","formatEN":"WAF conf file update [domain]"}
|
||||
func (b *BaseApi) UpdateWebsiteWafFile(c *gin.Context) {
|
||||
var req request.WebsiteWafFileUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateWafFile(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Website Nginx
|
||||
// @Summary Update website nginx conf
|
||||
// @Description 更新 网站 nginx 配置
|
||||
@@ -433,7 +370,7 @@ func (b *BaseApi) ChangeDefaultServer(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Load websit php conf
|
||||
// @Summary Load website php conf
|
||||
// @Description 获取网站 php 配置
|
||||
// @Accept json
|
||||
// @Param id path integer true "request"
|
||||
|
||||
@@ -21,18 +21,24 @@ type BackupInfo struct {
|
||||
Vars string `json:"vars"`
|
||||
}
|
||||
|
||||
type OneDriveInfo struct {
|
||||
ClientID string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
RedirectUri string `json:"redirect_uri"`
|
||||
}
|
||||
|
||||
type BackupSearchFile struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
}
|
||||
|
||||
type CommonBackup struct {
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website"`
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql"`
|
||||
Name string `json:"name"`
|
||||
DetailName string `json:"detailName"`
|
||||
}
|
||||
type CommonRecover struct {
|
||||
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive WebDAV"`
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website"`
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql"`
|
||||
Name string `json:"name"`
|
||||
DetailName string `json:"detailName"`
|
||||
File string `json:"file"`
|
||||
@@ -45,6 +51,11 @@ type RecordSearch struct {
|
||||
DetailName string `json:"detailName"`
|
||||
}
|
||||
|
||||
type RecordSearchByCronjob struct {
|
||||
PageInfo
|
||||
CronjobID uint `json:"cronjobID" validate:"required"`
|
||||
}
|
||||
|
||||
type BackupRecords struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
|
||||
@@ -2,8 +2,11 @@ package dto
|
||||
|
||||
type SearchCommandWithPage struct {
|
||||
SearchWithPage
|
||||
OrderBy string `json:"orderBy"`
|
||||
Order string `json:"order"`
|
||||
GroupID uint `json:"groupID"`
|
||||
Info string `json:"info"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type CommandOperate struct {
|
||||
|
||||
@@ -4,11 +4,12 @@ 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"`
|
||||
Filters string `json:"filters"`
|
||||
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"`
|
||||
Filters string `json:"filters"`
|
||||
ExcludeAppStore bool `json:"excludeAppStore"`
|
||||
}
|
||||
|
||||
type InspectReq struct {
|
||||
@@ -30,11 +31,15 @@ type ContainerInfo struct {
|
||||
|
||||
IsFromApp bool `json:"isFromApp"`
|
||||
IsFromCompose bool `json:"isFromCompose"`
|
||||
|
||||
AppName string `json:"appName"`
|
||||
AppInstallName string `json:"appInstallName"`
|
||||
Websites []string `json:"websites"`
|
||||
}
|
||||
|
||||
type ResourceLimit struct {
|
||||
CPU int `json:"cpu"`
|
||||
Memory int `json:"memory"`
|
||||
CPU int `json:"cpu"`
|
||||
Memory uint64 `json:"memory"`
|
||||
}
|
||||
|
||||
type ContainerOperate struct {
|
||||
@@ -43,6 +48,8 @@ type ContainerOperate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Image string `json:"image" validate:"required"`
|
||||
Network string `json:"network"`
|
||||
Ipv4 string `json:"ipv4"`
|
||||
Ipv6 string `json:"ipv6"`
|
||||
PublishAllPorts bool `json:"publishAllPorts"`
|
||||
ExposedPorts []PortHelper `json:"exposedPorts"`
|
||||
Tty bool `json:"tty"`
|
||||
@@ -93,6 +100,7 @@ type ContainerStats struct {
|
||||
}
|
||||
|
||||
type VolumeHelper struct {
|
||||
Type string `json:"type"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
ContainerDir string `json:"containerDir"`
|
||||
Mode string `json:"mode"`
|
||||
|
||||
+36
-43
@@ -3,49 +3,45 @@ package dto
|
||||
import "time"
|
||||
|
||||
type CronjobCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
SpecType string `json:"specType" validate:"required"`
|
||||
Week int `json:"week" validate:"number,max=6,min=0"`
|
||||
Day int `json:"day" validate:"number"`
|
||||
Hour int `json:"hour" validate:"number"`
|
||||
Minute int `json:"minute" validate:"number"`
|
||||
Second int `json:"second" validate:"number"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
Spec string `json:"spec" validate:"required"`
|
||||
|
||||
Script string `json:"script"`
|
||||
Command string `json:"command"`
|
||||
ContainerName string `json:"containerName"`
|
||||
AppID string `json:"appID"`
|
||||
Website string `json:"website"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
DBType string `json:"dbType"`
|
||||
DBName string `json:"dbName"`
|
||||
URL string `json:"url"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
KeepLocal bool `json:"keepLocal"`
|
||||
TargetDirID int `json:"targetDirID"`
|
||||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||
|
||||
BackupAccounts string `json:"backupAccounts"`
|
||||
DefaultDownload string `json:"defaultDownload"`
|
||||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||
}
|
||||
|
||||
type CronjobUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
SpecType string `json:"specType" validate:"required"`
|
||||
Week int `json:"week" validate:"number,max=6,min=0"`
|
||||
Day int `json:"day" validate:"number"`
|
||||
Hour int `json:"hour" validate:"number"`
|
||||
Minute int `json:"minute" validate:"number"`
|
||||
Second int `json:"second" validate:"number"`
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Spec string `json:"spec" validate:"required"`
|
||||
|
||||
Script string `json:"script"`
|
||||
Command string `json:"command"`
|
||||
ContainerName string `json:"containerName"`
|
||||
AppID string `json:"appID"`
|
||||
Website string `json:"website"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
DBType string `json:"dbType"`
|
||||
DBName string `json:"dbName"`
|
||||
URL string `json:"url"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
KeepLocal bool `json:"keepLocal"`
|
||||
TargetDirID int `json:"targetDirID"`
|
||||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||
|
||||
BackupAccounts string `json:"backupAccounts"`
|
||||
DefaultDownload string `json:"defaultDownload"`
|
||||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||
}
|
||||
|
||||
type CronjobUpdateStatus struct {
|
||||
@@ -59,6 +55,7 @@ type CronjobDownload struct {
|
||||
}
|
||||
|
||||
type CronjobClean struct {
|
||||
IsDelete bool `json:"isDelete"`
|
||||
CleanData bool `json:"cleanData"`
|
||||
CronjobID uint `json:"cronjobID" validate:"required"`
|
||||
}
|
||||
@@ -69,28 +66,24 @@ type CronjobBatchDelete struct {
|
||||
}
|
||||
|
||||
type CronjobInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
SpecType string `json:"specType"`
|
||||
Week int `json:"week"`
|
||||
Day int `json:"day"`
|
||||
Hour int `json:"hour"`
|
||||
Minute int `json:"minute"`
|
||||
Second int `json:"second"`
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Spec string `json:"spec"`
|
||||
|
||||
Script string `json:"script"`
|
||||
ContainerName string `json:"containerName"`
|
||||
AppID string `json:"appID"`
|
||||
Website string `json:"website"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
DBName string `json:"dbName"`
|
||||
URL string `json:"url"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
KeepLocal bool `json:"keepLocal"`
|
||||
TargetDir string `json:"targetDir"`
|
||||
TargetDirID int `json:"targetDirID"`
|
||||
RetainCopies int `json:"retainCopies"`
|
||||
Script string `json:"script"`
|
||||
Command string `json:"command"`
|
||||
ContainerName string `json:"containerName"`
|
||||
AppID string `json:"appID"`
|
||||
Website string `json:"website"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
DBType string `json:"dbType"`
|
||||
DBName string `json:"dbName"`
|
||||
URL string `json:"url"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
BackupAccounts string `json:"backupAccounts"`
|
||||
DefaultDownload string `json:"defaultDownload"`
|
||||
RetainCopies int `json:"retainCopies"`
|
||||
|
||||
LastRecordTime string `json:"lastRecordTime"`
|
||||
Status string `json:"status"`
|
||||
|
||||
@@ -24,6 +24,14 @@ type DashboardBase struct {
|
||||
CurrentInfo DashboardCurrent `json:"currentInfo"`
|
||||
}
|
||||
|
||||
type OsInfo struct {
|
||||
OS string `json:"os"`
|
||||
Platform string `json:"platform"`
|
||||
PlatformFamily string `json:"platformFamily"`
|
||||
KernelArch string `json:"kernelArch"`
|
||||
KernelVersion string `json:"kernelVersion"`
|
||||
}
|
||||
|
||||
type DashboardCurrent struct {
|
||||
Uptime uint64 `json:"uptime"`
|
||||
TimeSinceUptime string `json:"timeSinceUptime"`
|
||||
@@ -61,6 +69,8 @@ type DashboardCurrent struct {
|
||||
NetBytesSent uint64 `json:"netBytesSent"`
|
||||
NetBytesRecv uint64 `json:"netBytesRecv"`
|
||||
|
||||
GPUData []GPUInfo `json:"gpuData"`
|
||||
|
||||
ShotTime time.Time `json:"shotTime"`
|
||||
}
|
||||
|
||||
@@ -78,3 +88,18 @@ type DiskInfo struct {
|
||||
InodesFree uint64 `json:"inodesFree"`
|
||||
InodesUsedPercent float64 `json:"inodesUsedPercent"`
|
||||
}
|
||||
|
||||
type GPUInfo struct {
|
||||
Index uint `json:"index"`
|
||||
ProductName string `json:"productName"`
|
||||
GPUUtil string `json:"gpuUtil"`
|
||||
Temperature string `json:"temperature"`
|
||||
PerformanceState string `json:"performanceState"`
|
||||
PowerUsage string `json:"powerUsage"`
|
||||
PowerDraw string `json:"powerDraw"`
|
||||
MaxPowerLimit string `json:"maxPowerLimit"`
|
||||
MemoryUsage string `json:"memoryUsage"`
|
||||
MemUsed string `json:"memUsed"`
|
||||
MemTotal string `json:"memTotal"`
|
||||
FanSpeed string `json:"fanSpeed"`
|
||||
}
|
||||
|
||||
+38
-21
@@ -2,6 +2,27 @@ package dto
|
||||
|
||||
import "time"
|
||||
|
||||
// common
|
||||
type DBConfUpdateByFile struct {
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql redis"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
File string `json:"file"`
|
||||
}
|
||||
type ChangeDBInfo struct {
|
||||
ID uint `json:"id"`
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
Value string `json:"value" validate:"required"`
|
||||
}
|
||||
|
||||
type DBBaseInfo struct {
|
||||
Name string `json:"name"`
|
||||
ContainerName string `json:"containerName"`
|
||||
Port int64 `json:"port"`
|
||||
}
|
||||
|
||||
// mysql
|
||||
type MysqlDBSearch struct {
|
||||
PageInfo
|
||||
Info string `json:"info"`
|
||||
@@ -20,7 +41,7 @@ type MysqlDBInfo struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Permission string `json:"permission"`
|
||||
BackupCount int `json:"backupCount"`
|
||||
IsDelete bool `json:"isDelete"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
@@ -43,6 +64,14 @@ type MysqlDBCreate struct {
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type BindUser struct {
|
||||
Database string `json:"database" validate:"required"`
|
||||
DB string `json:"db" validate:"required"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Permission string `json:"permission" validate:"required"`
|
||||
}
|
||||
|
||||
type MysqlLoadDB struct {
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
|
||||
@@ -102,7 +131,7 @@ type MysqlStatus struct {
|
||||
}
|
||||
|
||||
type MysqlVariables struct {
|
||||
BinlogCachSize 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"`
|
||||
@@ -133,25 +162,6 @@ type MysqlVariablesUpdateHelper struct {
|
||||
Param string `json:"param"`
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
type MysqlConfUpdateByFile struct {
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
File string `json:"file"`
|
||||
}
|
||||
|
||||
type ChangeDBInfo struct {
|
||||
ID uint `json:"id"`
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
Value string `json:"value" validate:"required"`
|
||||
}
|
||||
|
||||
type DBBaseInfo struct {
|
||||
Name string `json:"name"`
|
||||
ContainerName string `json:"containerName"`
|
||||
Port int64 `json:"port"`
|
||||
}
|
||||
|
||||
// redis
|
||||
type ChangeRedisPass struct {
|
||||
@@ -256,6 +266,13 @@ type DatabaseOption struct {
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type DatabaseItem struct {
|
||||
ID uint `json:"id"`
|
||||
From string `json:"from"`
|
||||
Database string `json:"database"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type DatabaseCreate struct {
|
||||
Name string `json:"name" validate:"required,max=256"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package dto
|
||||
|
||||
import "time"
|
||||
|
||||
type PostgresqlDBSearch struct {
|
||||
PageInfo
|
||||
Info string `json:"info"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
OrderBy string `json:"orderBy"`
|
||||
Order string `json:"order"`
|
||||
}
|
||||
|
||||
type PostgresqlDBInfo struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Name string `json:"name"`
|
||||
From string `json:"from"`
|
||||
PostgresqlName string `json:"postgresqlName"`
|
||||
Format string `json:"format"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
SuperUser bool `json:"superUser"`
|
||||
IsDelete bool `json:"isDelete"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type PostgresqlOption struct {
|
||||
ID uint `json:"id"`
|
||||
From string `json:"from"`
|
||||
Type string `json:"type"`
|
||||
Database string `json:"database"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type PostgresqlDBCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
Format string `json:"format"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
SuperUser bool `json:"superUser"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type PostgresqlBindUser struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
SuperUser bool `json:"superUser"`
|
||||
}
|
||||
|
||||
type PostgresqlPrivileges struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
SuperUser bool `json:"superUser"`
|
||||
}
|
||||
|
||||
type PostgresqlLoadDB struct {
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Type string `json:"type" validate:"required,oneof=postgresql"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
}
|
||||
|
||||
type PostgresqlDBDeleteCheck struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=postgresql"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
}
|
||||
|
||||
type PostgresqlDBDelete struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=postgresql"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
ForceDelete bool `json:"forceDelete"`
|
||||
DeleteBackup bool `json:"deleteBackup"`
|
||||
}
|
||||
|
||||
type PostgresqlConfUpdateByFile struct {
|
||||
Type string `json:"type" validate:"required,oneof=postgresql mariadb"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
File string `json:"file"`
|
||||
}
|
||||
@@ -32,6 +32,7 @@ type AppContainerConfig struct {
|
||||
EditCompose bool `json:"editCompose"`
|
||||
DockerCompose string `json:"dockerCompose"`
|
||||
HostMode bool `json:"hostMode"`
|
||||
PullImage bool `json:"pullImage"`
|
||||
}
|
||||
|
||||
type AppInstalledSearch struct {
|
||||
@@ -67,6 +68,7 @@ type AppInstalledOperate struct {
|
||||
DeleteBackup bool `json:"deleteBackup"`
|
||||
DeleteDB bool `json:"deleteDB"`
|
||||
Backup bool `json:"backup"`
|
||||
PullImage bool `json:"pullImage"`
|
||||
}
|
||||
|
||||
type AppInstalledUpdate struct {
|
||||
|
||||
@@ -22,7 +22,7 @@ type FileCreate struct {
|
||||
Path string `json:"path" validate:"required"`
|
||||
Content string `json:"content"`
|
||||
IsDir bool `json:"isDir"`
|
||||
Mode int64 `json:"mode" validate:"required"`
|
||||
Mode int64 `json:"mode"`
|
||||
IsLink bool `json:"isLink"`
|
||||
IsSymlink bool `json:"isSymlink"`
|
||||
LinkPath string `json:"linkPath"`
|
||||
@@ -64,7 +64,7 @@ type FileDeCompress struct {
|
||||
|
||||
type FileEdit struct {
|
||||
Path string `json:"path" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type FileRename struct {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package request
|
||||
|
||||
import "github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
|
||||
type PHPExtensionsSearch struct {
|
||||
dto.PageInfo
|
||||
All bool `json:"all"`
|
||||
}
|
||||
|
||||
type PHPExtensionsCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Extensions string `json:"extensions" validate:"required"`
|
||||
}
|
||||
|
||||
type PHPExtensionsUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Extensions string `json:"extensions" validate:"required"`
|
||||
}
|
||||
|
||||
type PHPExtensionsDelete struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
}
|
||||
@@ -69,24 +69,12 @@ type WebsiteOp struct {
|
||||
Operate string `json:"operate"`
|
||||
}
|
||||
|
||||
type WebsiteWafReq struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Key string `json:"key" validate:"required"`
|
||||
Rule string `json:"rule" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteRedirectUpdate struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Key string `json:"key" validate:"required"`
|
||||
Enable bool `json:"enable"`
|
||||
}
|
||||
|
||||
type WebsiteWafUpdate struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Key string `json:"key" validate:"required"`
|
||||
Enable bool `json:"enable"`
|
||||
}
|
||||
|
||||
type WebsiteRecover struct {
|
||||
WebsiteName string `json:"websiteName" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
@@ -190,7 +178,7 @@ 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"`
|
||||
@@ -207,12 +195,12 @@ type WebsiteRedirectReq struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteWafFileUpdate struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=cc ip_white ip_block url_white url_block cookie_block args_check post_check ua_check file_ext_block user_agent"`
|
||||
}
|
||||
|
||||
type WebsiteCommonReq struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
type WafWebsite struct {
|
||||
Key string `json:"key"`
|
||||
Domains []string `json:"domains"`
|
||||
Host []string `json:"host"`
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ type WebsiteSSLRenew struct {
|
||||
}
|
||||
|
||||
type WebsiteSSLApply struct {
|
||||
ID uint `json:"ID" validate:"required"`
|
||||
ID uint `json:"ID" validate:"required"`
|
||||
SkipDNSCheck bool `json:"SkipDNSCheck"`
|
||||
}
|
||||
|
||||
type WebsiteAcmeAccountCreate struct {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
)
|
||||
@@ -72,9 +73,11 @@ type AppInstalledDTO struct {
|
||||
}
|
||||
|
||||
type DatabaseConn struct {
|
||||
Password string `json:"password"`
|
||||
ServiceName string `json:"serviceName"`
|
||||
Port int64 `json:"port"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
ContainerName string `json:"containerName"`
|
||||
ServiceName string `json:"serviceName"`
|
||||
Port int64 `json:"port"`
|
||||
}
|
||||
|
||||
type AppService struct {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package response
|
||||
|
||||
import "github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
|
||||
type PHPExtensionsDTO struct {
|
||||
model.PHPExtensions
|
||||
}
|
||||
@@ -13,6 +13,12 @@ type WebsiteDTO struct {
|
||||
RuntimeName string `json:"runtimeName"`
|
||||
}
|
||||
|
||||
type WebsiteOption struct {
|
||||
ID uint `json:"id"`
|
||||
PrimaryDomain string `json:"primaryDomain"`
|
||||
Alias string `json:"alias"`
|
||||
}
|
||||
|
||||
type WebsitePreInstallCheck struct {
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
@@ -25,11 +31,6 @@ type WebsiteNginxConfig struct {
|
||||
Params []NginxParam `json:"params"`
|
||||
}
|
||||
|
||||
type WebsiteWafConfig struct {
|
||||
Enable bool `json:"enable"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type WebsiteHTTPS struct {
|
||||
Enable bool `json:"enable"`
|
||||
HttpConfig string `json:"httpConfig"`
|
||||
|
||||
+19
-12
@@ -19,6 +19,7 @@ type SettingInfo struct {
|
||||
BindAddress string `json:"bindAddress"`
|
||||
PanelName string `json:"panelName"`
|
||||
Theme string `json:"theme"`
|
||||
MenuTabs string `json:"menuTabs"`
|
||||
Language string `json:"language"`
|
||||
DefaultNetwork string `json:"defaultNetwork"`
|
||||
LastCleanTime string `json:"lastCleanTime"`
|
||||
@@ -52,6 +53,10 @@ type SettingInfo struct {
|
||||
AppStoreSyncStatus string `json:"appStoreSyncStatus"`
|
||||
|
||||
FileRecycleBin string `json:"fileRecycleBin"`
|
||||
|
||||
SnapshotIgnore string `json:"snapshotIgnore"`
|
||||
XpackHideMenu string `json:"xpackHideMenu"`
|
||||
NoAuthSetting string `json:"noAuthSetting"`
|
||||
}
|
||||
|
||||
type SettingUpdate struct {
|
||||
@@ -60,7 +65,7 @@ type SettingUpdate struct {
|
||||
}
|
||||
|
||||
type SSLUpdate struct {
|
||||
SSLType string `json:"sslType" validate:"required,oneof=self select import-paste import-local"`
|
||||
SSLType string `json:"sslType" validate:"required,oneof=self select import import-paste import-local"`
|
||||
Domain string `json:"domain"`
|
||||
SSL string `json:"ssl" validate:"required,oneof=enable disable"`
|
||||
Cert string `json:"cert"`
|
||||
@@ -99,9 +104,10 @@ type SnapshotStatus struct {
|
||||
}
|
||||
|
||||
type SnapshotCreate struct {
|
||||
ID uint `json:"id"`
|
||||
From string `json:"from" validate:"required,oneof=OSS S3 SFTP MINIO COS KODO OneDrive WebDAV"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
ID uint `json:"id"`
|
||||
From string `json:"from" validate:"required"`
|
||||
DefaultDownload string `json:"defaultDownload" validate:"required"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
}
|
||||
type SnapshotRecover struct {
|
||||
IsNew bool `json:"isNew"`
|
||||
@@ -114,14 +120,15 @@ type SnapshotImport struct {
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
}
|
||||
type SnapshotInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
From string `json:"from"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Version string `json:"version"`
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
From string `json:"from"`
|
||||
DefaultDownload string `json:"defaultDownload"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Version string `json:"version"`
|
||||
|
||||
InterruptStep string `json:"interruptStep"`
|
||||
RecoverStatus string `json:"recoverStatus"`
|
||||
|
||||
@@ -12,6 +12,8 @@ type BackupAccount struct {
|
||||
|
||||
type BackupRecord struct {
|
||||
BaseModel
|
||||
From string `gorm:"type:varchar(64)" json:"from"`
|
||||
CronjobID uint `gorm:"type:decimal" json:"cronjobID"`
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
Name string `gorm:"type:varchar(64);not null" json:"name"`
|
||||
DetailName string `gorm:"type:varchar(256)" json:"detailName"`
|
||||
|
||||
@@ -5,32 +5,32 @@ import "time"
|
||||
type Cronjob struct {
|
||||
BaseModel
|
||||
|
||||
Name string `gorm:"type:varchar(64);not null;unique" json:"name"`
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
SpecType string `gorm:"type:varchar(64);not null" json:"specType"`
|
||||
Spec string `gorm:"type:varchar(64);not null" json:"spec"`
|
||||
Week uint64 `gorm:"type:decimal" json:"week"`
|
||||
Day uint64 `gorm:"type:decimal" json:"day"`
|
||||
Hour uint64 `gorm:"type:decimal" json:"hour"`
|
||||
Minute uint64 `gorm:"type:decimal" json:"minute"`
|
||||
Second uint64 `gorm:"type:decimal" json:"second"`
|
||||
Name string `gorm:"type:varchar(64);not null" json:"name"`
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
Spec string `gorm:"type:varchar(64);not null" json:"spec"`
|
||||
|
||||
Command string `gorm:"type:varchar(64)" json:"command"`
|
||||
ContainerName string `gorm:"type:varchar(64)" json:"containerName"`
|
||||
Script string `gorm:"longtext" json:"script"`
|
||||
Website string `gorm:"type:varchar(64)" json:"website"`
|
||||
AppID string `gorm:"type:varchar(64)" json:"appID"`
|
||||
DBType string `gorm:"type:varchar(64)" json:"dbType"`
|
||||
DBName string `gorm:"type:varchar(64)" json:"dbName"`
|
||||
URL string `gorm:"type:varchar(256)" json:"url"`
|
||||
SourceDir string `gorm:"type:varchar(256)" json:"sourceDir"`
|
||||
ExclusionRules string `gorm:"longtext" json:"exclusionRules"`
|
||||
|
||||
KeepLocal bool `gorm:"type:varchar(64)" json:"keepLocal"`
|
||||
TargetDirID uint64 `gorm:"type:decimal" json:"targetDirID"`
|
||||
RetainCopies uint64 `gorm:"type:decimal" json:"retainCopies"`
|
||||
// 已废弃
|
||||
KeepLocal bool `gorm:"type:varchar(64)" json:"keepLocal"`
|
||||
TargetDirID uint64 `gorm:"type:decimal" json:"targetDirID"`
|
||||
|
||||
Status string `gorm:"type:varchar(64)" json:"status"`
|
||||
EntryID uint64 `gorm:"type:decimal" json:"entryID"`
|
||||
Records []JobRecords `json:"records"`
|
||||
BackupAccounts string `gorm:"type:varchar(64)" json:"backupAccounts"`
|
||||
DefaultDownload string `gorm:"type:varchar(64)" json:"defaultDownload"`
|
||||
RetainCopies uint64 `gorm:"type:decimal" json:"retainCopies"`
|
||||
|
||||
Status string `gorm:"type:varchar(64)" json:"status"`
|
||||
EntryIDs string `gorm:"type:varchar(64)" json:"entryIDs"`
|
||||
Records []JobRecords `json:"records"`
|
||||
}
|
||||
|
||||
type JobRecords struct {
|
||||
|
||||
@@ -9,5 +9,6 @@ type DatabaseMysql struct {
|
||||
Username string `json:"username" gorm:"type:varchar(256);not null"`
|
||||
Password string `json:"password" gorm:"type:varchar(256);not null"`
|
||||
Permission string `json:"permission" gorm:"type:varchar(256);not null"`
|
||||
IsDelete bool `json:"isDelete" gorm:"type:varchar(64)"`
|
||||
Description string `json:"description" gorm:"type:varchar(256);"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package model
|
||||
|
||||
type DatabasePostgresql struct {
|
||||
BaseModel
|
||||
Name string `json:"name" gorm:"type:varchar(256);not null"`
|
||||
From string `json:"from" gorm:"type:varchar(256);not null;default:local"`
|
||||
PostgresqlName string `json:"postgresqlName" gorm:"type:varchar(64);not null"`
|
||||
Format string `json:"format" gorm:"type:varchar(64);not null"`
|
||||
Username string `json:"username" gorm:"type:varchar(256);not null"`
|
||||
Password string `json:"password" gorm:"type:varchar(256);not null"`
|
||||
SuperUser bool `json:"superUser" gorm:"type:varchar(64)"`
|
||||
IsDelete bool `json:"isDelete" gorm:"type:varchar(64)"`
|
||||
Description string `json:"description" gorm:"type:varchar(256);"`
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package model
|
||||
|
||||
type PHPExtensions struct {
|
||||
BaseModel
|
||||
Name string ` json:"name" gorm:"not null"`
|
||||
Extensions string `json:"extensions" gorm:"not null"`
|
||||
}
|
||||
@@ -2,12 +2,13 @@ package model
|
||||
|
||||
type Snapshot struct {
|
||||
BaseModel
|
||||
Name string `json:"name" gorm:"type:varchar(64);not null;unique"`
|
||||
Description string `json:"description" gorm:"type:varchar(256)"`
|
||||
From string `json:"from"`
|
||||
Status string `json:"status" gorm:"type:varchar(64)"`
|
||||
Message string `json:"message" gorm:"type:varchar(256)"`
|
||||
Version string `json:"version" gorm:"type:varchar(256)"`
|
||||
Name string `json:"name" gorm:"type:varchar(64);not null;unique"`
|
||||
Description string `json:"description" gorm:"type:varchar(256)"`
|
||||
From string `json:"from"`
|
||||
DefaultDownload string `json:"defaultDownload" gorm:"type:varchar(64)"`
|
||||
Status string `json:"status" gorm:"type:varchar(64)"`
|
||||
Message string `json:"message" gorm:"type:varchar(256)"`
|
||||
Version string `json:"version" gorm:"type:varchar(256)"`
|
||||
|
||||
InterruptStep string `json:"interruptStep" gorm:"type:varchar(64)"`
|
||||
RecoverStatus string `json:"recoverStatus" gorm:"type:varchar(64)"`
|
||||
|
||||
@@ -17,6 +17,7 @@ type IAppRepo interface {
|
||||
OrderByRecommend() DBOption
|
||||
GetRecommend() DBOption
|
||||
WithResource(resource string) DBOption
|
||||
WithLikeName(name string) DBOption
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.App, error)
|
||||
GetFirst(opts ...DBOption) (model.App, error)
|
||||
GetBy(opts ...DBOption) ([]model.App, error)
|
||||
@@ -31,6 +32,15 @@ func NewIAppRepo() IAppRepo {
|
||||
return &AppRepo{}
|
||||
}
|
||||
|
||||
func (a AppRepo) WithLikeName(name string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(name) == 0 {
|
||||
return g
|
||||
}
|
||||
return g.Where("name like ? or short_desc_zh like ? or short_desc_en like ?", "%"+name+"%", "%"+name+"%", "%"+name+"%")
|
||||
}
|
||||
}
|
||||
|
||||
func (a AppRepo) WithKey(key string) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("key = ?", key)
|
||||
|
||||
@@ -3,6 +3,7 @@ package repo
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
|
||||
"gorm.io/gorm/clause"
|
||||
|
||||
@@ -170,6 +171,7 @@ type RootInfo struct {
|
||||
Env string `json:"env"`
|
||||
Key string `json:"key"`
|
||||
Version string `json:"version"`
|
||||
AppPath string `json:"app_path"`
|
||||
}
|
||||
|
||||
func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error) {
|
||||
@@ -205,7 +207,7 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
|
||||
if ok {
|
||||
info.Password = password
|
||||
}
|
||||
case "mongodb", "postgresql":
|
||||
case "mongodb", constant.AppPostgresql:
|
||||
user, ok := envMap["PANEL_DB_ROOT_USER"].(string)
|
||||
if ok {
|
||||
info.UserName = user
|
||||
@@ -230,5 +232,7 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
|
||||
info.Param = appInstall.Param
|
||||
info.Version = appInstall.Version
|
||||
info.Key = app.Key
|
||||
appInstall.App = app
|
||||
info.AppPath = appInstall.GetAppPath()
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"gorm.io/gorm"
|
||||
@@ -23,6 +24,7 @@ type IBackupRepo interface {
|
||||
WithByDetailName(detailName string) DBOption
|
||||
WithByFileName(fileName string) DBOption
|
||||
WithByType(backupType string) DBOption
|
||||
WithByCronID(cronjobID uint) DBOption
|
||||
}
|
||||
|
||||
func NewIBackupRepo() IBackupRepo {
|
||||
@@ -125,3 +127,9 @@ func (u *BackupRepo) Delete(opts ...DBOption) error {
|
||||
func (u *BackupRepo) DeleteRecord(ctx context.Context, opts ...DBOption) error {
|
||||
return getTx(ctx, opts...).Delete(&model.BackupRecord{}).Error
|
||||
}
|
||||
|
||||
func (u *BackupRepo) WithByCronID(cronjobID uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("cronjob_id = ?", cronjobID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ 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
|
||||
}
|
||||
|
||||
func NewICommandRepo() ICommandRepo {
|
||||
@@ -79,3 +80,12 @@ func (u *CommandRepo) Delete(opts ...DBOption) error {
|
||||
}
|
||||
return db.Delete(&model.Command{}).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+"%")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ type ICronjobRepo interface {
|
||||
Page(limit, offset int, opts ...DBOption) (int64, []model.Cronjob, error)
|
||||
Create(cronjob *model.Cronjob) error
|
||||
WithByJobID(id int) DBOption
|
||||
WithByBackupID(id uint) DBOption
|
||||
WithByDbName(name string) DBOption
|
||||
WithByDefaultDownload(account string) DBOption
|
||||
WithByRecordDropID(id int) DBOption
|
||||
WithByRecordFile(file string) DBOption
|
||||
Save(id uint, cronjob model.Cronjob) error
|
||||
@@ -28,6 +29,7 @@ type ICronjobRepo interface {
|
||||
Delete(opts ...DBOption) error
|
||||
DeleteRecord(opts ...DBOption) error
|
||||
StartRecords(cronjobID uint, fromLocal bool, targetPath string) model.JobRecords
|
||||
UpdateRecords(id uint, vars map[string]interface{}) error
|
||||
EndRecords(record model.JobRecords, status, message, records string)
|
||||
PageRecords(page, size int, opts ...DBOption) (int64, []model.JobRecords, error)
|
||||
}
|
||||
@@ -116,9 +118,15 @@ func (c *CronjobRepo) WithByJobID(id int) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CronjobRepo) WithByBackupID(id uint) DBOption {
|
||||
func (c *CronjobRepo) WithByDbName(name string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("target_dir_id = ?", id)
|
||||
return g.Where("db_name = ?", name)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CronjobRepo) WithByDefaultDownload(account string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("default_download = ?", account)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +172,10 @@ func (u *CronjobRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.Cronjob{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
|
||||
func (u *CronjobRepo) UpdateRecords(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.JobRecords{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
|
||||
func (u *CronjobRepo) Delete(opts ...DBOption) error {
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type PostgresqlRepo struct{}
|
||||
|
||||
type IPostgresqlRepo interface {
|
||||
Get(opts ...DBOption) (model.DatabasePostgresql, error)
|
||||
WithByPostgresqlName(postgresqlName string) DBOption
|
||||
WithByFrom(from string) DBOption
|
||||
List(opts ...DBOption) ([]model.DatabasePostgresql, error)
|
||||
Page(limit, offset int, opts ...DBOption) (int64, []model.DatabasePostgresql, error)
|
||||
Create(ctx context.Context, postgresql *model.DatabasePostgresql) error
|
||||
Delete(ctx context.Context, opts ...DBOption) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
DeleteLocal(ctx context.Context) error
|
||||
}
|
||||
|
||||
func NewIPostgresqlRepo() IPostgresqlRepo {
|
||||
return &PostgresqlRepo{}
|
||||
}
|
||||
|
||||
func (u *PostgresqlRepo) Get(opts ...DBOption) (model.DatabasePostgresql, error) {
|
||||
var postgresql model.DatabasePostgresql
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
if err := db.First(&postgresql).Error; err != nil {
|
||||
return postgresql, err
|
||||
}
|
||||
|
||||
pass, err := encrypt.StringDecrypt(postgresql.Password)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("decrypt database db %s password failed, err: %v", postgresql.Name, err)
|
||||
}
|
||||
postgresql.Password = pass
|
||||
return postgresql, err
|
||||
}
|
||||
|
||||
func (u *PostgresqlRepo) List(opts ...DBOption) ([]model.DatabasePostgresql, error) {
|
||||
var postgresqls []model.DatabasePostgresql
|
||||
db := global.DB.Model(&model.DatabasePostgresql{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
if err := db.Find(&postgresqls).Error; err != nil {
|
||||
return postgresqls, err
|
||||
}
|
||||
for i := 0; i < len(postgresqls); i++ {
|
||||
pass, err := encrypt.StringDecrypt(postgresqls[i].Password)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("decrypt database db %s password failed, err: %v", postgresqls[i].Name, err)
|
||||
}
|
||||
postgresqls[i].Password = pass
|
||||
}
|
||||
return postgresqls, nil
|
||||
}
|
||||
|
||||
func (u *PostgresqlRepo) Page(page, size int, opts ...DBOption) (int64, []model.DatabasePostgresql, error) {
|
||||
var postgresqls []model.DatabasePostgresql
|
||||
db := global.DB.Model(&model.DatabasePostgresql{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
if err := db.Limit(size).Offset(size * (page - 1)).Find(&postgresqls).Error; err != nil {
|
||||
return count, postgresqls, err
|
||||
}
|
||||
for i := 0; i < len(postgresqls); i++ {
|
||||
pass, err := encrypt.StringDecrypt(postgresqls[i].Password)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("decrypt database db %s password failed, err: %v", postgresqls[i].Name, err)
|
||||
}
|
||||
postgresqls[i].Password = pass
|
||||
}
|
||||
return count, postgresqls, nil
|
||||
}
|
||||
|
||||
func (u *PostgresqlRepo) Create(ctx context.Context, postgresql *model.DatabasePostgresql) error {
|
||||
pass, err := encrypt.StringEncrypt(postgresql.Password)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decrypt database db %s password failed, err: %v", postgresql.Name, err)
|
||||
}
|
||||
postgresql.Password = pass
|
||||
return getTx(ctx).Create(postgresql).Error
|
||||
}
|
||||
|
||||
func (u *PostgresqlRepo) Delete(ctx context.Context, opts ...DBOption) error {
|
||||
return getTx(ctx, opts...).Delete(&model.DatabasePostgresql{}).Error
|
||||
}
|
||||
|
||||
func (u *PostgresqlRepo) DeleteLocal(ctx context.Context) error {
|
||||
return getTx(ctx).Where("`from` = ?", "local").Delete(&model.DatabasePostgresql{}).Error
|
||||
}
|
||||
|
||||
func (u *PostgresqlRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.DatabasePostgresql{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
|
||||
func (u *PostgresqlRepo) WithByPostgresqlName(postgresqlName string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("postgresql_name = ?", postgresqlName)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *PostgresqlRepo) WithByFrom(from string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(from) != 0 {
|
||||
return g.Where("`from` = ?", from)
|
||||
}
|
||||
return g
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
)
|
||||
|
||||
type PHPExtensionsRepo struct {
|
||||
}
|
||||
|
||||
type IPHPExtensionsRepo interface {
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.PHPExtensions, error)
|
||||
Save(extension *model.PHPExtensions) error
|
||||
Create(extension *model.PHPExtensions) error
|
||||
GetFirst(opts ...DBOption) (model.PHPExtensions, error)
|
||||
DeleteBy(opts ...DBOption) error
|
||||
List() ([]model.PHPExtensions, error)
|
||||
}
|
||||
|
||||
func NewIPHPExtensionsRepo() IPHPExtensionsRepo {
|
||||
return &PHPExtensionsRepo{}
|
||||
}
|
||||
|
||||
func (p *PHPExtensionsRepo) Page(page, size int, opts ...DBOption) (int64, []model.PHPExtensions, error) {
|
||||
var (
|
||||
phpExtensions []model.PHPExtensions
|
||||
)
|
||||
db := getDb(opts...).Model(&model.PHPExtensions{})
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&phpExtensions).Error
|
||||
return count, phpExtensions, err
|
||||
}
|
||||
|
||||
func (p *PHPExtensionsRepo) List() ([]model.PHPExtensions, error) {
|
||||
var (
|
||||
phpExtensions []model.PHPExtensions
|
||||
)
|
||||
err := getDb().Model(&model.PHPExtensions{}).Find(&phpExtensions).Error
|
||||
return phpExtensions, err
|
||||
}
|
||||
|
||||
func (p *PHPExtensionsRepo) Save(extension *model.PHPExtensions) error {
|
||||
return getDb().Save(&extension).Error
|
||||
}
|
||||
|
||||
func (p *PHPExtensionsRepo) Create(extension *model.PHPExtensions) error {
|
||||
return getDb().Create(&extension).Error
|
||||
}
|
||||
|
||||
func (p *PHPExtensionsRepo) GetFirst(opts ...DBOption) (model.PHPExtensions, error) {
|
||||
var extension model.PHPExtensions
|
||||
db := getDb(opts...).Model(&model.PHPExtensions{})
|
||||
err := db.First(&extension).Error
|
||||
return extension, err
|
||||
}
|
||||
|
||||
func (p *PHPExtensionsRepo) DeleteBy(opts ...DBOption) error {
|
||||
return getDb(opts...).Delete(&model.PHPExtensions{}).Error
|
||||
}
|
||||
@@ -68,20 +68,20 @@ func (u *SettingRepo) Update(key, value string) error {
|
||||
}
|
||||
|
||||
func (u *SettingRepo) CreateMonitorBase(model model.MonitorBase) error {
|
||||
return global.DB.Create(&model).Error
|
||||
return global.MonitorDB.Create(&model).Error
|
||||
}
|
||||
func (u *SettingRepo) BatchCreateMonitorIO(ioList []model.MonitorIO) error {
|
||||
return global.DB.CreateInBatches(ioList, len(ioList)).Error
|
||||
return global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error
|
||||
}
|
||||
func (u *SettingRepo) BatchCreateMonitorNet(ioList []model.MonitorNetwork) error {
|
||||
return global.DB.CreateInBatches(ioList, len(ioList)).Error
|
||||
return global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error
|
||||
}
|
||||
func (u *SettingRepo) DelMonitorBase(timeForDelete time.Time) error {
|
||||
return global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorBase{}).Error
|
||||
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorBase{}).Error
|
||||
}
|
||||
func (u *SettingRepo) DelMonitorIO(timeForDelete time.Time) error {
|
||||
return global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorIO{}).Error
|
||||
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorIO{}).Error
|
||||
}
|
||||
func (u *SettingRepo) DelMonitorNet(timeForDelete time.Time) error {
|
||||
return global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error
|
||||
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ type IWebsiteRepo interface {
|
||||
WithDefaultServer() DBOption
|
||||
WithDomainLike(domain string) DBOption
|
||||
WithRuntimeID(runtimeID uint) DBOption
|
||||
WithIDs(ids []uint) DBOption
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.Website, error)
|
||||
List(opts ...DBOption) ([]model.Website, error)
|
||||
GetFirst(opts ...DBOption) (model.Website, error)
|
||||
@@ -36,9 +37,15 @@ func NewIWebsiteRepo() IWebsiteRepo {
|
||||
type WebsiteRepo struct {
|
||||
}
|
||||
|
||||
func (w *WebsiteRepo) WithAppInstallId(appInstallId uint) DBOption {
|
||||
func (w *WebsiteRepo) WithAppInstallId(appInstallID uint) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("app_install_id = ?", appInstallId)
|
||||
return db.Where("app_install_id = ?", appInstallID)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WebsiteRepo) WithIDs(ids []uint) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("id in (?)", ids)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ type IWebsiteDomainRepo interface {
|
||||
WithWebsiteId(websiteId uint) DBOption
|
||||
WithPort(port int) DBOption
|
||||
WithDomain(domain string) DBOption
|
||||
WithDomainLike(domain string) DBOption
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.WebsiteDomain, error)
|
||||
GetFirst(opts ...DBOption) (model.WebsiteDomain, error)
|
||||
GetBy(opts ...DBOption) ([]model.WebsiteDomain, error)
|
||||
@@ -44,6 +45,11 @@ func (w WebsiteDomainRepo) WithDomain(domain string) DBOption {
|
||||
return db.Where("domain = ?", domain)
|
||||
}
|
||||
}
|
||||
func (w WebsiteDomainRepo) WithDomainLike(domain string) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("domain like ?", "%"+domain+"%")
|
||||
}
|
||||
}
|
||||
func (w WebsiteDomainRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteDomain, error) {
|
||||
var domains []model.WebsiteDomain
|
||||
db := getDb(opts...).Model(&model.WebsiteDomain{})
|
||||
|
||||
@@ -53,7 +53,7 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
|
||||
var opts []repo.DBOption
|
||||
opts = append(opts, appRepo.OrderByRecommend())
|
||||
if req.Name != "" {
|
||||
opts = append(opts, commonRepo.WithLikeName(req.Name))
|
||||
opts = append(opts, appRepo.WithLikeName(req.Name))
|
||||
}
|
||||
if req.Type != "" {
|
||||
opts = append(opts, appRepo.WithType(req.Type))
|
||||
@@ -397,7 +397,7 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
||||
}
|
||||
index++
|
||||
}
|
||||
if app.Limit == 0 && appInstall.Name != serviceName {
|
||||
if app.Limit == 0 && appInstall.Name != serviceName && len(servicesMap) == 1 {
|
||||
servicesMap[appInstall.Name] = servicesMap[serviceName]
|
||||
delete(servicesMap, serviceName)
|
||||
serviceName = appInstall.Name
|
||||
@@ -459,7 +459,7 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
||||
if err = runScript(appInstall, "init"); err != nil {
|
||||
return
|
||||
}
|
||||
upApp(appInstall)
|
||||
upApp(appInstall, req.PullImage)
|
||||
}()
|
||||
go updateToolApp(appInstall)
|
||||
return
|
||||
|
||||
@@ -12,8 +12,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
@@ -152,7 +150,7 @@ func (a *AppInstallService) CheckExist(req request.AppInstalledInfo) (*response.
|
||||
if reflect.DeepEqual(appInstall, model.AppInstall{}) {
|
||||
return res, nil
|
||||
}
|
||||
if err = syncByID(appInstall.ID); err != nil {
|
||||
if err = syncAppInstallStatus(&appInstall); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -184,9 +182,11 @@ func (a *AppInstallService) LoadConnInfo(req dto.OperationWithNameAndType) (resp
|
||||
if err != nil {
|
||||
return data, nil
|
||||
}
|
||||
data.Username = app.UserName
|
||||
data.Password = app.Password
|
||||
data.ServiceName = app.ServiceName
|
||||
data.Port = app.Port
|
||||
data.ContainerName = app.ContainerName
|
||||
return data, nil
|
||||
}
|
||||
|
||||
@@ -240,28 +240,28 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
|
||||
if err != nil {
|
||||
return handleErr(install, err, out)
|
||||
}
|
||||
return syncByID(install.ID)
|
||||
return syncAppInstallStatus(&install)
|
||||
case constant.Stop:
|
||||
out, err := compose.Stop(dockerComposePath)
|
||||
if err != nil {
|
||||
return handleErr(install, err, out)
|
||||
}
|
||||
return syncByID(install.ID)
|
||||
return syncAppInstallStatus(&install)
|
||||
case constant.Restart:
|
||||
out, err := compose.Restart(dockerComposePath)
|
||||
if err != nil {
|
||||
return handleErr(install, err, out)
|
||||
}
|
||||
return syncByID(install.ID)
|
||||
return syncAppInstallStatus(&install)
|
||||
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 syncByID(install.ID)
|
||||
return syncAppInstallStatus(&install)
|
||||
case constant.Upgrade:
|
||||
return upgradeInstall(install.ID, req.DetailId, req.Backup)
|
||||
return upgradeInstall(install.ID, req.DetailId, req.Backup, req.PullImage)
|
||||
case constant.Reload:
|
||||
return opNginx(install.ContainerName, constant.NginxReload)
|
||||
default:
|
||||
@@ -411,7 +411,7 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
|
||||
return err
|
||||
}
|
||||
for _, i := range allList {
|
||||
if i.Status == constant.Installing || i.Status == constant.Upgrading {
|
||||
if i.Status == constant.Installing || i.Status == constant.Upgrading || i.Status == constant.Rebuilding {
|
||||
if systemInit {
|
||||
i.Status = constant.Error
|
||||
i.Message = "1Panel restart causes the task to terminate"
|
||||
@@ -420,7 +420,7 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
|
||||
continue
|
||||
}
|
||||
if !systemInit {
|
||||
if err := syncByID(i.ID); err != nil {
|
||||
if err = syncAppInstallStatus(&i); err != nil {
|
||||
global.LOG.Errorf("sync install app[%s] error,mgs: %s", i.Name, err.Error())
|
||||
}
|
||||
}
|
||||
@@ -431,6 +431,9 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
|
||||
func (a *AppInstallService) GetServices(key string) ([]response.AppService, error) {
|
||||
var res []response.AppService
|
||||
if DatabaseKeys[key] > 0 {
|
||||
if key == constant.AppPostgres {
|
||||
key = constant.AppPostgresql
|
||||
}
|
||||
dbs, _ := databaseRepo.GetList(commonRepo.WithByType(key))
|
||||
if len(dbs) == 0 {
|
||||
return res, nil
|
||||
@@ -524,7 +527,7 @@ func (a *AppInstallService) ChangeAppPort(req request.PortUpdate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := updateInstallInfoInDB(req.Key, req.Name, "port", true, strconv.FormatInt(req.Port, 10)); err != nil {
|
||||
if err := updateInstallInfoInDB(req.Key, req.Name, "port", strconv.FormatInt(req.Port, 10)); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -636,10 +639,11 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
||||
for _, form := range appForm.FormFields {
|
||||
if v, ok := envs[form.EnvKey]; ok {
|
||||
appParam := response.AppParam{
|
||||
Edit: false,
|
||||
Key: form.EnvKey,
|
||||
Rule: form.Rule,
|
||||
Type: form.Type,
|
||||
Edit: false,
|
||||
Key: form.EnvKey,
|
||||
Rule: form.Rule,
|
||||
Type: form.Type,
|
||||
Multiple: form.Multiple,
|
||||
}
|
||||
if form.Edit {
|
||||
appParam.Edit = true
|
||||
@@ -651,10 +655,20 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
||||
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithServiceName(v.(string)))
|
||||
appParam.ShowValue = appInstall.Name
|
||||
} else if form.Type == "select" {
|
||||
for _, fv := range form.Values {
|
||||
if fv.Value == v {
|
||||
appParam.ShowValue = fv.Label
|
||||
break
|
||||
if form.Multiple {
|
||||
if v == "" {
|
||||
appParam.Value = []string{}
|
||||
} else {
|
||||
if str, ok := v.(string); ok {
|
||||
appParam.Value = strings.Split(str, ",")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, fv := range form.Values {
|
||||
if fv.Value == v {
|
||||
appParam.ShowValue = fv.Label
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
appParam.Values = form.Values
|
||||
@@ -662,14 +676,15 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
||||
params = append(params, appParam)
|
||||
} else {
|
||||
params = append(params, response.AppParam{
|
||||
Edit: form.Edit,
|
||||
Key: form.EnvKey,
|
||||
Rule: form.Rule,
|
||||
Type: form.Type,
|
||||
LabelZh: form.LabelZh,
|
||||
LabelEn: form.LabelEn,
|
||||
Value: form.Default,
|
||||
Values: form.Values,
|
||||
Edit: form.Edit,
|
||||
Key: form.EnvKey,
|
||||
Rule: form.Rule,
|
||||
Type: form.Type,
|
||||
LabelZh: form.LabelZh,
|
||||
LabelEn: form.LabelEn,
|
||||
Value: form.Default,
|
||||
Values: form.Values,
|
||||
Multiple: form.Multiple,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -685,15 +700,11 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func syncByID(installID uint) error {
|
||||
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(installID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if appInstall.Status == constant.Installing {
|
||||
func syncAppInstallStatus(appInstall *model.AppInstall) error {
|
||||
if appInstall.Status == constant.Installing || appInstall.Status == constant.Rebuilding || appInstall.Status == constant.Upgrading {
|
||||
return nil
|
||||
}
|
||||
containerNames, err := getContainerNames(appInstall)
|
||||
containerNames, err := getContainerNames(*appInstall)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -705,75 +716,69 @@ func syncByID(installID uint) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
errorContainers []string
|
||||
notFoundContainers []string
|
||||
runningContainers []string
|
||||
exitedContainers []string
|
||||
runningCount int
|
||||
exitedCount int
|
||||
pausedCount int
|
||||
exitedContainerNames []string
|
||||
total = len(containerNames)
|
||||
count = len(containers)
|
||||
)
|
||||
|
||||
for _, n := range containers {
|
||||
switch n.State {
|
||||
foundNames := make(map[string]bool)
|
||||
for _, con := range containers {
|
||||
foundNames[con.Names[0]] = true
|
||||
switch con.State {
|
||||
case "exited":
|
||||
exitedContainers = append(exitedContainers, n.Names[0])
|
||||
exitedContainerNames = append(exitedContainerNames, strings.TrimPrefix(con.Names[0], "/"))
|
||||
exitedCount++
|
||||
case "running":
|
||||
runningContainers = append(runningContainers, n.Names[0])
|
||||
default:
|
||||
errorContainers = append(errorContainers, n.Names[0])
|
||||
runningCount++
|
||||
case "paused":
|
||||
pausedCount++
|
||||
}
|
||||
}
|
||||
|
||||
var notFoundNames []string
|
||||
for _, name := range containerNames {
|
||||
exist := false
|
||||
for _, container := range containers {
|
||||
if common.ExistWithStrArray(name, container.Names) {
|
||||
exist = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exist {
|
||||
notFoundContainers = append(notFoundContainers, name)
|
||||
if !foundNames["/"+name] {
|
||||
notFoundNames = append(notFoundNames, name)
|
||||
}
|
||||
}
|
||||
|
||||
containerCount := len(containers)
|
||||
errCount := len(errorContainers)
|
||||
notFoundCount := len(notFoundContainers)
|
||||
existedCount := len(exitedContainers)
|
||||
normalCount := len(containerNames)
|
||||
runningCount := len(runningContainers)
|
||||
|
||||
if containerCount == 0 {
|
||||
appInstall.Status = constant.Error
|
||||
appInstall.Message = i18n.GetMsgWithMap("ErrContainerNotFound", map[string]interface{}{"name": strings.Join(containerNames, ",")})
|
||||
return appInstallRepo.Save(context.Background(), &appInstall)
|
||||
}
|
||||
if errCount == 0 && existedCount == 0 && notFoundCount == 0 {
|
||||
appInstall.Status = constant.Running
|
||||
return appInstallRepo.Save(context.Background(), &appInstall)
|
||||
}
|
||||
if existedCount == normalCount {
|
||||
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
|
||||
return appInstallRepo.Save(context.Background(), &appInstall)
|
||||
}
|
||||
if errCount == normalCount {
|
||||
appInstall.Status = constant.Error
|
||||
}
|
||||
if runningCount < normalCount {
|
||||
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)
|
||||
|
||||
var errMsg string
|
||||
if errCount > 0 {
|
||||
errMsg += i18n.GetMsgWithMap("ErrContainerMsg", map[string]interface{}{"name": strings.Join(errorContainers, ",")})
|
||||
}
|
||||
if notFoundCount > 0 {
|
||||
errMsg += i18n.GetMsgWithMap("ErrContainerNotFound", map[string]interface{}{"name": strings.Join(notFoundContainers, ",")})
|
||||
}
|
||||
appInstall.Message = errMsg
|
||||
return appInstallRepo.Save(context.Background(), &appInstall)
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateInstallInfoInDB(appKey, appName, param string, isRestart bool, value interface{}) error {
|
||||
func updateInstallInfoInDB(appKey, appName, param string, value interface{}) error {
|
||||
if param != "password" && param != "port" && param != "user-password" {
|
||||
return nil
|
||||
}
|
||||
@@ -781,7 +786,7 @@ func updateInstallInfoInDB(appKey, appName, param string, isRestart bool, value
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
envPath := fmt.Sprintf("%s/%s/%s/.env", constant.AppInstallDir, appKey, appInstall.Name)
|
||||
envPath := fmt.Sprintf("%s/%s/.env", appInstall.AppPath, appInstall.Name)
|
||||
lineBytes, err := os.ReadFile(envPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -790,7 +795,7 @@ func updateInstallInfoInDB(appKey, appName, param string, isRestart bool, value
|
||||
envKey := ""
|
||||
switch param {
|
||||
case "password":
|
||||
if appKey == "mysql" || appKey == "mariadb" {
|
||||
if appKey == "mysql" || appKey == "mariadb" || appKey == "postgresql" {
|
||||
envKey = "PANEL_DB_ROOT_PASSWORD="
|
||||
} else {
|
||||
envKey = "PANEL_REDIS_ROOT_PASSWORD="
|
||||
|
||||
+213
-103
@@ -5,6 +5,8 @@ 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"
|
||||
@@ -37,7 +39,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
composeV2 "github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -110,12 +111,13 @@ func checkPortExist(port int) error {
|
||||
}
|
||||
|
||||
var DatabaseKeys = map[string]uint{
|
||||
"mysql": 3306,
|
||||
"mariadb": 3306,
|
||||
"postgresql": 5432,
|
||||
"mongodb": 27017,
|
||||
"redis": 6379,
|
||||
"memcached": 11211,
|
||||
constant.AppMysql: 3306,
|
||||
constant.AppMariaDB: 3306,
|
||||
constant.AppPostgresql: 5432,
|
||||
constant.AppPostgres: 5432,
|
||||
constant.AppMongodb: 27017,
|
||||
constant.AppRedis: 6379,
|
||||
constant.AppMemcached: 11211,
|
||||
}
|
||||
|
||||
var ToolKeys = map[string]uint{
|
||||
@@ -154,7 +156,7 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
|
||||
}
|
||||
|
||||
switch app.Key {
|
||||
case "mysql", "mariadb", "postgresql", "mongodb":
|
||||
case constant.AppMysql, constant.AppMariaDB, constant.AppPostgresql, constant.AppMongodb:
|
||||
if password, ok := params["PANEL_DB_ROOT_PASSWORD"]; ok {
|
||||
if password != "" {
|
||||
database.Password = password.(string)
|
||||
@@ -176,7 +178,7 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
|
||||
|
||||
}
|
||||
}
|
||||
case "redis":
|
||||
case constant.AppRedis:
|
||||
if password, ok := params["PANEL_REDIS_ROOT_PASSWORD"]; ok {
|
||||
if password != "" {
|
||||
authParam := dto.RedisAuthParam{
|
||||
@@ -233,28 +235,55 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
|
||||
}
|
||||
var resourceId uint
|
||||
if dbConfig.DbName != "" && dbConfig.DbUser != "" && dbConfig.Password != "" {
|
||||
iMysqlRepo := repo.NewIMysqlRepo()
|
||||
oldMysqlDb, _ := iMysqlRepo.Get(commonRepo.WithByName(dbConfig.DbName), iMysqlRepo.WithByFrom(constant.ResourceLocal))
|
||||
resourceId = oldMysqlDb.ID
|
||||
if oldMysqlDb.ID > 0 {
|
||||
if oldMysqlDb.Username != dbConfig.DbUser || oldMysqlDb.Password != dbConfig.Password {
|
||||
return buserr.New(constant.ErrDbUserNotValid)
|
||||
switch database.Type {
|
||||
case constant.AppPostgresql, constant.AppPostgres:
|
||||
iPostgresqlRepo := repo.NewIPostgresqlRepo()
|
||||
oldPostgresqlDb, _ := iPostgresqlRepo.Get(commonRepo.WithByName(dbConfig.DbName), iPostgresqlRepo.WithByFrom(constant.ResourceLocal))
|
||||
resourceId = oldPostgresqlDb.ID
|
||||
if oldPostgresqlDb.ID > 0 {
|
||||
if oldPostgresqlDb.Username != dbConfig.DbUser || oldPostgresqlDb.Password != dbConfig.Password {
|
||||
return buserr.New(constant.ErrDbUserNotValid)
|
||||
}
|
||||
} else {
|
||||
var createPostgresql dto.PostgresqlDBCreate
|
||||
createPostgresql.Name = dbConfig.DbName
|
||||
createPostgresql.Username = dbConfig.DbUser
|
||||
createPostgresql.Database = database.Name
|
||||
createPostgresql.Format = "UTF8"
|
||||
createPostgresql.Password = dbConfig.Password
|
||||
createPostgresql.From = database.From
|
||||
createPostgresql.SuperUser = true
|
||||
pgdb, err := NewIPostgresqlService().Create(ctx, createPostgresql)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resourceId = pgdb.ID
|
||||
}
|
||||
} else {
|
||||
var createMysql dto.MysqlDBCreate
|
||||
createMysql.Name = dbConfig.DbName
|
||||
createMysql.Username = dbConfig.DbUser
|
||||
createMysql.Database = database.Name
|
||||
createMysql.Format = "utf8mb4"
|
||||
createMysql.Permission = "%"
|
||||
createMysql.Password = dbConfig.Password
|
||||
createMysql.From = database.From
|
||||
mysqldb, err := NewIMysqlService().Create(ctx, createMysql)
|
||||
if err != nil {
|
||||
return err
|
||||
case constant.AppMysql, constant.AppMariaDB:
|
||||
iMysqlRepo := repo.NewIMysqlRepo()
|
||||
oldMysqlDb, _ := iMysqlRepo.Get(commonRepo.WithByName(dbConfig.DbName), iMysqlRepo.WithByFrom(constant.ResourceLocal))
|
||||
resourceId = oldMysqlDb.ID
|
||||
if oldMysqlDb.ID > 0 {
|
||||
if oldMysqlDb.Username != dbConfig.DbUser || oldMysqlDb.Password != dbConfig.Password {
|
||||
return buserr.New(constant.ErrDbUserNotValid)
|
||||
}
|
||||
} else {
|
||||
var createMysql dto.MysqlDBCreate
|
||||
createMysql.Name = dbConfig.DbName
|
||||
createMysql.Username = dbConfig.DbUser
|
||||
createMysql.Database = database.Name
|
||||
createMysql.Format = "utf8mb4"
|
||||
createMysql.Permission = "%"
|
||||
createMysql.Password = dbConfig.Password
|
||||
createMysql.From = database.From
|
||||
mysqldb, err := NewIMysqlService().Create(ctx, createMysql)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resourceId = mysqldb.ID
|
||||
}
|
||||
resourceId = mysqldb.ID
|
||||
}
|
||||
|
||||
}
|
||||
var installResource model.AppInstallResource
|
||||
installResource.ResourceId = resourceId
|
||||
@@ -326,8 +355,8 @@ func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete b
|
||||
websiteApp, _ := appRepo.GetFirst(commonRepo.WithByID(websiteAppInstall.AppId))
|
||||
if websiteApp.Type == constant.RuntimePHP {
|
||||
go func() {
|
||||
_, _ = compose.Down(install.GetComposePath())
|
||||
_ = op.DeleteDir(install.GetPath())
|
||||
_, _ = compose.Down(websiteAppInstall.GetComposePath())
|
||||
_ = op.DeleteDir(websiteAppInstall.GetPath())
|
||||
}()
|
||||
_ = appInstallRepo.Delete(ctx, websiteAppInstall)
|
||||
}
|
||||
@@ -336,8 +365,11 @@ func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete b
|
||||
}
|
||||
_ = websiteRepo.DeleteAll(ctx)
|
||||
_ = websiteDomainRepo.DeleteAll(ctx)
|
||||
xpack.RemoveTamper("")
|
||||
case constant.AppMysql, constant.AppMariaDB:
|
||||
_ = mysqlRepo.Delete(ctx, mysqlRepo.WithByMysqlName(install.Name))
|
||||
case constant.AppPostgresql:
|
||||
_ = postgresqlRepo.Delete(ctx, postgresqlRepo.WithByPostgresqlName(install.Name))
|
||||
}
|
||||
|
||||
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType("app"), commonRepo.WithByName(install.App.Key), backupRepo.WithByDetailName(install.Name))
|
||||
@@ -364,32 +396,51 @@ func deleteLink(ctx context.Context, install *model.AppInstall, deleteDB bool, f
|
||||
return nil
|
||||
}
|
||||
for _, re := range resources {
|
||||
mysqlService := NewIMysqlService()
|
||||
if (re.Key == constant.AppMysql || re.Key == constant.AppMariaDB) && deleteDB {
|
||||
database, _ := mysqlRepo.Get(commonRepo.WithByID(re.ResourceId))
|
||||
if reflect.DeepEqual(database, model.DatabaseMysql{}) {
|
||||
continue
|
||||
}
|
||||
if err := mysqlService.Delete(ctx, dto.MysqlDBDelete{
|
||||
ID: database.ID,
|
||||
ForceDelete: forceDelete,
|
||||
DeleteBackup: deleteBackup,
|
||||
Type: re.Key,
|
||||
Database: database.MysqlName,
|
||||
}); err != nil && !forceDelete {
|
||||
return err
|
||||
if deleteDB {
|
||||
switch re.Key {
|
||||
case constant.AppMysql, constant.AppMariaDB:
|
||||
mysqlService := NewIMysqlService()
|
||||
database, _ := mysqlRepo.Get(commonRepo.WithByID(re.ResourceId))
|
||||
if reflect.DeepEqual(database, model.DatabaseMysql{}) {
|
||||
continue
|
||||
}
|
||||
if err := mysqlService.Delete(ctx, dto.MysqlDBDelete{
|
||||
ID: database.ID,
|
||||
ForceDelete: forceDelete,
|
||||
DeleteBackup: deleteBackup,
|
||||
Type: re.Key,
|
||||
Database: database.MysqlName,
|
||||
}); err != nil && !forceDelete {
|
||||
return err
|
||||
}
|
||||
case constant.AppPostgresql:
|
||||
pgsqlService := NewIPostgresqlService()
|
||||
database, _ := postgresqlRepo.Get(commonRepo.WithByID(re.ResourceId))
|
||||
if reflect.DeepEqual(database, model.DatabasePostgresql{}) {
|
||||
continue
|
||||
}
|
||||
if err := pgsqlService.Delete(ctx, dto.PostgresqlDBDelete{
|
||||
ID: database.ID,
|
||||
ForceDelete: forceDelete,
|
||||
DeleteBackup: deleteBackup,
|
||||
Type: re.Key,
|
||||
Database: database.PostgresqlName,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||
}
|
||||
|
||||
func upgradeInstall(installId uint, detailId uint, backup bool) error {
|
||||
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId))
|
||||
func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error {
|
||||
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
detail, err := appDetailRepo.GetFirst(commonRepo.WithByID(detailId))
|
||||
detail, err := appDetailRepo.GetFirst(commonRepo.WithByID(detailID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -443,23 +494,6 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
|
||||
detailDir = path.Join(constant.ResourceDir, "apps", "local", strings.TrimPrefix(install.App.Key, "local"), detail.Version)
|
||||
}
|
||||
|
||||
images, err := composeV2.GetDockerComposeImages([]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
|
||||
}
|
||||
}
|
||||
|
||||
command := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rn %s/* %s || true", detailDir, install.GetPath()))
|
||||
stdout, _ := command.CombinedOutput()
|
||||
if stdout != nil {
|
||||
@@ -524,7 +558,7 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
|
||||
|
||||
install.DockerCompose = string(composeByte)
|
||||
install.Version = detail.Version
|
||||
install.AppDetailId = detailId
|
||||
install.AppDetailId = detailID
|
||||
|
||||
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
@@ -543,6 +577,31 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
|
||||
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
|
||||
}
|
||||
@@ -630,8 +689,14 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
|
||||
envParams[k] = strconv.Itoa(int(t))
|
||||
case int:
|
||||
envParams[k] = strconv.Itoa(t)
|
||||
default:
|
||||
envParams[k] = t.(string)
|
||||
case []interface{}:
|
||||
strArray := make([]string, len(t))
|
||||
for i := range t {
|
||||
strArray[i] = strings.ToLower(fmt.Sprintf("%v", t[i]))
|
||||
}
|
||||
envParams[k] = strings.Join(strArray, ",")
|
||||
case map[string]interface{}:
|
||||
handleMap(t, envParams)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -764,7 +829,7 @@ func checkContainerNameIsExist(containerName, appDir string) (bool, error) {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
var options dockerTypes.ContainerListOptions
|
||||
var options container.ListOptions
|
||||
list, err := client.ContainerList(context.Background(), options)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -784,39 +849,36 @@ func checkContainerNameIsExist(containerName, appDir string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func upApp(appInstall *model.AppInstall) {
|
||||
func upApp(appInstall *model.AppInstall, pullImages bool) {
|
||||
upProject := func(appInstall *model.AppInstall) (err error) {
|
||||
if err == nil {
|
||||
var (
|
||||
out string
|
||||
errMsg string
|
||||
)
|
||||
if appInstall.App.Type != "php" {
|
||||
out, err = compose.Pull(appInstall.GetComposePath())
|
||||
if err != nil {
|
||||
if out != "" {
|
||||
if strings.Contains(out, "no such host") {
|
||||
errMsg = i18n.GetMsgByKey("ErrNoSuchHost") + ":"
|
||||
}
|
||||
if strings.Contains(out, "timeout") {
|
||||
errMsg = i18n.GetMsgByKey("ErrImagePullTimeOut") + ":"
|
||||
}
|
||||
appInstall.Message = errMsg + out
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
out, err = compose.Up(appInstall.GetComposePath())
|
||||
var (
|
||||
out string
|
||||
errMsg string
|
||||
)
|
||||
if pullImages && appInstall.App.Type != "php" {
|
||||
out, err = compose.Pull(appInstall.GetComposePath())
|
||||
if err != nil {
|
||||
if out != "" {
|
||||
if strings.Contains(out, "no such host") {
|
||||
errMsg = i18n.GetMsgByKey("ErrNoSuchHost") + ":"
|
||||
}
|
||||
if strings.Contains(out, "timeout") {
|
||||
errMsg = i18n.GetMsgByKey("ErrImagePullTimeOut") + ":"
|
||||
}
|
||||
appInstall.Message = errMsg + out
|
||||
}
|
||||
return err
|
||||
}
|
||||
return
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
out, err = compose.Up(appInstall.GetComposePath())
|
||||
if err != nil {
|
||||
if out != "" {
|
||||
appInstall.Message = errMsg + out
|
||||
}
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
if err := upProject(appInstall); err != nil {
|
||||
appInstall.Status = constant.Error
|
||||
@@ -839,11 +901,12 @@ func rebuildApp(appInstall model.AppInstall) error {
|
||||
_ = handleErr(appInstall, err, out)
|
||||
return
|
||||
}
|
||||
out, err = compose.Up(dockerComposePath)
|
||||
out, err = compose.Up(appInstall.GetComposePath())
|
||||
if err != nil {
|
||||
_ = handleErr(appInstall, err, out)
|
||||
return
|
||||
}
|
||||
|
||||
appInstall.Status = constant.Running
|
||||
_ = appInstallRepo.Save(context.Background(), &appInstall)
|
||||
}()
|
||||
@@ -931,6 +994,43 @@ func handleLocalAppDetail(versionDir string, appDetail *model.AppDetail) error {
|
||||
if err != nil {
|
||||
return buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
|
||||
}
|
||||
var appParam dto.AppForm
|
||||
if err = json.Unmarshal(dataJson, &appParam); err != nil {
|
||||
return buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
|
||||
}
|
||||
for _, formField := range appParam.FormFields {
|
||||
if strings.Contains(formField.EnvKey, " ") {
|
||||
return buserr.WithName(constant.ErrAppParamKey, formField.EnvKey)
|
||||
}
|
||||
}
|
||||
|
||||
var dataMap map[string]interface{}
|
||||
err = yaml.Unmarshal(paramByte, &dataMap)
|
||||
if err != nil {
|
||||
return buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
|
||||
}
|
||||
|
||||
additionalProperties, ok := dataMap["additionalProperties"].(map[string]interface{})
|
||||
if !ok {
|
||||
return buserr.WithName(constant.ErrAppParamKey, "additionalProperties")
|
||||
}
|
||||
|
||||
formFieldsInterface, ok := additionalProperties["formFields"]
|
||||
if ok {
|
||||
formFields, ok := formFieldsInterface.([]interface{})
|
||||
if !ok {
|
||||
return buserr.WithName(constant.ErrAppParamKey, "formFields")
|
||||
}
|
||||
for _, item := range formFields {
|
||||
field := item.(map[string]interface{})
|
||||
for key, value := range field {
|
||||
if value == nil {
|
||||
return buserr.WithName(constant.ErrAppParamKey, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
appDetail.Params = string(dataJson)
|
||||
return nil
|
||||
}
|
||||
@@ -982,8 +1082,8 @@ func handleErr(install model.AppInstall, err error, out string) error {
|
||||
if out != "" {
|
||||
install.Message = out
|
||||
reErr = errors.New(out)
|
||||
install.Status = constant.Error
|
||||
}
|
||||
install.Status = constant.Error
|
||||
_ = appInstallRepo.Save(context.Background(), &install)
|
||||
return reErr
|
||||
}
|
||||
@@ -994,6 +1094,10 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]respons
|
||||
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)
|
||||
}
|
||||
|
||||
installDTO := response.AppInstalledDTO{
|
||||
AppInstall: installed,
|
||||
Path: installed.GetPath(),
|
||||
@@ -1008,7 +1112,7 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]respons
|
||||
}
|
||||
var versions []string
|
||||
for _, detail := range details {
|
||||
if detail.IgnoreUpgrade {
|
||||
if detail.IgnoreUpgrade || installed.Version == "latest" {
|
||||
continue
|
||||
}
|
||||
if common.IsCrossVersion(installed.Version, detail.Version) && !app.CrossVersionUpdate {
|
||||
@@ -1123,14 +1227,20 @@ func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName
|
||||
return buserr.New(constant.ErrFileParse)
|
||||
}
|
||||
serviceValue := service.(map[string]interface{})
|
||||
deploy := map[string]interface{}{
|
||||
"resources": map[string]interface{}{
|
||||
"limits": map[string]interface{}{
|
||||
"cpus": "${CPUS}",
|
||||
"memory": "${MEMORY_LIMIT}",
|
||||
},
|
||||
},
|
||||
|
||||
deploy := map[string]interface{}{}
|
||||
if de, ok := serviceValue["deploy"]; ok {
|
||||
deploy = de.(map[string]interface{})
|
||||
}
|
||||
resource := map[string]interface{}{}
|
||||
if res, ok := deploy["resources"]; ok {
|
||||
resource = res.(map[string]interface{})
|
||||
}
|
||||
resource["limits"] = map[string]interface{}{
|
||||
"cpus": "${CPUS}",
|
||||
"memory": "${MEMORY_LIMIT}",
|
||||
}
|
||||
deploy["resources"] = resource
|
||||
serviceValue["deploy"] = deploy
|
||||
|
||||
ports, ok := serviceValue["ports"].([]interface{})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"strconv"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
@@ -19,6 +20,7 @@ type AuthService struct{}
|
||||
|
||||
type IAuthService interface {
|
||||
CheckIsSafety(code string) (string, error)
|
||||
GetResponsePage() (string, error)
|
||||
VerifyCode(code string) (bool, error)
|
||||
Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, error)
|
||||
LogOut(c *gin.Context) error
|
||||
@@ -42,7 +44,7 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*d
|
||||
if err != nil {
|
||||
return nil, constant.ErrAuth
|
||||
}
|
||||
if info.Password != pass || nameSetting.Value != info.Name {
|
||||
if !hmac.Equal([]byte(info.Password), []byte(pass)) || nameSetting.Value != info.Name {
|
||||
return nil, constant.ErrAuth
|
||||
}
|
||||
entranceSetting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
|
||||
@@ -78,7 +80,7 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance strin
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.Password != pass || nameSetting.Value != info.Name {
|
||||
if !hmac.Equal([]byte(info.Password), []byte(pass)) || nameSetting.Value != info.Name {
|
||||
return nil, constant.ErrAuth
|
||||
}
|
||||
entranceSetting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
|
||||
@@ -109,6 +111,10 @@ func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
httpsSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lifeTime, err := strconv.Atoi(setting.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -129,7 +135,7 @@ func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) (
|
||||
sessionUser, err := global.SESSION.Get(sID)
|
||||
if err != nil {
|
||||
sID = uuid.New().String()
|
||||
c.SetCookie(constant.SessionName, sID, 0, "", "", false, false)
|
||||
c.SetCookie(constant.SessionName, sID, 0, "", "", httpsSetting.Value == "enable", true)
|
||||
err := global.SESSION.Set(sID, sessionUser, lifeTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -144,9 +150,13 @@ func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) (
|
||||
}
|
||||
|
||||
func (u *AuthService) LogOut(c *gin.Context) error {
|
||||
httpsSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sID, _ := c.Cookie(constant.SessionName)
|
||||
if sID != "" {
|
||||
c.SetCookie(constant.SessionName, sID, -1, "", "", false, false)
|
||||
c.SetCookie(constant.SessionName, sID, -1, "", "", httpsSetting.Value == "enable", true)
|
||||
err := global.SESSION.Delete(sID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -160,7 +170,7 @@ func (u *AuthService) VerifyCode(code string) (bool, error) {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return setting.Value == code, nil
|
||||
return hmac.Equal([]byte(setting.Value), []byte(code)), nil
|
||||
}
|
||||
|
||||
func (u *AuthService) CheckIsSafety(code string) (string, error) {
|
||||
@@ -176,3 +186,11 @@ func (u *AuthService) CheckIsSafety(code string) (string, error) {
|
||||
}
|
||||
return "unpass", nil
|
||||
}
|
||||
|
||||
func (u *AuthService) GetResponsePage() (string, error) {
|
||||
pageCode, err := settingRepo.Get(settingRepo.WithByKey("NoAuthSetting"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return pageCode.Value, nil
|
||||
}
|
||||
|
||||
+200
-105
@@ -6,12 +6,12 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage/client"
|
||||
fileUtils "github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
@@ -29,7 +30,8 @@ type BackupService struct{}
|
||||
type IBackupService interface {
|
||||
List() ([]dto.BackupInfo, error)
|
||||
SearchRecordsWithPage(search dto.RecordSearch) (int64, []dto.BackupRecords, error)
|
||||
LoadOneDriveInfo() (string, error)
|
||||
SearchRecordsByCronjobWithPage(search dto.RecordSearchByCronjob) (int64, []dto.BackupRecords, error)
|
||||
LoadOneDriveInfo() (dto.OneDriveInfo, error)
|
||||
DownloadRecord(info dto.DownloadRecord) (string, error)
|
||||
Create(backupDto dto.BackupOperate) error
|
||||
GetBuckets(backupDto dto.ForBuckets) ([]interface{}, error)
|
||||
@@ -38,11 +40,14 @@ type IBackupService interface {
|
||||
BatchDeleteRecord(ids []uint) error
|
||||
NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error)
|
||||
|
||||
ListFiles(req dto.BackupSearchFile) ([]string, error)
|
||||
ListFiles(req dto.BackupSearchFile) []string
|
||||
|
||||
MysqlBackup(db dto.CommonBackup) error
|
||||
PostgresqlBackup(db dto.CommonBackup) error
|
||||
MysqlRecover(db dto.CommonRecover) error
|
||||
PostgresqlRecover(db dto.CommonRecover) error
|
||||
MysqlRecoverByUpload(req dto.CommonRecover) error
|
||||
PostgresqlRecoverByUpload(req dto.CommonRecover) error
|
||||
|
||||
RedisBackup() error
|
||||
RedisRecover(db dto.CommonRecover) error
|
||||
@@ -52,6 +57,8 @@ type IBackupService interface {
|
||||
|
||||
AppBackup(db dto.CommonBackup) error
|
||||
AppRecover(req dto.CommonRecover) error
|
||||
|
||||
Run()
|
||||
}
|
||||
|
||||
func NewIBackupService() IBackupService {
|
||||
@@ -81,65 +88,72 @@ func (u *BackupService) SearchRecordsWithPage(search dto.RecordSearch) (int64, [
|
||||
commonRepo.WithByType(search.Type),
|
||||
backupRepo.WithByDetailName(search.DetailName),
|
||||
)
|
||||
|
||||
var datas []dto.BackupRecords
|
||||
clientMap := make(map[string]loadSizeHelper)
|
||||
for i := 0; i < len(records); i++ {
|
||||
var item dto.BackupRecords
|
||||
if err := copier.Copy(&item, &records[i]); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
itemPath := path.Join(records[i].FileDir, records[i].FileName)
|
||||
if records[i].Source == "LOCAL" {
|
||||
fileInfo, err := os.Stat(itemPath)
|
||||
if err == nil {
|
||||
item.Size = fileInfo.Size()
|
||||
}
|
||||
datas = append(datas, item)
|
||||
continue
|
||||
}
|
||||
if _, ok := clientMap[records[i].Source]; !ok {
|
||||
backup, err := backupRepo.Get(commonRepo.WithByType(records[i].Source))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load backup model %s from db failed, err: %v", records[i].Source, err)
|
||||
return total, datas, err
|
||||
}
|
||||
client, err := u.NewClient(&backup)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load backup client %s from db failed, err: %v", records[i].Source, err)
|
||||
return total, datas, err
|
||||
}
|
||||
item.Size, _ = client.Size(path.Join(strings.TrimLeft(backup.BackupPath, "/"), itemPath))
|
||||
datas = append(datas, item)
|
||||
clientMap[records[i].Source] = loadSizeHelper{backupPath: strings.TrimLeft(backup.BackupPath, "/"), client: client}
|
||||
continue
|
||||
}
|
||||
item.Size, _ = clientMap[records[i].Source].client.Size(path.Join(clientMap[records[i].Source].backupPath, itemPath))
|
||||
datas = append(datas, item)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
datas, err := u.loadRecordSize(records)
|
||||
sort.Slice(datas, func(i, j int) bool {
|
||||
return datas[i].CreatedAt.After(datas[j].CreatedAt)
|
||||
})
|
||||
return total, datas, err
|
||||
}
|
||||
|
||||
func (u *BackupService) SearchRecordsByCronjobWithPage(search dto.RecordSearchByCronjob) (int64, []dto.BackupRecords, error) {
|
||||
total, records, err := backupRepo.PageRecord(
|
||||
search.Page, search.PageSize,
|
||||
commonRepo.WithOrderBy("created_at desc"),
|
||||
backupRepo.WithByCronID(search.CronjobID),
|
||||
)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
datas, err := u.loadRecordSize(records)
|
||||
sort.Slice(datas, func(i, j int) bool {
|
||||
return datas[i].CreatedAt.After(datas[j].CreatedAt)
|
||||
})
|
||||
return total, datas, err
|
||||
}
|
||||
|
||||
type loadSizeHelper struct {
|
||||
isOk bool
|
||||
backupPath string
|
||||
client cloud_storage.CloudStorageClient
|
||||
}
|
||||
|
||||
func (u *BackupService) LoadOneDriveInfo() (string, error) {
|
||||
OneDriveID, err := settingRepo.Get(settingRepo.WithByKey("OneDriveID"))
|
||||
func (u *BackupService) LoadOneDriveInfo() (dto.OneDriveInfo, error) {
|
||||
var data dto.OneDriveInfo
|
||||
data.RedirectUri = constant.OneDriveRedirectURI
|
||||
clientID, err := settingRepo.Get(settingRepo.WithByKey("OneDriveID"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
return data, err
|
||||
}
|
||||
idItem, err := base64.StdEncoding.DecodeString(OneDriveID.Value)
|
||||
idItem, err := base64.StdEncoding.DecodeString(clientID.Value)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return data, err
|
||||
}
|
||||
return string(idItem), err
|
||||
data.ClientID = string(idItem)
|
||||
clientSecret, err := settingRepo.Get(settingRepo.WithByKey("OneDriveSc"))
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
secretItem, err := base64.StdEncoding.DecodeString(clientSecret.Value)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
data.ClientSecret = string(secretItem)
|
||||
|
||||
return data, err
|
||||
}
|
||||
|
||||
func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error) {
|
||||
if info.Source == "LOCAL" {
|
||||
return info.FileDir + "/" + info.FileName, nil
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path.Join(localDir, info.FileDir, info.FileName), nil
|
||||
}
|
||||
backup, _ := backupRepo.Get(commonRepo.WithByType(info.Source))
|
||||
if backup.ID == 0 {
|
||||
@@ -202,6 +216,9 @@ func (u *BackupService) Create(req dto.BackupOperate) error {
|
||||
return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err)
|
||||
}
|
||||
}
|
||||
if backup.Type == constant.OneDrive {
|
||||
StartRefreshOneDriveToken()
|
||||
}
|
||||
if err := backupRepo.Create(&backup); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -229,7 +246,14 @@ func (u *BackupService) GetBuckets(backupDto dto.ForBuckets) ([]interface{}, err
|
||||
}
|
||||
|
||||
func (u *BackupService) Delete(id uint) error {
|
||||
cronjobs, _ := cronjobRepo.List(cronjobRepo.WithByBackupID(id))
|
||||
backup, _ := backupRepo.Get(commonRepo.WithByID(id))
|
||||
if backup.ID == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
if backup.Type == constant.OneDrive {
|
||||
global.Cron.Remove(global.OneDriveCronID)
|
||||
}
|
||||
cronjobs, _ := cronjobRepo.List(cronjobRepo.WithByDefaultDownload(backup.Type))
|
||||
if len(cronjobs) != 0 {
|
||||
return buserr.New(constant.ErrBackupInUsed)
|
||||
}
|
||||
@@ -242,22 +266,18 @@ func (u *BackupService) BatchDeleteRecord(ids []uint) error {
|
||||
return err
|
||||
}
|
||||
for _, record := range records {
|
||||
if record.Source == "LOCAL" {
|
||||
if err := os.Remove(record.FileDir + "/" + record.FileName); err != nil {
|
||||
global.LOG.Errorf("remove file %s failed, err: %v", record.FileDir+record.FileName, err)
|
||||
}
|
||||
} else {
|
||||
backupAccount, err := backupRepo.Get(commonRepo.WithByType(record.Source))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := u.NewClient(&backupAccount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = client.Delete(record.FileDir + record.FileName); err != nil {
|
||||
global.LOG.Errorf("remove file %s from %s failed, err: %v", record.FileDir+record.FileName, record.Source, err)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
return backupRepo.DeleteRecord(context.Background(), commonRepo.WithIdsIn(ids))
|
||||
@@ -310,27 +330,29 @@ func (u *BackupService) Update(req dto.BackupOperate) error {
|
||||
if backup.Type == "LOCAL" {
|
||||
if dir, ok := varMap["dir"]; ok {
|
||||
if dirStr, isStr := dir.(string); isStr {
|
||||
if strings.HasSuffix(dirStr, "/") {
|
||||
if strings.HasSuffix(dirStr, "/") && dirStr != "/" {
|
||||
dirStr = dirStr[:strings.LastIndex(dirStr, "/")]
|
||||
}
|
||||
if err := copyDir(oldDir, dirStr); err != nil {
|
||||
_ = backupRepo.Update(req.ID, (map[string]interface{}{"vars": oldVars}))
|
||||
return err
|
||||
}
|
||||
global.CONF.System.Backup = dirStr
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *BackupService) ListFiles(req dto.BackupSearchFile) ([]string, error) {
|
||||
func (u *BackupService) ListFiles(req dto.BackupSearchFile) []string {
|
||||
var datas []string
|
||||
backup, err := backupRepo.Get(backupRepo.WithByType(req.Type))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return datas
|
||||
}
|
||||
client, err := u.NewClient(&backup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return datas
|
||||
}
|
||||
prefix := "system_snapshot"
|
||||
if len(backup.BackupPath) != 0 {
|
||||
@@ -338,15 +360,15 @@ func (u *BackupService) ListFiles(req dto.BackupSearchFile) ([]string, error) {
|
||||
}
|
||||
files, err := client.ListObjects(prefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
global.LOG.Debugf("load files from %s failed, err: %v", req.Type, err)
|
||||
return datas
|
||||
}
|
||||
var datas []string
|
||||
for _, file := range files {
|
||||
if len(file) != 0 {
|
||||
datas = append(datas, path.Base(file))
|
||||
}
|
||||
}
|
||||
return datas, nil
|
||||
return datas
|
||||
}
|
||||
|
||||
func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error) {
|
||||
@@ -354,9 +376,6 @@ func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.Cl
|
||||
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if backup.Type == "LOCAL" {
|
||||
return nil, errors.New("not support")
|
||||
}
|
||||
varMap["bucket"] = backup.Bucket
|
||||
switch backup.Type {
|
||||
case constant.Sftp, constant.WebDAV:
|
||||
@@ -384,6 +403,15 @@ func (u *BackupService) loadByType(accountType string, accounts []model.BackupAc
|
||||
if err := copier.Copy(&item, &account); err != nil {
|
||||
global.LOG.Errorf("copy backup account to dto backup info failed, err: %v", err)
|
||||
}
|
||||
if account.Type == constant.OneDrive {
|
||||
varMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(item.Vars), &varMap); err != nil {
|
||||
return dto.BackupInfo{Type: accountType}
|
||||
}
|
||||
delete(varMap, "refresh_token")
|
||||
itemVars, _ := json.Marshal(varMap)
|
||||
item.Vars = string(itemVars)
|
||||
}
|
||||
return item
|
||||
}
|
||||
}
|
||||
@@ -395,48 +423,68 @@ 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)
|
||||
}
|
||||
|
||||
data := url.Values{}
|
||||
data.Set("client_id", global.CONF.System.OneDriveID)
|
||||
data.Set("client_secret", global.CONF.System.OneDriveSc)
|
||||
data.Set("grant_type", "authorization_code")
|
||||
data.Set("code", varMap["code"].(string))
|
||||
data.Set("redirect_uri", constant.OneDriveRedirectURI)
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest("POST", "https://login.microsoftonline.com/common/oauth2/v2.0/token", strings.NewReader(data.Encode()))
|
||||
token, refreshToken, err := client.RefreshToken("authorization_code", varMap)
|
||||
if err != nil {
|
||||
return fmt.Errorf("new http post client for access token failed, err: %v", err)
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("request for access token failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
delete(varMap, "code")
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read data from response body failed, err: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
token := map[string]interface{}{}
|
||||
if err := json.Unmarshal(respBody, &token); err != nil {
|
||||
return fmt.Errorf("unmarshal data from response body failed, err: %v", err)
|
||||
}
|
||||
accessToken, ok := token["refresh_token"].(string)
|
||||
if !ok {
|
||||
return errors.New("no such access token in response")
|
||||
}
|
||||
|
||||
backup.Credential = token
|
||||
varMap["refresh_status"] = constant.StatusSuccess
|
||||
varMap["refresh_time"] = time.Now().Format("2006-01-02 15:04:05")
|
||||
varMap["refresh_token"] = refreshToken
|
||||
itemVars, err := json.Marshal(varMap)
|
||||
if err != nil {
|
||||
return fmt.Errorf("json marshal var map failed, err: %v", err)
|
||||
}
|
||||
backup.Credential = accessToken
|
||||
backup.Vars = string(itemVars)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *BackupService) loadRecordSize(records []model.BackupRecord) ([]dto.BackupRecords, error) {
|
||||
var datas []dto.BackupRecords
|
||||
clientMap := make(map[string]loadSizeHelper)
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < len(records); i++ {
|
||||
var item dto.BackupRecords
|
||||
if err := copier.Copy(&item, &records[i]); err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
itemPath := path.Join(records[i].FileDir, records[i].FileName)
|
||||
if _, ok := clientMap[records[i].Source]; !ok {
|
||||
backup, err := backupRepo.Get(commonRepo.WithByType(records[i].Source))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load backup model %s from db failed, err: %v", records[i].Source, err)
|
||||
clientMap[records[i].Source] = loadSizeHelper{}
|
||||
datas = append(datas, item)
|
||||
continue
|
||||
}
|
||||
client, err := u.NewClient(&backup)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load backup client %s from db failed, err: %v", records[i].Source, err)
|
||||
clientMap[records[i].Source] = loadSizeHelper{}
|
||||
datas = append(datas, item)
|
||||
continue
|
||||
}
|
||||
item.Size, _ = client.Size(path.Join(strings.TrimLeft(backup.BackupPath, "/"), itemPath))
|
||||
datas = append(datas, item)
|
||||
clientMap[records[i].Source] = loadSizeHelper{backupPath: strings.TrimLeft(backup.BackupPath, "/"), client: client, isOk: true}
|
||||
continue
|
||||
}
|
||||
if clientMap[records[i].Source].isOk {
|
||||
wg.Add(1)
|
||||
go func(index int) {
|
||||
item.Size, _ = clientMap[records[index].Source].client.Size(path.Join(clientMap[records[index].Source].backupPath, itemPath))
|
||||
datas = append(datas, item)
|
||||
wg.Done()
|
||||
}(i)
|
||||
} else {
|
||||
datas = append(datas, item)
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
return datas, nil
|
||||
}
|
||||
|
||||
func loadLocalDir() (string, error) {
|
||||
backup, err := backupRepo.Get(commonRepo.WithByType("LOCAL"))
|
||||
if err != nil {
|
||||
@@ -518,3 +566,50 @@ func (u *BackupService) checkBackupConn(backup *model.BackupAccount) (bool, erro
|
||||
targetPath := strings.TrimPrefix(path.Join(backup.BackupPath, "test/1panel"), "/")
|
||||
return client.Upload(fileItem, targetPath)
|
||||
}
|
||||
|
||||
func StartRefreshOneDriveToken() {
|
||||
service := NewIBackupService()
|
||||
oneDriveCronID, err := global.Cron.AddJob("0 * * * *", service)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("can not add OneDrive corn job: %s", err.Error())
|
||||
return
|
||||
}
|
||||
global.OneDriveCronID = oneDriveCronID
|
||||
}
|
||||
|
||||
func (u *BackupService) Run() {
|
||||
var backupItem model.BackupAccount
|
||||
_ = global.DB.Where("`type` = ?", "OneDrive").First(&backupItem)
|
||||
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)
|
||||
varMap["refresh_status"] = constant.StatusSuccess
|
||||
varMap["refresh_time"] = time.Now().Format("2006-01-02 15:04:05")
|
||||
if err != nil {
|
||||
varMap["refresh_status"] = constant.StatusFailed
|
||||
varMap["refresh_msg"] = err.Error()
|
||||
global.LOG.Errorf("Failed to refresh OneDrive token, please retry, err: %v", err)
|
||||
return
|
||||
}
|
||||
varMap["refresh_token"] = refreshToken
|
||||
|
||||
varsItem, _ := json.Marshal(varMap)
|
||||
_ = global.DB.Model(&model.BackupAccount{}).
|
||||
Where("id = ?", backupItem.ID).
|
||||
Updates(map[string]interface{}{
|
||||
"credential": token,
|
||||
"vars": varsItem,
|
||||
}).Error
|
||||
global.LOG.Info("Successfully refreshed OneDrive token.")
|
||||
}
|
||||
|
||||
@@ -4,17 +4,19 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
|
||||
"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/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/pkg/errors"
|
||||
@@ -34,10 +36,10 @@ func (u *BackupService) AppBackup(req dto.CommonBackup) error {
|
||||
return err
|
||||
}
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
itemDir := fmt.Sprintf("app/%s/%s", req.Name, req.DetailName)
|
||||
backupDir := path.Join(localDir, itemDir)
|
||||
|
||||
backupDir := path.Join(localDir, fmt.Sprintf("app/%s/%s", req.Name, req.DetailName))
|
||||
|
||||
fileName := fmt.Sprintf("%s_%s.tar.gz", req.DetailName, timeNow)
|
||||
fileName := fmt.Sprintf("%s_%s.tar.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
|
||||
if err := handleAppBackup(&install, backupDir, fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -48,7 +50,7 @@ func (u *BackupService) AppBackup(req dto.CommonBackup) error {
|
||||
DetailName: req.DetailName,
|
||||
Source: "LOCAL",
|
||||
BackupType: "LOCAL",
|
||||
FileDir: backupDir,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
}
|
||||
|
||||
@@ -107,12 +109,21 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
|
||||
|
||||
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||
for _, resource := range resources {
|
||||
if resource.Key == "mysql" || resource.Key == "mariadb" {
|
||||
switch resource.Key {
|
||||
case constant.AppMysql, constant.AppMariaDB:
|
||||
db, err := mysqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handleMysqlBackup(db.MysqlName, db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", install.Name)); err != nil {
|
||||
if err := handleMysqlBackup(db.MysqlName, resource.Key, db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", install.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
case constant.AppPostgresql:
|
||||
db, err := postgresqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handlePostgresqlBackup(db.PostgresqlName, db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", install.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -191,7 +202,21 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
return err
|
||||
}
|
||||
}
|
||||
if database.Type == "mysql" || database.Type == "mariadb" {
|
||||
switch database.Type {
|
||||
case constant.AppPostgresql:
|
||||
db, err := postgresqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handlePostgresqlRecover(dto.CommonRecover{
|
||||
Name: database.Name,
|
||||
DetailName: db.Name,
|
||||
File: fmt.Sprintf("%s/%s.sql.gz", tmpPath, install.Name),
|
||||
}, true); err != nil {
|
||||
global.LOG.Errorf("handle recover from sql.gz failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
case constant.AppMysql, constant.AppMariaDB:
|
||||
db, err := mysqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -235,7 +260,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
_ = fileOp.DeleteDir(backPath)
|
||||
|
||||
if len(newEnvFile) != 0 {
|
||||
envPath := fmt.Sprintf("%s/%s/%s/.env", constant.AppInstallDir, install.App.Key, install.Name)
|
||||
envPath := fmt.Sprintf("%s/%s/.env", install.GetAppPath(), install.Name)
|
||||
file, err := os.OpenFile(envPath, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -279,6 +304,10 @@ func reCreateDB(dbID uint, database model.Database, oldEnv string) (*model.Datab
|
||||
Password: oldPassword,
|
||||
Permission: "%",
|
||||
})
|
||||
cronjobs, _ := cronjobRepo.List(cronjobRepo.WithByDbName(fmt.Sprintf("%v", dbID)))
|
||||
for _, job := range cronjobs {
|
||||
_ = cronjobRepo.Update(job.ID, map[string]interface{}{"db_name": fmt.Sprintf("%v", createDB.ID)})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, envMap, err
|
||||
}
|
||||
|
||||
@@ -2,16 +2,18 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/mysql/client"
|
||||
)
|
||||
@@ -23,10 +25,11 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
|
||||
}
|
||||
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
targetDir := path.Join(localDir, fmt.Sprintf("database/%s/%s/%s", req.Type, req.Name, req.DetailName))
|
||||
fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow)
|
||||
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))
|
||||
|
||||
if err := handleMysqlBackup(req.Name, req.DetailName, targetDir, fileName); err != nil {
|
||||
if err := handleMysqlBackup(req.Name, req.Type, req.DetailName, targetDir, fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -36,7 +39,7 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
|
||||
DetailName: req.DetailName,
|
||||
Source: "LOCAL",
|
||||
BackupType: "LOCAL",
|
||||
FileDir: targetDir,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
@@ -97,18 +100,20 @@ func (u *BackupService) MysqlRecoverByUpload(req dto.CommonRecover) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleMysqlBackup(database, dbName, targetDir, fileName string) error {
|
||||
func handleMysqlBackup(database, dbType, dbName, targetDir, fileName string) error {
|
||||
dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(dbName), mysqlRepo.WithByMysqlName(database))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cli, _, err := LoadMysqlClientByFrom(database)
|
||||
cli, version, err := LoadMysqlClientByFrom(database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
backupInfo := client.BackupInfo{
|
||||
Name: dbName,
|
||||
Type: dbType,
|
||||
Version: version,
|
||||
Format: dbInfo.Format,
|
||||
TargetDir: targetDir,
|
||||
FileName: fileName,
|
||||
@@ -131,7 +136,7 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cli, _, err := LoadMysqlClientByFrom(req.Name)
|
||||
cli, version, err := LoadMysqlClientByFrom(req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -140,6 +145,8 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
|
||||
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("database/%s/%s_%s.sql.gz", req.Type, req.DetailName, time.Now().Format("20060102150405")))
|
||||
if err := cli.Backup(client.BackupInfo{
|
||||
Name: req.DetailName,
|
||||
Type: req.Type,
|
||||
Version: version,
|
||||
Format: dbInfo.Format,
|
||||
TargetDir: path.Dir(rollbackFile),
|
||||
FileName: path.Base(rollbackFile),
|
||||
@@ -153,6 +160,8 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
|
||||
global.LOG.Info("recover failed, start to rollback now")
|
||||
if err := cli.Recover(client.RecoverInfo{
|
||||
Name: req.DetailName,
|
||||
Type: req.Type,
|
||||
Version: version,
|
||||
Format: dbInfo.Format,
|
||||
SourceFile: rollbackFile,
|
||||
|
||||
@@ -169,6 +178,8 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
|
||||
}
|
||||
if err := cli.Recover(client.RecoverInfo{
|
||||
Name: req.DetailName,
|
||||
Type: req.Type,
|
||||
Version: version,
|
||||
Format: dbInfo.Format,
|
||||
SourceFile: req.File,
|
||||
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
pgclient "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
|
||||
)
|
||||
|
||||
func (u *BackupService) PostgresqlBackup(req dto.CommonBackup) error {
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
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))
|
||||
|
||||
if err := handlePostgresqlBackup(req.Name, req.DetailName, targetDir, fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
record := &model.BackupRecord{
|
||||
Type: req.Type,
|
||||
Name: req.Name,
|
||||
DetailName: req.DetailName,
|
||||
Source: "LOCAL",
|
||||
BackupType: "LOCAL",
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (u *BackupService) PostgresqlRecover(req dto.CommonRecover) error {
|
||||
if err := handlePostgresqlRecover(req, false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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")
|
||||
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 {
|
||||
_ = os.RemoveAll(dstDir)
|
||||
return err
|
||||
}
|
||||
global.LOG.Infof("decompress file %s successful, now start to check test.sql is exist", req.File)
|
||||
hasTestSql := false
|
||||
_ = filepath.Walk(dstDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if !info.IsDir() && info.Name() == "test.sql" {
|
||||
hasTestSql = true
|
||||
file = path
|
||||
fileName = "test.sql"
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if !hasTestSql {
|
||||
_ = os.RemoveAll(dstDir)
|
||||
return fmt.Errorf("no such file named test.sql in %s", fileName)
|
||||
}
|
||||
defer func() {
|
||||
_ = os.RemoveAll(dstDir)
|
||||
}()
|
||||
}
|
||||
|
||||
req.File = path.Dir(file) + "/" + fileName
|
||||
if err := handlePostgresqlRecover(req, false); err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Info("recover from uploads successful!")
|
||||
return nil
|
||||
}
|
||||
func handlePostgresqlBackup(database, dbName, targetDir, fileName string) error {
|
||||
cli, err := LoadPostgresqlClientByFrom(database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
backupInfo := pgclient.BackupInfo{
|
||||
Name: dbName,
|
||||
TargetDir: targetDir,
|
||||
FileName: fileName,
|
||||
|
||||
Timeout: 300,
|
||||
}
|
||||
if err := cli.Backup(backupInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handlePostgresqlRecover(req dto.CommonRecover, isRollback bool) error {
|
||||
isOk := false
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(req.File) {
|
||||
return buserr.WithName("ErrFileNotFound", req.File)
|
||||
}
|
||||
dbInfo, err := postgresqlRepo.Get(commonRepo.WithByName(req.DetailName), postgresqlRepo.WithByPostgresqlName(req.Name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cli, err := LoadPostgresqlClientByFrom(req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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")))
|
||||
if err := cli.Backup(client.BackupInfo{
|
||||
Name: req.DetailName,
|
||||
TargetDir: path.Dir(rollbackFile),
|
||||
FileName: path.Base(rollbackFile),
|
||||
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("backup postgresql db %s for rollback before recover failed, err: %v", req.DetailName, err)
|
||||
}
|
||||
defer func() {
|
||||
if !isOk {
|
||||
global.LOG.Info("recover failed, start to rollback now")
|
||||
if err := cli.Recover(client.RecoverInfo{
|
||||
Name: req.DetailName,
|
||||
SourceFile: rollbackFile,
|
||||
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
global.LOG.Errorf("rollback postgresql db %s from %s failed, err: %v", req.DetailName, rollbackFile, err)
|
||||
}
|
||||
global.LOG.Infof("rollback postgresql db %s from %s successful", req.DetailName, rollbackFile)
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
} else {
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
}
|
||||
}()
|
||||
}
|
||||
if err := cli.Recover(client.RecoverInfo{
|
||||
Name: req.DetailName,
|
||||
SourceFile: req.File,
|
||||
Username: dbInfo.Username,
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
isOk = true
|
||||
return nil
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"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/compose"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/pkg/errors"
|
||||
@@ -34,7 +35,7 @@ func (u *BackupService) RedisBackup() error {
|
||||
}
|
||||
global.LOG.Infof("appendonly in redis conf is %s", appendonly)
|
||||
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
timeNow := time.Now().Format("20060102150405") + common.RandStrAndNum(5)
|
||||
fileName := fmt.Sprintf("%s.rdb", timeNow)
|
||||
if appendonly == "yes" {
|
||||
if strings.HasPrefix(redisInfo.Version, "6.") {
|
||||
@@ -43,7 +44,8 @@ func (u *BackupService) RedisBackup() error {
|
||||
fileName = fmt.Sprintf("%s.tar.gz", timeNow)
|
||||
}
|
||||
}
|
||||
backupDir := path.Join(localDir, fmt.Sprintf("database/redis/%s", redisInfo.Name))
|
||||
itemDir := fmt.Sprintf("database/redis/%s", redisInfo.Name)
|
||||
backupDir := path.Join(localDir, itemDir)
|
||||
if err := handleRedisBackup(redisInfo, backupDir, fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -51,7 +53,7 @@ func (u *BackupService) RedisBackup() error {
|
||||
Type: "redis",
|
||||
Source: "LOCAL",
|
||||
BackupType: "LOCAL",
|
||||
FileDir: backupDir,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"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/compose"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/pkg/errors"
|
||||
@@ -31,8 +32,9 @@ func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
|
||||
}
|
||||
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
backupDir := path.Join(localDir, fmt.Sprintf("website/%s", req.Name))
|
||||
fileName := fmt.Sprintf("%s_%s.tar.gz", website.PrimaryDomain, timeNow)
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@@ -43,7 +45,7 @@ func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
|
||||
DetailName: req.DetailName,
|
||||
Source: "LOCAL",
|
||||
BackupType: "LOCAL",
|
||||
FileDir: backupDir,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
|
||||
@@ -23,7 +23,7 @@ func NewICommandService() ICommandService {
|
||||
}
|
||||
|
||||
func (u *CommandService) List() ([]dto.CommandInfo, error) {
|
||||
commands, err := commandRepo.GetList()
|
||||
commands, err := commandRepo.GetList(commonRepo.WithOrderBy("name"))
|
||||
if err != nil {
|
||||
return nil, constant.ErrRecordNotFound
|
||||
}
|
||||
@@ -39,7 +39,7 @@ func (u *CommandService) List() ([]dto.CommandInfo, error) {
|
||||
}
|
||||
|
||||
func (u *CommandService) SearchForTree() ([]dto.CommandTree, error) {
|
||||
cmdList, err := commandRepo.GetList()
|
||||
cmdList, err := commandRepo.GetList(commonRepo.WithOrderBy("name"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -65,11 +65,11 @@ 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))
|
||||
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
|
||||
}
|
||||
groups, _ := groupRepo.GetList(commonRepo.WithByType("command"))
|
||||
groups, _ := groupRepo.GetList(commonRepo.WithByType("command"), commonRepo.WithOrderBy("name"))
|
||||
var dtoCommands []dto.CommandInfo
|
||||
for _, command := range commands {
|
||||
var item dto.CommandInfo
|
||||
|
||||
@@ -15,6 +15,9 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
@@ -26,6 +29,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/mount"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/client"
|
||||
@@ -84,15 +88,28 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
options := types.ContainerListOptions{All: true}
|
||||
options := container.ListOptions{
|
||||
All: true,
|
||||
}
|
||||
if len(req.Filters) != 0 {
|
||||
options.Filters = filters.NewArgs()
|
||||
options.Filters.Add("label", req.Filters)
|
||||
}
|
||||
list, err = client.ContainerList(context.Background(), options)
|
||||
containers, err := client.ContainerList(context.Background(), options)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if req.ExcludeAppStore {
|
||||
for _, item := range containers {
|
||||
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
|
||||
continue
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
} else {
|
||||
list = containers
|
||||
}
|
||||
|
||||
if len(req.Name) != 0 {
|
||||
length, count := len(list), 0
|
||||
for count < length {
|
||||
@@ -162,7 +179,7 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
}
|
||||
|
||||
ports := loadContainerPort(item.Ports)
|
||||
backDatas[i] = dto.ContainerInfo{
|
||||
info := dto.ContainerInfo{
|
||||
ContainerID: item.ID,
|
||||
CreateTime: time.Unix(item.Created, 0).Format("2006-01-02 15:04:05"),
|
||||
Name: item.Names[0][1:],
|
||||
@@ -174,6 +191,16 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
IsFromApp: IsFromApp,
|
||||
IsFromCompose: IsFromCompose,
|
||||
}
|
||||
install, _ := appInstallRepo.GetFirst(appInstallRepo.WithContainerName(info.Name))
|
||||
if install.ID > 0 {
|
||||
info.AppInstallName = install.Name
|
||||
info.AppName = install.App.Name
|
||||
websites, _ := websiteRepo.GetBy(websiteRepo.WithAppInstallId(install.ID))
|
||||
for _, website := range websites {
|
||||
info.Websites = append(info.Websites, website.PrimaryDomain)
|
||||
}
|
||||
}
|
||||
backDatas[i] = info
|
||||
if item.NetworkSettings != nil && len(item.NetworkSettings.Networks) > 0 {
|
||||
networks := make([]string, 0, len(item.NetworkSettings.Networks))
|
||||
for key := range item.NetworkSettings.Networks {
|
||||
@@ -192,7 +219,7 @@ func (u *ContainerService) List() ([]string, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containers, err := client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
|
||||
containers, err := client.ContainerList(context.Background(), container.ListOptions{All: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -213,7 +240,7 @@ func (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list, err := client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
|
||||
list, err := client.ContainerList(context.Background(), container.ListOptions{All: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -313,7 +340,7 @@ func (u *ContainerService) LoadResourceLimit() (*dto.ResourceLimit, error) {
|
||||
|
||||
data := dto.ResourceLimit{
|
||||
CPU: cpuCounts,
|
||||
Memory: int(memoryInfo.Total),
|
||||
Memory: memoryInfo.Total,
|
||||
}
|
||||
return &data, nil
|
||||
}
|
||||
@@ -352,14 +379,14 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerOperate) error {
|
||||
return err
|
||||
}
|
||||
global.LOG.Infof("new container info %s has been made, now start to create", req.Name)
|
||||
container, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)
|
||||
con, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)
|
||||
if err != nil {
|
||||
_ = client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
|
||||
_ = client.ContainerRemove(ctx, req.Name, container.RemoveOptions{RemoveVolumes: true, Force: true})
|
||||
return err
|
||||
}
|
||||
global.LOG.Infof("create container %s successful! now check if the container is started and delete the container information if it is not.", req.Name)
|
||||
if err := client.ContainerStart(ctx, container.ID, types.ContainerStartOptions{}); err != nil {
|
||||
_ = client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
|
||||
if err := client.ContainerStart(ctx, con.ID, container.StartOptions{}); err != nil {
|
||||
_ = client.ContainerRemove(ctx, req.Name, container.RemoveOptions{RemoveVolumes: true, Force: true})
|
||||
return fmt.Errorf("create successful but start failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
@@ -386,6 +413,18 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
networkSettings := oldContainer.NetworkSettings
|
||||
bridgeNetworkSettings := networkSettings.Networks[data.Network]
|
||||
if bridgeNetworkSettings.IPAMConfig != nil {
|
||||
ipv4Address := bridgeNetworkSettings.IPAMConfig.IPv4Address
|
||||
data.Ipv4 = ipv4Address
|
||||
ipv6Address := bridgeNetworkSettings.IPAMConfig.IPv6Address
|
||||
data.Ipv6 = ipv6Address
|
||||
} else {
|
||||
data.Ipv4 = bridgeNetworkSettings.IPAddress
|
||||
}
|
||||
|
||||
data.Cmd = oldContainer.Config.Cmd
|
||||
data.OpenStdin = oldContainer.Config.OpenStdin
|
||||
data.Tty = oldContainer.Config.Tty
|
||||
@@ -411,14 +450,14 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
|
||||
data.AutoRemove = oldContainer.HostConfig.AutoRemove
|
||||
data.Privileged = oldContainer.HostConfig.Privileged
|
||||
data.PublishAllPorts = oldContainer.HostConfig.PublishAllPorts
|
||||
data.RestartPolicy = oldContainer.HostConfig.RestartPolicy.Name
|
||||
data.RestartPolicy = string(oldContainer.HostConfig.RestartPolicy.Name)
|
||||
if oldContainer.HostConfig.NanoCPUs != 0 {
|
||||
data.NanoCPUs = float64(oldContainer.HostConfig.NanoCPUs) / 1000000000
|
||||
}
|
||||
if oldContainer.HostConfig.Memory != 0 {
|
||||
data.Memory = float64(oldContainer.HostConfig.Memory) / 1024 / 1024
|
||||
}
|
||||
data.Volumes = loadVolumeBinds(oldContainer.HostConfig.Binds)
|
||||
data.Volumes = loadVolumeBinds(oldContainer.Mounts)
|
||||
|
||||
return &data, nil
|
||||
}
|
||||
@@ -447,7 +486,7 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := client.ContainerRemove(ctx, req.ContainerID, types.ContainerRemoveOptions{Force: true}); err != nil {
|
||||
if err := client.ContainerRemove(ctx, req.ContainerID, container.RemoveOptions{Force: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -458,13 +497,13 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
|
||||
}
|
||||
|
||||
global.LOG.Infof("new container info %s has been update, now start to recreate", req.Name)
|
||||
container, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)
|
||||
con, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)
|
||||
if err != nil {
|
||||
reCreateAfterUpdate(req.Name, client, oldContainer.Config, oldContainer.HostConfig, oldContainer.NetworkSettings)
|
||||
return fmt.Errorf("update container failed, err: %v", err)
|
||||
}
|
||||
global.LOG.Infof("update container %s successful! now check if the container is started.", req.Name)
|
||||
if err := client.ContainerStart(ctx, container.ID, types.ContainerStartOptions{}); err != nil {
|
||||
if err := client.ContainerStart(ctx, con.ID, container.StartOptions{}); err != nil {
|
||||
return fmt.Errorf("update successful but start failed, err: %v", err)
|
||||
}
|
||||
|
||||
@@ -499,18 +538,18 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err := client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{Force: true}); err != nil {
|
||||
if err := client.ContainerRemove(ctx, req.Name, container.RemoveOptions{Force: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
global.LOG.Infof("new container info %s has been update, now start to recreate", req.Name)
|
||||
container, err := client.ContainerCreate(ctx, config, hostConf, &networkConf, &v1.Platform{}, req.Name)
|
||||
con, err := client.ContainerCreate(ctx, config, hostConf, &networkConf, &v1.Platform{}, req.Name)
|
||||
if err != nil {
|
||||
reCreateAfterUpdate(req.Name, client, oldContainer.Config, oldContainer.HostConfig, oldContainer.NetworkSettings)
|
||||
return fmt.Errorf("upgrade container failed, err: %v", err)
|
||||
}
|
||||
global.LOG.Infof("upgrade container %s successful! now check if the container is started.", req.Name)
|
||||
if err := client.ContainerStart(ctx, container.ID, types.ContainerStartOptions{}); err != nil {
|
||||
if err := client.ContainerStart(ctx, con.ID, container.StartOptions{}); err != nil {
|
||||
return fmt.Errorf("upgrade successful but start failed, err: %v", err)
|
||||
}
|
||||
|
||||
@@ -542,7 +581,7 @@ func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error
|
||||
global.LOG.Infof("start container %s operation %s", item, req.Operation)
|
||||
switch req.Operation {
|
||||
case constant.ContainerOpStart:
|
||||
err = client.ContainerStart(ctx, item, types.ContainerStartOptions{})
|
||||
err = client.ContainerStart(ctx, item, container.StartOptions{})
|
||||
case constant.ContainerOpStop:
|
||||
err = client.ContainerStop(ctx, item, container.StopOptions{})
|
||||
case constant.ContainerOpRestart:
|
||||
@@ -554,7 +593,7 @@ func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error
|
||||
case constant.ContainerOpUnpause:
|
||||
err = client.ContainerUnpause(ctx, item)
|
||||
case constant.ContainerOpRemove:
|
||||
err = client.ContainerRemove(ctx, item, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
|
||||
err = client.ContainerRemove(ctx, item, container.RemoveOptions{RemoveVolumes: true, Force: true})
|
||||
}
|
||||
}
|
||||
return err
|
||||
@@ -588,7 +627,7 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
|
||||
_ = os.Remove(file)
|
||||
}
|
||||
|
||||
if err := client.ContainerStart(ctx, containerItem.ID, types.ContainerStartOptions{}); err != nil {
|
||||
if err := client.ContainerStart(ctx, containerItem.ID, container.StartOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -599,70 +638,83 @@ func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, containerType,
|
||||
if cmd.CheckIllegal(container, since, tail) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
command := fmt.Sprintf("docker logs %s", container)
|
||||
commandName := "docker"
|
||||
commandArg := []string{"logs", container}
|
||||
if containerType == "compose" {
|
||||
command = fmt.Sprintf("docker-compose -f %s logs", container)
|
||||
commandName = "docker-compose"
|
||||
commandArg = []string{"-f", container, "logs"}
|
||||
}
|
||||
if tail != "0" {
|
||||
command += " --tail " + tail
|
||||
commandArg = append(commandArg, "--tail")
|
||||
commandArg = append(commandArg, tail)
|
||||
}
|
||||
if since != "all" {
|
||||
command += " --since " + since
|
||||
commandArg = append(commandArg, "--since")
|
||||
commandArg = append(commandArg, since)
|
||||
}
|
||||
if follow {
|
||||
command += " -f"
|
||||
commandArg = append(commandArg, "-f")
|
||||
}
|
||||
command += " 2>&1"
|
||||
cmd := exec.Command("bash", "-c", command)
|
||||
if !follow {
|
||||
cmd := exec.Command(commandName, commandArg...)
|
||||
cmd.Stderr = cmd.Stdout
|
||||
stdout, _ := cmd.CombinedOutput()
|
||||
if !utf8.Valid(stdout) {
|
||||
return errors.New("invalid utf8")
|
||||
}
|
||||
if err := wsConn.WriteMessage(websocket.TextMessage, stdout); err != nil {
|
||||
global.LOG.Errorf("send message with log to ws failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := exec.Command(commandName, commandArg...)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = cmd.Process.Signal(syscall.SIGTERM)
|
||||
_ = cmd.Wait()
|
||||
}()
|
||||
var exitCh chan struct{}
|
||||
if follow {
|
||||
go func() {
|
||||
_, wsData, _ := wsConn.ReadMessage()
|
||||
if string(wsData) == "close conn" {
|
||||
exitCh <- struct{}{}
|
||||
}
|
||||
}()
|
||||
return err
|
||||
}
|
||||
cmd.Stderr = cmd.Stdout
|
||||
if err := cmd.Start(); err != nil {
|
||||
_ = cmd.Process.Signal(syscall.SIGTERM)
|
||||
return err
|
||||
}
|
||||
exitCh := make(chan struct{})
|
||||
go func() {
|
||||
_, wsData, _ := wsConn.ReadMessage()
|
||||
if string(wsData) == "close conn" {
|
||||
_ = cmd.Process.Signal(syscall.SIGTERM)
|
||||
exitCh <- struct{}{}
|
||||
}
|
||||
}()
|
||||
|
||||
buffer := make([]byte, 1024)
|
||||
for {
|
||||
select {
|
||||
case <-exitCh:
|
||||
return nil
|
||||
default:
|
||||
n, err := stdout.Read(buffer)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return err
|
||||
go func() {
|
||||
buffer := make([]byte, 1024)
|
||||
for {
|
||||
select {
|
||||
case <-exitCh:
|
||||
return
|
||||
default:
|
||||
n, err := stdout.Read(buffer)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return
|
||||
}
|
||||
global.LOG.Errorf("read bytes from log failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
if !utf8.Valid(buffer[:n]) {
|
||||
continue
|
||||
}
|
||||
if err = wsConn.WriteMessage(websocket.TextMessage, buffer[:n]); err != nil {
|
||||
global.LOG.Errorf("send message with log to ws failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
global.LOG.Errorf("read bytes from log failed, err: %v", err)
|
||||
continue
|
||||
}
|
||||
if err = wsConn.WriteMessage(websocket.TextMessage, buffer[:n]); err != nil {
|
||||
global.LOG.Errorf("send message with log to ws failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
_ = cmd.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error) {
|
||||
@@ -693,7 +745,6 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error
|
||||
if cache, ok := stats.MemoryStats.Stats["cache"]; ok {
|
||||
data.Cache = float64(cache) / 1024 / 1024
|
||||
}
|
||||
data.Memory = data.Memory - data.Cache
|
||||
data.NetworkRX, data.NetworkTX = calculateNetwork(stats.Networks)
|
||||
data.ShotTime = stats.Read
|
||||
return &data, nil
|
||||
@@ -706,7 +757,7 @@ func (u *ContainerService) LoadContainerLogs(req dto.OperationWithNameAndType) s
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
options := types.ContainerListOptions{All: true}
|
||||
options := container.ListOptions{All: true}
|
||||
options.Filters = filters.NewArgs()
|
||||
options.Filters.Add("label", fmt.Sprintf("%s=%s", composeProjectLabel, req.Name))
|
||||
containers, err := cli.ContainerList(context.Background(), options)
|
||||
@@ -761,7 +812,7 @@ func calculateCPUPercentUnix(stats *types.StatsJSON) float64 {
|
||||
}
|
||||
func calculateMemPercentUnix(memStats types.MemoryStats) float64 {
|
||||
memPercent := 0.0
|
||||
memUsage := float64(memStats.Usage - memStats.Stats["cache"])
|
||||
memUsage := float64(memStats.Usage)
|
||||
memLimit := float64(memStats.Limit)
|
||||
if memUsage > 0.0 && memLimit > 0.0 {
|
||||
memPercent = (memUsage / memLimit) * 100.0
|
||||
@@ -865,6 +916,7 @@ func loadCpuAndMem(client *client.Client, container string) dto.ContainerListSta
|
||||
data.MemoryCache = stats.MemoryStats.Stats["cache"]
|
||||
data.MemoryUsage = stats.MemoryStats.Usage
|
||||
data.MemoryLimit = stats.MemoryStats.Limit
|
||||
|
||||
data.MemoryPercent = calculateMemPercentUnix(stats.MemoryStats)
|
||||
return data
|
||||
}
|
||||
@@ -946,8 +998,21 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
|
||||
case "host", "none", "bridge":
|
||||
hostConf.NetworkMode = container.NetworkMode(req.Network)
|
||||
}
|
||||
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: {}}
|
||||
if req.Ipv4 != "" || req.Ipv6 != "" {
|
||||
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{
|
||||
req.Network: {
|
||||
IPAMConfig: &network.EndpointIPAMConfig{
|
||||
IPv4Address: req.Ipv4,
|
||||
IPv6Address: req.Ipv6,
|
||||
},
|
||||
}}
|
||||
} else {
|
||||
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: {}}
|
||||
}
|
||||
} else {
|
||||
if req.Ipv4 != "" || req.Ipv6 != "" {
|
||||
return nil, nil, nil, fmt.Errorf("Please set up the network")
|
||||
}
|
||||
networkConf = network.NetworkingConfig{}
|
||||
}
|
||||
|
||||
@@ -955,7 +1020,7 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
|
||||
hostConf.AutoRemove = req.AutoRemove
|
||||
hostConf.CPUShares = req.CPUShares
|
||||
hostConf.PublishAllPorts = req.PublishAllPorts
|
||||
hostConf.RestartPolicy = container.RestartPolicy{Name: req.RestartPolicy}
|
||||
hostConf.RestartPolicy = container.RestartPolicy{Name: container.RestartPolicyMode(req.RestartPolicy)}
|
||||
if req.RestartPolicy == "on-failure" {
|
||||
hostConf.RestartPolicy.MaximumRetryCount = 5
|
||||
}
|
||||
@@ -964,10 +1029,19 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
|
||||
hostConf.MemorySwap = 0
|
||||
hostConf.PortBindings = portMap
|
||||
hostConf.Binds = []string{}
|
||||
hostConf.Mounts = []mount.Mount{}
|
||||
config.Volumes = make(map[string]struct{})
|
||||
for _, volume := range req.Volumes {
|
||||
config.Volumes[volume.ContainerDir] = struct{}{}
|
||||
hostConf.Binds = append(hostConf.Binds, fmt.Sprintf("%s:%s:%s", volume.SourceDir, volume.ContainerDir, volume.Mode))
|
||||
if volume.Type == "volume" {
|
||||
hostConf.Mounts = append(hostConf.Mounts, mount.Mount{
|
||||
Type: mount.Type(volume.Type),
|
||||
Source: volume.SourceDir,
|
||||
Target: volume.ContainerDir,
|
||||
})
|
||||
config.Volumes[volume.ContainerDir] = struct{}{}
|
||||
} else {
|
||||
hostConf.Binds = append(hostConf.Binds, fmt.Sprintf("%s:%s:%s", volume.SourceDir, volume.ContainerDir, volume.Mode))
|
||||
}
|
||||
}
|
||||
return &config, &hostConf, &networkConf, nil
|
||||
}
|
||||
@@ -988,46 +1062,27 @@ func reCreateAfterUpdate(name string, client *client.Client, config *container.C
|
||||
global.LOG.Errorf("recreate after container update failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
if err := client.ContainerStart(ctx, oldContainer.ID, types.ContainerStartOptions{}); err != nil {
|
||||
if err := client.ContainerStart(ctx, oldContainer.ID, container.StartOptions{}); err != nil {
|
||||
global.LOG.Errorf("restart after container update failed, err: %v", err)
|
||||
}
|
||||
global.LOG.Errorf("recreate after container update successful")
|
||||
}
|
||||
|
||||
func loadVolumeBinds(binds []string) []dto.VolumeHelper {
|
||||
func loadVolumeBinds(binds []types.MountPoint) []dto.VolumeHelper {
|
||||
var datas []dto.VolumeHelper
|
||||
for _, bind := range binds {
|
||||
parts := strings.Split(bind, ":")
|
||||
var volumeItem dto.VolumeHelper
|
||||
if len(parts) > 3 {
|
||||
continue
|
||||
volumeItem.Type = string(bind.Type)
|
||||
if bind.Type == "volume" {
|
||||
volumeItem.SourceDir = bind.Name
|
||||
} else {
|
||||
volumeItem.SourceDir = bind.Source
|
||||
}
|
||||
volumeItem.SourceDir = parts[0]
|
||||
if len(parts) == 1 {
|
||||
volumeItem.ContainerDir = parts[0]
|
||||
volumeItem.ContainerDir = bind.Destination
|
||||
volumeItem.Mode = "ro"
|
||||
if bind.RW {
|
||||
volumeItem.Mode = "rw"
|
||||
}
|
||||
if len(parts) == 2 {
|
||||
switch parts[1] {
|
||||
case "r", "ro":
|
||||
volumeItem.ContainerDir = parts[0]
|
||||
volumeItem.Mode = "ro"
|
||||
case "rw":
|
||||
volumeItem.ContainerDir = parts[0]
|
||||
volumeItem.Mode = "rw"
|
||||
default:
|
||||
volumeItem.ContainerDir = parts[1]
|
||||
volumeItem.Mode = "rw"
|
||||
}
|
||||
}
|
||||
if len(parts) == 3 {
|
||||
volumeItem.ContainerDir = parts[1]
|
||||
if parts[2] == "r" {
|
||||
volumeItem.Mode = "ro"
|
||||
} else {
|
||||
volumeItem.Mode = parts[2]
|
||||
}
|
||||
}
|
||||
datas = append(datas, volumeItem)
|
||||
}
|
||||
return datas
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -20,7 +21,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
@@ -40,7 +40,7 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
options := types.ContainerListOptions{All: true}
|
||||
options := container.ListOptions{All: true}
|
||||
options.Filters = filters.NewArgs()
|
||||
options.Filters.Add("label", composeProjectLabel)
|
||||
|
||||
|
||||
@@ -5,11 +5,12 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"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/jinzhu/copier"
|
||||
@@ -28,10 +29,10 @@ type ICronjobService interface {
|
||||
UpdateStatus(id uint, status string) error
|
||||
Delete(req dto.CronjobBatchDelete) error
|
||||
Download(down dto.CronjobDownload) (string, error)
|
||||
StartJob(cronjob *model.Cronjob) (int, error)
|
||||
StartJob(cronjob *model.Cronjob) (string, error)
|
||||
CleanRecord(req dto.CronjobClean) error
|
||||
|
||||
LoadRecordLog(req dto.OperateByID) (string, error)
|
||||
LoadRecordLog(req dto.OperateByID) string
|
||||
}
|
||||
|
||||
func NewICronjobService() ICronjobService {
|
||||
@@ -46,14 +47,6 @@ func (u *CronjobService) SearchWithPage(search dto.SearchWithPage) (int64, inter
|
||||
if err := copier.Copy(&item, &cronjob); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
if hasBackup(item.Type) {
|
||||
backup, _ := backupRepo.Get(commonRepo.WithByID(uint(item.TargetDirID)))
|
||||
if len(backup.Type) != 0 {
|
||||
item.TargetDir = backup.Type
|
||||
}
|
||||
} else {
|
||||
item.TargetDir = "-"
|
||||
}
|
||||
record, _ := cronjobRepo.RecordFirst(cronjob.ID)
|
||||
if record.ID != 0 {
|
||||
item.LastRecordTime = record.StartTime.Format("2006-01-02 15:04:05")
|
||||
@@ -83,19 +76,19 @@ func (u *CronjobService) SearchRecords(search dto.SearchRecord) (int64, interfac
|
||||
return total, dtoCronjobs, err
|
||||
}
|
||||
|
||||
func (u *CronjobService) LoadRecordLog(req dto.OperateByID) (string, error) {
|
||||
func (u *CronjobService) LoadRecordLog(req dto.OperateByID) string {
|
||||
record, err := cronjobRepo.GetRecord(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return "", err
|
||||
return ""
|
||||
}
|
||||
if _, err := os.Stat(record.Records); err != nil {
|
||||
return "", buserr.New("ErrHttpReqNotFound")
|
||||
return ""
|
||||
}
|
||||
content, err := os.ReadFile(record.Records)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return ""
|
||||
}
|
||||
return string(content), nil
|
||||
return string(content)
|
||||
}
|
||||
|
||||
func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
|
||||
@@ -103,24 +96,23 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req.CleanData && hasBackup(cronjob.Type) {
|
||||
cronjob.RetainCopies = 0
|
||||
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if backup.Type != "LOCAL" {
|
||||
localDir, err := loadLocalDir()
|
||||
if req.CleanData {
|
||||
if hasBackup(cronjob.Type) {
|
||||
accountMap, err := loadClientMap(cronjob.BackupAccounts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
|
||||
cronjob.RetainCopies = 0
|
||||
u.removeExpiredBackup(cronjob, accountMap, model.BackupRecord{})
|
||||
} else {
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, "", &cronjob, nil)
|
||||
u.removeExpiredLog(cronjob)
|
||||
}
|
||||
}
|
||||
if req.IsDelete {
|
||||
records, _ := backupRepo.ListRecord(backupRepo.WithByCronID(cronjob.ID))
|
||||
for _, records := range records {
|
||||
records.CronjobID = 0
|
||||
_ = backupRepo.UpdateRecord(&records)
|
||||
}
|
||||
}
|
||||
delRecords, err := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(req.CronjobID)))
|
||||
@@ -184,29 +176,40 @@ func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
cronjob.Status = constant.StatusEnable
|
||||
cronjob.Spec = loadSpec(cronjob)
|
||||
|
||||
global.LOG.Infof("create cronjob %s successful, spec: %s", cronjob.Name, cronjob.Spec)
|
||||
entryID, err := u.StartJob(&cronjob)
|
||||
spec := cronjob.Spec
|
||||
entryIDs, err := u.StartJob(&cronjob)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cronjob.EntryID = uint64(entryID)
|
||||
cronjob.Spec = spec
|
||||
cronjob.EntryIDs = entryIDs
|
||||
if err := cronjobRepo.Create(&cronjob); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) StartJob(cronjob *model.Cronjob) (int, error) {
|
||||
if cronjob.EntryID != 0 {
|
||||
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
|
||||
func (u *CronjobService) StartJob(cronjob *model.Cronjob) (string, error) {
|
||||
if len(cronjob.EntryIDs) != 0 {
|
||||
ids := strings.Split(cronjob.EntryIDs, ",")
|
||||
for _, id := range ids {
|
||||
idItem, _ := strconv.Atoi(id)
|
||||
global.Cron.Remove(cron.EntryID(idItem))
|
||||
}
|
||||
}
|
||||
entryID, err := u.AddCronJob(cronjob)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
specs := strings.Split(cronjob.Spec, ",")
|
||||
var ids []string
|
||||
for _, spec := range specs {
|
||||
cronjob.Spec = spec
|
||||
entryID, err := u.AddCronJob(cronjob)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ids = append(ids, fmt.Sprintf("%v", entryID))
|
||||
}
|
||||
return entryID, nil
|
||||
return strings.Join(ids, ","), nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error {
|
||||
@@ -215,9 +218,13 @@ func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error {
|
||||
if cronjob.ID == 0 {
|
||||
return errors.New("find cronjob in db failed")
|
||||
}
|
||||
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
|
||||
global.LOG.Infof("stop cronjob entryID: %d", cronjob.EntryID)
|
||||
if err := u.CleanRecord(dto.CronjobClean{CronjobID: id, CleanData: req.CleanData}); err != nil {
|
||||
ids := strings.Split(cronjob.EntryIDs, ",")
|
||||
for _, id := range ids {
|
||||
idItem, _ := strconv.Atoi(id)
|
||||
global.Cron.Remove(cron.EntryID(idItem))
|
||||
}
|
||||
global.LOG.Infof("stop cronjob entryID: %s", cronjob.EntryIDs)
|
||||
if err := u.CleanRecord(dto.CronjobClean{CronjobID: id, CleanData: req.CleanData, IsDelete: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cronjobRepo.Delete(commonRepo.WithByID(id)); err != nil {
|
||||
@@ -238,37 +245,38 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
upMap := make(map[string]interface{})
|
||||
cronjob.EntryID = cronModel.EntryID
|
||||
cronjob.EntryIDs = cronModel.EntryIDs
|
||||
cronjob.Type = cronModel.Type
|
||||
cronjob.Spec = loadSpec(cronjob)
|
||||
spec := cronjob.Spec
|
||||
if cronModel.Status == constant.StatusEnable {
|
||||
newEntryID, err := u.StartJob(&cronjob)
|
||||
newEntryIDs, err := u.StartJob(&cronjob)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
upMap["entry_id"] = newEntryID
|
||||
upMap["entry_ids"] = newEntryIDs
|
||||
} else {
|
||||
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
|
||||
ids := strings.Split(cronjob.EntryIDs, ",")
|
||||
for _, id := range ids {
|
||||
idItem, _ := strconv.Atoi(id)
|
||||
global.Cron.Remove(cron.EntryID(idItem))
|
||||
}
|
||||
}
|
||||
|
||||
upMap["name"] = req.Name
|
||||
upMap["spec"] = cronjob.Spec
|
||||
upMap["spec"] = spec
|
||||
upMap["script"] = req.Script
|
||||
upMap["command"] = req.Command
|
||||
upMap["container_name"] = req.ContainerName
|
||||
upMap["spec_type"] = req.SpecType
|
||||
upMap["week"] = req.Week
|
||||
upMap["day"] = req.Day
|
||||
upMap["hour"] = req.Hour
|
||||
upMap["minute"] = req.Minute
|
||||
upMap["second"] = req.Second
|
||||
upMap["app_id"] = req.AppID
|
||||
upMap["website"] = req.Website
|
||||
upMap["exclusion_rules"] = req.ExclusionRules
|
||||
upMap["db_type"] = req.DBType
|
||||
upMap["db_name"] = req.DBName
|
||||
upMap["url"] = req.URL
|
||||
upMap["source_dir"] = req.SourceDir
|
||||
upMap["keep_local"] = req.KeepLocal
|
||||
upMap["target_dir_id"] = req.TargetDirID
|
||||
|
||||
upMap["backup_accounts"] = req.BackupAccounts
|
||||
upMap["default_download"] = req.DefaultDownload
|
||||
upMap["retain_copies"] = req.RetainCopies
|
||||
return cronjobRepo.Update(id, upMap)
|
||||
}
|
||||
@@ -279,19 +287,23 @@ func (u *CronjobService) UpdateStatus(id uint, status string) error {
|
||||
return errors.WithMessage(constant.ErrRecordNotFound, "record not found")
|
||||
}
|
||||
var (
|
||||
entryID int
|
||||
err error
|
||||
entryIDs string
|
||||
err error
|
||||
)
|
||||
if status == constant.StatusEnable {
|
||||
entryID, err = u.StartJob(&cronjob)
|
||||
entryIDs, err = u.StartJob(&cronjob)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
|
||||
global.LOG.Infof("stop cronjob entryID: %d", cronjob.EntryID)
|
||||
ids := strings.Split(cronjob.EntryIDs, ",")
|
||||
for _, id := range ids {
|
||||
idItem, _ := strconv.Atoi(id)
|
||||
global.Cron.Remove(cron.EntryID(idItem))
|
||||
}
|
||||
global.LOG.Infof("stop cronjob entryID: %s", cronjob.EntryIDs)
|
||||
}
|
||||
return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"status": status, "entry_id": entryID})
|
||||
return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"status": status, "entry_ids": entryIDs})
|
||||
}
|
||||
|
||||
func (u *CronjobService) AddCronJob(cronjob *model.Cronjob) (int, error) {
|
||||
@@ -327,26 +339,3 @@ func mkdirAndWriteFile(cronjob *model.Cronjob, startTime time.Time, msg []byte)
|
||||
write.Flush()
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func loadSpec(cronjob model.Cronjob) string {
|
||||
switch cronjob.SpecType {
|
||||
case "perMonth":
|
||||
return fmt.Sprintf("%v %v %v * *", cronjob.Minute, cronjob.Hour, cronjob.Day)
|
||||
case "perWeek":
|
||||
return fmt.Sprintf("%v %v * * %v", cronjob.Minute, cronjob.Hour, cronjob.Week)
|
||||
case "perNDay":
|
||||
return fmt.Sprintf("%v %v */%v * *", cronjob.Minute, cronjob.Hour, cronjob.Day)
|
||||
case "perDay":
|
||||
return fmt.Sprintf("%v %v * * *", cronjob.Minute, cronjob.Hour)
|
||||
case "perNHour":
|
||||
return fmt.Sprintf("%v */%v * * *", cronjob.Minute, cronjob.Hour)
|
||||
case "perHour":
|
||||
return fmt.Sprintf("%v * * * *", cronjob.Minute)
|
||||
case "perNMinute":
|
||||
return fmt.Sprintf("@every %vm", cronjob.Minute)
|
||||
case "perNSecond":
|
||||
return fmt.Sprintf("@every %vs", cronjob.Second)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,386 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
)
|
||||
|
||||
func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time) error {
|
||||
var apps []model.AppInstall
|
||||
if cronjob.AppID == "all" {
|
||||
apps, _ = appInstallRepo.ListBy()
|
||||
} else {
|
||||
itemID, _ := (strconv.Atoi(cronjob.AppID))
|
||||
app, err := appInstallRepo.GetFirst(commonRepo.WithByID(uint(itemID)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
apps = append(apps, app)
|
||||
}
|
||||
accountMap, err := loadClientMap(cronjob.BackupAccounts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, app := range apps {
|
||||
var record model.BackupRecord
|
||||
record.From = "cronjob"
|
||||
record.Type = "app"
|
||||
record.CronjobID = cronjob.ID
|
||||
record.Name = app.App.Key
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
u.removeExpiredBackup(cronjob, accountMap, record)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Time) error {
|
||||
webs := loadWebsForJob(cronjob)
|
||||
accountMap, err := loadClientMap(cronjob.BackupAccounts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, web := range webs {
|
||||
var record model.BackupRecord
|
||||
record.From = "cronjob"
|
||||
record.Type = "website"
|
||||
record.CronjobID = cronjob.ID
|
||||
record.Name = web.PrimaryDomain
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
u.removeExpiredBackup(cronjob, accountMap, record)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Time) error {
|
||||
dbs := loadDbsForJob(cronjob)
|
||||
accountMap, err := loadClientMap(cronjob.BackupAccounts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, dbInfo := range dbs {
|
||||
var record model.BackupRecord
|
||||
record.From = "cronjob"
|
||||
record.Type = dbInfo.DBType
|
||||
record.CronjobID = cronjob.ID
|
||||
record.Name = dbInfo.Database
|
||||
record.DetailName = dbInfo.Name
|
||||
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))
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
|
||||
if err := handleMysqlBackup(dbInfo.Database, dbInfo.DBType, dbInfo.Name, backupDir, record.FileName); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := handlePostgresqlBackup(dbInfo.Database, dbInfo.Name, backupDir, record.FileName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
u.removeExpiredBackup(cronjob, accountMap, record)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.Time) error {
|
||||
accountMap, err := loadClientMap(cronjob.BackupAccounts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileName := fmt.Sprintf("directory%s_%s.tar.gz", strings.ReplaceAll(cronjob.SourceDir, "/", "_"), startTime.Format("20060102150405")+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 {
|
||||
return err
|
||||
}
|
||||
var record model.BackupRecord
|
||||
record.From = "cronjob"
|
||||
record.Type = "directory"
|
||||
record.CronjobID = cronjob.ID
|
||||
record.Name = cronjob.Name
|
||||
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, fileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
record.FileName = fileName
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
u.removeExpiredBackup(cronjob, accountMap, record)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.Time) error {
|
||||
accountMap, err := loadClientMap(cronjob.BackupAccounts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nameItem := startTime.Format("20060102150405") + 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 {
|
||||
return err
|
||||
}
|
||||
var record model.BackupRecord
|
||||
record.From = "cronjob"
|
||||
record.Type = "log"
|
||||
record.CronjobID = cronjob.ID
|
||||
record.Name = cronjob.Name
|
||||
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(path.Dir(backupDir), fileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
record.FileName = fileName
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
u.removeExpiredBackup(cronjob, accountMap, record)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, startTime time.Time, logPath string) error {
|
||||
accountMap, err := loadClientMap(cronjob.BackupAccounts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var record model.BackupRecord
|
||||
record.From = "cronjob"
|
||||
record.Type = "directory"
|
||||
record.CronjobID = cronjob.ID
|
||||
record.Name = cronjob.Name
|
||||
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
|
||||
record.FileDir = "system_snapshot"
|
||||
|
||||
req := dto.SnapshotCreate{
|
||||
From: record.BackupType,
|
||||
DefaultDownload: cronjob.DefaultDownload,
|
||||
}
|
||||
name, err := NewISnapshotService().HandleSnapshot(true, logPath, req, startTime.Format("20060102150405")+common.RandStrAndNum(5))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
record.FileName = name + ".tar.gz"
|
||||
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
u.removeExpiredBackup(cronjob, accountMap, record)
|
||||
return nil
|
||||
}
|
||||
|
||||
type databaseHelper struct {
|
||||
DBType string
|
||||
Database string
|
||||
Name string
|
||||
}
|
||||
|
||||
func loadDbsForJob(cronjob model.Cronjob) []databaseHelper {
|
||||
var dbs []databaseHelper
|
||||
if cronjob.DBName == "all" {
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
|
||||
mysqlItems, _ := mysqlRepo.List()
|
||||
for _, mysql := range mysqlItems {
|
||||
dbs = append(dbs, databaseHelper{
|
||||
DBType: cronjob.DBType,
|
||||
Database: mysql.MysqlName,
|
||||
Name: mysql.Name,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
pgItems, _ := postgresqlRepo.List()
|
||||
for _, pg := range pgItems {
|
||||
dbs = append(dbs, databaseHelper{
|
||||
DBType: cronjob.DBType,
|
||||
Database: pg.PostgresqlName,
|
||||
Name: pg.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
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,
|
||||
})
|
||||
}
|
||||
return dbs
|
||||
}
|
||||
|
||||
func loadWebsForJob(cronjob model.Cronjob) []model.Website {
|
||||
var weblist []model.Website
|
||||
if 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)
|
||||
}
|
||||
return weblist
|
||||
}
|
||||
|
||||
func loadRecordPath(cronjob model.Cronjob, accountMap map[string]cronjobUploadHelper) (string, string) {
|
||||
source := accountMap[fmt.Sprintf("%v", cronjob.DefaultDownload)].backType
|
||||
targets := strings.Split(cronjob.BackupAccounts, ",")
|
||||
var itemAccounts []string
|
||||
for _, target := range targets {
|
||||
if len(target) == 0 {
|
||||
continue
|
||||
}
|
||||
if len(accountMap[target].backType) != 0 {
|
||||
itemAccounts = append(itemAccounts, accountMap[target].backType)
|
||||
}
|
||||
}
|
||||
backupType := strings.Join(itemAccounts, ",")
|
||||
return source, backupType
|
||||
}
|
||||
|
||||
func handleBackupLogs(targetDir, fileName string) error {
|
||||
websites, err := websiteRepo.List()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(websites) != 0 {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
webItem := path.Join(nginxInstall.GetPath(), "www/sites")
|
||||
for _, website := range websites {
|
||||
dirItem := path.Join(targetDir, "website", website.Alias)
|
||||
if _, err := os.Stat(dirItem); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dirItem, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
itemDir := path.Join(webItem, website.Alias, "log")
|
||||
logFiles, _ := os.ReadDir(itemDir)
|
||||
if len(logFiles) != 0 {
|
||||
for i := 0; i < len(logFiles); i++ {
|
||||
if !logFiles[i].IsDir() {
|
||||
_ = common.CopyFile(path.Join(itemDir, logFiles[i].Name()), dirItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
itemDir2 := path.Join(global.CONF.System.Backup, "log/website", website.Alias)
|
||||
logFiles2, _ := os.ReadDir(itemDir2)
|
||||
if len(logFiles2) != 0 {
|
||||
for i := 0; i < len(logFiles2); i++ {
|
||||
if !logFiles2[i].IsDir() {
|
||||
_ = common.CopyFile(path.Join(itemDir2, logFiles2[i].Name()), dirItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
global.LOG.Debug("backup website log successful!")
|
||||
}
|
||||
|
||||
systemLogDir := path.Join(global.CONF.System.BaseDir, "1panel/log")
|
||||
systemDir := path.Join(targetDir, "system")
|
||||
if _, err := os.Stat(systemDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(systemDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
systemLogFiles, _ := os.ReadDir(systemLogDir)
|
||||
if len(systemLogFiles) != 0 {
|
||||
for i := 0; i < len(systemLogFiles); i++ {
|
||||
if !systemLogFiles[i].IsDir() {
|
||||
_ = common.CopyFile(path.Join(systemLogDir, systemLogFiles[i].Name()), systemDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
global.LOG.Debug("backup system log successful!")
|
||||
|
||||
loginLogFiles, _ := os.ReadDir("/var/log")
|
||||
loginDir := path.Join(targetDir, "login")
|
||||
if _, err := os.Stat(loginDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(loginDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(loginLogFiles) != 0 {
|
||||
for i := 0; i < len(loginLogFiles); i++ {
|
||||
if !loginLogFiles[i].IsDir() && (strings.HasPrefix(loginLogFiles[i].Name(), "secure") || strings.HasPrefix(loginLogFiles[i].Name(), "auth.log")) {
|
||||
_ = common.CopyFile(path.Join("/var/log", loginLogFiles[i].Name()), loginDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
global.LOG.Debug("backup ssh log successful!")
|
||||
|
||||
if err := handleTar(targetDir, path.Dir(targetDir), fileName, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = os.RemoveAll(targetDir)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
@@ -5,13 +5,14 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage"
|
||||
@@ -33,48 +34,62 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
if len(cronjob.Script) == 0 {
|
||||
return
|
||||
}
|
||||
record.Records = u.generateLogsPath(*cronjob, record.StartTime)
|
||||
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
|
||||
script := cronjob.Script
|
||||
if len(cronjob.ContainerName) != 0 {
|
||||
message, err = u.handleShell(cronjob.Type, cronjob.Name, fmt.Sprintf("docker exec %s %s", cronjob.ContainerName, cronjob.Script))
|
||||
} else {
|
||||
message, err = u.handleShell(cronjob.Type, cronjob.Name, cronjob.Script)
|
||||
command := "sh"
|
||||
if len(cronjob.Command) != 0 {
|
||||
command = cronjob.Command
|
||||
}
|
||||
script = fmt.Sprintf("docker exec %s %s -c \"%s\"", cronjob.ContainerName, command, strings.ReplaceAll(cronjob.Script, "\"", "\\\""))
|
||||
}
|
||||
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
|
||||
case "snapshot":
|
||||
messageItem := ""
|
||||
messageItem, record.File, err = u.handleSnapshot(cronjob, record.StartTime)
|
||||
message = []byte(messageItem)
|
||||
err = u.handleShell(cronjob.Type, cronjob.Name, script, record.Records)
|
||||
u.removeExpiredLog(*cronjob)
|
||||
case "curl":
|
||||
if len(cronjob.URL) == 0 {
|
||||
return
|
||||
}
|
||||
message, err = u.handleShell(cronjob.Type, cronjob.Name, fmt.Sprintf("curl '%s'", cronjob.URL))
|
||||
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
|
||||
record.Records = u.generateLogsPath(*cronjob, record.StartTime)
|
||||
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
|
||||
err = u.handleShell(cronjob.Type, cronjob.Name, fmt.Sprintf("curl '%s'", cronjob.URL), record.Records)
|
||||
u.removeExpiredLog(*cronjob)
|
||||
case "ntp":
|
||||
err = u.handleNtpSync()
|
||||
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
|
||||
case "website", "database", "app":
|
||||
record.File, err = u.handleBackup(cronjob, record.StartTime)
|
||||
case "directory":
|
||||
if len(cronjob.SourceDir) == 0 {
|
||||
return
|
||||
}
|
||||
record.File, err = u.handleBackup(cronjob, record.StartTime)
|
||||
u.removeExpiredLog(*cronjob)
|
||||
case "cutWebsiteLog":
|
||||
record.File, err = u.handleCutWebsiteLog(cronjob, record.StartTime)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("cut website log file failed, err: %v", err)
|
||||
}
|
||||
var messageItem []string
|
||||
messageItem, record.File, err = u.handleCutWebsiteLog(cronjob, record.StartTime)
|
||||
message = []byte(strings.Join(messageItem, "\n"))
|
||||
case "clean":
|
||||
messageItem := ""
|
||||
messageItem, err = u.handleSystemClean()
|
||||
message = []byte(messageItem)
|
||||
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
|
||||
u.removeExpiredLog(*cronjob)
|
||||
case "website":
|
||||
err = u.handleWebsite(*cronjob, record.StartTime)
|
||||
case "app":
|
||||
err = u.handleApp(*cronjob, record.StartTime)
|
||||
case "database":
|
||||
err = u.handleDatabase(*cronjob, record.StartTime)
|
||||
case "directory":
|
||||
if len(cronjob.SourceDir) == 0 {
|
||||
return
|
||||
}
|
||||
err = u.handleDirectory(*cronjob, record.StartTime)
|
||||
case "log":
|
||||
record.File, err = u.handleSystemLog(*cronjob, record.StartTime)
|
||||
err = u.handleSystemLog(*cronjob, record.StartTime)
|
||||
case "snapshot":
|
||||
record.Records = u.generateLogsPath(*cronjob, record.StartTime)
|
||||
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
|
||||
err = u.handleSnapshot(*cronjob, record.StartTime, record.Records)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), string(message))
|
||||
if len(message) != 0 {
|
||||
record.Records, _ = mkdirAndWriteFile(cronjob, record.StartTime, message)
|
||||
}
|
||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
|
||||
return
|
||||
}
|
||||
if len(message) != 0 {
|
||||
@@ -87,18 +102,17 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
}()
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleShell(cronType, cornName, script string) ([]byte, error) {
|
||||
func (u *CronjobService) handleShell(cronType, cornName, script, logPath string) error {
|
||||
handleDir := fmt.Sprintf("%s/task/%s/%s", constant.DataDir, cronType, cornName)
|
||||
if _, err := os.Stat(handleDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(handleDir, os.ModePerm); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
}
|
||||
stdout, err := cmd.ExecCronjobWithTimeOut(script, handleDir, 24*time.Hour)
|
||||
if err != nil {
|
||||
return []byte(stdout), err
|
||||
if err := cmd.ExecCronjobWithTimeOut(script, handleDir, logPath, 24*time.Hour); err != nil {
|
||||
return err
|
||||
}
|
||||
return []byte(stdout), nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleNtpSync() error {
|
||||
@@ -116,98 +130,6 @@ func (u *CronjobService) handleNtpSync() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleBackup(cronjob *model.Cronjob, startTime time.Time) (string, error) {
|
||||
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
global.LOG.Infof("start to backup %s %s to %s", cronjob.Type, cronjob.Name, backup.Type)
|
||||
|
||||
switch cronjob.Type {
|
||||
case "database":
|
||||
paths, err := u.handleDatabase(*cronjob, backup, startTime)
|
||||
return strings.Join(paths, ","), err
|
||||
case "app":
|
||||
paths, err := u.handleApp(*cronjob, backup, startTime)
|
||||
return strings.Join(paths, ","), err
|
||||
case "website":
|
||||
paths, err := u.handleWebsite(*cronjob, backup, startTime)
|
||||
return strings.Join(paths, ","), err
|
||||
default:
|
||||
fileName := fmt.Sprintf("directory%s_%s.tar.gz", strings.ReplaceAll(cronjob.SourceDir, "/", "_"), startTime.Format("20060102150405"))
|
||||
backupDir := path.Join(localDir, fmt.Sprintf("%s/%s", cronjob.Type, cronjob.Name))
|
||||
itemFileDir := fmt.Sprintf("%s/%s", cronjob.Type, cronjob.Name)
|
||||
global.LOG.Infof("handle tar %s to %s", backupDir, fileName)
|
||||
if err := handleTar(cronjob.SourceDir, backupDir, fileName, cronjob.ExclusionRules); err != nil {
|
||||
return "", err
|
||||
}
|
||||
var client cloud_storage.CloudStorageClient
|
||||
if backup.Type != "LOCAL" {
|
||||
if !cronjob.KeepLocal {
|
||||
defer func() {
|
||||
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, fileName))
|
||||
}()
|
||||
}
|
||||
client, err = NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(backup.BackupPath) != 0 {
|
||||
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
|
||||
}
|
||||
if _, err = client.Upload(backupDir+"/"+fileName, itemFileDir+"/"+fileName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, cronjob, client)
|
||||
if backup.Type == "LOCAL" || cronjob.KeepLocal {
|
||||
return fmt.Sprintf("%s/%s", backupDir, fileName), nil
|
||||
} else {
|
||||
return fmt.Sprintf("%s/%s", itemFileDir, fileName), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *CronjobService) HandleRmExpired(backType, backupPath, localDir string, cronjob *model.Cronjob, backClient cloud_storage.CloudStorageClient) {
|
||||
global.LOG.Infof("start to handle remove expired, retain copies: %d", cronjob.RetainCopies)
|
||||
records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), commonRepo.WithOrderBy("created_at desc"))
|
||||
if len(records) <= int(cronjob.RetainCopies) {
|
||||
return
|
||||
}
|
||||
for i := int(cronjob.RetainCopies); i < len(records); i++ {
|
||||
if len(records[i].File) != 0 {
|
||||
files := strings.Split(records[i].File, ",")
|
||||
for _, file := range files {
|
||||
_ = os.Remove(file)
|
||||
_ = backupRepo.DeleteRecord(context.TODO(), backupRepo.WithByFileName(path.Base(file)))
|
||||
if backType == "LOCAL" {
|
||||
continue
|
||||
}
|
||||
|
||||
fileItem := file
|
||||
if cronjob.KeepLocal {
|
||||
if len(backupPath) != 0 {
|
||||
fileItem = path.Join(strings.TrimPrefix(backupPath, "/") + strings.TrimPrefix(file, localDir+"/"))
|
||||
} else {
|
||||
fileItem = strings.TrimPrefix(file, localDir+"/")
|
||||
}
|
||||
}
|
||||
|
||||
if cronjob.Type == "snapshot" {
|
||||
_ = snapshotRepo.Delete(commonRepo.WithByName(strings.TrimSuffix(path.Base(fileItem), ".tar.gz")))
|
||||
}
|
||||
_, _ = backClient.Delete(fileItem)
|
||||
}
|
||||
}
|
||||
_ = cronjobRepo.DeleteRecord(commonRepo.WithByID(uint(records[i].ID)))
|
||||
_ = os.Remove(records[i].Records)
|
||||
}
|
||||
}
|
||||
|
||||
func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
|
||||
@@ -265,433 +187,190 @@ func handleUnTar(sourceFile, targetDir string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleDatabase(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
|
||||
var paths []string
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
|
||||
var dbs []model.DatabaseMysql
|
||||
if cronjob.DBName == "all" {
|
||||
dbs, err = mysqlRepo.List()
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
} else {
|
||||
itemID, _ := (strconv.Atoi(cronjob.DBName))
|
||||
dbs, err = mysqlRepo.List(commonRepo.WithByID(uint(itemID)))
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
}
|
||||
|
||||
var client cloud_storage.CloudStorageClient
|
||||
if backup.Type != "LOCAL" {
|
||||
client, err = NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, dbInfo := range dbs {
|
||||
var record model.BackupRecord
|
||||
|
||||
database, _ := databaseRepo.Get(commonRepo.WithByName(dbInfo.MysqlName))
|
||||
record.Type = database.Type
|
||||
record.Source = "LOCAL"
|
||||
record.BackupType = backup.Type
|
||||
|
||||
record.Name = dbInfo.MysqlName
|
||||
backupDir := path.Join(localDir, fmt.Sprintf("database/%s/%s/%s", database.Type, record.Name, dbInfo.Name))
|
||||
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format("20060102150405"))
|
||||
if err = handleMysqlBackup(dbInfo.MysqlName, dbInfo.Name, backupDir, record.FileName); err != nil {
|
||||
return paths, err
|
||||
}
|
||||
|
||||
record.DetailName = dbInfo.Name
|
||||
record.FileDir = backupDir
|
||||
itemFileDir := strings.TrimPrefix(backupDir, localDir+"/")
|
||||
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
|
||||
record.Source = backup.Type
|
||||
record.FileDir = itemFileDir
|
||||
}
|
||||
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return paths, err
|
||||
}
|
||||
if backup.Type != "LOCAL" {
|
||||
if !cronjob.KeepLocal {
|
||||
defer func() {
|
||||
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, record.FileName))
|
||||
}()
|
||||
}
|
||||
if len(backup.BackupPath) != 0 {
|
||||
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
|
||||
}
|
||||
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
|
||||
return paths, err
|
||||
}
|
||||
}
|
||||
if backup.Type == "LOCAL" || cronjob.KeepLocal {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s", record.FileDir, record.FileName))
|
||||
} else {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s", itemFileDir, record.FileName))
|
||||
}
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime time.Time) (string, error) {
|
||||
func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime time.Time) ([]string, string, error) {
|
||||
var (
|
||||
websites []string
|
||||
err error
|
||||
filePaths []string
|
||||
msgs []string
|
||||
)
|
||||
if cronjob.Website == "all" {
|
||||
websites, _ = NewIWebsiteService().GetWebsiteOptions()
|
||||
if len(websites) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
} else {
|
||||
websites = append(websites, cronjob.Website)
|
||||
}
|
||||
|
||||
websites := loadWebsForJob(*cronjob)
|
||||
nginx, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
return msgs, "", nil
|
||||
}
|
||||
baseDir := path.Join(nginx.GetPath(), "www", "sites")
|
||||
fileOp := files.NewFileOp()
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(websites))
|
||||
for _, websiteName := range websites {
|
||||
name := websiteName
|
||||
go func() {
|
||||
website, _ := websiteRepo.GetFirst(websiteRepo.WithDomain(name))
|
||||
if website.ID == 0 {
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
websiteLogDir := path.Join(baseDir, website.Alias, "log")
|
||||
srcAccessLogPath := path.Join(websiteLogDir, "access.log")
|
||||
srcErrorLogPath := path.Join(websiteLogDir, "error.log")
|
||||
dstLogDir := path.Join(global.CONF.System.Backup, "log", "website", website.Alias)
|
||||
if !fileOp.Stat(dstLogDir) {
|
||||
_ = os.MkdirAll(dstLogDir, 0755)
|
||||
}
|
||||
for _, website := range websites {
|
||||
websiteLogDir := path.Join(baseDir, website.Alias, "log")
|
||||
srcAccessLogPath := path.Join(websiteLogDir, "access.log")
|
||||
srcErrorLogPath := path.Join(websiteLogDir, "error.log")
|
||||
dstLogDir := path.Join(global.CONF.System.Backup, "log", "website", website.Alias)
|
||||
if !fileOp.Stat(dstLogDir) {
|
||||
_ = os.MkdirAll(dstLogDir, 0755)
|
||||
}
|
||||
|
||||
dstName := fmt.Sprintf("%s_log_%s.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
|
||||
filePaths = append(filePaths, path.Join(dstLogDir, dstName))
|
||||
if err = fileOp.Compress([]string{srcAccessLogPath, srcErrorLogPath}, dstLogDir, dstName, files.Gz); err != nil {
|
||||
global.LOG.Errorf("There was an error in compressing the website[%s] access.log, err: %v", website.PrimaryDomain, err)
|
||||
} else {
|
||||
_ = fileOp.WriteFile(srcAccessLogPath, strings.NewReader(""), 0755)
|
||||
_ = fileOp.WriteFile(srcErrorLogPath, strings.NewReader(""), 0755)
|
||||
}
|
||||
global.LOG.Infof("The website[%s] log file was successfully rotated in the directory [%s]", website.PrimaryDomain, dstLogDir)
|
||||
var record model.BackupRecord
|
||||
record.Type = "cutWebsiteLog"
|
||||
record.Name = cronjob.Website
|
||||
record.Source = "LOCAL"
|
||||
record.BackupType = "LOCAL"
|
||||
record.FileDir = dstLogDir
|
||||
record.FileName = dstName
|
||||
if err = backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
|
||||
return strings.Join(filePaths, ","), nil
|
||||
}
|
||||
dstName := fmt.Sprintf("%s_log_%s.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
|
||||
dstFilePath := path.Join(dstLogDir, dstName)
|
||||
filePaths = append(filePaths, dstFilePath)
|
||||
|
||||
func (u *CronjobService) handleApp(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
|
||||
var paths []string
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
|
||||
var applist []model.AppInstall
|
||||
if cronjob.AppID == "all" {
|
||||
applist, err = appInstallRepo.ListBy()
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
} else {
|
||||
itemID, _ := (strconv.Atoi(cronjob.AppID))
|
||||
app, err := appInstallRepo.GetFirst(commonRepo.WithByID(uint(itemID)))
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
applist = append(applist, app)
|
||||
}
|
||||
|
||||
var client cloud_storage.CloudStorageClient
|
||||
if backup.Type != "LOCAL" {
|
||||
client, err = NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, app := range applist {
|
||||
var record model.BackupRecord
|
||||
record.Type = "app"
|
||||
record.Name = app.App.Key
|
||||
record.DetailName = app.Name
|
||||
record.Source = "LOCAL"
|
||||
record.BackupType = backup.Type
|
||||
backupDir := path.Join(localDir, fmt.Sprintf("app/%s/%s", app.App.Key, app.Name))
|
||||
record.FileDir = backupDir
|
||||
itemFileDir := strings.TrimPrefix(backupDir, localDir+"/")
|
||||
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
|
||||
record.Source = backup.Type
|
||||
record.FileDir = strings.TrimPrefix(backupDir, localDir+"/")
|
||||
}
|
||||
record.FileName = fmt.Sprintf("app_%s_%s.tar.gz", app.Name, startTime.Format("20060102150405"))
|
||||
if err := handleAppBackup(&app, backupDir, record.FileName); err != nil {
|
||||
return paths, err
|
||||
}
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return paths, err
|
||||
}
|
||||
if backup.Type != "LOCAL" {
|
||||
if !cronjob.KeepLocal {
|
||||
defer func() {
|
||||
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, record.FileName))
|
||||
}()
|
||||
}
|
||||
if len(backup.BackupPath) != 0 {
|
||||
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
|
||||
}
|
||||
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
|
||||
return paths, err
|
||||
}
|
||||
}
|
||||
if backup.Type == "LOCAL" || cronjob.KeepLocal {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s", record.FileDir, record.FileName))
|
||||
if err = backupLogFile(dstFilePath, websiteLogDir, fileOp); err != nil {
|
||||
websiteErr := buserr.WithNameAndErr("ErrCutWebsiteLog", website.PrimaryDomain, err)
|
||||
err = websiteErr
|
||||
msgs = append(msgs, websiteErr.Error())
|
||||
global.LOG.Error(websiteErr.Error())
|
||||
continue
|
||||
} else {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s", itemFileDir, record.FileName))
|
||||
_ = fileOp.WriteFile(srcAccessLogPath, strings.NewReader(""), 0755)
|
||||
_ = fileOp.WriteFile(srcErrorLogPath, strings.NewReader(""), 0755)
|
||||
}
|
||||
msg := i18n.GetMsgWithMap("CutWebsiteLogSuccess", map[string]interface{}{"name": website.PrimaryDomain, "path": dstFilePath})
|
||||
global.LOG.Infof(msg)
|
||||
msgs = append(msgs, msg)
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
|
||||
return paths, nil
|
||||
u.removeExpiredLog(*cronjob)
|
||||
return msgs, strings.Join(filePaths, ","), err
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
|
||||
var paths []string
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
|
||||
var weblist []string
|
||||
if cronjob.Website == "all" {
|
||||
weblist, err = NewIWebsiteService().GetWebsiteOptions()
|
||||
if err != nil {
|
||||
return paths, err
|
||||
func backupLogFile(dstFilePath, websiteLogDir string, fileOp files.FileOp) error {
|
||||
if err := cmd.ExecCmd(fmt.Sprintf("tar -czf %s -C %s %s", dstFilePath, websiteLogDir, strings.Join([]string{"access.log", "error.log"}, " "))); err != nil {
|
||||
dstDir := path.Dir(dstFilePath)
|
||||
if err = fileOp.Copy(path.Join(websiteLogDir, "access.log"), dstDir); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
weblist = append(weblist, cronjob.Website)
|
||||
}
|
||||
|
||||
var client cloud_storage.CloudStorageClient
|
||||
if backup.Type != "LOCAL" {
|
||||
client, err = NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return paths, err
|
||||
if err = fileOp.Copy(path.Join(websiteLogDir, "error.log"), dstDir); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, websiteItem := range weblist {
|
||||
var record model.BackupRecord
|
||||
record.Type = "website"
|
||||
record.Name = cronjob.Website
|
||||
record.Source = "LOCAL"
|
||||
record.BackupType = backup.Type
|
||||
website, err := websiteRepo.GetFirst(websiteRepo.WithDomain(websiteItem))
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
backupDir := path.Join(localDir, fmt.Sprintf("website/%s", website.PrimaryDomain))
|
||||
record.FileDir = backupDir
|
||||
itemFileDir := strings.TrimPrefix(backupDir, localDir+"/")
|
||||
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
|
||||
record.Source = backup.Type
|
||||
record.FileDir = strings.TrimPrefix(backupDir, localDir+"/")
|
||||
}
|
||||
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
|
||||
if err := handleWebsiteBackup(&website, backupDir, record.FileName); err != nil {
|
||||
return paths, err
|
||||
}
|
||||
record.Name = website.PrimaryDomain
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return paths, err
|
||||
}
|
||||
if backup.Type != "LOCAL" {
|
||||
if !cronjob.KeepLocal {
|
||||
defer func() {
|
||||
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, record.FileName))
|
||||
}()
|
||||
}
|
||||
if len(backup.BackupPath) != 0 {
|
||||
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
|
||||
}
|
||||
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
|
||||
return paths, err
|
||||
}
|
||||
}
|
||||
if backup.Type == "LOCAL" || cronjob.KeepLocal {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s", record.FileDir, record.FileName))
|
||||
} else {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s", itemFileDir, record.FileName))
|
||||
if err = cmd.ExecCmd(fmt.Sprintf("tar -czf %s -C %s %s", dstFilePath, dstDir, strings.Join([]string{"access.log", "error.log"}, " "))); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = fileOp.DeleteFile(path.Join(dstDir, "access.log"))
|
||||
_ = fileOp.DeleteFile(path.Join(dstDir, "error.log"))
|
||||
return nil
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleSnapshot(cronjob *model.Cronjob, startTime time.Time) (string, string, error) {
|
||||
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
client, err := NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
req := dto.SnapshotCreate{
|
||||
From: backup.Type,
|
||||
}
|
||||
message, name, err := NewISnapshotService().HandleSnapshot(true, req, startTime.Format("20060102150405"))
|
||||
if err != nil {
|
||||
return message, "", err
|
||||
}
|
||||
|
||||
path := path.Join(strings.TrimPrefix(backup.BackupPath, "/"), "system_snapshot", name+".tar.gz")
|
||||
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, "", cronjob, client)
|
||||
return message, path, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleSystemClean() (string, error) {
|
||||
return NewIDeviceService().CleanForCronjob()
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.Time) (string, error) {
|
||||
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
|
||||
func loadClientMap(backupAccounts string) (map[string]cronjobUploadHelper, error) {
|
||||
clients := make(map[string]cronjobUploadHelper)
|
||||
accounts, err := backupRepo.List()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pathItem := path.Join(global.CONF.System.BaseDir, "1panel/tmp/log", startTime.Format("20060102150405"))
|
||||
websites, err := websiteRepo.List()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(websites) != 0 {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return "", err
|
||||
targets := strings.Split(backupAccounts, ",")
|
||||
for _, target := range targets {
|
||||
if len(target) == 0 {
|
||||
continue
|
||||
}
|
||||
webItem := path.Join(nginxInstall.GetPath(), "www/sites")
|
||||
for _, website := range websites {
|
||||
dirItem := path.Join(pathItem, "website", website.Alias)
|
||||
if _, err := os.Stat(dirItem); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dirItem, os.ModePerm); err != nil {
|
||||
return "", err
|
||||
for _, account := range accounts {
|
||||
if target == account.Type {
|
||||
client, err := NewIBackupService().NewClient(&account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
itemDir := path.Join(webItem, website.Alias, "log")
|
||||
logFiles, _ := os.ReadDir(itemDir)
|
||||
if len(logFiles) != 0 {
|
||||
for i := 0; i < len(logFiles); i++ {
|
||||
if !logFiles[i].IsDir() {
|
||||
_ = cpBinary([]string{path.Join(itemDir, logFiles[i].Name())}, dirItem)
|
||||
}
|
||||
pathItem := account.BackupPath
|
||||
if account.BackupPath != "/" {
|
||||
pathItem = strings.TrimPrefix(account.BackupPath, "/")
|
||||
}
|
||||
}
|
||||
itemDir2 := path.Join(global.CONF.System.Backup, "log/website", website.Alias)
|
||||
logFiles2, _ := os.ReadDir(itemDir2)
|
||||
if len(logFiles2) != 0 {
|
||||
for i := 0; i < len(logFiles2); i++ {
|
||||
if !logFiles2[i].IsDir() {
|
||||
_ = cpBinary([]string{path.Join(itemDir2, logFiles2[i].Name())}, dirItem)
|
||||
}
|
||||
clients[target] = cronjobUploadHelper{
|
||||
client: client,
|
||||
backupPath: pathItem,
|
||||
backType: account.Type,
|
||||
}
|
||||
}
|
||||
}
|
||||
global.LOG.Debug("backup website log successful!")
|
||||
}
|
||||
return clients, nil
|
||||
}
|
||||
|
||||
systemLogDir := path.Join(global.CONF.System.BaseDir, "1panel/log")
|
||||
systemDir := path.Join(pathItem, "system")
|
||||
if _, err := os.Stat(systemDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(systemDir, os.ModePerm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
systemLogFiles, _ := os.ReadDir(systemLogDir)
|
||||
if len(systemLogFiles) != 0 {
|
||||
for i := 0; i < len(systemLogFiles); i++ {
|
||||
if !systemLogFiles[i].IsDir() {
|
||||
_ = cpBinary([]string{path.Join(systemLogDir, systemLogFiles[i].Name())}, systemDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
global.LOG.Debug("backup system log successful!")
|
||||
type cronjobUploadHelper struct {
|
||||
backupPath string
|
||||
backType string
|
||||
client cloud_storage.CloudStorageClient
|
||||
}
|
||||
|
||||
loginLogFiles, _ := os.ReadDir("/var/log")
|
||||
loginDir := path.Join(pathItem, "login")
|
||||
if _, err := os.Stat(loginDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(loginDir, os.ModePerm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if len(loginLogFiles) != 0 {
|
||||
for i := 0; i < len(loginLogFiles); i++ {
|
||||
if !loginLogFiles[i].IsDir() && (strings.HasPrefix(loginLogFiles[i].Name(), "secure") || strings.HasPrefix(loginLogFiles[i].Name(), "auth.log")) {
|
||||
_ = cpBinary([]string{path.Join("/var/log", loginLogFiles[i].Name())}, loginDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
global.LOG.Debug("backup ssh log successful!")
|
||||
|
||||
fileName := fmt.Sprintf("system_log_%s.tar.gz", startTime.Format("20060102150405"))
|
||||
targetDir := path.Dir(pathItem)
|
||||
if err := handleTar(pathItem, targetDir, fileName, ""); err != nil {
|
||||
return "", err
|
||||
}
|
||||
func (u *CronjobService) uploadCronjobBackFile(cronjob model.Cronjob, accountMap map[string]cronjobUploadHelper, file string) (string, error) {
|
||||
defer func() {
|
||||
os.RemoveAll(pathItem)
|
||||
os.RemoveAll(path.Join(targetDir, fileName))
|
||||
_ = os.Remove(file)
|
||||
}()
|
||||
accounts := strings.Split(cronjob.BackupAccounts, ",")
|
||||
cloudSrc := strings.TrimPrefix(file, global.CONF.System.TmpDir+"/")
|
||||
for _, account := range accounts {
|
||||
if len(account) != 0 {
|
||||
global.LOG.Debugf("start upload file to %s, dir: %s", account, path.Join(accountMap[account].backupPath, cloudSrc))
|
||||
if _, err := accountMap[account].client.Upload(file, path.Join(accountMap[account].backupPath, cloudSrc)); err != nil {
|
||||
return "", err
|
||||
}
|
||||
global.LOG.Debugf("upload successful!")
|
||||
}
|
||||
}
|
||||
return cloudSrc, nil
|
||||
}
|
||||
|
||||
client, err := NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return "", err
|
||||
func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap map[string]cronjobUploadHelper, record model.BackupRecord) {
|
||||
global.LOG.Infof("start to handle remove expired, retain copies: %d", cronjob.RetainCopies)
|
||||
var opts []repo.DBOption
|
||||
opts = append(opts, commonRepo.WithByFrom("cronjob"))
|
||||
opts = append(opts, backupRepo.WithByCronID(cronjob.ID))
|
||||
opts = append(opts, commonRepo.WithOrderBy("created_at desc"))
|
||||
if record.ID != 0 {
|
||||
opts = append(opts, backupRepo.WithByType(record.Type))
|
||||
opts = append(opts, commonRepo.WithByName(record.Name))
|
||||
opts = append(opts, backupRepo.WithByDetailName(record.DetailName))
|
||||
}
|
||||
records, _ := backupRepo.ListRecord(opts...)
|
||||
if len(records) <= int(cronjob.RetainCopies) {
|
||||
return
|
||||
}
|
||||
for i := int(cronjob.RetainCopies); i < len(records); i++ {
|
||||
accounts := strings.Split(cronjob.BackupAccounts, ",")
|
||||
if cronjob.Type == "snapshot" {
|
||||
for _, account := range accounts {
|
||||
if len(account) != 0 {
|
||||
_, _ = accountMap[account].client.Delete(path.Join(accountMap[account].backupPath, "system_snapshot", records[i].FileName))
|
||||
}
|
||||
}
|
||||
_ = snapshotRepo.Delete(commonRepo.WithByName(strings.TrimSuffix(records[i].FileName, ".tar.gz")))
|
||||
} else {
|
||||
for _, account := range accounts {
|
||||
if len(account) != 0 {
|
||||
_, _ = accountMap[account].client.Delete(path.Join(accountMap[account].backupPath, records[i].FileDir, records[i].FileName))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = backupRepo.DeleteRecord(context.Background(), commonRepo.WithByID(records[i].ID))
|
||||
}
|
||||
}
|
||||
|
||||
func (u *CronjobService) removeExpiredLog(cronjob model.Cronjob) {
|
||||
global.LOG.Infof("start to handle remove expired, retain copies: %d", cronjob.RetainCopies)
|
||||
records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), commonRepo.WithOrderBy("created_at desc"))
|
||||
if len(records) <= int(cronjob.RetainCopies) {
|
||||
return
|
||||
}
|
||||
for i := int(cronjob.RetainCopies); i < len(records); i++ {
|
||||
if len(records[i].File) != 0 {
|
||||
files := strings.Split(records[i].File, ",")
|
||||
for _, file := range files {
|
||||
_ = os.Remove(file)
|
||||
}
|
||||
}
|
||||
_ = cronjobRepo.DeleteRecord(commonRepo.WithByID(uint(records[i].ID)))
|
||||
_ = os.Remove(records[i].Records)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *CronjobService) generateLogsPath(cronjob model.Cronjob, startTime time.Time) string {
|
||||
dir := fmt.Sprintf("%s/task/%s/%s", constant.DataDir, cronjob.Type, cronjob.Name)
|
||||
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
||||
_ = os.MkdirAll(dir, os.ModePerm)
|
||||
}
|
||||
|
||||
targetPath := "log/" + fileName
|
||||
if len(backup.BackupPath) != 0 {
|
||||
targetPath = strings.TrimPrefix(backup.BackupPath, "/") + "/log/" + fileName
|
||||
}
|
||||
|
||||
if _, err = client.Upload(path.Join(targetDir, fileName), targetPath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, "", &cronjob, client)
|
||||
return targetPath, nil
|
||||
path := fmt.Sprintf("%s/%s.log", dir, startTime.Format("20060102150405"))
|
||||
return path
|
||||
}
|
||||
|
||||
func hasBackup(cronjobType string) bool {
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"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"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"github.com/shirou/gopsutil/v3/host"
|
||||
@@ -22,6 +24,7 @@ import (
|
||||
type DashboardService struct{}
|
||||
|
||||
type IDashboardService interface {
|
||||
LoadOsInfo() (*dto.OsInfo, error)
|
||||
LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error)
|
||||
LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent
|
||||
|
||||
@@ -49,6 +52,27 @@ func (u *DashboardService) Restart(operation string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *DashboardService) LoadOsInfo() (*dto.OsInfo, error) {
|
||||
var baseInfo dto.OsInfo
|
||||
hostInfo, err := host.Info()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
baseInfo.OS = hostInfo.OS
|
||||
baseInfo.Platform = hostInfo.Platform
|
||||
baseInfo.PlatformFamily = hostInfo.PlatformFamily
|
||||
baseInfo.KernelArch = hostInfo.KernelArch
|
||||
baseInfo.KernelVersion = hostInfo.KernelVersion
|
||||
|
||||
if baseInfo.KernelArch == "armv7l" {
|
||||
baseInfo.KernelArch = "armv7"
|
||||
}
|
||||
if baseInfo.KernelArch == "x86_64" {
|
||||
baseInfo.KernelArch = "amd64"
|
||||
}
|
||||
return &baseInfo, nil
|
||||
}
|
||||
|
||||
func (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error) {
|
||||
var baseInfo dto.DashboardBase
|
||||
hostInfo, err := host.Info()
|
||||
@@ -132,6 +156,7 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
|
||||
currentInfo.SwapMemoryUsedPercent = swapInfo.UsedPercent
|
||||
|
||||
currentInfo.DiskData = loadDiskInfo()
|
||||
currentInfo.GPUData = loadGPUInfo()
|
||||
|
||||
if ioOption == "all" {
|
||||
diskInfo, _ := disk.IOCounters()
|
||||
@@ -183,7 +208,10 @@ func loadDiskInfo() []dto.DiskInfo {
|
||||
var datas []dto.DiskInfo
|
||||
stdout, err := cmd.ExecWithTimeOut("df -hT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev", 2*time.Second)
|
||||
if err != nil {
|
||||
return datas
|
||||
stdout, err = cmd.ExecWithTimeOut("df -lhT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev", 1*time.Second)
|
||||
if err != nil {
|
||||
return datas
|
||||
}
|
||||
}
|
||||
lines := strings.Split(stdout, "\n")
|
||||
|
||||
@@ -264,3 +292,21 @@ func loadDiskInfo() []dto.DiskInfo {
|
||||
})
|
||||
return datas
|
||||
}
|
||||
|
||||
func loadGPUInfo() []dto.GPUInfo {
|
||||
list := xpack.LoadGpuInfo()
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
}
|
||||
var data []dto.GPUInfo
|
||||
for _, gpu := range list {
|
||||
var dataItem dto.GPUInfo
|
||||
if err := copier.Copy(&dataItem, &gpu); err != nil {
|
||||
continue
|
||||
}
|
||||
dataItem.PowerUsage = dataItem.PowerDraw + " / " + dataItem.MaxPowerLimit
|
||||
dataItem.MemoryUsage = dataItem.MemUsed + " / " + dataItem.MemTotal
|
||||
data = append(data, dataItem)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
+133
-48
@@ -6,6 +6,9 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/postgresql"
|
||||
client2 "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
@@ -28,6 +31,7 @@ type IDatabaseService interface {
|
||||
DeleteCheck(id uint) ([]string, error)
|
||||
Delete(req dto.DatabaseDelete) error
|
||||
List(dbType string) ([]dto.DatabaseOption, error)
|
||||
LoadItems(dbType string) ([]dto.DatabaseItem, error)
|
||||
}
|
||||
|
||||
func NewIDatabaseService() IDatabaseService {
|
||||
@@ -77,24 +81,66 @@ func (u *DatabaseService) List(dbType string) ([]dto.DatabaseOption, error) {
|
||||
return datas, err
|
||||
}
|
||||
|
||||
func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
||||
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
|
||||
SSL: req.SSL,
|
||||
RootCert: req.RootCert,
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
Timeout: 6,
|
||||
}); err != nil {
|
||||
return false
|
||||
func (u *DatabaseService) LoadItems(dbType string) ([]dto.DatabaseItem, error) {
|
||||
dbs, err := databaseRepo.GetList(databaseRepo.WithTypeList(dbType))
|
||||
var datas []dto.DatabaseItem
|
||||
for _, db := range dbs {
|
||||
if dbType == "postgresql" {
|
||||
items, _ := postgresqlRepo.List(postgresqlRepo.WithByPostgresqlName(db.Name))
|
||||
for _, item := range items {
|
||||
var dItem dto.DatabaseItem
|
||||
if err := copier.Copy(&dItem, &item); err != nil {
|
||||
continue
|
||||
}
|
||||
dItem.Database = db.Name
|
||||
datas = append(datas, dItem)
|
||||
}
|
||||
} else {
|
||||
items, _ := mysqlRepo.List(mysqlRepo.WithByMysqlName(db.Name))
|
||||
for _, item := range items {
|
||||
var dItem dto.DatabaseItem
|
||||
if err := copier.Copy(&dItem, &item); err != nil {
|
||||
continue
|
||||
}
|
||||
dItem.Database = db.Name
|
||||
datas = append(datas, dItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
return datas, err
|
||||
}
|
||||
|
||||
func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
||||
switch req.Type {
|
||||
case constant.AppPostgresql:
|
||||
_, err := postgresql.NewPostgresqlClient(client2.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: 6,
|
||||
})
|
||||
return err == nil
|
||||
case "mysql", "mariadb":
|
||||
_, err := mysql.NewMysqlClient(client.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
|
||||
SSL: req.SSL,
|
||||
RootCert: req.RootCert,
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
Timeout: 6,
|
||||
})
|
||||
return err == nil
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
|
||||
@@ -105,22 +151,39 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
|
||||
}
|
||||
return constant.ErrRecordExist
|
||||
}
|
||||
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
switch req.Type {
|
||||
case constant.AppPostgresql:
|
||||
if _, err := postgresql.NewPostgresqlClient(client2.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: 6,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
case "mysql", "mariadb":
|
||||
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
|
||||
SSL: req.SSL,
|
||||
RootCert: req.RootCert,
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
Timeout: 6,
|
||||
}); err != nil {
|
||||
return err
|
||||
SSL: req.SSL,
|
||||
RootCert: req.RootCert,
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
Timeout: 6,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.New("database type not supported")
|
||||
}
|
||||
|
||||
if err := copier.Copy(&db, &req); err != nil {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
@@ -170,30 +233,51 @@ func (u *DatabaseService) Delete(req dto.DatabaseDelete) error {
|
||||
return err
|
||||
}
|
||||
if db.From != "local" {
|
||||
if err := mysqlRepo.Delete(context.Background(), mysqlRepo.WithByMysqlName(db.Name)); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
if db.Type == "mysql" || db.Type == "mariadb" {
|
||||
if err := mysqlRepo.Delete(context.Background(), mysqlRepo.WithByMysqlName(db.Name)); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := postgresqlRepo.Delete(context.Background(), postgresqlRepo.WithByPostgresqlName(db.Name)); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
||||
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
switch req.Type {
|
||||
case constant.AppPostgresql:
|
||||
if _, err := postgresql.NewPostgresqlClient(client2.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
case "mysql", "mariadb":
|
||||
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
|
||||
SSL: req.SSL,
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
RootCert: req.RootCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return err
|
||||
SSL: req.SSL,
|
||||
RootCert: req.RootCert,
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.New("database type not supported")
|
||||
}
|
||||
|
||||
pass, err := encrypt.StringEncrypt(req.Password)
|
||||
@@ -208,6 +292,7 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
||||
upMap["port"] = req.Port
|
||||
upMap["username"] = req.Username
|
||||
upMap["password"] = pass
|
||||
upMap["description"] = req.Description
|
||||
upMap["ssl"] = req.SSL
|
||||
upMap["client_key"] = req.ClientKey
|
||||
upMap["client_cert"] = req.ClientCert
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"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/compose"
|
||||
)
|
||||
|
||||
type DBCommonService struct{}
|
||||
|
||||
type IDBCommonService interface {
|
||||
LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error)
|
||||
LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error)
|
||||
|
||||
UpdateConfByFile(req dto.DBConfUpdateByFile) error
|
||||
}
|
||||
|
||||
func NewIDBCommonService() IDBCommonService {
|
||||
return &DBCommonService{}
|
||||
}
|
||||
|
||||
func (u *DBCommonService) LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error) {
|
||||
var data dto.DBBaseInfo
|
||||
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data.ContainerName = app.ContainerName
|
||||
data.Name = app.Name
|
||||
data.Port = int64(app.Port)
|
||||
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func (u *DBCommonService) LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error) {
|
||||
filePath := ""
|
||||
switch req.Type {
|
||||
case "mysql-conf":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/conf/my.cnf", req.Name))
|
||||
case "mariadb-conf":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/conf/my.cnf", req.Name))
|
||||
case "postgresql-conf":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/postgresql/%s/data/postgresql.conf", req.Name))
|
||||
case "redis-conf":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/redis/%s/conf/redis.conf", req.Name))
|
||||
case "mysql-slow-logs":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
|
||||
case "mariadb-slow-logs":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/db/data/1Panel-slow.log", req.Name))
|
||||
}
|
||||
if _, err := os.Stat(filePath); err != nil {
|
||||
return "", buserr.New("ErrHttpReqNotFound")
|
||||
}
|
||||
content, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
func (u *DBCommonService) UpdateConfByFile(req dto.DBConfUpdateByFile) error {
|
||||
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := ""
|
||||
switch req.Type {
|
||||
case constant.AppMariaDB, constant.AppMysql:
|
||||
path = fmt.Sprintf("%s/%s/%s/conf/my.cnf", constant.AppInstallDir, req.Type, app.Name)
|
||||
case constant.AppPostgresql:
|
||||
path = fmt.Sprintf("%s/%s/%s/data/postgresql.conf", constant.AppInstallDir, req.Type, app.Name)
|
||||
case constant.AppRedis:
|
||||
path = fmt.Sprintf("%s/%s/%s/conf/redis.conf", constant.AppInstallDir, req.Type, app.Name)
|
||||
}
|
||||
file, err := os.OpenFile(path, 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()
|
||||
if _, err := compose.Restart(fmt.Sprintf("%s/%s/%s/docker-compose.yml", constant.AppInstallDir, req.Type, app.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -35,21 +34,18 @@ type IMysqlService interface {
|
||||
SearchWithPage(search dto.MysqlDBSearch) (int64, interface{}, error)
|
||||
ListDBOption() ([]dto.MysqlOption, error)
|
||||
Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error)
|
||||
BindUser(req dto.BindUser) error
|
||||
LoadFromRemote(req dto.MysqlLoadDB) error
|
||||
ChangeAccess(info dto.ChangeDBInfo) error
|
||||
ChangePassword(info dto.ChangeDBInfo) error
|
||||
UpdateVariables(req dto.MysqlVariablesUpdate) error
|
||||
UpdateConfByFile(info dto.MysqlConfUpdateByFile) error
|
||||
UpdateDescription(req dto.UpdateDescription) error
|
||||
DeleteCheck(req dto.MysqlDBDeleteCheck) ([]string, error)
|
||||
Delete(ctx context.Context, req dto.MysqlDBDelete) error
|
||||
|
||||
LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlStatus, error)
|
||||
LoadVariables(req dto.OperationWithNameAndType) (*dto.MysqlVariables, error)
|
||||
LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error)
|
||||
LoadRemoteAccess(req dto.OperationWithNameAndType) (bool, error)
|
||||
|
||||
LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error)
|
||||
}
|
||||
|
||||
func NewIMysqlService() IMysqlService {
|
||||
@@ -144,6 +140,42 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
|
||||
return &createItem, nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) BindUser(req dto.BindUser) error {
|
||||
dbItem, err := mysqlRepo.Get(mysqlRepo.WithByMysqlName(req.Database), commonRepo.WithByName(req.DB))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cli, version, err := LoadMysqlClientByFrom(req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
if err := cli.CreateUser(client.CreateInfo{
|
||||
Name: dbItem.Name,
|
||||
Format: dbItem.Format,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Permission: req.Permission,
|
||||
Version: version,
|
||||
Timeout: 300,
|
||||
}, false); err != nil {
|
||||
return err
|
||||
}
|
||||
pass, err := encrypt.StringEncrypt(req.Password)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decrypt database db password failed, err: %v", err)
|
||||
}
|
||||
if err := mysqlRepo.Update(dbItem.ID, map[string]interface{}{
|
||||
"username": req.Username,
|
||||
"password": pass,
|
||||
"permission": req.Permission,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) LoadFromRemote(req dto.MysqlLoadDB) error {
|
||||
client, version, err := LoadMysqlClientByFrom(req.Database)
|
||||
if err != nil {
|
||||
@@ -158,11 +190,13 @@ func (u *MysqlService) LoadFromRemote(req dto.MysqlLoadDB) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deleteList := databases
|
||||
for _, data := range datas {
|
||||
hasOld := false
|
||||
for _, oldData := range databases {
|
||||
if strings.EqualFold(oldData.Name, data.Name) && strings.EqualFold(oldData.MysqlName, data.MysqlName) {
|
||||
for i := 0; i < len(databases); i++ {
|
||||
if strings.EqualFold(databases[i].Name, data.Name) && strings.EqualFold(databases[i].MysqlName, data.MysqlName) {
|
||||
hasOld = true
|
||||
deleteList = append(deleteList[:i], deleteList[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -176,6 +210,9 @@ func (u *MysqlService) LoadFromRemote(req dto.MysqlLoadDB) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, delItem := range deleteList {
|
||||
_ = mysqlRepo.Update(delItem.ID, map[string]interface{}{"is_delete": true})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -310,7 +347,7 @@ func (u *MysqlService) ChangePassword(req dto.ChangeDBInfo) error {
|
||||
}
|
||||
|
||||
global.LOG.Infof("start to update mysql password used by app %s-%s", appModel.Key, appInstall.Name)
|
||||
if err := updateInstallInfoInDB(appModel.Key, appInstall.Name, "user-password", true, req.Value); err != nil {
|
||||
if err := updateInstallInfoInDB(appModel.Key, appInstall.Name, "user-password", req.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -323,7 +360,7 @@ func (u *MysqlService) ChangePassword(req dto.ChangeDBInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := updateInstallInfoInDB(req.Type, req.Database, "password", false, req.Value); err != nil {
|
||||
if err := updateInstallInfoInDB(req.Type, req.Database, "password", req.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -369,26 +406,6 @@ func (u *MysqlService) ChangeAccess(req dto.ChangeDBInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) UpdateConfByFile(req dto.MysqlConfUpdateByFile) error {
|
||||
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := fmt.Sprintf("%s/%s/%s/conf/my.cnf", constant.AppInstallDir, req.Type, app.Name)
|
||||
file, err := os.OpenFile(path, 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()
|
||||
if _, err := compose.Restart(fmt.Sprintf("%s/%s/%s/docker-compose.yml", constant.AppInstallDir, req.Type, app.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) UpdateVariables(req dto.MysqlVariablesUpdate) error {
|
||||
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
|
||||
if err != nil {
|
||||
@@ -434,19 +451,6 @@ func (u *MysqlService) UpdateVariables(req dto.MysqlVariablesUpdate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error) {
|
||||
var data dto.DBBaseInfo
|
||||
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data.ContainerName = app.ContainerName
|
||||
data.Name = app.Name
|
||||
data.Port = int64(app.Port)
|
||||
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) LoadRemoteAccess(req dto.OperationWithNameAndType) (bool, error) {
|
||||
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
|
||||
if err != nil {
|
||||
@@ -528,30 +532,6 @@ func (u *MysqlService) LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlS
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error) {
|
||||
filePath := ""
|
||||
switch req.Type {
|
||||
case "mysql-conf":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/conf/my.cnf", req.Name))
|
||||
case "mariadb-conf":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/conf/my.cnf", req.Name))
|
||||
case "redis-conf":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/redis/%s/conf/redis.conf", req.Name))
|
||||
case "mysql-slow-logs":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
|
||||
case "mariadb-slow-logs":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/db/data/1Panel-slow.log", req.Name))
|
||||
}
|
||||
if _, err := os.Stat(filePath); err != nil {
|
||||
return "", buserr.New("ErrHttpReqNotFound")
|
||||
}
|
||||
content, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
func executeSqlForMaps(containerName, password, command string) (map[string]string, error) {
|
||||
cmd := exec.Command("docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
|
||||
@@ -0,0 +1,418 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"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/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/postgresql"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
|
||||
_ "github.com/jackc/pgx/v5/stdlib"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type PostgresqlService struct{}
|
||||
|
||||
type IPostgresqlService interface {
|
||||
SearchWithPage(search dto.PostgresqlDBSearch) (int64, interface{}, error)
|
||||
ListDBOption() ([]dto.PostgresqlOption, error)
|
||||
BindUser(req dto.PostgresqlBindUser) error
|
||||
Create(ctx context.Context, req dto.PostgresqlDBCreate) (*model.DatabasePostgresql, error)
|
||||
LoadFromRemote(database string) error
|
||||
ChangePrivileges(req dto.PostgresqlPrivileges) error
|
||||
ChangePassword(info dto.ChangeDBInfo) error
|
||||
UpdateDescription(req dto.UpdateDescription) error
|
||||
DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]string, error)
|
||||
Delete(ctx context.Context, req dto.PostgresqlDBDelete) error
|
||||
}
|
||||
|
||||
func NewIPostgresqlService() IPostgresqlService {
|
||||
return &PostgresqlService{}
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) SearchWithPage(search dto.PostgresqlDBSearch) (int64, interface{}, error) {
|
||||
total, postgresqls, err := postgresqlRepo.Page(search.Page, search.PageSize,
|
||||
postgresqlRepo.WithByPostgresqlName(search.Database),
|
||||
commonRepo.WithLikeName(search.Info),
|
||||
commonRepo.WithOrderRuleBy(search.OrderBy, search.Order),
|
||||
)
|
||||
var dtoPostgresqls []dto.PostgresqlDBInfo
|
||||
for _, pg := range postgresqls {
|
||||
var item dto.PostgresqlDBInfo
|
||||
if err := copier.Copy(&item, &pg); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
dtoPostgresqls = append(dtoPostgresqls, item)
|
||||
}
|
||||
return total, dtoPostgresqls, err
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) ListDBOption() ([]dto.PostgresqlOption, error) {
|
||||
postgresqls, err := postgresqlRepo.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
databases, err := databaseRepo.GetList(databaseRepo.WithTypeList("postgresql,mariadb"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var dbs []dto.PostgresqlOption
|
||||
for _, pg := range postgresqls {
|
||||
var item dto.PostgresqlOption
|
||||
if err := copier.Copy(&item, &pg); err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
item.Database = pg.PostgresqlName
|
||||
for _, database := range databases {
|
||||
if database.Name == item.Database {
|
||||
item.Type = database.Type
|
||||
}
|
||||
}
|
||||
dbs = append(dbs, item)
|
||||
}
|
||||
return dbs, err
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) BindUser(req dto.PostgresqlBindUser) error {
|
||||
if cmd.CheckIllegal(req.Name, req.Username, req.Password) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
dbItem, err := postgresqlRepo.Get(postgresqlRepo.WithByPostgresqlName(req.Database), commonRepo.WithByName(req.Name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pgClient, err := LoadPostgresqlClientByFrom(req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := pgClient.CreateUser(client.CreateInfo{
|
||||
Name: req.Name,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
SuperUser: req.SuperUser,
|
||||
Timeout: 300,
|
||||
}, false); err != nil {
|
||||
return err
|
||||
}
|
||||
pass, err := encrypt.StringEncrypt(req.Password)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decrypt database db password failed, err: %v", err)
|
||||
}
|
||||
if err := postgresqlRepo.Update(dbItem.ID, map[string]interface{}{
|
||||
"username": req.Username,
|
||||
"password": pass,
|
||||
"super_user": req.SuperUser,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) Create(ctx context.Context, req dto.PostgresqlDBCreate) (*model.DatabasePostgresql, error) {
|
||||
if cmd.CheckIllegal(req.Name, req.Username, req.Password, req.Format) {
|
||||
return nil, buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
|
||||
pgsql, _ := postgresqlRepo.Get(commonRepo.WithByName(req.Name), postgresqlRepo.WithByPostgresqlName(req.Database), databaseRepo.WithByFrom(req.From))
|
||||
if pgsql.ID != 0 {
|
||||
return nil, constant.ErrRecordExist
|
||||
}
|
||||
|
||||
var createItem model.DatabasePostgresql
|
||||
if err := copier.Copy(&createItem, &req); err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
|
||||
if req.From == "local" && req.Username == "root" {
|
||||
return nil, errors.New("Cannot set root as user name")
|
||||
}
|
||||
|
||||
cli, err := LoadPostgresqlClientByFrom(req.Database)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
createItem.PostgresqlName = req.Database
|
||||
defer cli.Close()
|
||||
if err := cli.Create(client.CreateInfo{
|
||||
Name: req.Name,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
SuperUser: req.SuperUser,
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
global.LOG.Infof("create database %s successful!", req.Name)
|
||||
if err := postgresqlRepo.Create(ctx, &createItem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &createItem, nil
|
||||
}
|
||||
|
||||
func LoadPostgresqlClientByFrom(database string) (postgresql.PostgresqlClient, error) {
|
||||
var (
|
||||
dbInfo client.DBInfo
|
||||
err error
|
||||
)
|
||||
|
||||
dbInfo.Timeout = 300
|
||||
databaseItem, err := databaseRepo.Get(commonRepo.WithByName(database))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbInfo.From = databaseItem.From
|
||||
dbInfo.Database = database
|
||||
if dbInfo.From != "local" {
|
||||
dbInfo.Address = databaseItem.Address
|
||||
dbInfo.Port = databaseItem.Port
|
||||
dbInfo.Username = databaseItem.Username
|
||||
dbInfo.Password = databaseItem.Password
|
||||
} else {
|
||||
app, err := appInstallRepo.LoadBaseInfo(databaseItem.Type, database)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbInfo.From = "local"
|
||||
dbInfo.Address = app.ContainerName
|
||||
dbInfo.Username = app.UserName
|
||||
dbInfo.Password = app.Password
|
||||
dbInfo.Port = uint(app.Port)
|
||||
}
|
||||
|
||||
cli, err := postgresql.NewPostgresqlClient(dbInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cli, nil
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) LoadFromRemote(database string) error {
|
||||
client, err := LoadPostgresqlClientByFrom(database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
databases, err := postgresqlRepo.List(postgresqlRepo.WithByPostgresqlName(database))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
datas, err := client.SyncDB()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deleteList := databases
|
||||
for _, data := range datas {
|
||||
hasOld := false
|
||||
for i := 0; i < len(databases); i++ {
|
||||
if strings.EqualFold(databases[i].Name, data.Name) && strings.EqualFold(databases[i].PostgresqlName, data.PostgresqlName) {
|
||||
hasOld = true
|
||||
deleteList = append(deleteList[:i], deleteList[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasOld {
|
||||
var createItem model.DatabasePostgresql
|
||||
if err := copier.Copy(&createItem, &data); err != nil {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
if err := postgresqlRepo.Create(context.Background(), &createItem); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, delItem := range deleteList {
|
||||
_ = postgresqlRepo.Update(delItem.ID, map[string]interface{}{"is_delete": true})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) UpdateDescription(req dto.UpdateDescription) error {
|
||||
return postgresqlRepo.Update(req.ID, map[string]interface{}{"description": req.Description})
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]string, error) {
|
||||
var appInUsed []string
|
||||
db, err := postgresqlRepo.Get(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return appInUsed, err
|
||||
}
|
||||
|
||||
if db.From == "local" {
|
||||
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
|
||||
if err != nil {
|
||||
return appInUsed, err
|
||||
}
|
||||
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(db.ID))
|
||||
for _, app := range apps {
|
||||
appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId))
|
||||
if appInstall.ID != 0 {
|
||||
appInUsed = append(appInUsed, appInstall.Name)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(db.ID))
|
||||
for _, app := range apps {
|
||||
appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId))
|
||||
if appInstall.ID != 0 {
|
||||
appInUsed = append(appInUsed, appInstall.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return appInUsed, nil
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) Delete(ctx context.Context, req dto.PostgresqlDBDelete) error {
|
||||
db, err := postgresqlRepo.Get(commonRepo.WithByID(req.ID))
|
||||
if err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
cli, err := LoadPostgresqlClientByFrom(req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
if err := cli.Delete(client.DeleteInfo{
|
||||
Name: db.Name,
|
||||
Username: db.Username,
|
||||
ForceDelete: req.ForceDelete,
|
||||
Timeout: 300,
|
||||
}); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
|
||||
if req.DeleteBackup {
|
||||
uploadDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/uploads/database/%s/%s/%s", req.Type, req.Database, db.Name))
|
||||
if _, err := os.Stat(uploadDir); err == nil {
|
||||
_ = os.RemoveAll(uploadDir)
|
||||
}
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
backupDir := path.Join(localDir, fmt.Sprintf("database/%s/%s/%s", req.Type, db.PostgresqlName, db.Name))
|
||||
if _, err := os.Stat(backupDir); err == nil {
|
||||
_ = os.RemoveAll(backupDir)
|
||||
}
|
||||
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType(req.Type), commonRepo.WithByName(req.Database), backupRepo.WithByDetailName(db.Name))
|
||||
global.LOG.Infof("delete database %s-%s backups successful", req.Database, db.Name)
|
||||
}
|
||||
|
||||
_ = postgresqlRepo.Delete(ctx, commonRepo.WithByID(db.ID))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) ChangePrivileges(req dto.PostgresqlPrivileges) error {
|
||||
if cmd.CheckIllegal(req.Database, req.Username) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
dbItem, err := postgresqlRepo.Get(postgresqlRepo.WithByPostgresqlName(req.Database), commonRepo.WithByName(req.Name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cli, err := LoadPostgresqlClientByFrom(req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
if err := cli.ChangePrivileges(client.Privileges{Username: req.Username, SuperUser: req.SuperUser, Timeout: 300}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := postgresqlRepo.Update(dbItem.ID, map[string]interface{}{
|
||||
"super_user": req.SuperUser,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) ChangePassword(req dto.ChangeDBInfo) error {
|
||||
if cmd.CheckIllegal(req.Value) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
cli, err := LoadPostgresqlClientByFrom(req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
var (
|
||||
postgresqlData model.DatabasePostgresql
|
||||
passwordInfo client.PasswordChangeInfo
|
||||
)
|
||||
passwordInfo.Password = req.Value
|
||||
passwordInfo.Timeout = 300
|
||||
|
||||
if req.ID != 0 {
|
||||
postgresqlData, err = postgresqlRepo.Get(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
passwordInfo.Username = postgresqlData.Username
|
||||
} else {
|
||||
dbItem, err := databaseRepo.Get(commonRepo.WithByType(req.Type), commonRepo.WithByFrom(req.From))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
passwordInfo.Username = dbItem.Username
|
||||
}
|
||||
if err := cli.ChangePassword(passwordInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if req.ID != 0 {
|
||||
var appRess []model.AppInstallResource
|
||||
if req.From == "local" {
|
||||
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appRess, _ = appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(postgresqlData.ID))
|
||||
} else {
|
||||
appRess, _ = appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(postgresqlData.ID))
|
||||
}
|
||||
for _, appRes := range appRess {
|
||||
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(appRes.AppInstallId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appModel, err := appRepo.GetFirst(commonRepo.WithByID(appInstall.AppId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
global.LOG.Infof("start to update postgresql password used by app %s-%s", appModel.Key, appInstall.Name)
|
||||
if err := updateInstallInfoInDB(appModel.Key, appInstall.Name, "user-password", req.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
global.LOG.Info("execute password change sql successful")
|
||||
pass, err := encrypt.StringEncrypt(req.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decrypt database db password failed, err: %v", err)
|
||||
}
|
||||
_ = postgresqlRepo.Update(postgresqlData.ID, map[string]interface{}{"password": pass})
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := updateInstallInfoInDB(req.Type, req.Database, "password", req.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
|
||||
confs = append(confs, redisConfig{key: "timeout", value: req.Timeout})
|
||||
confs = append(confs, redisConfig{key: "maxclients", value: req.Maxclients})
|
||||
confs = append(confs, redisConfig{key: "maxmemory", value: req.Maxmemory})
|
||||
if err := confSet(redisInfo.Name, confs); err != nil {
|
||||
if err := confSet(redisInfo.Name, "", confs); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := compose.Restart(fmt.Sprintf("%s/redis/%s/docker-compose.yml", constant.AppInstallDir, redisInfo.Name)); err != nil {
|
||||
@@ -55,10 +55,10 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
|
||||
}
|
||||
|
||||
func (u *RedisService) ChangePassword(req dto.ChangeRedisPass) error {
|
||||
if err := updateInstallInfoInDB("redis", "", "password", true, req.Value); err != nil {
|
||||
if err := updateInstallInfoInDB("redis", "", "password", req.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := updateInstallInfoInDB("redis-commander", "", "password", true, req.Value); err != nil {
|
||||
if err := updateInstallInfoInDB("redis-commander", "", "password", req.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ func (u *RedisService) UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate)
|
||||
confs = append(confs, redisConfig{key: "appendonly", value: req.Appendonly})
|
||||
confs = append(confs, redisConfig{key: "appendfsync", value: req.Appendfsync})
|
||||
}
|
||||
if err := confSet(redisInfo.Name, confs); err != nil {
|
||||
if err := confSet(redisInfo.Name, req.Type, confs); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := compose.Restart(fmt.Sprintf("%s/redis/%s/docker-compose.yml", constant.AppInstallDir, redisInfo.Name)); err != nil {
|
||||
@@ -213,7 +213,7 @@ type redisConfig struct {
|
||||
value string
|
||||
}
|
||||
|
||||
func confSet(redisName string, changeConf []redisConfig) error {
|
||||
func confSet(redisName string, updateType string, changeConf []redisConfig) error {
|
||||
path := fmt.Sprintf("%s/redis/%s/conf/redis.conf", constant.AppInstallDir, redisName)
|
||||
lineBytes, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
@@ -221,19 +221,38 @@ func confSet(redisName string, changeConf []redisConfig) error {
|
||||
}
|
||||
files := strings.Split(string(lineBytes), "\n")
|
||||
|
||||
startIndex, endIndex := 0, 0
|
||||
startIndex, endIndex, emptyLine := 0, 0, 0
|
||||
var newFiles []string
|
||||
for i := 0; i < len(files); i++ {
|
||||
if files[i] == "# Redis configuration rewrite by 1Panel" {
|
||||
startIndex = i
|
||||
newFiles = append(newFiles, files[i])
|
||||
continue
|
||||
}
|
||||
if files[i] == "# End Redis configuration rewrite by 1Panel" {
|
||||
endIndex = i
|
||||
break
|
||||
}
|
||||
if startIndex == 0 && strings.HasPrefix(files[i], "save ") {
|
||||
newFiles = append(newFiles, "# "+files[i])
|
||||
continue
|
||||
}
|
||||
if startIndex != 0 && endIndex == 0 && (len(files[i]) == 0 || (updateType == "rbd" && strings.HasPrefix(files[i], "save "))) {
|
||||
emptyLine++
|
||||
continue
|
||||
}
|
||||
newFiles = append(newFiles, files[i])
|
||||
}
|
||||
endIndex = endIndex - emptyLine
|
||||
for _, item := range changeConf {
|
||||
if item.key == "save" {
|
||||
saveVal := strings.Split(item.value, ",")
|
||||
for i := 0; i < len(saveVal); i++ {
|
||||
newFiles = append(newFiles, "save "+saveVal[i])
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
isExist := false
|
||||
for i := startIndex; i < endIndex; i++ {
|
||||
if strings.HasPrefix(newFiles[i], item.key) || strings.HasPrefix(newFiles[i], "# "+item.key) {
|
||||
|
||||
@@ -353,7 +353,7 @@ func loadHosts() []dto.HostHelper {
|
||||
lines := strings.Split(string(hostConf), "\n")
|
||||
for _, line := range lines {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) < 2 {
|
||||
if len(parts) < 2 || strings.HasPrefix(strings.TrimPrefix(line, " "), "#") {
|
||||
continue
|
||||
}
|
||||
list = append(list, dto.HostHelper{IP: parts[0], Host: strings.Join(parts[1:], " ")})
|
||||
|
||||
@@ -181,7 +181,7 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate) error {
|
||||
} else {
|
||||
daemonMap["iptables"] = false
|
||||
}
|
||||
case "Dirver":
|
||||
case "Driver":
|
||||
if opts, ok := daemonMap["exec-opts"]; ok {
|
||||
if optsValue, isArray := opts.([]interface{}); isArray {
|
||||
for i := 0; i < len(optsValue); i++ {
|
||||
|
||||
@@ -12,8 +12,9 @@ var (
|
||||
appInstallRepo = repo.NewIAppInstallRepo()
|
||||
appInstallResourceRepo = repo.NewIAppInstallResourceRpo()
|
||||
|
||||
mysqlRepo = repo.NewIMysqlRepo()
|
||||
databaseRepo = repo.NewIDatabaseRepo()
|
||||
mysqlRepo = repo.NewIMysqlRepo()
|
||||
postgresqlRepo = repo.NewIPostgresqlRepo()
|
||||
databaseRepo = repo.NewIDatabaseRepo()
|
||||
|
||||
imageRepoRepo = repo.NewIImageRepoRepo()
|
||||
composeRepo = repo.NewIComposeTemplateRepo()
|
||||
@@ -37,7 +38,8 @@ var (
|
||||
logRepo = repo.NewILogRepo()
|
||||
snapshotRepo = repo.NewISnapshotRepo()
|
||||
|
||||
runtimeRepo = repo.NewIRunTimeRepo()
|
||||
runtimeRepo = repo.NewIRunTimeRepo()
|
||||
phpExtensionsRepo = repo.NewIPHPExtensionsRepo()
|
||||
|
||||
favoriteRepo = repo.NewIFavoriteRepo()
|
||||
)
|
||||
|
||||
@@ -107,7 +107,7 @@ func (u *Fail2BanService) UpdateConf(req dto.Fail2BanUpdate) error {
|
||||
if req.Value == "firewallcmd-ipset" || req.Value == "ufw" {
|
||||
itemName := "ufw"
|
||||
if req.Value == "firewallcmd-ipset" {
|
||||
itemName = "firewallcmd"
|
||||
itemName = "firewalld"
|
||||
}
|
||||
client, err := firewall.NewFirewallClient()
|
||||
if err != nil {
|
||||
|
||||
@@ -116,22 +116,32 @@ func (f *FileService) GetFileTree(op request.FileOption) ([]response.FileTree, e
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
if op.IsDir {
|
||||
return fo.CreateDir(op.Path, fs.FileMode(op.Mode))
|
||||
} else {
|
||||
if op.IsLink {
|
||||
if !fo.Stat(op.LinkPath) {
|
||||
return buserr.New(constant.ErrLinkPathNotFound)
|
||||
}
|
||||
return fo.LinkFile(op.LinkPath, op.Path, op.IsSymlink)
|
||||
mode := op.Mode
|
||||
if mode == 0 {
|
||||
fileInfo, err := os.Stat(filepath.Dir(op.Path))
|
||||
if err == nil {
|
||||
mode = int64(fileInfo.Mode().Perm())
|
||||
} else {
|
||||
return fo.CreateFile(op.Path)
|
||||
mode = 0755
|
||||
}
|
||||
}
|
||||
if op.IsDir {
|
||||
return fo.CreateDirWithMode(op.Path, fs.FileMode(mode))
|
||||
}
|
||||
if op.IsLink {
|
||||
if !fo.Stat(op.LinkPath) {
|
||||
return buserr.New(constant.ErrLinkPathNotFound)
|
||||
}
|
||||
return fo.LinkFile(op.LinkPath, op.Path, op.IsSymlink)
|
||||
}
|
||||
return fo.CreateFileWithMode(op.Path, fs.FileMode(mode))
|
||||
}
|
||||
|
||||
func (f *FileService) Delete(op request.FileDelete) error {
|
||||
@@ -236,6 +246,9 @@ func (f *FileService) SaveContent(edit request.FileEdit) error {
|
||||
}
|
||||
|
||||
func (f *FileService) ChangeName(req request.FileRename) error {
|
||||
if files.IsInvalidChar(req.NewName) {
|
||||
return buserr.New("ErrInvalidChar")
|
||||
}
|
||||
fo := files.NewFileOp()
|
||||
return fo.Rename(req.OldName, req.NewName)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
@@ -28,6 +30,7 @@ type ImageService struct{}
|
||||
type IImageService interface {
|
||||
Page(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
List() ([]dto.Options, error)
|
||||
ListAll() ([]dto.ImageInfo, error)
|
||||
ImageBuild(req dto.ImageBuild) (string, error)
|
||||
ImagePull(req dto.ImagePull) (string, error)
|
||||
ImageLoad(req dto.ImageLoad) error
|
||||
@@ -42,7 +45,7 @@ func NewIImageService() IImageService {
|
||||
}
|
||||
func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error) {
|
||||
var (
|
||||
list []types.ImageSummary
|
||||
list []image.Summary
|
||||
records []dto.ImageInfo
|
||||
backDatas []dto.ImageInfo
|
||||
)
|
||||
@@ -50,11 +53,11 @@ func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
list, err = client.ImageList(context.Background(), types.ImageListOptions{})
|
||||
list, err = client.ImageList(context.Background(), image.ListOptions{})
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
containers, _ := client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
|
||||
containers, _ := client.ContainerList(context.Background(), container.ListOptions{All: true})
|
||||
if len(req.Info) != 0 {
|
||||
length, count := len(list), 0
|
||||
for count < length {
|
||||
@@ -97,16 +100,40 @@ func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
return int64(total), backDatas, nil
|
||||
}
|
||||
|
||||
func (u *ImageService) ListAll() ([]dto.ImageInfo, error) {
|
||||
var records []dto.ImageInfo
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list, err := client.ImageList(context.Background(), image.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containers, _ := client.ContainerList(context.Background(), container.ListOptions{All: true})
|
||||
for _, image := range list {
|
||||
size := formatFileSize(image.Size)
|
||||
records = append(records, dto.ImageInfo{
|
||||
ID: image.ID,
|
||||
Tags: image.RepoTags,
|
||||
IsUsed: checkUsed(image.ID, containers),
|
||||
CreatedAt: time.Unix(image.Created, 0),
|
||||
Size: size,
|
||||
})
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func (u *ImageService) List() ([]dto.Options, error) {
|
||||
var (
|
||||
list []types.ImageSummary
|
||||
list []image.Summary
|
||||
backDatas []dto.Options
|
||||
)
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list, err = client.ImageList(context.Background(), types.ImageListOptions{})
|
||||
list, err = client.ImageList(context.Background(), image.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -211,6 +211,6 @@ func StartMonitor(removeBefore bool, interval string) error {
|
||||
go service.saveIODataToDB(ctx, float64(intervalItem))
|
||||
go service.saveNetDataToDB(ctx, float64(intervalItem))
|
||||
|
||||
global.MonitorCronID = int(monitorID)
|
||||
global.MonitorCronID = monitorID
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -26,6 +27,7 @@ type INginxService interface {
|
||||
UpdateConfigByScope(req request.NginxConfigUpdate) error
|
||||
GetStatus() (response.NginxStatus, error)
|
||||
UpdateConfigFile(req request.NginxConfigFileUpdate) error
|
||||
ClearProxyCache() error
|
||||
}
|
||||
|
||||
func NewINginxService() INginxService {
|
||||
@@ -121,3 +123,22 @@ func (n NginxService) UpdateConfigFile(req request.NginxConfigFileUpdate) error
|
||||
}
|
||||
return nginxCheckAndReload(string(oldContent), filePath, nginxInstall.ContainerName)
|
||||
}
|
||||
|
||||
func (n NginxService) ClearProxyCache() error {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cacheDir := path.Join(nginxInstall.GetPath(), "www/common/proxy/proxy_cache_dir")
|
||||
fileOp := files.NewFileOp()
|
||||
if fileOp.Stat(cacheDir) {
|
||||
if err = fileOp.CleanDir(cacheDir); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = compose.Restart(nginxInstall.GetComposePath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -37,7 +37,10 @@ func getNginxFull(website *model.Website) (dto.NginxFull, error) {
|
||||
if err != nil {
|
||||
return nginxFull, err
|
||||
}
|
||||
config := parser.NewStringParser(string(content)).Parse()
|
||||
config, err := parser.NewStringParser(string(content)).Parse()
|
||||
if err != nil {
|
||||
return dto.NginxFull{}, err
|
||||
}
|
||||
config.FilePath = nginxConfig.FilePath
|
||||
nginxConfig.OldContent = string(content)
|
||||
nginxConfig.Config = config
|
||||
@@ -54,7 +57,10 @@ func getNginxFull(website *model.Website) (dto.NginxFull, error) {
|
||||
if err != nil {
|
||||
return nginxFull, err
|
||||
}
|
||||
siteConfig := parser.NewStringParser(string(siteNginxContent)).Parse()
|
||||
siteConfig, err := parser.NewStringParser(string(siteNginxContent)).Parse()
|
||||
if err != nil {
|
||||
return dto.NginxFull{}, err
|
||||
}
|
||||
siteConfig.FilePath = siteConfigPath
|
||||
siteNginxConfig.Config = siteConfig
|
||||
siteNginxConfig.OldContent = string(siteNginxContent)
|
||||
@@ -156,15 +162,22 @@ func deleteNginxConfig(scope string, params []dto.NginxParam, website *model.Web
|
||||
}
|
||||
|
||||
func getNginxParamsFromStaticFile(scope dto.NginxKey, newParams []dto.NginxParam) []dto.NginxParam {
|
||||
newConfig := &components.Config{}
|
||||
var (
|
||||
newConfig = &components.Config{}
|
||||
err error
|
||||
)
|
||||
|
||||
updateScope := "in"
|
||||
switch scope {
|
||||
case dto.SSL:
|
||||
newConfig = parser.NewStringParser(string(nginx_conf.SSL)).Parse()
|
||||
newConfig, err = parser.NewStringParser(string(nginx_conf.SSL)).Parse()
|
||||
case dto.CACHE:
|
||||
newConfig = parser.NewStringParser(string(nginx_conf.Cache)).Parse()
|
||||
newConfig, err = parser.NewStringParser(string(nginx_conf.Cache)).Parse()
|
||||
case dto.ProxyCache:
|
||||
newConfig = parser.NewStringParser(string(nginx_conf.ProxyCache)).Parse()
|
||||
newConfig, err = parser.NewStringParser(string(nginx_conf.ProxyCache)).Parse()
|
||||
}
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for _, dir := range newConfig.GetDirectives() {
|
||||
addParam := dto.NginxParam{
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
)
|
||||
|
||||
type PHPExtensionsService struct {
|
||||
}
|
||||
|
||||
type IPHPExtensionsService interface {
|
||||
Page(req request.PHPExtensionsSearch) (int64, []response.PHPExtensionsDTO, error)
|
||||
List() ([]response.PHPExtensionsDTO, error)
|
||||
Create(req request.PHPExtensionsCreate) error
|
||||
Update(req request.PHPExtensionsUpdate) error
|
||||
Delete(req request.PHPExtensionsDelete) error
|
||||
}
|
||||
|
||||
func NewIPHPExtensionsService() IPHPExtensionsService {
|
||||
return &PHPExtensionsService{}
|
||||
}
|
||||
|
||||
func (p PHPExtensionsService) Page(req request.PHPExtensionsSearch) (int64, []response.PHPExtensionsDTO, error) {
|
||||
var (
|
||||
total int64
|
||||
extensions []model.PHPExtensions
|
||||
err error
|
||||
result []response.PHPExtensionsDTO
|
||||
)
|
||||
total, extensions, err = phpExtensionsRepo.Page(req.Page, req.PageSize)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
for _, extension := range extensions {
|
||||
result = append(result, response.PHPExtensionsDTO{
|
||||
PHPExtensions: extension,
|
||||
})
|
||||
}
|
||||
return total, result, nil
|
||||
}
|
||||
|
||||
func (p PHPExtensionsService) List() ([]response.PHPExtensionsDTO, error) {
|
||||
var (
|
||||
extensions []model.PHPExtensions
|
||||
err error
|
||||
result []response.PHPExtensionsDTO
|
||||
)
|
||||
extensions, err = phpExtensionsRepo.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, extension := range extensions {
|
||||
result = append(result, response.PHPExtensionsDTO{
|
||||
PHPExtensions: extension,
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (p PHPExtensionsService) Create(req request.PHPExtensionsCreate) error {
|
||||
exist, _ := phpExtensionsRepo.GetFirst(commonRepo.WithByName(req.Name))
|
||||
if exist.ID > 0 {
|
||||
return buserr.New(constant.ErrNameIsExist)
|
||||
}
|
||||
extension := model.PHPExtensions{
|
||||
Name: req.Name,
|
||||
Extensions: req.Extensions,
|
||||
}
|
||||
return phpExtensionsRepo.Create(&extension)
|
||||
}
|
||||
|
||||
func (p PHPExtensionsService) Update(req request.PHPExtensionsUpdate) error {
|
||||
exist, err := phpExtensionsRepo.GetFirst(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
exist.Extensions = req.Extensions
|
||||
return phpExtensionsRepo.Save(&exist)
|
||||
}
|
||||
|
||||
func (p PHPExtensionsService) Delete(req request.PHPExtensionsDelete) error {
|
||||
return phpExtensionsRepo.DeleteBy(commonRepo.WithByID(req.ID))
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user