Compare commits
316 Commits
v1.2.4
...
pr@dev@dar
Author | SHA1 | Date | |
---|---|---|---|
![]() |
201e0562b6 | ||
![]() |
87327053dc | ||
![]() |
5e7d5c0235 | ||
![]() |
1ccc56100c | ||
![]() |
ef948bca76 | ||
![]() |
d01a964534 | ||
![]() |
05004cb141 | ||
![]() |
dfcef390d0 | ||
![]() |
ff3b41686c | ||
![]() |
7dd5082bac | ||
![]() |
ec05e81286 | ||
![]() |
e7a7d391e4 | ||
![]() |
e6b1ef30a5 | ||
![]() |
f40b053e5b | ||
![]() |
1a891cb048 | ||
![]() |
66007c07e2 | ||
![]() |
5058a814aa | ||
![]() |
37d8244414 | ||
![]() |
cda5112f5e | ||
![]() |
919f10cc11 | ||
![]() |
66b12800e4 | ||
![]() |
227856b45b | ||
![]() |
44ca529027 | ||
![]() |
885e3b60f0 | ||
![]() |
07bbf057d0 | ||
![]() |
b9227caaf8 | ||
![]() |
55ed67eaed | ||
![]() |
ac55385696 | ||
![]() |
f9b93e8c85 | ||
![]() |
ac7f33a29c | ||
![]() |
4de99f66a8 | ||
![]() |
c0c1d519bf | ||
![]() |
f22980f4ce | ||
![]() |
7b297e824c | ||
![]() |
28bd0e3cc2 | ||
![]() |
f90c009782 | ||
![]() |
a54913f788 | ||
![]() |
ea3dca79aa | ||
![]() |
2b207597b9 | ||
![]() |
044bb79ae9 | ||
![]() |
afcebb46a0 | ||
![]() |
10427ddd65 | ||
![]() |
0ac2b9df7a | ||
![]() |
695aacbe14 | ||
![]() |
71107fa42f | ||
![]() |
d36dfc5490 | ||
![]() |
a463f237b1 | ||
![]() |
ec105ede83 | ||
![]() |
92aff95b3a | ||
![]() |
1fcc345a7a | ||
![]() |
7e9a09c960 | ||
![]() |
8212ff117b | ||
![]() |
b5a1ffe338 | ||
![]() |
759382bcfb | ||
![]() |
12eeb6503c | ||
![]() |
9fa9772c07 | ||
![]() |
35334c1650 | ||
![]() |
006c27fee5 | ||
![]() |
271be81557 | ||
![]() |
b61d8aaabc | ||
![]() |
319afd2d51 | ||
![]() |
bd2facebee | ||
![]() |
3cbaa052c8 | ||
![]() |
a0ceb62372 | ||
![]() |
dddd190911 | ||
![]() |
f70b0049d9 | ||
![]() |
6fea06729e | ||
![]() |
24e6fe89c8 | ||
![]() |
e507611cad | ||
![]() |
72dcdbad1e | ||
![]() |
0a55dec949 | ||
![]() |
555a32c273 | ||
![]() |
fdf2a9d247 | ||
![]() |
30bb64d058 | ||
![]() |
695d4b4a16 | ||
![]() |
01c08a8ef9 | ||
![]() |
dd9f2edf40 | ||
![]() |
105c249d97 | ||
![]() |
b780df96af | ||
![]() |
46495937b1 | ||
![]() |
597c9ea4c0 | ||
![]() |
4662f4703c | ||
![]() |
5f750e6f49 | ||
![]() |
152ba81c34 | ||
![]() |
4bf76aacb1 | ||
![]() |
6c4c73e825 | ||
![]() |
917851dcd7 | ||
![]() |
2e5bf4202c | ||
![]() |
d4319fa55c | ||
![]() |
2ff7726442 | ||
![]() |
38bf54ec3b | ||
![]() |
a1c76600e2 | ||
![]() |
fbcb3da422 | ||
![]() |
3978fb3e46 | ||
![]() |
64f80a95ab | ||
![]() |
847c14ddda | ||
![]() |
38c0d290e7 | ||
![]() |
c403eb55b1 | ||
![]() |
506d78cb00 | ||
![]() |
27918e2dc5 | ||
![]() |
d44231a0ff | ||
![]() |
11a58fde91 | ||
![]() |
3a8c3b5816 | ||
![]() |
50deda27ca | ||
![]() |
4482fa23e6 | ||
![]() |
4c83a3bf36 | ||
![]() |
514538179e | ||
![]() |
4c8245045d | ||
![]() |
efe185e5dc | ||
![]() |
62becf819d | ||
![]() |
f02f32456e | ||
![]() |
57916ed6d7 | ||
![]() |
efa3d06673 | ||
![]() |
14f7435f82 | ||
![]() |
dbeaaa49f3 | ||
![]() |
7546391c17 | ||
![]() |
d8d2ee0f46 | ||
![]() |
3c57fa76bf | ||
![]() |
3fa4a240f7 | ||
![]() |
ae38239b47 | ||
![]() |
6a63b2e5c4 | ||
![]() |
3ead633343 | ||
![]() |
e71f765f2a | ||
![]() |
2b7f68f3fe | ||
![]() |
c82e20efd7 | ||
![]() |
aa37e3885c | ||
![]() |
7918023322 | ||
![]() |
80304937e1 | ||
![]() |
352978b54d | ||
![]() |
b7cda1d2f1 | ||
![]() |
20dbd6c181 | ||
![]() |
8808e1b0c3 | ||
![]() |
47dda4ac9f | ||
![]() |
b1d40960c4 | ||
![]() |
5222caecf9 | ||
![]() |
bc8d4cbb0f | ||
![]() |
0bb31f6caf | ||
![]() |
03c8e4d3cb | ||
![]() |
68ad653545 | ||
![]() |
fd5b34d644 | ||
![]() |
000096bc26 | ||
![]() |
02023102d3 | ||
![]() |
95bc3d9855 | ||
![]() |
f9fb16198b | ||
![]() |
11ae05163c | ||
![]() |
0275716ff5 | ||
![]() |
e9bdca5279 | ||
![]() |
cd84116a03 | ||
![]() |
457effae85 | ||
![]() |
9361c358f1 | ||
![]() |
39f952e460 | ||
![]() |
7c600a357c | ||
![]() |
8f036a0c3d | ||
![]() |
1184ae2b52 | ||
![]() |
de7ef19034 | ||
![]() |
e91b7caf4f | ||
![]() |
033b3c10c0 | ||
![]() |
97774c88d5 | ||
![]() |
95e1c73ced | ||
![]() |
d2a067db77 | ||
![]() |
586dee9e70 | ||
![]() |
6a717b2517 | ||
![]() |
203af988fa | ||
![]() |
911166152a | ||
![]() |
b3ecddb20f | ||
![]() |
a41d4e55aa | ||
![]() |
c7a3ef7d72 | ||
![]() |
b7495a63e9 | ||
![]() |
6b3093aa09 | ||
![]() |
8f8369c989 | ||
![]() |
f79293be69 | ||
![]() |
4631da2212 | ||
![]() |
e2a7c51de0 | ||
![]() |
633e26b1de | ||
![]() |
08992f83b5 | ||
![]() |
921e886e71 | ||
![]() |
9fc4cad80e | ||
![]() |
4fdb81642a | ||
![]() |
056c771d2e | ||
![]() |
84183bdfeb | ||
![]() |
4e786fee31 | ||
![]() |
9f4e5050dd | ||
![]() |
bb49a610a2 | ||
![]() |
5e3e580f51 | ||
![]() |
75fa498a7c | ||
![]() |
05e5f06cf1 | ||
![]() |
3a17f4f29f | ||
![]() |
5e7524e4f8 | ||
![]() |
b2e17d4c42 | ||
![]() |
b88303d36a | ||
![]() |
5a5b0e3a1b | ||
![]() |
e504e55a34 | ||
![]() |
b6758ff92d | ||
![]() |
957499e4d7 | ||
![]() |
3773d64aa7 | ||
![]() |
d89f823bef | ||
![]() |
a8b7c3d8c5 | ||
![]() |
9a45ce3110 | ||
![]() |
3ad3b180af | ||
![]() |
6919ce7b5c | ||
![]() |
e7a9c3814b | ||
![]() |
488eb319a1 | ||
![]() |
4f650cad9d | ||
![]() |
ce9c2e6d3c | ||
![]() |
90a5e741fd | ||
![]() |
bfedd02d1d | ||
![]() |
0187d2f5c1 | ||
![]() |
7d968348f5 | ||
![]() |
317017a2b4 | ||
![]() |
35ca52620c | ||
![]() |
01ed60fcb7 | ||
![]() |
80599a3576 | ||
![]() |
d40b2734a9 | ||
![]() |
6abd313bd2 | ||
![]() |
88f573e2a6 | ||
![]() |
eb55e16465 | ||
![]() |
bb48964cca | ||
![]() |
cbdf1ad7b7 | ||
![]() |
df77ac4318 | ||
![]() |
b3caa343d2 | ||
![]() |
49f4f316f8 | ||
![]() |
2207711400 | ||
![]() |
763f450f43 | ||
![]() |
0fa11213bb | ||
![]() |
c743775a1e | ||
![]() |
b854aa3bfe | ||
![]() |
8dfba82a70 | ||
![]() |
824051e89e | ||
![]() |
ab45cc27d5 | ||
![]() |
627e467fdf | ||
![]() |
ca3b4b02e0 | ||
![]() |
34ac685e65 | ||
![]() |
7a66e71215 | ||
![]() |
1e96a1ae84 | ||
![]() |
ce0342e235 | ||
![]() |
a48ea497b0 | ||
![]() |
4b3c9419f3 | ||
![]() |
f51b09dc40 | ||
![]() |
800f9e2d38 | ||
![]() |
b5093e4d93 | ||
![]() |
10684f9aac | ||
![]() |
7c0327c1b8 | ||
![]() |
9f5a03419e | ||
![]() |
ec843f2396 | ||
![]() |
2d6925ac4f | ||
![]() |
7cbaa4f63d | ||
![]() |
ebb9e7a2a0 | ||
![]() |
84e44127b3 | ||
![]() |
0b66bb5eff | ||
![]() |
e70ec0e978 | ||
![]() |
d62f566bb3 | ||
![]() |
4e1c3be03a | ||
![]() |
792e96e95f | ||
![]() |
c1acd8f5f0 | ||
![]() |
d64e1713fb | ||
![]() |
3e6fefe1b7 | ||
![]() |
48e3ef3e73 | ||
![]() |
c76c24e102 | ||
![]() |
4b25dafb92 | ||
![]() |
c8237d59be | ||
![]() |
c2629e3945 | ||
![]() |
b5864ca3a3 | ||
![]() |
b84c983727 | ||
![]() |
aa3e8783ae | ||
![]() |
f65f0d86aa | ||
![]() |
93b03c7212 | ||
![]() |
c96b999b2c | ||
![]() |
da155fadf2 | ||
![]() |
c69a1c8ec2 | ||
![]() |
83ac2f1ff7 | ||
![]() |
f77972fa38 | ||
![]() |
d7c08295f8 | ||
![]() |
be6b7157f4 | ||
![]() |
b979c2574c | ||
![]() |
f7c76c012f | ||
![]() |
13abe8cb66 | ||
![]() |
7596099aa1 | ||
![]() |
626782102a | ||
![]() |
3e068a0020 | ||
![]() |
09af1e9cdf | ||
![]() |
5ac9298db0 | ||
![]() |
0d084861e0 | ||
![]() |
c052887d58 | ||
![]() |
f5cd45438b | ||
![]() |
a0a1cc410f | ||
![]() |
74cfc11a37 | ||
![]() |
53600900f2 | ||
![]() |
ce19107c95 | ||
![]() |
7c56ed7b16 | ||
![]() |
152cc76e3f | ||
![]() |
8fd4060562 | ||
![]() |
c2f5908a9d | ||
![]() |
57aa2aba74 | ||
![]() |
d851aeed45 | ||
![]() |
03d338d6c9 | ||
![]() |
a74cdcc061 | ||
![]() |
4fca9aeb48 | ||
![]() |
205e32dde8 | ||
![]() |
d065558865 | ||
![]() |
950c6b9d08 | ||
![]() |
d20d5946f2 | ||
![]() |
72bc99bddc | ||
![]() |
8c4016792b | ||
![]() |
2975cf6d5a | ||
![]() |
73d93d6104 | ||
![]() |
7452cc19e0 | ||
![]() |
efd545882f | ||
![]() |
b19cdd9339 | ||
![]() |
da54794aca | ||
![]() |
a8df6d0668 | ||
![]() |
1f65d2243e | ||
![]() |
36db30471c | ||
![]() |
80e22ffc82 | ||
![]() |
872581fa4b | ||
![]() |
aeabed70db | ||
![]() |
d443103e2c |
34
.github/workflows/build-test.yml
vendored
Normal file
34
.github/workflows/build-test.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- dev
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
name: Build Test
|
||||
|
||||
jobs:
|
||||
build-linux-binary:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18.14'
|
||||
- name: Build Web
|
||||
id: build_frontend
|
||||
run: |
|
||||
cd frontend && npm install && npm run build:pro
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.20.x'
|
||||
- name: Build Server
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
with:
|
||||
args: release --snapshot --clean
|
66
.github/workflows/release-drafter.yml
vendored
Normal file
66
.github/workflows/release-drafter.yml
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
on:
|
||||
push:
|
||||
# Sequence of patterns matched against refs/tags
|
||||
tags:
|
||||
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||
|
||||
name: Create Release And Upload assets
|
||||
|
||||
jobs:
|
||||
create-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18.14'
|
||||
- name: Build Web
|
||||
run: |
|
||||
cd frontend && npm install && npm run build:pro
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.20.x'
|
||||
- name: Build Release
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: latest
|
||||
args: release --skip-publish --clean
|
||||
- name: Upload Assets
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
draft: true
|
||||
body: |
|
||||
# 一、安装和升级
|
||||
|
||||
## 1.1 一键安装
|
||||
```sh
|
||||
curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh
|
||||
```
|
||||
|
||||
## 1.2 在线升级
|
||||
|
||||
登录 1Panel Web 控制台,在页面右下角点击 **【检查更新】** 进行在线升级。
|
||||
|
||||
>更多信息请查阅在线文档:https://1panel.cn/docs/
|
||||
|
||||
# 二、更新日志
|
||||
|
||||
files: |
|
||||
dist/*.tar.gz
|
||||
dist/checksums.txt
|
||||
- name: Setup OSSUTIL
|
||||
uses: yizhoumo/setup-ossutil@v1
|
||||
with:
|
||||
endpoint: ${{ secrets.OSS_ENDPOINT }}
|
||||
access-key-id: ${{ secrets.OSS_ACCESS_KEY_ID }}
|
||||
access-key-secret: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
|
||||
ossutil-version: '1.7.14'
|
||||
- name: Upload Assets to OSS
|
||||
run: ossutil cp -r dist/ oss://resource-fit2cloud-com/1panel/package/stable/${{ github.ref_name }}/release/ --include "*.tar.gz" --include "checksums.txt" --only-current-dir --force
|
10
.gitignore
vendored
10
.gitignore
vendored
@@ -23,3 +23,13 @@ cmd/server/web/assets
|
||||
cmd/server/web/monacoeditorwork
|
||||
cmd/server/web/index.html
|
||||
frontend/auto-imports.d.ts
|
||||
frontend/components.d.ts
|
||||
|
||||
.history/
|
||||
dist/
|
||||
1pctl
|
||||
1panel.service
|
||||
install.sh
|
||||
quick_start.sh
|
||||
cmd/server/web/.DS_Store
|
||||
cmd/server/.DS_Store
|
||||
|
58
.goreleaser.yaml
Normal file
58
.goreleaser.yaml
Normal file
@@ -0,0 +1,58 @@
|
||||
# This is an example .goreleaser.yml file with some sensible defaults.
|
||||
# Make sure to check the documentation at https://goreleaser.com
|
||||
before:
|
||||
hooks:
|
||||
# - export NODE_OPTIONS="--max-old-space-size=8192"
|
||||
# - make build_web
|
||||
- chmod +x ./script.sh
|
||||
- ./script.sh
|
||||
- sed -i 's@ORIGINAL_VERSION=.*@ORIGINAL_VERSION=v{{ .Version }}@g' 1pctl
|
||||
- go mod tidy
|
||||
|
||||
builds:
|
||||
- main: ./cmd/server/main.go
|
||||
binary: 1panel
|
||||
flags:
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -w -s
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
goarm:
|
||||
- 7
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
- arm
|
||||
- ppc64le
|
||||
- s390x
|
||||
|
||||
archives:
|
||||
- format: tar.gz
|
||||
name_template: "1panel-v{{ .Version }}-{{ .Os }}-{{ .Arch }}{{- if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
wrap_in_directory: true
|
||||
files:
|
||||
- 1pctl
|
||||
- 1panel.service
|
||||
- install.sh
|
||||
- README.md
|
||||
- LICENSE
|
||||
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
snapshot:
|
||||
name_template: "{{ incpatch .Version }}-next"
|
||||
release:
|
||||
draft: true
|
||||
mode: append
|
||||
extra_files:
|
||||
- glob: dist/*.tar.gz
|
||||
- glob: dist/checksums.txt
|
||||
name_template: "Release {{.Tag}}"
|
||||
|
||||
# The lines beneath this are called `modelines`. See `:help modeline`
|
||||
# Feel free to remove those if you don't want/use them.
|
||||
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
||||
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ansible.python.interpreterPath": "/opt/homebrew/bin/python3"
|
||||
}
|
19
Makefile
19
Makefile
@@ -10,20 +10,29 @@ WEB_PATH=$(BASE_PAH)/frontend
|
||||
SERVER_PATH=$(BASE_PAH)/backend
|
||||
MAIN= $(BASE_PAH)/cmd/server/main.go
|
||||
APP_NAME=1panel
|
||||
ASSERT_PATH= $(BASE_PAH)/cmd/server/web/assets
|
||||
|
||||
clean_assets:
|
||||
rm -rf $(ASSERT_PATH)
|
||||
|
||||
upx_bin:
|
||||
upx $(BUILD_PATH)/$(APP_NAME)
|
||||
|
||||
build_frontend:
|
||||
cd $(WEB_PATH) && npm install && npm run build:dev
|
||||
cd $(WEB_PATH) && npm install && npm run build:pro
|
||||
|
||||
build_backend_on_linux:
|
||||
cd $(SERVER_PATH) \
|
||||
&& CGO_ENABLED=1 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-static -fpic"' -tags 'osusergo,netgo' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
&& GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
|
||||
build_backend_on_darwin:
|
||||
cd $(SERVER_PATH) \
|
||||
&& CGO_ENABLED=1 GOOS=linux GOARCH=amd64 CC=x86_64-linux-musl-gcc CXX=x86_64-linux-musl-g++ $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-static -fpic"' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
&& GOOS=linux GOARCH=amd64 $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
|
||||
build_backend_on_archlinux:
|
||||
cd $(SERVER_PATH) \
|
||||
&& CGO_ENABLED=1 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-fpic"' -tags osusergo -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
&& GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
|
||||
build_all: build_frontend build_backend_on_linux
|
||||
build_all: build_frontend build_backend_on_linux
|
||||
|
||||
build_on_local: clean_assets build_frontend build_backend_on_darwin upx_bin
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -39,6 +40,15 @@ func (b *BaseApi) SearchApp(c *gin.Context) {
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"应用商店同步","formatEN":"App store synchronization"}
|
||||
func (b *BaseApi) SyncApp(c *gin.Context) {
|
||||
go appService.SyncAppListFromLocal()
|
||||
res, err := appService.GetAppUpdate()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if !res.CanUpdate {
|
||||
helper.SuccessWithMsg(c, i18n.GetMsgByKey("AppStoreIsUpToDate"))
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
global.LOG.Infof("sync app list start ...")
|
||||
if err := appService.SyncAppListFromRemote(); err != nil {
|
||||
@@ -120,6 +130,22 @@ func (b *BaseApi) GetAppDetailByID(c *gin.Context) {
|
||||
helper.SuccessWithData(c, appDetailDTO)
|
||||
}
|
||||
|
||||
// @Tags App
|
||||
// @Summary Get Ignore App
|
||||
// @Description 获取忽略的应用版本
|
||||
// @Accept json
|
||||
// @Success 200 {object} response.IgnoredApp
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/ingored [get]
|
||||
func (b *BaseApi) GetIgnoredApp(c *gin.Context) {
|
||||
res, err := appService.GetIgnoredApp()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags App
|
||||
// @Summary Install app
|
||||
// @Description 安装应用
|
||||
@@ -128,7 +154,7 @@ func (b *BaseApi) GetAppDetailByID(c *gin.Context) {
|
||||
// @Success 200 {object} model.AppInstall
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/install [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"name","input_value":"name","isList":false,"db":"app_installs","output_colume":"app_id","output_value":"appId"},{"info":"appId","isList":false,"db":"apps","output_colume":"key","output_value":"appKey"}],"formatZH":"安装应用 [appKey]-[name]","formatEN":"Install app [appKey]-[name]"}
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[{"input_column":"name","input_value":"name","isList":false,"db":"app_installs","output_column":"app_id","output_value":"appId"},{"info":"appId","isList":false,"db":"apps","output_column":"key","output_value":"appKey"}],"formatZH":"安装应用 [appKey]-[name]","formatEN":"Install app [appKey]-[name]"}
|
||||
func (b *BaseApi) InstallApp(c *gin.Context) {
|
||||
var req request.AppInstallCreate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
@@ -118,7 +118,7 @@ func (b *BaseApi) LoadConnInfo(c *gin.Context) {
|
||||
// @Description 删除前检查
|
||||
// @Accept json
|
||||
// @Param appInstallId path integer true "App install id"
|
||||
// @Success 200 {anrry} dto.AppResource
|
||||
// @Success 200 {array} dto.AppResource
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/installed/delete/check/:appInstallId [get]
|
||||
func (b *BaseApi) DeleteCheck(c *gin.Context) {
|
||||
@@ -159,7 +159,7 @@ func (b *BaseApi) SyncInstalled(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/installed/op [post]
|
||||
// @x-panel-log {"bodyKeys":["installId","operate"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"installId","isList":false,"db":"app_installs","output_colume":"app_id","output_value":"appId"},{"input_colume":"id","input_value":"installId","isList":false,"db":"app_installs","output_colume":"name","output_value":"appName"},{"input_colume":"id","input_value":"appId","isList":false,"db":"apps","output_colume":"key","output_value":"appKey"}],"formatZH":"[appKey] 应用 [appName] [operate]","formatEN":"[appKey] App [appName] [operate]"}
|
||||
// @x-panel-log {"bodyKeys":["installId","operate"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"installId","isList":false,"db":"app_installs","output_column":"app_id","output_value":"appId"},{"input_column":"id","input_value":"installId","isList":false,"db":"app_installs","output_column":"name","output_value":"appName"},{"input_column":"id","input_value":"appId","isList":false,"db":"apps","output_column":"key","output_value":"appKey"}],"formatZH":"[operate] 应用 [appKey][appName]","formatEN":"[operate] App [appKey][appName]"}
|
||||
func (b *BaseApi) OperateInstalled(c *gin.Context) {
|
||||
var req request.AppInstalledOperate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -178,7 +178,7 @@ func (b *BaseApi) OperateInstalled(c *gin.Context) {
|
||||
// @Description 通过 key 获取应用 service
|
||||
// @Accept json
|
||||
// @Param key path string true "request"
|
||||
// @Success 200 {anrry} response.AppService
|
||||
// @Success 200 {array} response.AppService
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/services/:key [get]
|
||||
func (b *BaseApi) GetServices(c *gin.Context) {
|
||||
@@ -196,7 +196,7 @@ func (b *BaseApi) GetServices(c *gin.Context) {
|
||||
// @Description 通过 install id 获取应用更新版本
|
||||
// @Accept json
|
||||
// @Param appInstallId path integer true "request"
|
||||
// @Success 200 {anrry} dto.AppVersion
|
||||
// @Success 200 {array} dto.AppVersion
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/installed/:appInstallId/versions [get]
|
||||
func (b *BaseApi) GetUpdateVersions(c *gin.Context) {
|
||||
@@ -305,3 +305,25 @@ func (b *BaseApi) UpdateInstalled(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags App
|
||||
// @Summary ignore App Update
|
||||
// @Description 忽略应用升级版本
|
||||
// @Accept json
|
||||
// @Param request body request.AppInstalledIgnoreUpgrade true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/installed/ignore [post]
|
||||
// @x-panel-log {"bodyKeys":["installId"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"忽略应用 [installId] 版本升级","formatEN":"Application param update [installId]"}
|
||||
func (b *BaseApi) IgnoreUpgrade(c *gin.Context) {
|
||||
var req request.AppInstalledIgnoreUpgrade
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := appInstallService.IgnoreUpgrade(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
@@ -103,7 +103,12 @@ func (b *BaseApi) Captcha(c *gin.Context) {
|
||||
// @Router /auth/issafety [get]
|
||||
func (b *BaseApi) CheckIsSafety(c *gin.Context) {
|
||||
code := c.DefaultQuery("code", "")
|
||||
helper.SuccessWithData(c, authService.CheckIsSafety(code))
|
||||
status, err := authService.CheckIsSafety(code)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, status)
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
|
@@ -60,7 +60,7 @@ func (b *BaseApi) CreateBackup(c *gin.Context) {
|
||||
// @Description 获取 bucket 列表
|
||||
// @Accept json
|
||||
// @Param request body dto.ForBuckets true "request"
|
||||
// @Success 200 {anrry} string
|
||||
// @Success 200 {array} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/search [post]
|
||||
func (b *BaseApi) ListBuckets(c *gin.Context) {
|
||||
@@ -98,6 +98,22 @@ func (b *BaseApi) ListBuckets(c *gin.Context) {
|
||||
helper.SuccessWithData(c, buckets)
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Load OneDrive info
|
||||
// @Description 获取 OneDrive 信息
|
||||
// @Accept json
|
||||
// @Success 200 string clientID
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/onedrive [get]
|
||||
func (b *BaseApi) LoadOneDriveInfo(c *gin.Context) {
|
||||
clientID, err := backupService.LoadOneDriveInfo()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, clientID)
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Delete backup account
|
||||
// @Description 删除备份账号
|
||||
@@ -106,7 +122,7 @@ func (b *BaseApi) ListBuckets(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":true,"db":"backup_accounts","output_colume":"type","output_value":"types"}],"formatZH":"删除备份账号 [types]","formatEN":"delete backup account [types]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":true,"db":"backup_accounts","output_column":"type","output_value":"types"}],"formatZH":"删除备份账号 [types]","formatEN":"delete backup account [types]"}
|
||||
func (b *BaseApi) DeleteBackup(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -188,7 +204,7 @@ func (b *BaseApi) DownloadRecord(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/record/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"backup_records","output_colume":"file_name","output_value":"files"}],"formatZH":"删除备份记录 [files]","formatEN":"delete backup records [files]"}
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"backup_records","output_column":"file_name","output_value":"files"}],"formatZH":"删除备份记录 [files]","formatEN":"delete backup records [files]"}
|
||||
func (b *BaseApi) DeleteBackupRecord(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -253,7 +269,7 @@ func (b *BaseApi) UpdateBackup(c *gin.Context) {
|
||||
// @Tags Backup Account
|
||||
// @Summary List backup accounts
|
||||
// @Description 获取备份账号列表
|
||||
// @Success 200 {anrry} dto.BackupInfo
|
||||
// @Success 200 {array} dto.BackupInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/search [get]
|
||||
func (b *BaseApi) ListBackup(c *gin.Context) {
|
||||
@@ -271,7 +287,7 @@ func (b *BaseApi) ListBackup(c *gin.Context) {
|
||||
// @Description 获取备份账号内文件列表
|
||||
// @Accept json
|
||||
// @Param request body dto.BackupSearchFile true "request"
|
||||
// @Success 200 {anrry} string
|
||||
// @Success 200 {array} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/search/files [post]
|
||||
func (b *BaseApi) LoadFilesFromBackup(c *gin.Context) {
|
||||
|
@@ -85,7 +85,7 @@ func (b *BaseApi) ListCommand(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/command/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"commands","output_colume":"name","output_value":"names"}],"formatZH":"删除快捷命令 [names]","formatEN":"delete quick command [names]"}
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"commands","output_column":"name","output_value":"names"}],"formatZH":"删除快捷命令 [names]","formatEN":"delete quick command [names]"}
|
||||
func (b *BaseApi) DeleteCommand(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
@@ -66,7 +66,7 @@ func (b *BaseApi) SearchComposeTemplate(c *gin.Context) {
|
||||
// @Summary List compose templates
|
||||
// @Description 获取容器编排模版列表
|
||||
// @Produce json
|
||||
// @Success 200 {anrry} dto.ComposeTemplateInfo
|
||||
// @Success 200 {array} dto.ComposeTemplateInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/template [get]
|
||||
func (b *BaseApi) ListComposeTemplate(c *gin.Context) {
|
||||
@@ -87,7 +87,7 @@ func (b *BaseApi) ListComposeTemplate(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/template/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"compose_templates","output_colume":"name","output_value":"names"}],"formatZH":"删除 compose 模版 [names]","formatEN":"delete compose template [names]"}
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"compose_templates","output_column":"name","output_value":"names"}],"formatZH":"删除 compose 模版 [names]","formatEN":"delete compose template [names]"}
|
||||
func (b *BaseApi) DeleteComposeTemplate(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -114,7 +114,7 @@ func (b *BaseApi) DeleteComposeTemplate(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/template/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"compose_templates","output_colume":"name","output_value":"name"}],"formatZH":"更新 compose 模版 [name]","formatEN":"update compose template information [name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"compose_templates","output_column":"name","output_value":"name"}],"formatZH":"更新 compose 模版 [name]","formatEN":"update compose template information [name]"}
|
||||
func (b *BaseApi) UpdateComposeTemplate(c *gin.Context) {
|
||||
var req dto.ComposeTemplateUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
@@ -40,6 +40,23 @@ func (b *BaseApi) SearchContainer(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary List containers
|
||||
// @Description 获取容器名称
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/list [post]
|
||||
func (b *BaseApi) ListContainer(c *gin.Context) {
|
||||
list, err := containerService.List()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Container Compose
|
||||
// @Summary Page composes
|
||||
// @Description 获取编排列表分页
|
||||
@@ -153,17 +170,97 @@ func (b *BaseApi) OperatorCompose(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Update container
|
||||
// @Description 更新容器
|
||||
// @Accept json
|
||||
// @Param request body dto.ContainerOperate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/update [post]
|
||||
// @x-panel-log {"bodyKeys":["name","image"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新容器 [name][image]","formatEN":"update container [name][image]"}
|
||||
func (b *BaseApi) ContainerUpdate(c *gin.Context) {
|
||||
var req dto.ContainerOperate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := containerService.ContainerUpdate(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Load container info
|
||||
// @Description 获取容器表单信息
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithName true "request"
|
||||
// @Success 200 {object} dto.ContainerOperate
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/info [post]
|
||||
func (b *BaseApi) ContainerInfo(c *gin.Context) {
|
||||
var req dto.OperationWithName
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
data, err := containerService.ContainerInfo(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Summary Load container limis
|
||||
// @Description 获取容器限制
|
||||
// @Success 200 {object} dto.ResourceLimit
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/limit [get]
|
||||
func (b *BaseApi) LoadResouceLimit(c *gin.Context) {
|
||||
data, err := containerService.LoadResouceLimit()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Summary Load container stats
|
||||
// @Description 获取容器列表资源占用
|
||||
// @Success 200 {array} dto.ContainerListStats
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/list/stats [get]
|
||||
func (b *BaseApi) ContainerListStats(c *gin.Context) {
|
||||
datas, err := containerService.ContainerListStats()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, datas)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Create container
|
||||
// @Description 创建容器
|
||||
// @Accept json
|
||||
// @Param request body dto.ContainerCreate true "request"
|
||||
// @Param request body dto.ContainerOperate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers [post]
|
||||
// @x-panel-log {"bodyKeys":["name","image"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建容器 [name][image]","formatEN":"create container [name][image]"}
|
||||
func (b *BaseApi) ContainerCreate(c *gin.Context) {
|
||||
var req dto.ContainerCreate
|
||||
var req dto.ContainerOperate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
@@ -179,6 +276,85 @@ func (b *BaseApi) ContainerCreate(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Upgrade container
|
||||
// @Description 更新容器镜像
|
||||
// @Accept json
|
||||
// @Param request body dto.ContainerUpgrade true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/upgrade [post]
|
||||
// @x-panel-log {"bodyKeys":["name","image"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新容器镜像 [name][image]","formatEN":"upgrade container image [name][image]"}
|
||||
func (b *BaseApi) ContainerUpgrade(c *gin.Context) {
|
||||
var req dto.ContainerUpgrade
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := containerService.ContainerUpgrade(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Clean container
|
||||
// @Description 容器清理
|
||||
// @Accept json
|
||||
// @Param request body dto.ContainerPrune true "request"
|
||||
// @Success 200 {object} dto.ContainerPruneReport
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/prune [post]
|
||||
// @x-panel-log {"bodyKeys":["pruneType"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"清理容器 [pruneType]","formatEN":"clean container [pruneType]"}
|
||||
func (b *BaseApi) ContainerPrune(c *gin.Context) {
|
||||
var req dto.ContainerPrune
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
report, err := containerService.Prune(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, report)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Clean container log
|
||||
// @Description 清理容器日志
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithName true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/clean/log [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"清理容器 [name] 日志","formatEN":"clean container [name] logs"}
|
||||
func (b *BaseApi) CleanContainerLog(c *gin.Context) {
|
||||
var req dto.OperationWithName
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := containerService.ContainerLogClean(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Operate Container
|
||||
// @Description 容器操作
|
||||
@@ -209,7 +385,7 @@ func (b *BaseApi) ContainerOperation(c *gin.Context) {
|
||||
// @Summary Container stats
|
||||
// @Description 容器监控信息
|
||||
// @Param id path integer true "容器id"
|
||||
// @Success 200 {object} dto.ContainterStats
|
||||
// @Success 200 {object} dto.ContainerStats
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/stats/:id [get]
|
||||
func (b *BaseApi) ContainerStats(c *gin.Context) {
|
||||
@@ -257,27 +433,29 @@ func (b *BaseApi) Inspect(c *gin.Context) {
|
||||
// @Tags Container
|
||||
// @Summary Container logs
|
||||
// @Description 容器日志
|
||||
// @Accept json
|
||||
// @Param request body dto.ContainerLog true "request"
|
||||
// @Success 200 {string} logs
|
||||
// @Param container query string false "容器名称"
|
||||
// @Param since query string false "时间筛选"
|
||||
// @Param follow query string false "是否追踪"
|
||||
// @Param tail query string false "显示行号"
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/search/log [post]
|
||||
func (b *BaseApi) ContainerLogs(c *gin.Context) {
|
||||
var req dto.ContainerLog
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
logs, err := containerService.ContainerLogs(req)
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
defer wsConn.Close()
|
||||
|
||||
container := c.Query("container")
|
||||
since := c.Query("since")
|
||||
follow := c.Query("follow") == "true"
|
||||
tail := c.Query("tail")
|
||||
|
||||
if err := containerService.ContainerLogs(wsConn, container, since, tail, follow); err != nil {
|
||||
_ = wsConn.WriteMessage(1, []byte(err.Error()))
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, logs)
|
||||
}
|
||||
|
||||
// @Tags Container Network
|
||||
@@ -311,6 +489,23 @@ func (b *BaseApi) SearchNetwork(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Container Network
|
||||
// @Summary List networks
|
||||
// @Description 获取容器网络列表
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {array} dto.Options
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/network [get]
|
||||
func (b *BaseApi) ListNetwork(c *gin.Context) {
|
||||
list, err := containerService.ListNetwork()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Container Network
|
||||
// @Summary Delete network
|
||||
// @Description 删除容器网络
|
||||
@@ -342,13 +537,13 @@ func (b *BaseApi) DeleteNetwork(c *gin.Context) {
|
||||
// @Summary Create network
|
||||
// @Description 创建容器网络
|
||||
// @Accept json
|
||||
// @Param request body dto.NetworkCreat true "request"
|
||||
// @Param request body dto.NetworkCreate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/network [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建容器网络 name","formatEN":"create container network [name]"}
|
||||
func (b *BaseApi) CreateNetwork(c *gin.Context) {
|
||||
var req dto.NetworkCreat
|
||||
var req dto.NetworkCreate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
@@ -400,11 +595,10 @@ func (b *BaseApi) SearchVolume(c *gin.Context) {
|
||||
// @Summary List volumes
|
||||
// @Description 获取容器存储卷列表
|
||||
// @Accept json
|
||||
// @Param request body dto.PageInfo true "request"
|
||||
// @Produce json
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Success 200 {array} dto.Options
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/volume/search [get]
|
||||
// @Router /containers/volume [get]
|
||||
func (b *BaseApi) ListVolume(c *gin.Context) {
|
||||
list, err := containerService.ListVolume()
|
||||
if err != nil {
|
||||
@@ -445,13 +639,13 @@ func (b *BaseApi) DeleteVolume(c *gin.Context) {
|
||||
// @Summary Create volume
|
||||
// @Description 创建容器存储卷
|
||||
// @Accept json
|
||||
// @Param request body dto.VolumeCreat true "request"
|
||||
// @Param request body dto.VolumeCreate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/volume [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建容器存储卷 [name]","formatEN":"create container volume [name]"}
|
||||
func (b *BaseApi) CreateVolume(c *gin.Context) {
|
||||
var req dto.VolumeCreat
|
||||
var req dto.VolumeCreate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
|
@@ -102,7 +102,7 @@ func (b *BaseApi) SearchJobRecords(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /cronjobs/records/clean [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"cronjobs","output_colume":"name","output_value":"name"}],"formatZH":"清空计划任务记录 [name]","formatEN":"clean cronjob [name] records"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"cronjobs","output_column":"name","output_value":"name"}],"formatZH":"清空计划任务记录 [name]","formatEN":"clean cronjob [name] records"}
|
||||
func (b *BaseApi) CleanRecord(c *gin.Context) {
|
||||
var req dto.CronjobClean
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -126,7 +126,7 @@ func (b *BaseApi) CleanRecord(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /cronjobs/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"cronjobs","output_colume":"name","output_value":"names"}],"formatZH":"删除计划任务 [names]","formatEN":"delete cronjob [names]"}
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"cronjobs","output_column":"name","output_value":"names"}],"formatZH":"删除计划任务 [names]","formatEN":"delete cronjob [names]"}
|
||||
func (b *BaseApi) DeleteCronjob(c *gin.Context) {
|
||||
var req dto.CronjobBatchDelete
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -153,7 +153,7 @@ func (b *BaseApi) DeleteCronjob(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /cronjobs/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"cronjobs","output_colume":"name","output_value":"name"}],"formatZH":"更新计划任务 [name]","formatEN":"update cronjob [name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"cronjobs","output_column":"name","output_value":"name"}],"formatZH":"更新计划任务 [name]","formatEN":"update cronjob [name]"}
|
||||
func (b *BaseApi) UpdateCronjob(c *gin.Context) {
|
||||
var req dto.CronjobUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -180,7 +180,7 @@ func (b *BaseApi) UpdateCronjob(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /cronjobs/status [post]
|
||||
// @x-panel-log {"bodyKeys":["id","status"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"cronjobs","output_colume":"name","output_value":"name"}],"formatZH":"修改计划任务 [name] 状态为 [status]","formatEN":"change the status of cronjob [name] to [status]."}
|
||||
// @x-panel-log {"bodyKeys":["id","status"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"cronjobs","output_column":"name","output_value":"name"}],"formatZH":"修改计划任务 [name] 状态为 [status]","formatEN":"change the status of cronjob [name] to [status]."}
|
||||
func (b *BaseApi) UpdateCronjobStatus(c *gin.Context) {
|
||||
var req dto.CronjobUpdateStatus
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -207,7 +207,7 @@ func (b *BaseApi) UpdateCronjobStatus(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /cronjobs/download [post]
|
||||
// @x-panel-log {"bodyKeys":["recordID"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"recordID","isList":false,"db":"job_records","output_colume":"file","output_value":"file"}],"formatZH":"下载计划任务记录 [file]","formatEN":"download the cronjob record [file]"}
|
||||
// @x-panel-log {"bodyKeys":["recordID"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"recordID","isList":false,"db":"job_records","output_column":"file","output_value":"file"}],"formatZH":"下载计划任务记录 [file]","formatEN":"download the cronjob record [file]"}
|
||||
func (b *BaseApi) TargetDownload(c *gin.Context) {
|
||||
var req dto.CronjobDownload
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -235,7 +235,7 @@ func (b *BaseApi) TargetDownload(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /cronjobs/handle [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"cronjobs","output_colume":"name","output_value":"name"}],"formatZH":"手动执行计划任务 [name]","formatEN":"manually execute the cronjob [name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"cronjobs","output_column":"name","output_value":"name"}],"formatZH":"手动执行计划任务 [name]","formatEN":"manually execute the cronjob [name]"}
|
||||
func (b *BaseApi) HandleOnce(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
@@ -54,7 +54,7 @@ func (b *BaseApi) CreateMysql(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/description/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"database_mysqls","output_colume":"name","output_value":"name"}],"formatZH":"mysql 数据库 [name] 描述信息修改 [description]","formatEN":"The description of the mysql database [name] is modified => [description]"}
|
||||
// @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_mysqls","output_column":"name","output_value":"name"}],"formatZH":"mysql 数据库 [name] 描述信息修改 [description]","formatEN":"The description of the mysql database [name] is modified => [description]"}
|
||||
func (b *BaseApi) UpdateMysqlDescription(c *gin.Context) {
|
||||
var req dto.UpdateDescription
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -80,7 +80,7 @@ func (b *BaseApi) UpdateMysqlDescription(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/change/password [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"database_mysqls","output_colume":"name","output_value":"name"}],"formatZH":"更新数据库 [name] 密码","formatEN":"Update database [name] password"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_mysqls","output_column":"name","output_value":"name"}],"formatZH":"更新数据库 [name] 密码","formatEN":"Update database [name] password"}
|
||||
func (b *BaseApi) ChangeMysqlPassword(c *gin.Context) {
|
||||
var req dto.ChangeDBInfo
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -115,7 +115,7 @@ func (b *BaseApi) ChangeMysqlPassword(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/change/access [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"database_mysqls","output_colume":"name","output_value":"name"}],"formatZH":"更新数据库 [name] 访问权限","formatEN":"Update database [name] access"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_mysqls","output_column":"name","output_value":"name"}],"formatZH":"更新数据库 [name] 访问权限","formatEN":"Update database [name] access"}
|
||||
func (b *BaseApi) ChangeMysqlAccess(c *gin.Context) {
|
||||
var req dto.ChangeDBInfo
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -216,7 +216,7 @@ func (b *BaseApi) SearchMysql(c *gin.Context) {
|
||||
// @Description 获取 mysql 数据库列表
|
||||
// @Accept json
|
||||
// @Param request body dto.PageInfo true "request"
|
||||
// @Success 200 {anrry} string
|
||||
// @Success 200 {array} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/options [get]
|
||||
func (b *BaseApi) ListDBName(c *gin.Context) {
|
||||
@@ -234,7 +234,7 @@ func (b *BaseApi) ListDBName(c *gin.Context) {
|
||||
// @Description Mysql 数据库删除前检查
|
||||
// @Accept json
|
||||
// @Param request body dto.OperateByID true "request"
|
||||
// @Success 200 {anrry} string
|
||||
// @Success 200 {array} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/del/check [post]
|
||||
func (b *BaseApi) DeleteCheckMysql(c *gin.Context) {
|
||||
@@ -264,7 +264,7 @@ func (b *BaseApi) DeleteCheckMysql(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"database_mysqls","output_colume":"name","output_value":"name"}],"formatZH":"删除 mysql 数据库 [name]","formatEN":"delete mysql database [name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_mysqls","output_column":"name","output_value":"name"}],"formatZH":"删除 mysql 数据库 [name]","formatEN":"delete mysql database [name]"}
|
||||
func (b *BaseApi) DeleteMysql(c *gin.Context) {
|
||||
var req dto.MysqlDBDelete
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
@@ -58,13 +58,13 @@ func (b *BaseApi) LoadDaemonJson(c *gin.Context) {
|
||||
// @Summary Update docker daemon.json
|
||||
// @Description 修改 docker 配置信息
|
||||
// @Accept json
|
||||
// @Param request body dto.DaemonJsonConf true "request"
|
||||
// @Param request body dto.SettingUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/daemonjson/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新 docker daemon.json 配置","formatEN":"Updated the docker daemon.json configuration"}
|
||||
// @x-panel-log {"bodyKeys":["key", "value"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新 docker daemon.json 配置 [key]=>[value]","formatEN":"Updated the docker daemon.json configuration [key]=>[value]"}
|
||||
func (b *BaseApi) UpdateDaemonJson(c *gin.Context) {
|
||||
var req dto.DaemonJsonConf
|
||||
var req dto.SettingUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
@@ -78,6 +78,30 @@ func (b *BaseApi) UpdateDaemonJson(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Container Docker
|
||||
// @Summary Update docker daemon.json log option
|
||||
// @Description 修改 docker 日志配置
|
||||
// @Accept json
|
||||
// @Param request body dto.LogOption true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/daemonjson/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新 docker daemon.json 日志配置","formatEN":"Updated the docker daemon.json log option"}
|
||||
func (b *BaseApi) UpdateLogOption(c *gin.Context) {
|
||||
var req dto.LogOption
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := dockerService.UpdateLogOption(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Container Docker
|
||||
// @Summary Update docker daemon.json by upload file
|
||||
// @Description 上传替换 docker 配置文件
|
||||
|
@@ -26,9 +26,10 @@ var (
|
||||
|
||||
cronjobService = service.NewICronjobService()
|
||||
|
||||
hostService = service.NewIHostService()
|
||||
groupService = service.NewIGroupService()
|
||||
fileService = service.NewIFileService()
|
||||
hostService = service.NewIHostService()
|
||||
groupService = service.NewIGroupService()
|
||||
fileService = service.NewIFileService()
|
||||
sshService = service.NewISSHService()
|
||||
firewallService = service.NewIFirewallService()
|
||||
|
||||
settingService = service.NewISettingService()
|
||||
@@ -48,4 +49,5 @@ var (
|
||||
upgradeService = service.NewIUpgradeService()
|
||||
|
||||
runtimeService = service.NewRuntimeService()
|
||||
processService = service.NewIProcessService()
|
||||
)
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -51,7 +52,7 @@ func (b *BaseApi) ListFiles(c *gin.Context) {
|
||||
// @Description 分页获取上传文件
|
||||
// @Accept json
|
||||
// @Param request body request.SearchUploadWithPage true "request"
|
||||
// @Success 200 {anrry} response.FileInfo
|
||||
// @Success 200 {array} response.FileInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /files/upload/search [post]
|
||||
func (b *BaseApi) SearchUploadWithPage(c *gin.Context) {
|
||||
@@ -80,7 +81,7 @@ func (b *BaseApi) SearchUploadWithPage(c *gin.Context) {
|
||||
// @Description 加载文件树
|
||||
// @Accept json
|
||||
// @Param request body request.FileOption true "request"
|
||||
// @Success 200 {anrry} response.FileTree
|
||||
// @Success 200 {array} response.FileTree
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /files/tree [post]
|
||||
func (b *BaseApi) GetFileTree(c *gin.Context) {
|
||||
@@ -454,17 +455,93 @@ func (b *BaseApi) MoveFile(c *gin.Context) {
|
||||
// @Router /files/download [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"下载文件 [name]","formatEN":"Download file [name]"}
|
||||
func (b *BaseApi) Download(c *gin.Context) {
|
||||
var req request.FileDownload
|
||||
filePath := c.Query("path")
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
}
|
||||
info, _ := file.Stat()
|
||||
c.Header("Content-Length", strconv.FormatInt(info.Size(), 10))
|
||||
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(info.Name()))
|
||||
http.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), file)
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
// @Summary Chunk Download file
|
||||
// @Description 分片下载下载文件
|
||||
// @Accept json
|
||||
// @Param request body request.FileDownload true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /files/chunkdownload [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"下载文件 [name]","formatEN":"Download file [name]"}
|
||||
func (b *BaseApi) DownloadChunkFiles(c *gin.Context) {
|
||||
var req request.FileChunkDownload
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
filePath, err := fileService.FileDownload(req)
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(req.Path) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrPathNotFound, nil)
|
||||
return
|
||||
}
|
||||
filePath := req.Path
|
||||
fstFile, err := fileOp.OpenFile(filePath)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
c.File(filePath)
|
||||
info, err := fstFile.Stat()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if info.IsDir() {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrFileDownloadDir, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.Writer.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", req.Name))
|
||||
c.Writer.Header().Set("Content-Type", "application/octet-stream")
|
||||
c.Writer.Header().Set("Content-Length", strconv.FormatInt(info.Size(), 10))
|
||||
c.Writer.Header().Set("Accept-Ranges", "bytes")
|
||||
|
||||
if c.Request.Header.Get("Range") != "" {
|
||||
rangeHeader := c.Request.Header.Get("Range")
|
||||
rangeArr := strings.Split(rangeHeader, "=")[1]
|
||||
rangeParts := strings.Split(rangeArr, "-")
|
||||
|
||||
startPos, _ := strconv.ParseInt(rangeParts[0], 10, 64)
|
||||
|
||||
var endPos int64
|
||||
if rangeParts[1] == "" {
|
||||
endPos = info.Size() - 1
|
||||
} else {
|
||||
endPos, _ = strconv.ParseInt(rangeParts[1], 10, 64)
|
||||
}
|
||||
|
||||
c.Writer.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", startPos, endPos, info.Size()))
|
||||
c.Writer.WriteHeader(http.StatusPartialContent)
|
||||
|
||||
buffer := make([]byte, 1024*1024)
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, _ = file.Seek(startPos, 0)
|
||||
reader := io.LimitReader(file, endPos-startPos+1)
|
||||
_, err = io.CopyBuffer(c.Writer, reader, buffer)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
c.File(filePath)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
@@ -575,6 +652,7 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int)
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /files/chunkupload [post]
|
||||
func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
|
||||
var err error
|
||||
fileForm, err := c.FormFile("chunk")
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
@@ -585,19 +663,16 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
chunkIndex, err := strconv.Atoi(c.PostForm("chunkIndex"))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
chunkCount, err := strconv.Atoi(c.PostForm("chunkCount"))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
tmpDir := path.Join(global.CONF.System.TmpDir, "upload")
|
||||
if !fileOp.Stat(tmpDir) {
|
||||
@@ -606,37 +681,50 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
filename := c.PostForm("filename")
|
||||
fileDir := filepath.Join(tmpDir, filename)
|
||||
|
||||
_ = os.MkdirAll(fileDir, 0755)
|
||||
if chunkIndex == 0 {
|
||||
if fileOp.Stat(fileDir) {
|
||||
_ = fileOp.DeleteDir(fileDir)
|
||||
}
|
||||
_ = os.MkdirAll(fileDir, 0755)
|
||||
}
|
||||
filePath := filepath.Join(fileDir, filename)
|
||||
|
||||
emptyFile, err := os.Create(filePath)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
_ = os.Remove(fileDir)
|
||||
}
|
||||
}()
|
||||
var (
|
||||
emptyFile *os.File
|
||||
chunkData []byte
|
||||
)
|
||||
|
||||
emptyFile, err = os.Create(filePath)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
defer emptyFile.Close()
|
||||
|
||||
chunkData, err := io.ReadAll(uploadFile)
|
||||
chunkData, err = io.ReadAll(uploadFile)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrFileUpload, err)
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, buserr.WithMap(constant.ErrFileUpload, map[string]interface{}{"name": filename, "detail": err.Error()}, err))
|
||||
return
|
||||
}
|
||||
|
||||
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", filename, chunkIndex))
|
||||
err = os.WriteFile(chunkPath, chunkData, 0644)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrFileUpload, err)
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, buserr.WithMap(constant.ErrFileUpload, map[string]interface{}{"name": filename, "detail": err.Error()}, err))
|
||||
return
|
||||
}
|
||||
|
||||
if chunkIndex+1 == chunkCount {
|
||||
err = mergeChunks(filename, fileDir, c.PostForm("path"), chunkCount)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrFileUpload, err)
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, buserr.WithMap(constant.ErrFileUpload, map[string]interface{}{"name": filename, "detail": err.Error()}, err))
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, true)
|
||||
@@ -651,19 +739,12 @@ var wsUpgrade = websocket.Upgrader{
|
||||
},
|
||||
}
|
||||
|
||||
var WsManager = websocket2.Manager{
|
||||
Group: make(map[string]*websocket2.Client),
|
||||
Register: make(chan *websocket2.Client, 128),
|
||||
UnRegister: make(chan *websocket2.Client, 128),
|
||||
ClientCount: 0,
|
||||
}
|
||||
|
||||
func (b *BaseApi) Ws(c *gin.Context) {
|
||||
ws, err := wsUpgrade.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
wsClient := websocket2.NewWsClient("wsClient", ws)
|
||||
wsClient := websocket2.NewWsClient("fileClient", ws)
|
||||
go wsClient.Read()
|
||||
go wsClient.Write()
|
||||
}
|
||||
|
@@ -149,7 +149,7 @@ func (b *BaseApi) BatchOperateRule(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := firewallService.BacthOperateRule(req); err != nil {
|
||||
if err := firewallService.BatchOperateRule(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
@@ -42,7 +42,7 @@ func (b *BaseApi) CreateGroup(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /groups/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"groups","output_colume":"name","output_value":"name"},{"input_colume":"id","input_value":"id","isList":false,"db":"groups","output_colume":"type","output_value":"type"}],"formatZH":"删除组 [type][name]","formatEN":"delete group [type][name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"groups","output_column":"name","output_value":"name"},{"input_column":"id","input_value":"id","isList":false,"db":"groups","output_column":"type","output_value":"type"}],"formatZH":"删除组 [type][name]","formatEN":"delete group [type][name]"}
|
||||
func (b *BaseApi) DeleteGroup(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -92,7 +92,7 @@ func (b *BaseApi) UpdateGroup(c *gin.Context) {
|
||||
// @Description 查询系统组
|
||||
// @Accept json
|
||||
// @Param request body dto.GroupSearch true "request"
|
||||
// @Success 200 {anrry} dto.GroupInfo
|
||||
// @Success 200 {array} dto.GroupInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /groups/search [post]
|
||||
func (b *BaseApi) ListGroup(c *gin.Context) {
|
||||
|
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/copier"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -36,7 +36,14 @@ func (b *BaseApi) CreateHost(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.Password = string(password)
|
||||
passwordItem, err := encrypt.StringEncrypt(string(password))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.Password = passwordItem
|
||||
req.PrivateKey = ""
|
||||
req.PassPhrase = ""
|
||||
}
|
||||
if req.AuthMode == "key" && len(req.PrivateKey) != 0 {
|
||||
privateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)
|
||||
@@ -44,7 +51,22 @@ func (b *BaseApi) CreateHost(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.PrivateKey = string(privateKey)
|
||||
keyItem, err := encrypt.StringEncrypt(string(privateKey))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.Password = keyItem
|
||||
|
||||
if len(req.PassPhrase) != 0 {
|
||||
pass, err := encrypt.StringEncrypt(req.PassPhrase)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.PassPhrase = pass
|
||||
}
|
||||
req.Password = ""
|
||||
}
|
||||
|
||||
host, err := hostService.Create(req)
|
||||
@@ -102,7 +124,7 @@ func (b *BaseApi) TestByID(c *gin.Context) {
|
||||
// @Description 加载主机树
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchForTree true "request"
|
||||
// @Success 200 {anrry} dto.HostTree
|
||||
// @Success 200 {array} dto.HostTree
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/tree [post]
|
||||
func (b *BaseApi) HostTree(c *gin.Context) {
|
||||
@@ -126,7 +148,7 @@ func (b *BaseApi) HostTree(c *gin.Context) {
|
||||
// @Description 获取主机列表分页
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchHostWithPage true "request"
|
||||
// @Success 200 {anrry} dto.HostTree
|
||||
// @Success 200 {array} dto.HostTree
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/search [post]
|
||||
func (b *BaseApi) SearchHost(c *gin.Context) {
|
||||
@@ -148,33 +170,6 @@ func (b *BaseApi) SearchHost(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Host
|
||||
// @Summary Load host info
|
||||
// @Description 加载主机信息
|
||||
// @Accept json
|
||||
// @Param id path integer true "request"
|
||||
// @Success 200 {object} dto.HostInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/:id [get]
|
||||
func (b *BaseApi) GetHostInfo(c *gin.Context) {
|
||||
id, err := helper.GetParamID(c)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
host, err := hostService.GetHostInfo(id)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
var hostDto dto.HostInfo
|
||||
if err := copier.Copy(&hostDto, host); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, hostDto)
|
||||
}
|
||||
|
||||
// @Tags Host
|
||||
// @Summary Delete host
|
||||
// @Description 删除主机
|
||||
@@ -183,7 +178,7 @@ func (b *BaseApi) GetHostInfo(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"hosts","output_colume":"addr","output_value":"addrs"}],"formatZH":"删除主机 [addrs]","formatEN":"delete host [addrs]"}
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"hosts","output_column":"addr","output_value":"addrs"}],"formatZH":"删除主机 [addrs]","formatEN":"delete host [addrs]"}
|
||||
func (b *BaseApi) DeleteHost(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -227,7 +222,12 @@ func (b *BaseApi) UpdateHost(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.Password = string(password)
|
||||
passwordItem, err := encrypt.StringEncrypt(string(password))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.Password = passwordItem
|
||||
}
|
||||
if req.AuthMode == "key" && len(req.PrivateKey) != 0 {
|
||||
privateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)
|
||||
@@ -235,7 +235,21 @@ func (b *BaseApi) UpdateHost(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.PrivateKey = string(privateKey)
|
||||
keyItem, err := encrypt.StringEncrypt(string(privateKey))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.PrivateKey = keyItem
|
||||
|
||||
if len(req.PassPhrase) != 0 {
|
||||
pass, err := encrypt.StringEncrypt(req.PassPhrase)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.PassPhrase = pass
|
||||
}
|
||||
}
|
||||
|
||||
upMap := make(map[string]interface{})
|
||||
@@ -246,10 +260,12 @@ func (b *BaseApi) UpdateHost(c *gin.Context) {
|
||||
upMap["user"] = req.User
|
||||
upMap["auth_mode"] = req.AuthMode
|
||||
upMap["remember_password"] = req.RememberPassword
|
||||
if len(req.Password) != 0 {
|
||||
if req.AuthMode == "password" {
|
||||
upMap["password"] = req.Password
|
||||
}
|
||||
if len(req.PrivateKey) != 0 {
|
||||
upMap["private_key"] = ""
|
||||
upMap["pass_phrase"] = ""
|
||||
} else {
|
||||
upMap["password"] = ""
|
||||
upMap["private_key"] = req.PrivateKey
|
||||
upMap["pass_phrase"] = req.PassPhrase
|
||||
}
|
||||
@@ -269,7 +285,7 @@ func (b *BaseApi) UpdateHost(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/update/group [post]
|
||||
// @x-panel-log {"bodyKeys":["id","group"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"hosts","output_colume":"addr","output_value":"addr"}],"formatZH":"切换主机[addr]分组 => [group]","formatEN":"change host [addr] group => [group]"}
|
||||
// @x-panel-log {"bodyKeys":["id","group"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"hosts","output_column":"addr","output_value":"addr"}],"formatZH":"切换主机[addr]分组 => [group]","formatEN":"change host [addr] group => [group]"}
|
||||
func (b *BaseApi) UpdateHostGroup(c *gin.Context) {
|
||||
var req dto.ChangeHostGroup
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
@@ -44,7 +44,7 @@ func (b *BaseApi) SearchImage(c *gin.Context) {
|
||||
// @Summary List images
|
||||
// @Description 获取镜像列表
|
||||
// @Produce json
|
||||
// @Success 200 {anrry} dto.Options
|
||||
// @Success 200 {array} dto.Options
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/image [get]
|
||||
func (b *BaseApi) ListImage(c *gin.Context) {
|
||||
@@ -93,7 +93,7 @@ func (b *BaseApi) ImageBuild(c *gin.Context) {
|
||||
// @Success 200 {string} log
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/image/pull [post]
|
||||
// @x-panel-log {"bodyKeys":["repoID","imageName"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"repoID","isList":false,"db":"image_repos","output_colume":"name","output_value":"reponame"}],"formatZH":"镜像拉取 [reponame][imageName]","formatEN":"image pull [reponame][imageName]"}
|
||||
// @x-panel-log {"bodyKeys":["repoID","imageName"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"repoID","isList":false,"db":"image_repos","output_column":"name","output_value":"reponame"}],"formatZH":"镜像拉取 [reponame][imageName]","formatEN":"image pull [reponame][imageName]"}
|
||||
func (b *BaseApi) ImagePull(c *gin.Context) {
|
||||
var req dto.ImagePull
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -122,7 +122,7 @@ func (b *BaseApi) ImagePull(c *gin.Context) {
|
||||
// @Success 200 {string} log
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/image/push [post]
|
||||
// @x-panel-log {"bodyKeys":["repoID","tagName","name"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"repoID","isList":false,"db":"image_repos","output_colume":"name","output_value":"reponame"}],"formatZH":"[tagName] 推送到 [reponame][name]","formatEN":"push [tagName] to [reponame][name]"}
|
||||
// @x-panel-log {"bodyKeys":["repoID","tagName","name"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"repoID","isList":false,"db":"image_repos","output_column":"name","output_value":"reponame"}],"formatZH":"[tagName] 推送到 [reponame][name]","formatEN":"push [tagName] to [reponame][name]"}
|
||||
func (b *BaseApi) ImagePush(c *gin.Context) {
|
||||
var req dto.ImagePush
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -207,7 +207,7 @@ func (b *BaseApi) ImageSave(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/image/tag [post]
|
||||
// @x-panel-log {"bodyKeys":["repoID","targetName"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"repoID","isList":false,"db":"image_repos","output_colume":"name","output_value":"reponame"}],"formatZH":"tag 镜像 [reponame][targetName]","formatEN":"tag image [reponame][targetName]"}
|
||||
// @x-panel-log {"bodyKeys":["repoID","targetName"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"repoID","isList":false,"db":"image_repos","output_column":"name","output_value":"reponame"}],"formatZH":"tag 镜像 [reponame][targetName]","formatEN":"tag image [reponame][targetName]"}
|
||||
func (b *BaseApi) ImageTag(c *gin.Context) {
|
||||
var req dto.ImageTag
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
@@ -44,7 +44,7 @@ func (b *BaseApi) SearchRepo(c *gin.Context) {
|
||||
// @Summary List image repos
|
||||
// @Description 获取镜像仓库列表
|
||||
// @Produce json
|
||||
// @Success 200 {anrry} dto.ImageRepoOption
|
||||
// @Success 200 {array} dto.ImageRepoOption
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/repo [get]
|
||||
func (b *BaseApi) ListRepo(c *gin.Context) {
|
||||
@@ -119,7 +119,7 @@ func (b *BaseApi) CreateRepo(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/repo/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"image_repos","output_colume":"name","output_value":"names"}],"formatZH":"删除镜像仓库 [names]","formatEN":"delete image repo [names]"}
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"image_repos","output_column":"name","output_value":"names"}],"formatZH":"删除镜像仓库 [names]","formatEN":"delete image repo [names]"}
|
||||
func (b *BaseApi) DeleteRepo(c *gin.Context) {
|
||||
var req dto.ImageRepoDelete
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -147,7 +147,7 @@ func (b *BaseApi) DeleteRepo(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/repo/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"image_repos","output_colume":"name","output_value":"name"}],"formatZH":"更新镜像仓库 [name]","formatEN":"update image repo information [name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"image_repos","output_column":"name","output_value":"name"}],"formatZH":"更新镜像仓库 [name]","formatEN":"update image repo information [name]"}
|
||||
func (b *BaseApi) UpdateRepo(c *gin.Context) {
|
||||
var req dto.ImageRepoUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
@@ -27,7 +27,7 @@ func (b *BaseApi) GetNginx(c *gin.Context) {
|
||||
// @Description 获取部分 OpenResty 配置信息
|
||||
// @Accept json
|
||||
// @Param request body request.NginxScopeReq true "request"
|
||||
// @Success 200 {anrry} response.NginxParam
|
||||
// @Success 200 {array} response.NginxParam
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /openResty/scope [post]
|
||||
func (b *BaseApi) GetNginxConfigByScope(c *gin.Context) {
|
||||
@@ -53,7 +53,7 @@ func (b *BaseApi) GetNginxConfigByScope(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /openResty/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteId","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新 nginx 配置 [domain]","formatEN":"Update nginx conf [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"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
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
40
backend/app/api/v1/process.go
Normal file
40
backend/app/api/v1/process.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
websocket2 "github.com/1Panel-dev/1Panel/backend/utils/websocket"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (b *BaseApi) ProcessWs(c *gin.Context) {
|
||||
ws, err := wsUpgrade.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
wsClient := websocket2.NewWsClient("processClient", ws)
|
||||
go wsClient.Read()
|
||||
go wsClient.Write()
|
||||
}
|
||||
|
||||
// @Tags Process
|
||||
// @Summary Stop Process
|
||||
// @Description 停止进程
|
||||
// @Param request body request.ProcessReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /process/stop [post]
|
||||
// @x-panel-log {"bodyKeys":["PID"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"结束进程 [PID]","formatEN":"结束进程 [PID]"}
|
||||
func (b *BaseApi) StopProcess(c *gin.Context) {
|
||||
var req request.ProcessReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := processService.StopProcess(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
@@ -2,14 +2,14 @@ package v1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"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/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/mfa"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -189,26 +189,40 @@ func (b *BaseApi) HandlePasswordExpired(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Sync system time
|
||||
// @Description 系统时间同步
|
||||
// @Success 200 {string} ntime
|
||||
// @Summary Load time zone options
|
||||
// @Description 加载系统可用时区
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/time/sync [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"系统时间同步","formatEN":"sync system time"}
|
||||
func (b *BaseApi) SyncTime(c *gin.Context) {
|
||||
ntime, err := ntp.GetRemoteTime()
|
||||
// @Router /settings/time/option [get]
|
||||
func (b *BaseApi) LoadTimeZone(c *gin.Context) {
|
||||
zones, err := settingService.LoadTimeZone()
|
||||
if err != nil {
|
||||
helper.SuccessWithData(c, time.Now().Format("2006-01-02 15:04:05 MST -0700"))
|
||||
return
|
||||
}
|
||||
|
||||
ts := ntime.Format("2006-01-02 15:04:05")
|
||||
if err := ntp.UpdateSystemDate(ts); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, zones)
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, ntime.Format("2006-01-02 15:04:05 MST -0700"))
|
||||
// @Tags System Setting
|
||||
// @Summary Sync system time
|
||||
// @Description 系统时间同步
|
||||
// @Accept json
|
||||
// @Param request body dto.SyncTime true "request"
|
||||
// @Success 200 {string} ntime
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/time/sync [post]
|
||||
// @x-panel-log {"bodyKeys":["ntpSite"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"系统时间同步[ntpSite]","formatEN":"sync system time [ntpSite]"}
|
||||
func (b *BaseApi) SyncTime(c *gin.Context) {
|
||||
var req dto.SyncTime
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := settingService.SyncTime(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
@@ -248,11 +262,23 @@ func (b *BaseApi) CleanMonitor(c *gin.Context) {
|
||||
// @Tags System Setting
|
||||
// @Summary Load mfa info
|
||||
// @Description 获取 mfa 信息
|
||||
// @Param interval path string true "request"
|
||||
// @Success 200 {object} mfa.Otp
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/mfa [get]
|
||||
// @Router /settings/mfa/:interval [get]
|
||||
func (b *BaseApi) GetMFA(c *gin.Context) {
|
||||
otp, err := mfa.GetOtp("admin")
|
||||
intervalStr, ok := c.Params.Get("interval")
|
||||
if !ok {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error interval in path"))
|
||||
return
|
||||
}
|
||||
interval, err := strconv.Atoi(intervalStr)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, fmt.Errorf("type conversion failed, err: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
otp, err := mfa.GetOtp("admin", interval)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
@@ -276,12 +302,17 @@ func (b *BaseApi) MFABind(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
success := mfa.ValidCode(req.Code, req.Secret)
|
||||
success := mfa.ValidCode(req.Code, req.Interval, req.Secret)
|
||||
if !success {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, errors.New("code is not valid"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := settingService.Update("MFAInterval", req.Interval); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := settingService.Update("MFAStatus", "enable"); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
|
@@ -68,7 +68,7 @@ func (b *BaseApi) ImportSnapshot(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/snapshot/description/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"snapshots","output_colume":"name","output_value":"name"}],"formatZH":"快照 [name] 描述信息修改 [description]","formatEN":"The description of the snapshot [name] is modified => [description]"}
|
||||
// @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"snapshots","output_column":"name","output_value":"name"}],"formatZH":"快照 [name] 描述信息修改 [description]","formatEN":"The description of the snapshot [name] is modified => [description]"}
|
||||
func (b *BaseApi) UpdateSnapDescription(c *gin.Context) {
|
||||
var req dto.UpdateDescription
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -119,7 +119,7 @@ func (b *BaseApi) SearchSnapshot(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/snapshot/recover [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"snapshots","output_colume":"name","output_value":"name"}],"formatZH":"从系统快照 [name] 恢复","formatEN":"Recover from system backup [name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"snapshots","output_column":"name","output_value":"name"}],"formatZH":"从系统快照 [name] 恢复","formatEN":"Recover from system backup [name]"}
|
||||
func (b *BaseApi) RecoverSnapshot(c *gin.Context) {
|
||||
var req dto.SnapshotRecover
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -146,7 +146,7 @@ func (b *BaseApi) RecoverSnapshot(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/snapshot/rollback [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"snapshots","output_colume":"name","output_value":"name"}],"formatZH":"从系统快照 [name] 回滚","formatEN":"Rollback from system backup [name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"snapshots","output_column":"name","output_value":"name"}],"formatZH":"从系统快照 [name] 回滚","formatEN":"Rollback from system backup [name]"}
|
||||
func (b *BaseApi) RollbackSnapshot(c *gin.Context) {
|
||||
var req dto.SnapshotRecover
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -173,7 +173,7 @@ func (b *BaseApi) RollbackSnapshot(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/snapshot/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"snapshots","output_colume":"name","output_value":"name"}],"formatZH":"删除系统快照 [name]","formatEN":"Delete system backup [name]"}
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"snapshots","output_column":"name","output_value":"name"}],"formatZH":"删除系统快照 [name]","formatEN":"Delete system backup [name]"}
|
||||
func (b *BaseApi) DeleteSnapshot(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
185
backend/app/api/v1/ssh.go
Normal file
185
backend/app/api/v1/ssh.go
Normal file
@@ -0,0 +1,185 @@
|
||||
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/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Load host ssh setting info
|
||||
// @Description 加载 SSH 配置信息
|
||||
// @Success 200 {object} dto.SSHInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/ssh/search [post]
|
||||
func (b *BaseApi) GetSSHInfo(c *gin.Context) {
|
||||
info, err := sshService.GetSSHInfo()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, info)
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Operate ssh
|
||||
// @Description 修改 SSH 服务状态
|
||||
// @Accept json
|
||||
// @Param request body dto.Operate true "request"
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/ssh/operate [post]
|
||||
// @x-panel-log {"bodyKeys":["operation"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"[operation] SSH ","formatEN":"[operation] SSH"}
|
||||
func (b *BaseApi) OperateSSH(c *gin.Context) {
|
||||
var req dto.Operate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := sshService.OperateSSH(req.Operation); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Update host ssh setting
|
||||
// @Description 更新 SSH 配置
|
||||
// @Accept json
|
||||
// @Param request body dto.SettingUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/ssh/update [post]
|
||||
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"修改 SSH 配置 [key] => [value]","formatEN":"update SSH setting [key] => [value]"}
|
||||
func (b *BaseApi) UpdateSSH(c *gin.Context) {
|
||||
var req dto.SettingUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := sshService.Update(req.Key, req.Value); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Update host ssh setting by file
|
||||
// @Description 上传文件更新 SSH 配置
|
||||
// @Accept json
|
||||
// @Param request body dto.SSHConf true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/conffile/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"修改 SSH 配置文件","formatEN":"update SSH conf"}
|
||||
func (b *BaseApi) UpdateSSHByfile(c *gin.Context) {
|
||||
var req dto.SSHConf
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := sshService.UpdateByFile(req.File); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Generate host ssh secret
|
||||
// @Description 生成 ssh 密钥
|
||||
// @Accept json
|
||||
// @Param request body dto.GenerateSSH true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/ssh/generate [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"生成 SSH 密钥 ","formatEN":"generate SSH secret"}
|
||||
func (b *BaseApi) GenerateSSH(c *gin.Context) {
|
||||
var req dto.GenerateSSH
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := sshService.GenerateSSH(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Load host ssh secret
|
||||
// @Description 获取 ssh 密钥
|
||||
// @Accept json
|
||||
// @Param request body dto.GenerateLoad true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/ssh/secret [post]
|
||||
func (b *BaseApi) LoadSSHSecret(c *gin.Context) {
|
||||
var req dto.GenerateLoad
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := sshService.LoadSSHSecret(req.EncryptionMode)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Load host ssh logs
|
||||
// @Description 获取 ssh 登录日志
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchSSHLog true "request"
|
||||
// @Success 200 {object} dto.SSHLog
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/ssh/logs [post]
|
||||
func (b *BaseApi) LoadSSHLogs(c *gin.Context) {
|
||||
var req dto.SearchSSHLog
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := sshService.LoadLog(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
@@ -5,12 +5,10 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/copier"
|
||||
@@ -22,24 +20,27 @@ import (
|
||||
)
|
||||
|
||||
func (b *BaseApi) WsSsh(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Query("id"))
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
defer wsConn.Close()
|
||||
|
||||
id, err := strconv.Atoi(c.Query("id"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param id in request")) {
|
||||
return
|
||||
}
|
||||
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
|
||||
return
|
||||
}
|
||||
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||
return
|
||||
}
|
||||
host, err := hostService.GetHostInfo(uint(id))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "load host info by id failed")) {
|
||||
return
|
||||
}
|
||||
var connInfo ssh.ConnInfo
|
||||
@@ -49,13 +50,6 @@ func (b *BaseApi) WsSsh(c *gin.Context) {
|
||||
connInfo.PassPhrase = []byte(host.PassPhrase)
|
||||
}
|
||||
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
defer wsConn.Close()
|
||||
|
||||
client, err := connInfo.NewClient()
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "failed to set up the connection. Please check the host information")) {
|
||||
return
|
||||
@@ -85,38 +79,36 @@ func (b *BaseApi) WsSsh(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
||||
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
redisConf, err := redisService.LoadConf()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load redis container failed, err: %v", err)
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
|
||||
return
|
||||
}
|
||||
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||
return
|
||||
}
|
||||
redisConf, err := redisService.LoadConf()
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "load redis container failed")) {
|
||||
return
|
||||
}
|
||||
|
||||
defer wsConn.Close()
|
||||
commands := "redis-cli"
|
||||
if len(redisConf.Requirepass) != 0 {
|
||||
commands = fmt.Sprintf("redis-cli -a %s --no-auth-warning", redisConf.Requirepass)
|
||||
}
|
||||
pidMap := loadMapFromDockerTop(redisConf.ContainerName)
|
||||
slave, err := terminal.NewCommand(fmt.Sprintf("docker exec -it %s %s", redisConf.ContainerName, commands))
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(redisConf.ContainerName, commands)
|
||||
defer killBash(redisConf.ContainerName, commands, pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave)
|
||||
@@ -137,24 +129,6 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
|
||||
containerID := c.Query("containerid")
|
||||
command := c.Query("command")
|
||||
user := c.Query("user")
|
||||
if len(command) == 0 || len(containerID) == 0 {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error param of command or containerID"))
|
||||
return
|
||||
}
|
||||
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||
@@ -162,11 +136,33 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
|
||||
}
|
||||
defer wsConn.Close()
|
||||
|
||||
cmds := fmt.Sprintf("docker exec %s %s", containerID, command)
|
||||
if len(user) != 0 {
|
||||
cmds = fmt.Sprintf("docker exec -u %s %s %s", user, containerID, command)
|
||||
containerID := c.Query("containerid")
|
||||
command := c.Query("command")
|
||||
user := c.Query("user")
|
||||
if len(command) == 0 || len(containerID) == 0 {
|
||||
if wshandleError(wsConn, errors.New("error param of command or containerID")) {
|
||||
return
|
||||
}
|
||||
}
|
||||
stdout, err := cmd.Exec(cmds)
|
||||
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
|
||||
return
|
||||
}
|
||||
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||
return
|
||||
}
|
||||
|
||||
cmds := []string{"exec", containerID, command}
|
||||
if len(user) != 0 {
|
||||
cmds = []string{"exec", "-u", user, containerID, command}
|
||||
}
|
||||
if cmd.CheckIllegal(user, containerID, command) {
|
||||
if wshandleError(wsConn, errors.New(" The command contains illegal characters.")) {
|
||||
return
|
||||
}
|
||||
}
|
||||
stdout, err := cmd.ExecWithCheck("docker", cmds...)
|
||||
if wshandleError(wsConn, errors.WithMessage(err, stdout)) {
|
||||
return
|
||||
}
|
||||
@@ -175,11 +171,12 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
|
||||
if len(user) != 0 {
|
||||
commands = fmt.Sprintf("docker exec -it -u %s %s %s", user, containerID, command)
|
||||
}
|
||||
pidMap := loadMapFromDockerTop(containerID)
|
||||
slave, err := terminal.NewCommand(commands)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(containerID, command)
|
||||
defer killBash(containerID, command, pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave)
|
||||
@@ -219,13 +216,40 @@ func wshandleError(ws *websocket.Conn, err error) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func killBash(containerID, comm string) {
|
||||
sudo := ""
|
||||
if cmd.HasNoPasswordSudo() {
|
||||
sudo = "sudo"
|
||||
func loadMapFromDockerTop(containerID string) map[string]string {
|
||||
pidMap := make(map[string]string)
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
|
||||
stdout, err := cmd.Execf("%s docker top %s -eo pid,command ", sudo, containerID)
|
||||
if err != nil {
|
||||
return pidMap
|
||||
}
|
||||
lines := strings.Split(stdout, "\n")
|
||||
for _, line := range lines {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
pidMap[parts[0]] = parts[1]
|
||||
}
|
||||
return pidMap
|
||||
}
|
||||
|
||||
func killBash(containerID, comm string, pidMap map[string]string) {
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
newPidMap := loadMapFromDockerTop(containerID)
|
||||
for pid, command := range newPidMap {
|
||||
isOld := false
|
||||
for pid2 := range pidMap {
|
||||
if pid == pid2 {
|
||||
isOld = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isOld && command == comm {
|
||||
_, _ = cmd.Execf("%s kill -9 %s", sudo, pid)
|
||||
}
|
||||
}
|
||||
command := exec.Command("sh", "-c", fmt.Sprintf("%s kill -9 $(docker top %s -eo pid,command | grep '%s' | awk '{print $1}')", sudo, containerID, comm))
|
||||
_, _ = command.CombinedOutput()
|
||||
}
|
||||
|
||||
var upGrader = websocket.Upgrader{
|
||||
|
@@ -36,7 +36,7 @@ func (b *BaseApi) PageWebsite(c *gin.Context) {
|
||||
// @Tags Website
|
||||
// @Summary List websites
|
||||
// @Description 获取网站列表
|
||||
// @Success 200 {anrry} response.WebsiteDTO
|
||||
// @Success 200 {array} response.WebsiteDTO
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/list [get]
|
||||
func (b *BaseApi) GetWebsites(c *gin.Context) {
|
||||
@@ -51,7 +51,7 @@ func (b *BaseApi) GetWebsites(c *gin.Context) {
|
||||
// @Tags Website
|
||||
// @Summary List website names
|
||||
// @Description 获取网站列表
|
||||
// @Success 200 {anrry} string
|
||||
// @Success 200 {array} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/options [get]
|
||||
func (b *BaseApi) GetWebsiteOptions(c *gin.Context) {
|
||||
@@ -95,7 +95,7 @@ func (b *BaseApi) CreateWebsite(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/operate [post]
|
||||
// @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"[operate] 网站 [domain]","formatEN":"[operate] website [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"[operate] 网站 [domain]","formatEN":"[operate] website [domain]"}
|
||||
func (b *BaseApi) OpWebsite(c *gin.Context) {
|
||||
var req request.WebsiteOp
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -118,7 +118,7 @@ func (b *BaseApi) OpWebsite(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"删除网站 [domain]","formatEN":"Delete website [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"删除网站 [domain]","formatEN":"Delete website [domain]"}
|
||||
func (b *BaseApi) DeleteWebsite(c *gin.Context) {
|
||||
var req request.WebsiteDelete
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -207,7 +207,7 @@ func (b *BaseApi) GetWebsiteNginx(c *gin.Context) {
|
||||
// @Description 通过网站 id 查询域名
|
||||
// @Accept json
|
||||
// @Param websiteId path integer true "request"
|
||||
// @Success 200 {anrry} model.WebsiteDomain
|
||||
// @Success 200 {array} model.WebsiteDomain
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/domains/:websiteId [get]
|
||||
func (b *BaseApi) GetWebDomains(c *gin.Context) {
|
||||
@@ -232,7 +232,7 @@ func (b *BaseApi) GetWebDomains(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/domains/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_domains","output_colume":"domain","output_value":"domain"}],"formatZH":"删除域名 [domain]","formatEN":"Delete domain [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_domains","output_column":"domain","output_value":"domain"}],"formatZH":"删除域名 [domain]","formatEN":"Delete domain [domain]"}
|
||||
func (b *BaseApi) DeleteWebDomain(c *gin.Context) {
|
||||
var req request.WebsiteDomainDelete
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -300,7 +300,7 @@ func (b *BaseApi) GetNginxConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/config/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteId","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"nginx 配置修改 [domain]","formatEN":"Nginx conf update [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"nginx 配置修改 [domain]","formatEN":"Nginx conf update [domain]"}
|
||||
func (b *BaseApi) UpdateNginxConfig(c *gin.Context) {
|
||||
var req request.NginxConfigUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -344,7 +344,7 @@ func (b *BaseApi) GetHTTPSConfig(c *gin.Context) {
|
||||
// @Success 200 {object} response.WebsiteHTTPS
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/:id/https [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteId","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] https 配置","formatEN":"Update website https [domain] conf"}
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] https 配置","formatEN":"Update website https [domain] conf"}
|
||||
func (b *BaseApi) UpdateHTTPSConfig(c *gin.Context) {
|
||||
var req request.WebsiteHTTPSOp
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -367,7 +367,7 @@ func (b *BaseApi) UpdateHTTPSConfig(c *gin.Context) {
|
||||
// @Description 网站创建前检查
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteInstallCheckReq true "request"
|
||||
// @Success 200 {anrry} request.WebsitePreInstallCheck
|
||||
// @Success 200 {array} response.WebsitePreInstallCheck
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/check [post]
|
||||
func (b *BaseApi) CreateWebsiteCheck(c *gin.Context) {
|
||||
@@ -414,7 +414,7 @@ func (b *BaseApi) GetWebsiteWafConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/waf/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteId","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"WAF 配置修改 [domain]","formatEN":"WAF conf update [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"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 := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -436,7 +436,7 @@ func (b *BaseApi) UpdateWebsiteWafConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/nginx/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"[domain] Nginx 配置修改","formatEN":"[domain] Nginx conf update"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"[domain] Nginx 配置修改","formatEN":"[domain] Nginx conf update"}
|
||||
func (b *BaseApi) UpdateWebsiteNginxConfig(c *gin.Context) {
|
||||
var req request.WebsiteNginxUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -458,7 +458,7 @@ func (b *BaseApi) UpdateWebsiteNginxConfig(c *gin.Context) {
|
||||
// @Success 200 {object} response.WebsiteLog
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/log [post]
|
||||
// @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"[domain][operate] 日志","formatEN":"[domain][operate] logs"}
|
||||
// @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"[domain][operate] 日志","formatEN":"[domain][operate] logs"}
|
||||
func (b *BaseApi) OpWebsiteLog(c *gin.Context) {
|
||||
var req request.WebsiteLogReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -481,7 +481,7 @@ func (b *BaseApi) OpWebsiteLog(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/default/server [post]
|
||||
// @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"修改默认 server => [domain]","formatEN":"Change default server => [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"修改默认 server => [domain]","formatEN":"Change default server => [domain]"}
|
||||
func (b *BaseApi) ChangeDefaultServer(c *gin.Context) {
|
||||
var req request.WebsiteDefaultUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -525,7 +525,7 @@ func (b *BaseApi) GetWebsitePHPConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/php/config [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"[domain] PHP 配置修改","formatEN":"[domain] PHP conf update"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"[domain] PHP 配置修改","formatEN":"[domain] PHP conf update"}
|
||||
func (b *BaseApi) UpdateWebsitePHPConfig(c *gin.Context) {
|
||||
var req request.WebsitePHPConfigUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -541,13 +541,13 @@ func (b *BaseApi) UpdateWebsitePHPConfig(c *gin.Context) {
|
||||
|
||||
// @Tags Website PHP
|
||||
// @Summary Update php conf
|
||||
// @Description 更新 php 配置
|
||||
// @Description 更新 php 配置文件
|
||||
// @Accept json
|
||||
// @Param request body request.WebsitePHPFileUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/php/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteId","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"php 配置修改 [domain]","formatEN":"Nginx conf update [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"php 配置修改 [domain]","formatEN":"Nginx conf update [domain]"}
|
||||
func (b *BaseApi) UpdatePHPFile(c *gin.Context) {
|
||||
var req request.WebsitePHPFileUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -591,7 +591,7 @@ func (b *BaseApi) GetRewriteConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/rewrite/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteID","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"伪静态配置修改 [domain]","formatEN":"Nginx conf rewrite update [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteID","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"伪静态配置修改 [domain]","formatEN":"Nginx conf rewrite update [domain]"}
|
||||
func (b *BaseApi) UpdateRewriteConfig(c *gin.Context) {
|
||||
var req request.NginxRewriteUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -613,7 +613,7 @@ func (b *BaseApi) UpdateRewriteConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/dir/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] 目录","formatEN":"Update domain [domain] dir"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] 目录","formatEN":"Update domain [domain] dir"}
|
||||
func (b *BaseApi) UpdateSiteDir(c *gin.Context) {
|
||||
var req request.WebsiteUpdateDir
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -635,7 +635,7 @@ func (b *BaseApi) UpdateSiteDir(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/dir/permission [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] 目录权限","formatEN":"Update domain [domain] dir permission"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] 目录权限","formatEN":"Update domain [domain] dir permission"}
|
||||
func (b *BaseApi) UpdateSiteDirPermission(c *gin.Context) {
|
||||
var req request.WebsiteUpdateDirPermission
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -679,7 +679,7 @@ func (b *BaseApi) GetProxyConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/proxies/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"修改网站 [domain] 反向代理配置 ","formatEN":"Update domain [domain] proxy config"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"修改网站 [domain] 反向代理配置 ","formatEN":"Update domain [domain] proxy config"}
|
||||
func (b *BaseApi) UpdateProxyConfig(c *gin.Context) {
|
||||
var req request.WebsiteProxyConfig
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -702,7 +702,7 @@ func (b *BaseApi) UpdateProxyConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/proxy/file [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteID","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新反向代理文件 [domain]","formatEN":"Nginx conf proxy file update [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteID","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新反向代理文件 [domain]","formatEN":"Nginx conf proxy file update [domain]"}
|
||||
func (b *BaseApi) UpdateProxyConfigFile(c *gin.Context) {
|
||||
var req request.NginxProxyUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -758,3 +758,46 @@ func (b *BaseApi) UpdateAuthConfig(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Get AntiLeech conf
|
||||
// @Description 获取防盗链配置
|
||||
// @Accept json
|
||||
// @Param request body request.NginxCommonReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/leech [post]
|
||||
func (b *BaseApi) GetAntiLeech(c *gin.Context) {
|
||||
var req request.NginxCommonReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
res, err := websiteService.GetAntiLeech(req.WebsiteID)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update AntiLeech
|
||||
// @Description 更新防盗链配置
|
||||
// @Accept json
|
||||
// @Param request body request.NginxAntiLeechUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/leech/update [post]
|
||||
func (b *BaseApi) UpdateAntiLeech(c *gin.Context) {
|
||||
var req request.NginxAntiLeechUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateAntiLeech(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
@@ -64,7 +64,7 @@ func (b *BaseApi) CreateWebsiteAcmeAccount(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/acme/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_acme_accounts","output_colume":"email","output_value":"email"}],"formatZH":"删除网站 acme [email]","formatEN":"Delete website acme [email]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_acme_accounts","output_column":"email","output_value":"email"}],"formatZH":"删除网站 acme [email]","formatEN":"Delete website acme [email]"}
|
||||
func (b *BaseApi) DeleteWebsiteAcmeAccount(c *gin.Context) {
|
||||
var req request.WebsiteResourceReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
@@ -85,7 +85,7 @@ func (b *BaseApi) UpdateWebsiteDnsAccount(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/dns/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_dns_accounts","output_colume":"name","output_value":"name"}],"formatZH":"删除网站 dns [name]","formatEN":"Delete website dns [name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_dns_accounts","output_column":"name","output_value":"name"}],"formatZH":"删除网站 dns [name]","formatEN":"Delete website dns [name]"}
|
||||
func (b *BaseApi) DeleteWebsiteDnsAccount(c *gin.Context) {
|
||||
var req request.WebsiteResourceReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
@@ -35,7 +35,7 @@ func (b *BaseApi) PageWebsiteSSL(c *gin.Context) {
|
||||
Items: accounts,
|
||||
})
|
||||
} else {
|
||||
list, err := websiteSSLService.Search()
|
||||
list, err := websiteSSLService.Search(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
@@ -75,7 +75,7 @@ func (b *BaseApi) CreateWebsiteSSL(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/ssl/renew [post]
|
||||
// @x-panel-log {"bodyKeys":["SSLId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"SSLId","isList":false,"db":"website_ssls","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"重置 ssl [domain]","formatEN":"Renew ssl [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["SSLId"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"SSLId","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"重置 ssl [domain]","formatEN":"Renew ssl [domain]"}
|
||||
func (b *BaseApi) RenewWebsiteSSL(c *gin.Context) {
|
||||
var req request.WebsiteSSLRenew
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -94,7 +94,7 @@ func (b *BaseApi) RenewWebsiteSSL(c *gin.Context) {
|
||||
// @Description 解析网站 ssl
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteDNSReq true "request"
|
||||
// @Success 200 {anrry} response.WebsiteDNSRes
|
||||
// @Success 200 {array} response.WebsiteDNSRes
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/ssl/resolve [post]
|
||||
func (b *BaseApi) GetDNSResolve(c *gin.Context) {
|
||||
@@ -119,7 +119,7 @@ func (b *BaseApi) GetDNSResolve(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/ssl/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_ssls","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"删除 ssl [domain]","formatEN":"Delete ssl [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"删除 ssl [domain]","formatEN":"Delete ssl [domain]"}
|
||||
func (b *BaseApi) DeleteWebsiteSSL(c *gin.Context) {
|
||||
var req request.WebsiteResourceReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -185,7 +185,7 @@ func (b *BaseApi) GetWebsiteSSLById(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/ssl/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_ssls","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新证书设置 [domain]","formatEN":"Update ssl config [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新证书设置 [domain]","formatEN":"Update ssl config [domain]"}
|
||||
func (b *BaseApi) UpdateWebsiteSSL(c *gin.Context) {
|
||||
var req request.WebsiteSSLUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
)
|
||||
|
||||
type AppDatabase struct {
|
||||
@@ -32,19 +32,47 @@ type AppVersion struct {
|
||||
}
|
||||
|
||||
type AppList struct {
|
||||
Version string `json:"version"`
|
||||
Tags []Tag `json:"tags"`
|
||||
Items []AppDefine `json:"items"`
|
||||
Valid bool `json:"valid"`
|
||||
Violations []string `json:"violations"`
|
||||
LastModified int `json:"lastModified"`
|
||||
|
||||
Apps []AppDefine `json:"apps"`
|
||||
Extra ExtraProperties `json:"additionalProperties"`
|
||||
}
|
||||
|
||||
type AppDefine struct {
|
||||
Key string `json:"key"`
|
||||
Icon string `json:"icon"`
|
||||
Name string `json:"name"`
|
||||
ReadMe string `json:"readMe"`
|
||||
LastModified int `json:"lastModified"`
|
||||
|
||||
AppProperty AppProperty `json:"additionalProperties"`
|
||||
Versions []AppConfigVersion `json:"versions"`
|
||||
}
|
||||
|
||||
type LocalAppAppDefine struct {
|
||||
AppProperty model.App `json:"additionalProperties" yaml:"additionalProperties"`
|
||||
}
|
||||
|
||||
type LocalAppParam struct {
|
||||
AppParams LocalAppInstallDefine `json:"additionalProperties" yaml:"additionalProperties"`
|
||||
}
|
||||
|
||||
type LocalAppInstallDefine struct {
|
||||
FormFields interface{} `json:"formFields" yaml:"formFields"`
|
||||
}
|
||||
|
||||
type ExtraProperties struct {
|
||||
Tags []Tag `json:"tags"`
|
||||
}
|
||||
|
||||
type AppProperty struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Tags []string `json:"tags"`
|
||||
Versions []string `json:"versions"`
|
||||
ShortDescZh string `json:"shortDescZh"`
|
||||
ShortDescEn string `json:"shortDescEn"`
|
||||
Type string `json:"type"`
|
||||
Key string `json:"key"`
|
||||
Required []string `json:"Required"`
|
||||
CrossVersionUpdate bool `json:"crossVersionUpdate"`
|
||||
Limit int `json:"limit"`
|
||||
@@ -54,9 +82,12 @@ type AppDefine struct {
|
||||
Document string `json:"document"`
|
||||
}
|
||||
|
||||
func (define AppDefine) GetRequired() string {
|
||||
by, _ := json.Marshal(define.Required)
|
||||
return string(by)
|
||||
type AppConfigVersion struct {
|
||||
Name string `json:"name"`
|
||||
LastModified int `json:"lastModified"`
|
||||
DownloadUrl string `json:"downloadUrl"`
|
||||
DownloadCallBackUrl string `json:"downloadCallBackUrl"`
|
||||
AppForm interface{} `json:"additionalProperties"`
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
@@ -79,6 +110,7 @@ type AppFormFields struct {
|
||||
Edit bool `json:"edit"`
|
||||
Rule string `json:"rule"`
|
||||
Multiple bool `json:"multiple"`
|
||||
Child interface{} `json:"child"`
|
||||
Values []AppFormValue `json:"values"`
|
||||
}
|
||||
|
||||
|
@@ -12,8 +12,9 @@ type UserLoginInfo struct {
|
||||
}
|
||||
|
||||
type MfaCredential struct {
|
||||
Secret string `json:"secret"`
|
||||
Code string `json:"code"`
|
||||
Secret string `json:"secret"`
|
||||
Code string `json:"code"`
|
||||
Interval string `json:"interval"`
|
||||
}
|
||||
|
||||
type Login struct {
|
||||
|
@@ -8,15 +8,17 @@ type BackupOperate struct {
|
||||
Bucket string `json:"bucket"`
|
||||
AccessKey string `json:"accessKey"`
|
||||
Credential string `json:"credential"`
|
||||
BackupPath string `json:"backupPath"`
|
||||
Vars string `json:"vars" validate:"required"`
|
||||
}
|
||||
|
||||
type BackupInfo struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Type string `json:"type"`
|
||||
Bucket string `json:"bucket"`
|
||||
Vars string `json:"vars"`
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Type string `json:"type"`
|
||||
Bucket string `json:"bucket"`
|
||||
BackupPath string `json:"backupPath"`
|
||||
Vars string `json:"vars"`
|
||||
}
|
||||
|
||||
type BackupSearch struct {
|
||||
@@ -36,7 +38,7 @@ type CommonBackup struct {
|
||||
DetailName string `json:"detailName"`
|
||||
}
|
||||
type CommonRecover struct {
|
||||
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO"`
|
||||
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive"`
|
||||
Type string `json:"type" validate:"required,oneof=app mysql redis website"`
|
||||
Name string `json:"name"`
|
||||
DetailName string `json:"detailName"`
|
||||
@@ -60,7 +62,7 @@ type BackupRecords struct {
|
||||
}
|
||||
|
||||
type DownloadRecord struct {
|
||||
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO"`
|
||||
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive"`
|
||||
FileDir string `json:"fileDir" validate:"required"`
|
||||
FileName string `json:"fileName" validate:"required"`
|
||||
}
|
||||
|
@@ -2,7 +2,9 @@ package dto
|
||||
|
||||
type SearchWithPage struct {
|
||||
PageInfo
|
||||
Info string `json:"info"`
|
||||
Info string `json:"info"`
|
||||
OrderBy string `json:"orderBy"`
|
||||
Order string `json:"order"`
|
||||
}
|
||||
|
||||
type PageInfo struct {
|
||||
@@ -23,6 +25,10 @@ type OperateByID struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
type Operate struct {
|
||||
Operation string `json:"operation" validate:"required"`
|
||||
}
|
||||
|
||||
type BatchDeleteReq struct {
|
||||
Ids []uint `json:"ids" validate:"required"`
|
||||
}
|
||||
|
@@ -5,6 +5,8 @@ import "time"
|
||||
type PageContainer struct {
|
||||
PageInfo
|
||||
Name string `json:"name"`
|
||||
OrderBy string `json:"orderBy"`
|
||||
Order string `json:"order"`
|
||||
Filters string `json:"filters"`
|
||||
}
|
||||
|
||||
@@ -22,22 +24,29 @@ type ContainerInfo struct {
|
||||
State string `json:"state"`
|
||||
RunTime string `json:"runTime"`
|
||||
|
||||
CPUPercent float64 `json:"cpuPercent"`
|
||||
MemoryPercent float64 `json:"memoryPercent"`
|
||||
Ports []string `json:"ports"`
|
||||
Ports []string `json:"ports"`
|
||||
|
||||
IsFromApp bool `json:"isFromApp"`
|
||||
IsFromCompose bool `json:"isFromCompose"`
|
||||
}
|
||||
|
||||
type ContainerCreate struct {
|
||||
type ResourceLimit struct {
|
||||
CPU int `json:"cpu"`
|
||||
Memory int `json:"memory"`
|
||||
}
|
||||
|
||||
type ContainerOperate struct {
|
||||
ContainerID string `json:"containerID"`
|
||||
ForcePull bool `json:"forcePull"`
|
||||
Name string `json:"name"`
|
||||
Image string `json:"image"`
|
||||
Network string `json:"network"`
|
||||
PublishAllPorts bool `json:"publishAllPorts"`
|
||||
ExposedPorts []PortHelper `json:"exposedPorts"`
|
||||
Cmd []string `json:"cmd"`
|
||||
NanoCPUs int64 `json:"nanoCPUs"`
|
||||
Memory int64 `json:"memory"`
|
||||
CPUShares int64 `json:"cpuShares"`
|
||||
NanoCPUs float64 `json:"nanoCPUs"`
|
||||
Memory float64 `json:"memory"`
|
||||
AutoRemove bool `json:"autoRemove"`
|
||||
Volumes []VolumeHelper `json:"volumes"`
|
||||
Labels []string `json:"labels"`
|
||||
@@ -45,7 +54,19 @@ type ContainerCreate struct {
|
||||
RestartPolicy string `json:"restartPolicy"`
|
||||
}
|
||||
|
||||
type ContainterStats struct {
|
||||
type ContainerUpgrade struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Image string `json:"image" validate:"required"`
|
||||
ForcePull bool `json:"forcePull"`
|
||||
}
|
||||
|
||||
type ContainerListStats struct {
|
||||
ContainerID string `json:"containerID"`
|
||||
CPUPercent float64 `json:"cpuPercent"`
|
||||
MemoryPercent float64 `json:"memoryPercent"`
|
||||
}
|
||||
|
||||
type ContainerStats struct {
|
||||
CPUPercent float64 `json:"cpuPercent"`
|
||||
Memory float64 `json:"memory"`
|
||||
Cache float64 `json:"cache"`
|
||||
@@ -69,17 +90,22 @@ type PortHelper struct {
|
||||
Protocol string `json:"protocol"`
|
||||
}
|
||||
|
||||
type ContainerLog struct {
|
||||
ContainerID string `json:"containerID" validate:"required"`
|
||||
Mode string `json:"mode" validate:"required"`
|
||||
}
|
||||
|
||||
type ContainerOperation struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Operation string `json:"operation" validate:"required,oneof=start stop restart kill pause unpause rename remove"`
|
||||
NewName string `json:"newName"`
|
||||
}
|
||||
|
||||
type ContainerPrune struct {
|
||||
PruneType string `json:"pruneType" validate:"required,oneof=container image volume network"`
|
||||
WithTagAll bool `json:"withTagAll"`
|
||||
}
|
||||
|
||||
type ContainerPruneReport struct {
|
||||
DeletedNumber int `json:"deletedNumber"`
|
||||
SpaceReclaimed int `json:"spaceReclaimed"`
|
||||
}
|
||||
|
||||
type Network struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
@@ -91,7 +117,7 @@ type Network struct {
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Attachable bool `json:"attachable"`
|
||||
}
|
||||
type NetworkCreat struct {
|
||||
type NetworkCreate struct {
|
||||
Name string `json:"name"`
|
||||
Driver string `json:"driver"`
|
||||
Options []string `json:"options"`
|
||||
@@ -108,7 +134,7 @@ type Volume struct {
|
||||
Mountpoint string `json:"mountpoint"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
type VolumeCreat struct {
|
||||
type VolumeCreate struct {
|
||||
Name string `json:"name"`
|
||||
Driver string `json:"driver"`
|
||||
Options []string `json:"options"`
|
||||
|
@@ -6,12 +6,14 @@ 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=7,min=1"`
|
||||
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"`
|
||||
|
||||
Script string `json:"script"`
|
||||
ContainerName string `json:"containerName"`
|
||||
Website string `json:"website"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
DBName string `json:"dbName"`
|
||||
@@ -26,12 +28,14 @@ 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=7,min=1"`
|
||||
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"`
|
||||
|
||||
Script string `json:"script"`
|
||||
ContainerName string `json:"containerName"`
|
||||
Website string `json:"website"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
DBName string `json:"dbName"`
|
||||
@@ -71,8 +75,10 @@ type CronjobInfo struct {
|
||||
Day int `json:"day"`
|
||||
Hour int `json:"hour"`
|
||||
Minute int `json:"minute"`
|
||||
Second int `json:"second"`
|
||||
|
||||
Script string `json:"script"`
|
||||
ContainerName string `json:"containerName"`
|
||||
Website string `json:"website"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
DBName string `json:"dbName"`
|
||||
@@ -83,7 +89,7 @@ type CronjobInfo struct {
|
||||
TargetDirID int `json:"targetDirID"`
|
||||
RetainCopies int `json:"retainCopies"`
|
||||
|
||||
LastRecrodTime string `json:"lastRecrodTime"`
|
||||
LastRecordTime string `json:"lastRecordTime"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
|
@@ -13,10 +13,16 @@ type DaemonJsonConf struct {
|
||||
LiveRestore bool `json:"liveRestore"`
|
||||
IPTables bool `json:"iptables"`
|
||||
CgroupDriver string `json:"cgroupDriver"`
|
||||
|
||||
LogMaxSize string `json:"logMaxSize"`
|
||||
LogMaxFile string `json:"logMaxFile"`
|
||||
}
|
||||
|
||||
type LogOption struct {
|
||||
LogMaxSize string `json:"logMaxSize"`
|
||||
LogMaxFile string `json:"logMaxFile"`
|
||||
}
|
||||
|
||||
type DockerOperation struct {
|
||||
StopSocket bool `json:"stopSocket"`
|
||||
StopService bool `json:"stopService"`
|
||||
Operation string `json:"operation" validate:"required,oneof=start restart stop"`
|
||||
Operation string `json:"operation" validate:"required,oneof=start restart stop"`
|
||||
}
|
||||
|
@@ -10,35 +10,35 @@ type ImageInfo struct {
|
||||
}
|
||||
|
||||
type ImageLoad struct {
|
||||
Path string `josn:"path" validate:"required"`
|
||||
Path string `json:"path" validate:"required"`
|
||||
}
|
||||
|
||||
type ImageBuild struct {
|
||||
From string `josn:"from" validate:"required"`
|
||||
From string `json:"from" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Dockerfile string `josn:"dockerfile" validate:"required"`
|
||||
Tags []string `josn:"tags"`
|
||||
Dockerfile string `json:"dockerfile" validate:"required"`
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
type ImagePull struct {
|
||||
RepoID uint `josn:"repoID"`
|
||||
ImageName string `josn:"imageName" validate:"required"`
|
||||
RepoID uint `json:"repoID"`
|
||||
ImageName string `json:"imageName" validate:"required"`
|
||||
}
|
||||
|
||||
type ImageTag struct {
|
||||
RepoID uint `josn:"repoID"`
|
||||
RepoID uint `json:"repoID"`
|
||||
SourceID string `json:"sourceID" validate:"required"`
|
||||
TargetName string `josn:"targetName" validate:"required"`
|
||||
TargetName string `json:"targetName" validate:"required"`
|
||||
}
|
||||
|
||||
type ImagePush struct {
|
||||
RepoID uint `josn:"repoID" validate:"required"`
|
||||
RepoID uint `json:"repoID" validate:"required"`
|
||||
TagName string `json:"tagName" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
|
||||
type ImageSave struct {
|
||||
TagName string `json:"tagName" validate:"required"`
|
||||
Path string `josn:"path" validate:"required"`
|
||||
Path string `json:"path" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
|
@@ -18,6 +18,18 @@ type AppInstallCreate struct {
|
||||
Params map[string]interface{} `json:"params"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Services map[string]string `json:"services"`
|
||||
AppContainerConfig
|
||||
}
|
||||
|
||||
type AppContainerConfig struct {
|
||||
Advanced bool `json:"advanced"`
|
||||
CpuQuota float64 `json:"cpuQuota"`
|
||||
MemoryLimit float64 `json:"memoryLimit"`
|
||||
MemoryUnit string `json:"memoryUnit"`
|
||||
ContainerName string `json:"containerName"`
|
||||
AllowPort bool `json:"allowPort"`
|
||||
EditCompose bool `json:"editCompose"`
|
||||
DockerCompose string `json:"dockerCompose"`
|
||||
}
|
||||
|
||||
type AppInstalledSearch struct {
|
||||
@@ -51,6 +63,12 @@ type AppInstalledOperate struct {
|
||||
type AppInstalledUpdate struct {
|
||||
InstallId uint `json:"installId" validate:"required"`
|
||||
Params map[string]interface{} `json:"params" validate:"required"`
|
||||
AppContainerConfig
|
||||
}
|
||||
|
||||
type AppInstalledIgnoreUpgrade struct {
|
||||
DetailID uint `json:"detailID" validate:"required"`
|
||||
Operate string `json:"operate" validate:"required,oneof=cancel ignore"`
|
||||
}
|
||||
|
||||
type PortUpdate struct {
|
||||
|
@@ -82,6 +82,11 @@ type FileDownload struct {
|
||||
Compress bool `json:"compress" validate:"required"`
|
||||
}
|
||||
|
||||
type FileChunkDownload struct {
|
||||
Path string `json:"path" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
|
||||
type DirSizeReq struct {
|
||||
Path string `json:"path" validate:"required"`
|
||||
}
|
||||
|
@@ -48,3 +48,21 @@ type NginxAuthUpdate struct {
|
||||
type NginxAuthReq struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
}
|
||||
|
||||
type NginxCommonReq struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
}
|
||||
|
||||
type NginxAntiLeechUpdate struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
Extends string `json:"extends" validate:"required"`
|
||||
Return string `json:"return" validate:"required"`
|
||||
Enable bool `json:"enable" validate:"required"`
|
||||
ServerNames []string `json:"serverNames"`
|
||||
Cache bool `json:"cache"`
|
||||
CacheTime int `json:"cacheTime"`
|
||||
CacheUint string `json:"cacheUint"`
|
||||
NoneRef bool `json:"noneRef"`
|
||||
LogEnable bool `json:"logEnable"`
|
||||
Blocked bool `json:"blocked"`
|
||||
}
|
||||
|
5
backend/app/dto/request/process.go
Normal file
5
backend/app/dto/request/process.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package request
|
||||
|
||||
type ProcessReq struct {
|
||||
PID int32 `json:"PID" validate:"required"`
|
||||
}
|
@@ -7,6 +7,8 @@ import (
|
||||
type WebsiteSearch struct {
|
||||
dto.PageInfo
|
||||
Name string `json:"name"`
|
||||
OrderBy string `json:"orderBy"`
|
||||
Order string `json:"order"`
|
||||
WebsiteGroupID uint `json:"websiteGroupId"`
|
||||
}
|
||||
|
||||
@@ -18,6 +20,7 @@ type WebsiteCreate struct {
|
||||
OtherDomains string `json:"otherDomains"`
|
||||
Proxy string `json:"proxy"`
|
||||
WebsiteGroupID uint `json:"webSiteGroupID" validate:"required"`
|
||||
IPV6 bool `json:"IPV6"`
|
||||
|
||||
AppType string `json:"appType" validate:"oneof=new installed"`
|
||||
AppInstall NewAppInstall `json:"appInstall"`
|
||||
@@ -37,6 +40,8 @@ type NewAppInstall struct {
|
||||
Name string `json:"name"`
|
||||
AppDetailId uint `json:"appDetailID"`
|
||||
Params map[string]interface{} `json:"params"`
|
||||
|
||||
AppContainerConfig
|
||||
}
|
||||
|
||||
type WebsiteInstallCheckReq struct {
|
||||
@@ -49,6 +54,7 @@ type WebsiteUpdate struct {
|
||||
Remark string `json:"remark"`
|
||||
WebsiteGroupID uint `json:"webSiteGroupID" validate:"required"`
|
||||
ExpireDate string `json:"expireDate"`
|
||||
IPV6 bool `json:"IPV6"`
|
||||
}
|
||||
|
||||
type WebsiteDelete struct {
|
||||
@@ -109,15 +115,18 @@ type WebsiteDomainDelete struct {
|
||||
}
|
||||
|
||||
type WebsiteHTTPSOp struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Enable bool `json:"enable" validate:"required"`
|
||||
WebsiteSSLID uint `json:"websiteSSLId"`
|
||||
Type string `json:"type" validate:"oneof=existed auto manual"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
Certificate string `json:"certificate"`
|
||||
HttpConfig string `json:"HttpConfig" validate:"oneof=HTTPSOnly HTTPAlso HTTPToHTTPS"`
|
||||
SSLProtocol []string `json:"SSLProtocol"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Enable bool `json:"enable" validate:"required"`
|
||||
WebsiteSSLID uint `json:"websiteSSLId"`
|
||||
Type string `json:"type" validate:"oneof=existed auto manual"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
Certificate string `json:"certificate"`
|
||||
PrivateKeyPath string `json:"privateKeyPath"`
|
||||
CertificatePath string `json:"certificatePath"`
|
||||
ImportType string `json:"importType"`
|
||||
HttpConfig string `json:"httpConfig" validate:"oneof=HTTPSOnly HTTPAlso HTTPToHTTPS"`
|
||||
SSLProtocol []string `json:"SSLProtocol"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
}
|
||||
|
||||
type WebsiteNginxUpdate struct {
|
||||
@@ -136,8 +145,11 @@ type WebsiteDefaultUpdate struct {
|
||||
}
|
||||
|
||||
type WebsitePHPConfigUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Params map[string]string `json:"params" validate:"required"`
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Params map[string]string `json:"params"`
|
||||
Scope string `json:"scope" validate:"required"`
|
||||
DisableFunctions []string `json:"disableFunctions"`
|
||||
UploadMaxSize string `json:"uploadMaxSize"`
|
||||
}
|
||||
|
||||
type WebsitePHPFileUpdate struct {
|
||||
|
@@ -4,6 +4,7 @@ import "github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
|
||||
type WebsiteSSLSearch struct {
|
||||
dto.PageInfo
|
||||
AcmeAccountID string `json:"acmeAccountID"`
|
||||
}
|
||||
|
||||
type WebsiteSSLCreate struct {
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
@@ -12,15 +13,15 @@ type AppRes struct {
|
||||
}
|
||||
|
||||
type AppUpdateRes struct {
|
||||
Version string `json:"version"`
|
||||
CanUpdate bool `json:"canUpdate"`
|
||||
DownloadPath string `json:"downloadPath"`
|
||||
CanUpdate bool `json:"canUpdate"`
|
||||
AppStoreLastModified int `json:"appStoreLastModified"`
|
||||
}
|
||||
|
||||
type AppDTO struct {
|
||||
model.App
|
||||
Versions []string `json:"versions"`
|
||||
Tags []model.Tag `json:"tags"`
|
||||
Installed bool `json:"installed"`
|
||||
Versions []string `json:"versions"`
|
||||
Tags []model.Tag `json:"tags"`
|
||||
}
|
||||
|
||||
type TagDTO struct {
|
||||
@@ -47,6 +48,13 @@ type AppDetailDTO struct {
|
||||
Image string `json:"image"`
|
||||
}
|
||||
|
||||
type IgnoredApp struct {
|
||||
Icon string `json:"icon"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
DetailID uint `json:"detailID"`
|
||||
}
|
||||
|
||||
type AppInstalledDTO struct {
|
||||
model.AppInstall
|
||||
Total int `json:"total"`
|
||||
@@ -54,6 +62,7 @@ type AppInstalledDTO struct {
|
||||
AppName string `json:"appName"`
|
||||
Icon string `json:"icon"`
|
||||
CanUpdate bool `json:"canUpdate"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
type DatabaseConn struct {
|
||||
@@ -81,3 +90,8 @@ type AppParam struct {
|
||||
Required bool `json:"required"`
|
||||
Multiple bool `json:"multiple"`
|
||||
}
|
||||
|
||||
type AppConfig struct {
|
||||
Params []AppParam `json:"params"`
|
||||
request.AppContainerConfig
|
||||
}
|
||||
|
@@ -21,3 +21,16 @@ type NginxAuthRes struct {
|
||||
Enable bool `json:"enable"`
|
||||
Items []dto.NginxAuth `json:"items"`
|
||||
}
|
||||
|
||||
type NginxAntiLeechRes struct {
|
||||
Enable bool `json:"enable"`
|
||||
Extends string `json:"extends"`
|
||||
Return string `json:"return"`
|
||||
ServerNames []string `json:"serverNames"`
|
||||
Cache bool `json:"cache"`
|
||||
CacheTime int `json:"cacheTime"`
|
||||
CacheUint string `json:"cacheUint"`
|
||||
NoneRef bool `json:"noneRef"`
|
||||
LogEnable bool `json:"logEnable"`
|
||||
Blocked bool `json:"blocked"`
|
||||
}
|
||||
|
@@ -45,7 +45,9 @@ type WebsiteLog struct {
|
||||
}
|
||||
|
||||
type PHPConfig struct {
|
||||
Params map[string]string `json:"params"`
|
||||
Params map[string]string `json:"params"`
|
||||
DisableFunctions []string `json:"disableFunctions"`
|
||||
UploadMaxSize string `json:"uploadMaxSize"`
|
||||
}
|
||||
|
||||
type NginxRewriteRes struct {
|
||||
|
@@ -5,10 +5,13 @@ import "time"
|
||||
type SettingInfo struct {
|
||||
UserName string `json:"userName"`
|
||||
Email string `json:"email"`
|
||||
SystemIP string `json:"systemIP"`
|
||||
SystemVersion string `json:"systemVersion"`
|
||||
|
||||
SessionTimeout string `json:"sessionTimeout"`
|
||||
LocalTime string `json:"localTime"`
|
||||
TimeZone string `json:"timeZone"`
|
||||
NtpSite string `json:"ntpSite"`
|
||||
|
||||
Port string `json:"port"`
|
||||
PanelName string `json:"panelName"`
|
||||
@@ -18,14 +21,18 @@ type SettingInfo struct {
|
||||
ServerPort string `json:"serverPort"`
|
||||
SSL string `json:"ssl"`
|
||||
SSLType string `json:"sslType"`
|
||||
BindDomain string `json:"bindDomain"`
|
||||
AllowIPs string `json:"allowIPs"`
|
||||
SecurityEntrance string `json:"securityEntrance"`
|
||||
ExpirationDays string `json:"expirationDays"`
|
||||
ExpirationTime string `json:"expirationTime"`
|
||||
ComplexityVerification string `json:"complexityVerification"`
|
||||
MFAStatus string `json:"mfaStatus"`
|
||||
MFASecret string `json:"mfaSecret"`
|
||||
MFAInterval string `json:"mfaInterval"`
|
||||
|
||||
MonitorStatus string `json:"monitorStatus"`
|
||||
MonitorInterval string `json:"monitorInterval"`
|
||||
MonitorStoreDays string `json:"monitorStoreDays"`
|
||||
|
||||
MessageType string `json:"messageType"`
|
||||
@@ -33,7 +40,8 @@ type SettingInfo struct {
|
||||
WeChatVars string `json:"weChatVars"`
|
||||
DingVars string `json:"dingVars"`
|
||||
|
||||
AppStoreVersion string `json:"appStoreVersion"`
|
||||
AppStoreVersion string `json:"appStoreVersion"`
|
||||
AppStoreLastModified string `json:"appStoreLastModified"`
|
||||
}
|
||||
|
||||
type SettingUpdate struct {
|
||||
@@ -68,7 +76,7 @@ type PortUpdate struct {
|
||||
}
|
||||
|
||||
type SnapshotCreate struct {
|
||||
From string `json:"from" validate:"required,oneof=OSS S3 SFTP MINIO COS KODO"`
|
||||
From string `json:"from" validate:"required,oneof=OSS S3 SFTP MINIO COS KODO OneDrive"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
}
|
||||
type SnapshotRecover struct {
|
||||
@@ -106,6 +114,10 @@ type UpgradeInfo struct {
|
||||
ReleaseNote string `json:"releaseNote"`
|
||||
}
|
||||
|
||||
type SyncTime struct {
|
||||
NtpSite string `json:"ntpSite"`
|
||||
}
|
||||
|
||||
type Upgrade struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
49
backend/app/dto/ssh.go
Normal file
49
backend/app/dto/ssh.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package dto
|
||||
|
||||
import "time"
|
||||
|
||||
type SSHInfo struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Port string `json:"port"`
|
||||
ListenAddress string `json:"listenAddress"`
|
||||
PasswordAuthentication string `json:"passwordAuthentication"`
|
||||
PubkeyAuthentication string `json:"pubkeyAuthentication"`
|
||||
PermitRootLogin string `json:"permitRootLogin"`
|
||||
UseDNS string `json:"useDNS"`
|
||||
}
|
||||
|
||||
type GenerateSSH struct {
|
||||
EncryptionMode string `json:"encryptionMode" validate:"required,oneof=rsa ed25519 ecdsa dsa"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type GenerateLoad struct {
|
||||
EncryptionMode string `json:"encryptionMode" validate:"required,oneof=rsa ed25519 ecdsa dsa"`
|
||||
}
|
||||
|
||||
type SSHConf struct {
|
||||
File string `json:"file"`
|
||||
}
|
||||
type SearchSSHLog struct {
|
||||
PageInfo
|
||||
Info string `json:"info"`
|
||||
Status string `json:"Status" validate:"required,oneof=Success Failed All"`
|
||||
}
|
||||
type SSHLog struct {
|
||||
Logs []SSHHistory `json:"logs"`
|
||||
TotalCount int `json:"totalCount"`
|
||||
SuccessfulCount int `json:"successfulCount"`
|
||||
FailedCount int `json:"failedCount"`
|
||||
}
|
||||
type SSHHistory struct {
|
||||
Date time.Time `json:"date"`
|
||||
DateStr string `json:"dateStr"`
|
||||
Area string `json:"area"`
|
||||
User string `json:"user"`
|
||||
AuthMode string `json:"authMode"`
|
||||
Address string `json:"address"`
|
||||
Port string `json:"port"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
}
|
@@ -2,22 +2,25 @@ package model
|
||||
|
||||
type App struct {
|
||||
BaseModel
|
||||
Name string `json:"name" gorm:"type:varchar(64);not null"`
|
||||
Key string `json:"key" gorm:"type:varchar(64);not null;uniqueIndex"`
|
||||
ShortDescZh string `json:"shortDescZh" gorm:"type:longtext;"`
|
||||
ShortDescEn string `json:"shortDescEn" gorm:"type:longtext;"`
|
||||
Icon string `json:"icon" gorm:"type:longtext;"`
|
||||
Type string `json:"type" gorm:"type:varchar(64);not null"`
|
||||
Status string `json:"status" gorm:"type:varchar(64);not null"`
|
||||
Required string `json:"required" gorm:"type:varchar(64);not null"`
|
||||
CrossVersionUpdate bool `json:"crossVersionUpdate"`
|
||||
Limit int `json:"limit" gorm:"type:Integer;not null"`
|
||||
Website string `json:"website" gorm:"type:varchar(64);not null"`
|
||||
Github string `json:"github" gorm:"type:varchar(64);not null"`
|
||||
Document string `json:"document" gorm:"type:varchar(64);not null"`
|
||||
Recommend int `json:"recommend" gorm:"type:Integer;not null"`
|
||||
Resource string `json:"resource" gorm:"type:varchar;not null;default:remote"`
|
||||
Details []AppDetail `json:"-" gorm:"-:migration"`
|
||||
TagsKey []string `json:"-" gorm:"-"`
|
||||
AppTags []AppTag `json:"-" gorm:"-:migration"`
|
||||
Name string `json:"name" gorm:"type:varchar(64);not null"`
|
||||
Key string `json:"key" gorm:"type:varchar(64);not null;"`
|
||||
ShortDescZh string `json:"shortDescZh" yaml:"shortDescZh" gorm:"type:longtext;"`
|
||||
ShortDescEn string `json:"shortDescEn" yaml:"shortDescEn" gorm:"type:longtext;"`
|
||||
Icon string `json:"icon" gorm:"type:longtext;"`
|
||||
Type string `json:"type" gorm:"type:varchar(64);not null"`
|
||||
Status string `json:"status" gorm:"type:varchar(64);not null"`
|
||||
Required string `json:"required" gorm:"type:varchar(64);"`
|
||||
CrossVersionUpdate bool `json:"crossVersionUpdate"`
|
||||
Limit int `json:"limit" gorm:"type:Integer;not null"`
|
||||
Website string `json:"website" gorm:"type:varchar(64);not null"`
|
||||
Github string `json:"github" gorm:"type:varchar(64);not null"`
|
||||
Document string `json:"document" gorm:"type:varchar(64);not null"`
|
||||
Recommend int `json:"recommend" gorm:"type:Integer;not null"`
|
||||
Resource string `json:"resource" gorm:"type:varchar;not null;default:remote"`
|
||||
ReadMe string `json:"readMe" gorm:"type:varchar;"`
|
||||
LastModified int `json:"lastModified" gorm:"type:Integer;"`
|
||||
|
||||
Details []AppDetail `json:"-" gorm:"-:migration"`
|
||||
TagsKey []string `json:"tags" yaml:"tags" gorm:"-"`
|
||||
AppTags []AppTag `json:"-" gorm:"-:migration"`
|
||||
}
|
||||
|
@@ -2,11 +2,15 @@ package model
|
||||
|
||||
type AppDetail struct {
|
||||
BaseModel
|
||||
AppId uint `json:"appId" gorm:"type:integer;not null"`
|
||||
Version string `json:"version" gorm:"type:varchar(64);not null"`
|
||||
Params string `json:"-" gorm:"type:longtext;"`
|
||||
DockerCompose string `json:"-" gorm:"type:longtext;not null"`
|
||||
Readme string `json:"readme" gorm:"type:longtext;"`
|
||||
Status string `json:"status" gorm:"type:varchar(64);not null"`
|
||||
LastVersion string `json:"lastVersion" gorm:"type:varchar(64);"`
|
||||
AppId uint `json:"appId" gorm:"type:integer;not null"`
|
||||
Version string `json:"version" gorm:"type:varchar(64);not null"`
|
||||
Params string `json:"-" gorm:"type:longtext;"`
|
||||
DockerCompose string `json:"dockerCompose" gorm:"type:longtext;"`
|
||||
Status string `json:"status" gorm:"type:varchar(64);not null"`
|
||||
LastVersion string `json:"lastVersion" gorm:"type:varchar(64);"`
|
||||
LastModified int `json:"lastModified" gorm:"type:integer;"`
|
||||
DownloadUrl string `json:"downloadUrl" gorm:"type:varchar;"`
|
||||
DownloadCallBackUrl string `json:"downloadCallBackUrl" gorm:"type:longtext;"`
|
||||
Update bool `json:"update"`
|
||||
IgnoreUpgrade bool `json:"ignoreUpgrade"`
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ type BackupAccount struct {
|
||||
Bucket string `gorm:"type:varchar(256)" json:"bucket"`
|
||||
AccessKey string `gorm:"type:varchar(256)" json:"accessKey"`
|
||||
Credential string `gorm:"type:varchar(256)" json:"credential"`
|
||||
BackupPath string `gorm:"type:varchar(256)" json:"backupPath"`
|
||||
Vars string `gorm:"type:longText" json:"vars"`
|
||||
}
|
||||
|
||||
|
@@ -13,7 +13,9 @@ type Cronjob struct {
|
||||
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"`
|
||||
|
||||
ContainerName string `gorm:"type:varchar(64)" json:"containerName"`
|
||||
Script string `gorm:"longtext" json:"script"`
|
||||
Website string `gorm:"type:varchar(64)" json:"website"`
|
||||
DBName string `gorm:"type:varchar(64)" json:"dbName"`
|
||||
|
@@ -19,6 +19,7 @@ type Website struct {
|
||||
ErrorLog bool `json:"errorLog"`
|
||||
AccessLog bool `json:"accessLog"`
|
||||
DefaultServer bool `json:"defaultServer"`
|
||||
IPV6 bool `json:"IPV6"`
|
||||
Rewrite string `gorm:"type:varchar" json:"rewrite"`
|
||||
|
||||
WebsiteGroupID uint `gorm:"type:integer" json:"webSiteGroupId"`
|
||||
|
@@ -24,6 +24,7 @@ type IAppRepo interface {
|
||||
GetByKey(ctx context.Context, key string) (model.App, error)
|
||||
Create(ctx context.Context, app *model.App) error
|
||||
Save(ctx context.Context, app *model.App) error
|
||||
BatchDelete(ctx context.Context, apps []model.App) error
|
||||
}
|
||||
|
||||
func NewIAppRepo() IAppRepo {
|
||||
@@ -106,3 +107,7 @@ func (a AppRepo) Create(ctx context.Context, app *model.App) error {
|
||||
func (a AppRepo) Save(ctx context.Context, app *model.App) error {
|
||||
return getTx(ctx).Omit(clause.Associations).Save(app).Error
|
||||
}
|
||||
|
||||
func (a AppRepo) BatchDelete(ctx context.Context, apps []model.App) error {
|
||||
return getTx(ctx).Omit(clause.Associations).Delete(&apps).Error
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type AppDetailRepo struct {
|
||||
@@ -12,12 +13,14 @@ type AppDetailRepo struct {
|
||||
type IAppDetailRepo interface {
|
||||
WithVersion(version string) DBOption
|
||||
WithAppId(id uint) DBOption
|
||||
WithIgnored() DBOption
|
||||
GetFirst(opts ...DBOption) (model.AppDetail, error)
|
||||
Update(ctx context.Context, detail model.AppDetail) error
|
||||
BatchCreate(ctx context.Context, details []model.AppDetail) error
|
||||
DeleteByAppIds(ctx context.Context, appIds []uint) error
|
||||
GetBy(opts ...DBOption) ([]model.AppDetail, error)
|
||||
BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error
|
||||
BatchDelete(ctx context.Context, appDetails []model.AppDetail) error
|
||||
}
|
||||
|
||||
func NewIAppDetailRepo() IAppDetailRepo {
|
||||
@@ -29,12 +32,19 @@ func (a AppDetailRepo) WithVersion(version string) DBOption {
|
||||
return g.Where("version = ?", version)
|
||||
}
|
||||
}
|
||||
|
||||
func (a AppDetailRepo) WithAppId(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("app_id = ?", id)
|
||||
}
|
||||
}
|
||||
|
||||
func (a AppDetailRepo) WithIgnored() DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("ignore_upgrade = 1")
|
||||
}
|
||||
}
|
||||
|
||||
func (a AppDetailRepo) GetFirst(opts ...DBOption) (model.AppDetail, error) {
|
||||
var detail model.AppDetail
|
||||
err := getDb(opts...).Model(&model.AppDetail{}).Find(&detail).Error
|
||||
@@ -66,3 +76,7 @@ func (a AppDetailRepo) BatchUpdateBy(maps map[string]interface{}, opts ...DBOpti
|
||||
}
|
||||
return db.Updates(&maps).Error
|
||||
}
|
||||
|
||||
func (a AppDetailRepo) BatchDelete(ctx context.Context, appDetails []model.AppDetail) error {
|
||||
return getTx(ctx).Omit(clause.Associations).Delete(&appDetails).Error
|
||||
}
|
||||
|
@@ -19,8 +19,10 @@ type IAppInstallRepo interface {
|
||||
WithAppIdsIn(appIds []uint) DBOption
|
||||
WithStatus(status string) DBOption
|
||||
WithServiceName(serviceName string) DBOption
|
||||
WithContainerName(containerName string) DBOption
|
||||
WithPort(port int) DBOption
|
||||
WithIdNotInWebsite() DBOption
|
||||
WithIDNotIs(id uint) DBOption
|
||||
ListBy(opts ...DBOption) ([]model.AppInstall, error)
|
||||
GetFirst(opts ...DBOption) (model.AppInstall, error)
|
||||
Create(ctx context.Context, install *model.AppInstall) error
|
||||
@@ -55,6 +57,12 @@ func (a *AppInstallRepo) WithAppId(appId uint) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AppInstallRepo) WithIDNotIs(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("id != ?", id)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AppInstallRepo) WithAppIdsIn(appIds []uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("app_id in (?)", appIds)
|
||||
@@ -73,6 +81,12 @@ func (a *AppInstallRepo) WithServiceName(serviceName string) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AppInstallRepo) WithContainerName(containerName string) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("container_name = ?", containerName)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AppInstallRepo) WithPort(port int) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("https_port = ? or http_port = ?", port, port)
|
||||
|
@@ -19,6 +19,7 @@ type IAppInstallResourceRpo interface {
|
||||
GetFirst(opts ...DBOption) (model.AppInstallResource, error)
|
||||
Create(ctx context.Context, resource *model.AppInstallResource) error
|
||||
DeleteBy(ctx context.Context, opts ...DBOption) error
|
||||
BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error
|
||||
}
|
||||
|
||||
func NewIAppInstallResourceRpo() IAppInstallResourceRpo {
|
||||
@@ -71,3 +72,11 @@ func (a AppInstallResourceRpo) Create(ctx context.Context, resource *model.AppIn
|
||||
func (a AppInstallResourceRpo) DeleteBy(ctx context.Context, opts ...DBOption) error {
|
||||
return getTx(ctx, opts...).Delete(&model.AppInstallResource{}).Error
|
||||
}
|
||||
|
||||
func (a *AppInstallResourceRpo) BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error {
|
||||
db := getDb(opts...).Model(&model.AppInstallResource{})
|
||||
if len(opts) == 0 {
|
||||
db = db.Where("1=1")
|
||||
}
|
||||
return db.Updates(&maps).Error
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
@@ -16,6 +17,7 @@ type ICommonRepo interface {
|
||||
WithByName(name string) DBOption
|
||||
WithByType(tp string) DBOption
|
||||
WithOrderBy(orderStr string) DBOption
|
||||
WithOrderRuleBy(orderBy, order string) DBOption
|
||||
WithByGroupID(groupID uint) DBOption
|
||||
WithLikeName(name string) DBOption
|
||||
WithIdsIn(ids []uint) DBOption
|
||||
@@ -93,6 +95,21 @@ func (c *CommonRepo) WithOrderBy(orderStr string) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CommonRepo) WithOrderRuleBy(orderBy, order string) DBOption {
|
||||
switch order {
|
||||
case constant.OrderDesc:
|
||||
order = "desc"
|
||||
case constant.OrderAsc:
|
||||
order = "asc"
|
||||
default:
|
||||
orderBy = "created_at"
|
||||
order = "desc"
|
||||
}
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Order(fmt.Sprintf("%s %s", orderBy, order))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CommonRepo) WithIdsIn(ids []uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("id in (?)", ids)
|
||||
|
@@ -15,6 +15,7 @@ type IComposeTemplateRepo interface {
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
Delete(opts ...DBOption) error
|
||||
|
||||
GetRecord(opts ...DBOption) (model.Compose, error)
|
||||
CreateRecord(compose *model.Compose) error
|
||||
DeleteRecord(opts ...DBOption) error
|
||||
ListRecord() ([]model.Compose, error)
|
||||
@@ -72,6 +73,16 @@ func (u *ComposeTemplateRepo) Delete(opts ...DBOption) error {
|
||||
return db.Delete(&model.ComposeTemplate{}).Error
|
||||
}
|
||||
|
||||
func (u *ComposeTemplateRepo) GetRecord(opts ...DBOption) (model.Compose, error) {
|
||||
var compose model.Compose
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.First(&compose).Error
|
||||
return compose, err
|
||||
}
|
||||
|
||||
func (u *ComposeTemplateRepo) ListRecord() ([]model.Compose, error) {
|
||||
var composes []model.Compose
|
||||
if err := global.DB.Find(&composes).Error; err != nil {
|
||||
|
@@ -83,7 +83,7 @@ func (u *CronjobRepo) Page(page, size int, opts ...DBOption) (int64, []model.Cro
|
||||
}
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Order("created_at desc").Limit(size).Offset(size * (page - 1)).Find(&cronjobs).Error
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&cronjobs).Error
|
||||
return count, cronjobs, err
|
||||
}
|
||||
|
||||
|
@@ -14,6 +14,7 @@ type IRuntimeRepo interface {
|
||||
WithImage(image string) DBOption
|
||||
WithNotId(id uint) DBOption
|
||||
WithStatus(status string) DBOption
|
||||
WithDetailId(id uint) DBOption
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error)
|
||||
Create(ctx context.Context, runtime *model.Runtime) error
|
||||
Save(runtime *model.Runtime) error
|
||||
@@ -43,6 +44,12 @@ func (r *RuntimeRepo) WithImage(image string) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) WithDetailId(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("app_detail_id = ?", id)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) WithNotId(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("id != ?", id)
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"gorm.io/gorm"
|
||||
@@ -14,6 +16,13 @@ type ISettingRepo interface {
|
||||
Create(key, value string) error
|
||||
Update(key, value string) error
|
||||
WithByKey(key string) DBOption
|
||||
|
||||
CreateMonitorBase(model model.MonitorBase) error
|
||||
BatchCreateMonitorIO(ioList []model.MonitorIO) error
|
||||
BatchCreateMonitorNet(ioList []model.MonitorNetwork) error
|
||||
DelMonitorBase(timeForDelete time.Time) error
|
||||
DelMonitorIO(timeForDelete time.Time) error
|
||||
DelMonitorNet(timeForDelete time.Time) error
|
||||
}
|
||||
|
||||
func NewISettingRepo() ISettingRepo {
|
||||
@@ -57,3 +66,22 @@ func (c *SettingRepo) WithByKey(key string) DBOption {
|
||||
func (u *SettingRepo) Update(key, value string) error {
|
||||
return global.DB.Model(&model.Setting{}).Where("key = ?", key).Updates(map[string]interface{}{"value": value}).Error
|
||||
}
|
||||
|
||||
func (u *SettingRepo) CreateMonitorBase(model model.MonitorBase) error {
|
||||
return global.DB.Create(&model).Error
|
||||
}
|
||||
func (u *SettingRepo) BatchCreateMonitorIO(ioList []model.MonitorIO) error {
|
||||
return global.DB.CreateInBatches(ioList, len(ioList)).Error
|
||||
}
|
||||
func (u *SettingRepo) BatchCreateMonitorNet(ioList []model.MonitorNetwork) error {
|
||||
return global.DB.CreateInBatches(ioList, len(ioList)).Error
|
||||
}
|
||||
func (u *SettingRepo) DelMonitorBase(timeForDelete time.Time) error {
|
||||
return global.DB.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
|
||||
}
|
||||
func (u *SettingRepo) DelMonitorNet(timeForDelete time.Time) error {
|
||||
return global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error
|
||||
}
|
||||
|
@@ -5,25 +5,26 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"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/i18n"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
http2 "github.com/1Panel-dev/1Panel/backend/utils/http"
|
||||
"gopkg.in/yaml.v3"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type AppService struct {
|
||||
@@ -39,6 +40,7 @@ type IAppService interface {
|
||||
GetAppUpdate() (*response.AppUpdateRes, error)
|
||||
GetAppDetailByID(id uint) (*response.AppDetailDTO, error)
|
||||
SyncAppListFromLocal()
|
||||
GetIgnoredApp() ([]response.IgnoredApp, error)
|
||||
}
|
||||
|
||||
func NewIAppService() IAppService {
|
||||
@@ -82,12 +84,16 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
|
||||
return nil, err
|
||||
}
|
||||
var appDTOs []*response.AppDTO
|
||||
for _, a := range apps {
|
||||
for _, ap := range apps {
|
||||
ap.ReadMe = ""
|
||||
ap.Website = ""
|
||||
ap.Document = ""
|
||||
ap.Github = ""
|
||||
appDTO := &response.AppDTO{
|
||||
App: a,
|
||||
App: ap,
|
||||
}
|
||||
appDTOs = append(appDTOs, appDTO)
|
||||
appTags, err := appTagRepo.GetByAppId(a.ID)
|
||||
appTags, err := appTagRepo.GetByAppId(ap.ID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -100,6 +106,8 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
|
||||
continue
|
||||
}
|
||||
appDTO.Tags = tags
|
||||
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(ap.ID))
|
||||
appDTO.Installed = len(installs) > 0
|
||||
}
|
||||
res.Items = appDTOs
|
||||
res.Total = total
|
||||
@@ -160,7 +168,13 @@ func (a AppService) GetAppDetail(appId uint, version, appType string) (response.
|
||||
return appDetailDTO, err
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
buildPath := path.Join(constant.AppResourceDir, app.Key, "versions", detail.Version, "build")
|
||||
versionPath := path.Join(constant.AppResourceDir, app.Resource, app.Key, detail.Version)
|
||||
if !fileOp.Stat(versionPath) || detail.Update {
|
||||
if err = downloadApp(app, detail, nil); err != nil {
|
||||
return appDetailDTO, err
|
||||
}
|
||||
}
|
||||
buildPath := path.Join(versionPath, "build")
|
||||
paramsPath := path.Join(buildPath, "config.json")
|
||||
if !fileOp.Stat(paramsPath) {
|
||||
return appDetailDTO, buserr.New(constant.ErrFileNotExist)
|
||||
@@ -224,6 +238,27 @@ func (a AppService) GetAppDetailByID(id uint) (*response.AppDetailDTO, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (a AppService) GetIgnoredApp() ([]response.IgnoredApp, error) {
|
||||
var res []response.IgnoredApp
|
||||
details, _ := appDetailRepo.GetBy(appDetailRepo.WithIgnored())
|
||||
if len(details) == 0 {
|
||||
return res, nil
|
||||
}
|
||||
for _, detail := range details {
|
||||
app, err := appRepo.GetFirst(commonRepo.WithByID(detail.AppId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, response.IgnoredApp{
|
||||
Name: app.Name,
|
||||
Version: detail.Version,
|
||||
DetailID: detail.ID,
|
||||
Icon: app.Icon,
|
||||
})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (appInstall *model.AppInstall, err error) {
|
||||
if err = docker.CreateDefaultDockerNetwork(); err != nil {
|
||||
err = buserr.WithDetail(constant.Err1PanelNetworkFailed, err.Error(), nil)
|
||||
@@ -239,21 +274,30 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
||||
appDetail model.AppDetail
|
||||
app model.App
|
||||
)
|
||||
httpPort, err = checkPort("PANEL_APP_PORT_HTTP", req.Params)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
httpsPort, err = checkPort("PANEL_APP_PORT_HTTPS", req.Params)
|
||||
if err != nil {
|
||||
return
|
||||
for key := range req.Params {
|
||||
if !strings.Contains(key, "PANEL_APP_PORT") {
|
||||
continue
|
||||
}
|
||||
var port int
|
||||
if port, err = checkPort(key, req.Params); err == nil {
|
||||
if key == "PANEL_APP_PORT_HTTP" {
|
||||
httpPort = port
|
||||
}
|
||||
if key == "PANEL_APP_PORT_HTTPS" {
|
||||
httpsPort = port
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
appDetail, err = appDetailRepo.GetFirst(commonRepo.WithByID(req.AppDetailId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
app, err = appRepo.GetFirst(commonRepo.WithByID(appDetail.AppId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
if err = checkRequiredAndLimit(app); err != nil {
|
||||
return
|
||||
@@ -270,35 +314,55 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
||||
App: app,
|
||||
}
|
||||
composeMap := make(map[string]interface{})
|
||||
if err = yaml.Unmarshal([]byte(appDetail.DockerCompose), &composeMap); err != nil {
|
||||
return
|
||||
if req.EditCompose {
|
||||
if err = yaml.Unmarshal([]byte(req.DockerCompose), &composeMap); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err = yaml.Unmarshal([]byte(appDetail.DockerCompose), &composeMap); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
value, ok := composeMap["services"]
|
||||
if !ok {
|
||||
err = buserr.New("")
|
||||
err = buserr.New(constant.ErrFileParse)
|
||||
return
|
||||
}
|
||||
servicesMap := value.(map[string]interface{})
|
||||
changeKeys := make(map[string]string, len(servicesMap))
|
||||
containerName := constant.ContainerPrefix + app.Key + "-" + common.RandStr(4)
|
||||
if req.Advanced && req.ContainerName != "" {
|
||||
containerName = req.ContainerName
|
||||
appInstalls, _ := appInstallRepo.ListBy(appInstallRepo.WithContainerName(containerName))
|
||||
if len(appInstalls) > 0 {
|
||||
err = buserr.New(constant.ErrContainerName)
|
||||
return
|
||||
}
|
||||
containerExist := false
|
||||
containerExist, err = checkContainerNameIsExist(req.ContainerName, appInstall.GetPath())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if containerExist {
|
||||
err = buserr.New(constant.ErrContainerName)
|
||||
return
|
||||
}
|
||||
}
|
||||
req.Params[constant.ContainerName] = containerName
|
||||
appInstall.ContainerName = containerName
|
||||
|
||||
index := 0
|
||||
for k := range servicesMap {
|
||||
serviceName := k + "-" + common.RandStr(4)
|
||||
changeKeys[k] = serviceName
|
||||
containerName := constant.ContainerPrefix + k + "-" + common.RandStr(4)
|
||||
appInstall.ServiceName = k
|
||||
if index > 0 {
|
||||
continue
|
||||
}
|
||||
req.Params["CONTAINER_NAME"] = containerName
|
||||
appInstall.ServiceName = serviceName
|
||||
appInstall.ContainerName = containerName
|
||||
index++
|
||||
}
|
||||
for k, v := range changeKeys {
|
||||
servicesMap[v] = servicesMap[k]
|
||||
delete(servicesMap, k)
|
||||
}
|
||||
|
||||
if err = addDockerComposeCommonParam(composeMap, appInstall.ServiceName, req.AppContainerConfig, req.Params); err != nil {
|
||||
return
|
||||
}
|
||||
var (
|
||||
composeByte []byte
|
||||
paramByte []byte
|
||||
@@ -318,34 +382,262 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err = copyAppData(app.Key, appDetail.Version, req.Name, req.Params, app.Resource == constant.AppResourceLocal); err != nil {
|
||||
return
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
if err = fileOp.WriteFile(appInstall.GetComposePath(), strings.NewReader(string(composeByte)), 0775); err != nil {
|
||||
return
|
||||
}
|
||||
paramByte, err = json.Marshal(req.Params)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
appInstall.Env = string(paramByte)
|
||||
|
||||
if err = appInstallRepo.Create(ctx, appInstall); err != nil {
|
||||
return
|
||||
}
|
||||
if err = createLink(ctx, app, appInstall, req.Params); err != nil {
|
||||
return
|
||||
}
|
||||
if err = upAppPre(app, appInstall); err != nil {
|
||||
return
|
||||
}
|
||||
go upApp(appInstall)
|
||||
go func() {
|
||||
if err = copyData(app, appDetail, appInstall, req); err != nil {
|
||||
if appInstall.Status == constant.Installing {
|
||||
appInstall.Status = constant.Error
|
||||
appInstall.Message = err.Error()
|
||||
}
|
||||
_ = appInstallRepo.Save(context.Background(), appInstall)
|
||||
return
|
||||
}
|
||||
if err = upAppPre(app, appInstall); err != nil {
|
||||
return
|
||||
}
|
||||
upApp(appInstall)
|
||||
}()
|
||||
go updateToolApp(appInstall)
|
||||
return
|
||||
}
|
||||
|
||||
func (a AppService) SyncAppListFromLocal() {
|
||||
fileOp := files.NewFileOp()
|
||||
localAppDir := constant.LocalAppResourceDir
|
||||
if !fileOp.Stat(localAppDir) {
|
||||
return
|
||||
}
|
||||
var (
|
||||
err error
|
||||
dirEntries []os.DirEntry
|
||||
localApps []model.App
|
||||
)
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
global.LOG.Errorf("sync app failed %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
global.LOG.Infof("start sync local apps...")
|
||||
dirEntries, err = os.ReadDir(localAppDir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, dirEntry := range dirEntries {
|
||||
if dirEntry.IsDir() {
|
||||
appDir := path.Join(localAppDir, dirEntry.Name())
|
||||
appDirEntries, err := os.ReadDir(appDir)
|
||||
if err != nil {
|
||||
global.LOG.Errorf(i18n.GetMsgWithMap("ErrAppDirNull", map[string]interface{}{"name": dirEntry.Name(), "err": err.Error()}))
|
||||
continue
|
||||
}
|
||||
app, err := handleLocalApp(appDir)
|
||||
if err != nil {
|
||||
global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppErr", map[string]interface{}{"name": dirEntry.Name(), "err": err.Error()}))
|
||||
continue
|
||||
}
|
||||
var appDetails []model.AppDetail
|
||||
for _, appDirEntry := range appDirEntries {
|
||||
if appDirEntry.IsDir() {
|
||||
appDetail := model.AppDetail{
|
||||
Version: appDirEntry.Name(),
|
||||
Status: constant.AppNormal,
|
||||
}
|
||||
versionDir := path.Join(appDir, appDirEntry.Name())
|
||||
if err = handleLocalAppDetail(versionDir, &appDetail); err != nil {
|
||||
global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppVersionErr", map[string]interface{}{"name": app.Name, "version": appDetail.Version, "err": err.Error()}))
|
||||
continue
|
||||
}
|
||||
appDetails = append(appDetails, appDetail)
|
||||
}
|
||||
}
|
||||
if len(appDetails) > 0 {
|
||||
app.Details = appDetails
|
||||
localApps = append(localApps, *app)
|
||||
} else {
|
||||
global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppVersionNull", map[string]interface{}{"name": app.Name}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
newApps []model.App
|
||||
deleteApps []model.App
|
||||
updateApps []model.App
|
||||
oldAppIds []uint
|
||||
|
||||
deleteAppIds []uint
|
||||
deleteAppDetails []model.AppDetail
|
||||
newAppDetails []model.AppDetail
|
||||
updateDetails []model.AppDetail
|
||||
|
||||
appTags []*model.AppTag
|
||||
)
|
||||
|
||||
oldApps, _ := appRepo.GetBy(appRepo.WithResource(constant.AppResourceLocal))
|
||||
apps := make(map[string]model.App, len(oldApps))
|
||||
for _, old := range oldApps {
|
||||
old.Status = constant.AppTakeDown
|
||||
apps[old.Key] = old
|
||||
}
|
||||
for _, app := range localApps {
|
||||
if oldApp, ok := apps[app.Key]; ok {
|
||||
app.ID = oldApp.ID
|
||||
appDetails := make(map[string]model.AppDetail, len(oldApp.Details))
|
||||
for _, old := range oldApp.Details {
|
||||
old.Status = constant.AppTakeDown
|
||||
appDetails[old.Version] = old
|
||||
}
|
||||
for i, newDetail := range app.Details {
|
||||
version := newDetail.Version
|
||||
newDetail.Status = constant.AppNormal
|
||||
newDetail.AppId = app.ID
|
||||
oldDetail, exist := appDetails[version]
|
||||
if exist {
|
||||
newDetail.ID = oldDetail.ID
|
||||
delete(appDetails, version)
|
||||
}
|
||||
app.Details[i] = newDetail
|
||||
}
|
||||
for _, v := range appDetails {
|
||||
app.Details = append(app.Details, v)
|
||||
}
|
||||
}
|
||||
app.TagsKey = append(app.TagsKey, constant.AppResourceLocal)
|
||||
apps[app.Key] = app
|
||||
}
|
||||
|
||||
for _, app := range apps {
|
||||
if app.ID == 0 {
|
||||
newApps = append(newApps, app)
|
||||
} else {
|
||||
oldAppIds = append(oldAppIds, app.ID)
|
||||
if app.Status == constant.AppTakeDown {
|
||||
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(app.ID))
|
||||
if len(installs) > 0 {
|
||||
updateApps = append(updateApps, app)
|
||||
continue
|
||||
}
|
||||
deleteAppIds = append(deleteAppIds, app.ID)
|
||||
deleteApps = append(deleteApps, app)
|
||||
deleteAppDetails = append(deleteAppDetails, app.Details...)
|
||||
} else {
|
||||
updateApps = append(updateApps, app)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
tags, _ := tagRepo.All()
|
||||
tagMap := make(map[string]uint, len(tags))
|
||||
for _, tag := range tags {
|
||||
tagMap[tag.Key] = tag.ID
|
||||
}
|
||||
|
||||
tx, ctx := getTxAndContext()
|
||||
defer tx.Rollback()
|
||||
if len(newApps) > 0 {
|
||||
if err = appRepo.BatchCreate(ctx, newApps); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, update := range updateApps {
|
||||
if err = appRepo.Save(ctx, &update); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(deleteApps) > 0 {
|
||||
if err = appRepo.BatchDelete(ctx, deleteApps); err != nil {
|
||||
return
|
||||
}
|
||||
if err = appDetailRepo.DeleteByAppIds(ctx, deleteAppIds); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {
|
||||
return
|
||||
}
|
||||
for _, newApp := range newApps {
|
||||
if newApp.ID > 0 {
|
||||
for _, detail := range newApp.Details {
|
||||
detail.AppId = newApp.ID
|
||||
newAppDetails = append(newAppDetails, detail)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, update := range updateApps {
|
||||
for _, detail := range update.Details {
|
||||
if detail.ID == 0 {
|
||||
detail.AppId = update.ID
|
||||
newAppDetails = append(newAppDetails, detail)
|
||||
} else {
|
||||
if detail.Status == constant.AppNormal {
|
||||
updateDetails = append(updateDetails, detail)
|
||||
} else {
|
||||
deleteAppDetails = append(deleteAppDetails, detail)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allApps := append(newApps, updateApps...)
|
||||
for _, app := range allApps {
|
||||
for _, t := range app.TagsKey {
|
||||
tagId, ok := tagMap[t]
|
||||
if ok {
|
||||
appTags = append(appTags, &model.AppTag{
|
||||
AppId: app.ID,
|
||||
TagId: tagId,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(newAppDetails) > 0 {
|
||||
if err = appDetailRepo.BatchCreate(ctx, newAppDetails); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, updateAppDetail := range updateDetails {
|
||||
if err = appDetailRepo.Update(ctx, updateAppDetail); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(deleteAppDetails) > 0 {
|
||||
if err = appDetailRepo.BatchDelete(ctx, deleteAppDetails); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(oldAppIds) > 0 {
|
||||
if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(appTags) > 0 {
|
||||
if err = appTagRepo.BatchCreate(ctx, appTags); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
global.LOG.Infof("sync local apps success")
|
||||
}
|
||||
|
||||
func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
|
||||
res := &response.AppUpdateRes{
|
||||
CanUpdate: false,
|
||||
@@ -354,8 +646,8 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
versionUrl := fmt.Sprintf("%s/%s/%s/appstore/apps.json", global.CONF.System.RepoUrl, global.CONF.System.Mode, setting.SystemVersion)
|
||||
versionRes, err := http.Get(versionUrl)
|
||||
versionUrl := fmt.Sprintf("%s/%s/1panel.json.version.txt", global.CONF.System.AppRepo, global.CONF.System.Mode)
|
||||
versionRes, err := http2.GetHttpRes(versionUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -364,182 +656,49 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list := &dto.AppList{}
|
||||
if err = json.Unmarshal(body, list); err != nil {
|
||||
lastModifiedStr := string(body)
|
||||
|
||||
lastModified, err := strconv.Atoi(lastModifiedStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.Version = list.Version
|
||||
if setting.AppStoreVersion == "" || common.CompareVersion(list.Version, setting.AppStoreVersion) {
|
||||
appStoreLastModified, _ := strconv.Atoi(setting.AppStoreLastModified)
|
||||
if setting.AppStoreLastModified == "" || lastModified != appStoreLastModified {
|
||||
res.CanUpdate = true
|
||||
res.DownloadPath = fmt.Sprintf("%s/%s/%s/appstore/apps-%s.tar.gz", global.CONF.System.RepoUrl, global.CONF.System.Mode, setting.SystemVersion, list.Version)
|
||||
return res, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (a AppService) SyncAppListFromLocal() {
|
||||
func getAppFromRepo(downloadPath string) error {
|
||||
downloadUrl := downloadPath
|
||||
global.LOG.Infof("download file from %s", downloadUrl)
|
||||
fileOp := files.NewFileOp()
|
||||
appDir := constant.LocalAppResourceDir
|
||||
listFile := path.Join(appDir, "list.json")
|
||||
if !fileOp.Stat(listFile) {
|
||||
return
|
||||
packagePath := path.Join(constant.ResourceDir, path.Base(downloadUrl))
|
||||
if err := fileOp.DownloadFile(downloadUrl, packagePath); err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Infof("start sync local apps...")
|
||||
content, err := fileOp.GetContent(listFile)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get list.json content failed %s", err.Error())
|
||||
return
|
||||
if err := fileOp.Decompress(packagePath, constant.ResourceDir, files.Zip); err != nil {
|
||||
return err
|
||||
}
|
||||
list := &dto.AppList{}
|
||||
if err := json.Unmarshal(content, list); err != nil {
|
||||
global.LOG.Errorf("unmarshal list.json failed %s", err.Error())
|
||||
return
|
||||
}
|
||||
oldApps, _ := appRepo.GetBy(appRepo.WithResource(constant.AppResourceLocal))
|
||||
appsMap := getApps(oldApps, list.Items, true)
|
||||
for _, l := range list.Items {
|
||||
localKey := "local" + l.Key
|
||||
app := appsMap[localKey]
|
||||
icon, err := os.ReadFile(path.Join(appDir, l.Key, "metadata", "logo.png"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] icon error: %s", l.Name, err.Error())
|
||||
continue
|
||||
}
|
||||
iconStr := base64.StdEncoding.EncodeToString(icon)
|
||||
app.Icon = iconStr
|
||||
app.TagsKey = append(l.Tags, "Local")
|
||||
app.Recommend = 9999
|
||||
versions := l.Versions
|
||||
detailsMap := getAppDetails(app.Details, versions)
|
||||
|
||||
for _, v := range versions {
|
||||
detail := detailsMap[v]
|
||||
detailPath := path.Join(appDir, l.Key, "versions", v)
|
||||
if _, err := os.Stat(detailPath); err != nil {
|
||||
global.LOG.Errorf("get [%s] folder error: %s", detailPath, err.Error())
|
||||
continue
|
||||
}
|
||||
readmeStr, err := os.ReadFile(path.Join(detailPath, "README.md"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] README error: %s", detailPath, err.Error())
|
||||
}
|
||||
detail.Readme = string(readmeStr)
|
||||
dockerComposeStr, err := os.ReadFile(path.Join(detailPath, "docker-compose.yml"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] docker-compose.yml error: %s", detailPath, err.Error())
|
||||
continue
|
||||
}
|
||||
detail.DockerCompose = string(dockerComposeStr)
|
||||
paramStr, err := os.ReadFile(path.Join(detailPath, "config.json"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] form.json error: %s", detailPath, err.Error())
|
||||
}
|
||||
detail.Params = string(paramStr)
|
||||
detailsMap[v] = detail
|
||||
}
|
||||
var newDetails []model.AppDetail
|
||||
for _, v := range detailsMap {
|
||||
newDetails = append(newDetails, v)
|
||||
}
|
||||
app.Details = newDetails
|
||||
appsMap[localKey] = app
|
||||
}
|
||||
var (
|
||||
addAppArray []model.App
|
||||
updateArray []model.App
|
||||
appIds []uint
|
||||
)
|
||||
for _, v := range appsMap {
|
||||
if v.ID == 0 {
|
||||
addAppArray = append(addAppArray, v)
|
||||
} else {
|
||||
updateArray = append(updateArray, v)
|
||||
appIds = append(appIds, v.ID)
|
||||
}
|
||||
}
|
||||
tx, ctx := getTxAndContext()
|
||||
if len(addAppArray) > 0 {
|
||||
if err := appRepo.BatchCreate(ctx, addAppArray); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, update := range updateArray {
|
||||
if err := appRepo.Save(ctx, &update); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := appTagRepo.DeleteByAppIds(ctx, appIds); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
apps := append(addAppArray, updateArray...)
|
||||
var (
|
||||
addDetails []model.AppDetail
|
||||
updateDetails []model.AppDetail
|
||||
appTags []*model.AppTag
|
||||
)
|
||||
tags, _ := tagRepo.All()
|
||||
tagMap := make(map[string]uint, len(tags))
|
||||
for _, app := range tags {
|
||||
tagMap[app.Key] = app.ID
|
||||
}
|
||||
for _, a := range apps {
|
||||
for _, t := range a.TagsKey {
|
||||
tagId, ok := tagMap[t]
|
||||
if ok {
|
||||
appTags = append(appTags, &model.AppTag{
|
||||
AppId: a.ID,
|
||||
TagId: tagId,
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, d := range a.Details {
|
||||
d.AppId = a.ID
|
||||
if d.ID == 0 {
|
||||
addDetails = append(addDetails, d)
|
||||
} else {
|
||||
updateDetails = append(updateDetails, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(addDetails) > 0 {
|
||||
if err := appDetailRepo.BatchCreate(ctx, addDetails); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, u := range updateDetails {
|
||||
if err := appDetailRepo.Update(ctx, u); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(appTags) > 0 {
|
||||
if err := appTagRepo.BatchCreate(ctx, appTags); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
global.LOG.Infof("sync local apps success")
|
||||
defer func() {
|
||||
_ = fileOp.DeleteFile(packagePath)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AppService) SyncAppListFromRemote() error {
|
||||
updateRes, err := a.GetAppUpdate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !updateRes.CanUpdate {
|
||||
global.LOG.Infof("The latest version is [%s] The app store is already up to date", updateRes.Version)
|
||||
return nil
|
||||
}
|
||||
if err := getAppFromRepo(updateRes.DownloadPath, updateRes.Version); err != nil {
|
||||
global.LOG.Errorf("get app from oss error: %s", err.Error())
|
||||
if err = getAppFromRepo(fmt.Sprintf("%s/%s/1panel.json.zip", global.CONF.System.AppRepo, global.CONF.System.Mode)); err != nil {
|
||||
return err
|
||||
}
|
||||
appDir := constant.AppResourceDir
|
||||
listFile := path.Join(appDir, "list.json")
|
||||
listFile := path.Join(constant.ResourceDir, "1panel.json")
|
||||
content, err := os.ReadFile(listFile)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -548,11 +707,13 @@ func (a AppService) SyncAppListFromRemote() error {
|
||||
if err := json.Unmarshal(content, list); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
tags []*model.Tag
|
||||
appTags []*model.AppTag
|
||||
tags []*model.Tag
|
||||
appTags []*model.AppTag
|
||||
oldAppIds []uint
|
||||
)
|
||||
for _, t := range list.Tags {
|
||||
for _, t := range list.Extra.Tags {
|
||||
tags = append(tags, &model.Tag{
|
||||
Key: t.Key,
|
||||
Name: t.Name,
|
||||
@@ -562,145 +723,191 @@ func (a AppService) SyncAppListFromRemote() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appsMap := getApps(oldApps, list.Items, false)
|
||||
for _, l := range list.Items {
|
||||
app := appsMap[l.Key]
|
||||
icon, err := os.ReadFile(path.Join(appDir, l.Key, "metadata", "logo.png"))
|
||||
for _, old := range oldApps {
|
||||
oldAppIds = append(oldAppIds, old.ID)
|
||||
}
|
||||
|
||||
baseRemoteUrl := fmt.Sprintf("%s/%s/1panel", global.CONF.System.AppRepo, global.CONF.System.Mode)
|
||||
appsMap := getApps(oldApps, list.Apps)
|
||||
for _, l := range list.Apps {
|
||||
app := appsMap[l.AppProperty.Key]
|
||||
iconRes, err := http.Get(l.Icon)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] icon error: %s", l.Name, err.Error())
|
||||
continue
|
||||
return err
|
||||
}
|
||||
iconStr := base64.StdEncoding.EncodeToString(icon)
|
||||
body, err := io.ReadAll(iconRes.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iconStr := base64.StdEncoding.EncodeToString(body)
|
||||
app.Icon = iconStr
|
||||
app.TagsKey = l.Tags
|
||||
if l.Recommend > 0 {
|
||||
app.Recommend = l.Recommend
|
||||
app.TagsKey = l.AppProperty.Tags
|
||||
if l.AppProperty.Recommend > 0 {
|
||||
app.Recommend = l.AppProperty.Recommend
|
||||
} else {
|
||||
app.Recommend = 9999
|
||||
}
|
||||
|
||||
app.ReadMe = l.ReadMe
|
||||
app.LastModified = l.LastModified
|
||||
versions := l.Versions
|
||||
detailsMap := getAppDetails(app.Details, versions)
|
||||
|
||||
for _, v := range versions {
|
||||
detail := detailsMap[v]
|
||||
detailPath := path.Join(appDir, l.Key, "versions", v)
|
||||
if _, err := os.Stat(detailPath); err != nil {
|
||||
global.LOG.Errorf("get [%s] folder error: %s", detailPath, err.Error())
|
||||
continue
|
||||
}
|
||||
readmeStr, err := os.ReadFile(path.Join(detailPath, "README.md"))
|
||||
version := v.Name
|
||||
detail := detailsMap[version]
|
||||
|
||||
dockerComposeUrl := fmt.Sprintf("%s/%s/%s/%s", baseRemoteUrl, app.Key, version, "docker-compose.yml")
|
||||
composeRes, err := http.Get(dockerComposeUrl)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] README error: %s", detailPath, err.Error())
|
||||
return err
|
||||
}
|
||||
detail.Readme = string(readmeStr)
|
||||
dockerComposeStr, err := os.ReadFile(path.Join(detailPath, "docker-compose.yml"))
|
||||
bodyContent, err := io.ReadAll(composeRes.Body)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] docker-compose.yml error: %s", detailPath, err.Error())
|
||||
continue
|
||||
return err
|
||||
}
|
||||
detail.DockerCompose = string(dockerComposeStr)
|
||||
paramStr, err := os.ReadFile(path.Join(detailPath, "config.json"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] form.json error: %s", detailPath, err.Error())
|
||||
}
|
||||
detail.Params = string(paramStr)
|
||||
detailsMap[v] = detail
|
||||
detail.DockerCompose = string(bodyContent)
|
||||
|
||||
paramByte, _ := json.Marshal(v.AppForm)
|
||||
detail.Params = string(paramByte)
|
||||
detail.DownloadUrl = v.DownloadUrl
|
||||
detail.DownloadCallBackUrl = v.DownloadCallBackUrl
|
||||
detail.Update = true
|
||||
detail.LastModified = v.LastModified
|
||||
detailsMap[version] = detail
|
||||
}
|
||||
var newDetails []model.AppDetail
|
||||
for _, v := range detailsMap {
|
||||
newDetails = append(newDetails, v)
|
||||
for _, detail := range detailsMap {
|
||||
newDetails = append(newDetails, detail)
|
||||
}
|
||||
app.Details = newDetails
|
||||
appsMap[l.Key] = app
|
||||
appsMap[l.AppProperty.Key] = app
|
||||
}
|
||||
|
||||
var (
|
||||
addAppArray []model.App
|
||||
updateArray []model.App
|
||||
tagMap = make(map[string]uint, len(tags))
|
||||
addAppArray []model.App
|
||||
updateAppArray []model.App
|
||||
deleteAppArray []model.App
|
||||
deleteIds []uint
|
||||
tagMap = make(map[string]uint, len(tags))
|
||||
)
|
||||
|
||||
for _, v := range appsMap {
|
||||
if v.ID == 0 {
|
||||
addAppArray = append(addAppArray, v)
|
||||
} else {
|
||||
updateArray = append(updateArray, v)
|
||||
if v.Status == constant.AppTakeDown {
|
||||
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(v.ID))
|
||||
if len(installs) > 0 {
|
||||
updateAppArray = append(updateAppArray, v)
|
||||
continue
|
||||
}
|
||||
deleteAppArray = append(deleteAppArray, v)
|
||||
deleteIds = append(deleteIds, v.ID)
|
||||
} else {
|
||||
updateAppArray = append(updateAppArray, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
tx, ctx := getTxAndContext()
|
||||
defer tx.Rollback()
|
||||
if len(addAppArray) > 0 {
|
||||
if err := appRepo.BatchCreate(ctx, addAppArray); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(deleteAppArray) > 0 {
|
||||
if err := appRepo.BatchDelete(ctx, deleteAppArray); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := appDetailRepo.DeleteByAppIds(ctx, deleteIds); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := tagRepo.DeleteAll(ctx); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if len(tags) > 0 {
|
||||
if err := tagRepo.BatchCreate(ctx, tags); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
for _, t := range tags {
|
||||
tagMap[t.Key] = t.ID
|
||||
}
|
||||
}
|
||||
for _, update := range updateArray {
|
||||
for _, update := range updateAppArray {
|
||||
if err := appRepo.Save(ctx, &update); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
apps := append(addAppArray, updateArray...)
|
||||
apps := append(addAppArray, updateAppArray...)
|
||||
|
||||
var (
|
||||
addDetails []model.AppDetail
|
||||
updateDetails []model.AppDetail
|
||||
deleteDetails []model.AppDetail
|
||||
)
|
||||
for _, a := range apps {
|
||||
for _, t := range a.TagsKey {
|
||||
for _, app := range apps {
|
||||
for _, t := range app.TagsKey {
|
||||
tagId, ok := tagMap[t]
|
||||
if ok {
|
||||
appTags = append(appTags, &model.AppTag{
|
||||
AppId: a.ID,
|
||||
AppId: app.ID,
|
||||
TagId: tagId,
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, d := range a.Details {
|
||||
d.AppId = a.ID
|
||||
for _, d := range app.Details {
|
||||
d.AppId = app.ID
|
||||
if d.ID == 0 {
|
||||
addDetails = append(addDetails, d)
|
||||
} else {
|
||||
updateDetails = append(updateDetails, d)
|
||||
if d.Status == constant.AppTakeDown {
|
||||
runtime, _ := runtimeRepo.GetFirst(runtimeRepo.WithDetailId(d.ID))
|
||||
if runtime != nil {
|
||||
updateDetails = append(updateDetails, d)
|
||||
continue
|
||||
}
|
||||
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithDetailIdsIn([]uint{d.ID}))
|
||||
if len(installs) > 0 {
|
||||
updateDetails = append(updateDetails, d)
|
||||
continue
|
||||
}
|
||||
deleteDetails = append(deleteDetails, d)
|
||||
} else {
|
||||
updateDetails = append(updateDetails, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(addDetails) > 0 {
|
||||
if err := appDetailRepo.BatchCreate(ctx, addDetails); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(deleteDetails) > 0 {
|
||||
if err := appDetailRepo.BatchDelete(ctx, deleteDetails); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, u := range updateDetails {
|
||||
if err := appDetailRepo.Update(ctx, u); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := appTagRepo.DeleteAll(ctx); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
|
||||
if len(oldAppIds) > 0 {
|
||||
if err := appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(appTags) > 0 {
|
||||
if err := appTagRepo.BatchCreate(ctx, appTags); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
if err := NewISettingService().Update("AppStoreLastModified", strconv.Itoa(list.LastModified)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
@@ -11,6 +12,9 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/env"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx"
|
||||
"github.com/joho/godotenv"
|
||||
@@ -42,10 +46,11 @@ type IAppInstallService interface {
|
||||
SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstalledDTO, error)
|
||||
Operate(req request.AppInstalledOperate) error
|
||||
Update(req request.AppInstalledUpdate) error
|
||||
IgnoreUpgrade(req request.AppInstalledIgnoreUpgrade) error
|
||||
SyncAll(systemInit bool) error
|
||||
GetServices(key string) ([]response.AppService, error)
|
||||
GetUpdateVersions(installId uint) ([]dto.AppVersion, error)
|
||||
GetParams(id uint) ([]response.AppParam, error)
|
||||
GetParams(id uint) (*response.AppConfig, error)
|
||||
ChangeAppPort(req request.PortUpdate) error
|
||||
GetDefaultConfigByKey(key string) (string, error)
|
||||
DeleteCheck(installId uint) ([]dto.AppResource, error)
|
||||
@@ -183,6 +188,9 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !req.ForceDelete && !files.NewFileOp().Stat(install.GetPath()) {
|
||||
return buserr.New(constant.ErrInstallDirNotFound)
|
||||
}
|
||||
dockerComposePath := install.GetComposePath()
|
||||
switch req.Operate {
|
||||
case constant.Rebuild:
|
||||
@@ -225,35 +233,68 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
return err
|
||||
}
|
||||
changePort := false
|
||||
var (
|
||||
oldPorts []int
|
||||
newPorts []int
|
||||
)
|
||||
port, ok := req.Params["PANEL_APP_PORT_HTTP"]
|
||||
if ok {
|
||||
portN := int(math.Ceil(port.(float64)))
|
||||
if portN != installed.HttpPort {
|
||||
oldPorts = append(oldPorts, installed.HttpPort)
|
||||
changePort = true
|
||||
httpPort, err := checkPort("PANEL_APP_PORT_HTTP", req.Params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
installed.HttpPort = httpPort
|
||||
newPorts = append(newPorts, httpPort)
|
||||
}
|
||||
}
|
||||
ports, ok := req.Params["PANEL_APP_PORT_HTTPS"]
|
||||
if ok {
|
||||
portN := int(math.Ceil(ports.(float64)))
|
||||
if portN != installed.HttpsPort {
|
||||
oldPorts = append(oldPorts, installed.HttpsPort)
|
||||
httpsPort, err := checkPort("PANEL_APP_PORT_HTTPS", req.Params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
installed.HttpsPort = httpsPort
|
||||
newPorts = append(newPorts, httpsPort)
|
||||
}
|
||||
}
|
||||
|
||||
backupDockerCompose := installed.DockerCompose
|
||||
if req.Advanced {
|
||||
composeMap := make(map[string]interface{})
|
||||
if req.EditCompose {
|
||||
if err = yaml.Unmarshal([]byte(req.DockerCompose), &composeMap); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err = yaml.Unmarshal([]byte(installed.DockerCompose), &composeMap); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = addDockerComposeCommonParam(composeMap, installed.ServiceName, req.AppContainerConfig, req.Params); err != nil {
|
||||
return err
|
||||
}
|
||||
composeByte, err := yaml.Marshal(composeMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
installed.DockerCompose = string(composeByte)
|
||||
if req.ContainerName == "" {
|
||||
req.Params[constant.ContainerName] = installed.ContainerName
|
||||
} else {
|
||||
req.Params[constant.ContainerName] = req.ContainerName
|
||||
if installed.ContainerName != req.ContainerName {
|
||||
exist, _ := appInstallRepo.GetFirst(appInstallRepo.WithContainerName(req.ContainerName), appInstallRepo.WithIDNotIs(installed.ID))
|
||||
if exist.ID > 0 {
|
||||
return buserr.New(constant.ErrContainerName)
|
||||
}
|
||||
containerExist, err := checkContainerNameIsExist(req.ContainerName, installed.GetPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if containerExist {
|
||||
return buserr.New(constant.ErrContainerName)
|
||||
}
|
||||
installed.ContainerName = req.ContainerName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,6 +303,7 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
backupEnvMaps := oldEnvMaps
|
||||
handleMap(req.Params, oldEnvMaps)
|
||||
paramByte, err := json.Marshal(oldEnvMaps)
|
||||
if err != nil {
|
||||
@@ -271,57 +313,74 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
if err := env.Write(oldEnvMaps, envPath); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = appInstallRepo.Save(context.Background(), &installed)
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
_ = fileOp.WriteFile(installed.GetComposePath(), strings.NewReader(installed.DockerCompose), 0755)
|
||||
if err := rebuildApp(installed); err != nil {
|
||||
_ = env.Write(backupEnvMaps, envPath)
|
||||
_ = fileOp.WriteFile(installed.GetComposePath(), strings.NewReader(backupDockerCompose), 0755)
|
||||
return err
|
||||
}
|
||||
installed.Status = constant.Running
|
||||
_ = appInstallRepo.Save(context.Background(), &installed)
|
||||
|
||||
website, _ := websiteRepo.GetFirst(websiteRepo.WithAppInstallId(installed.ID))
|
||||
if changePort && website.ID != 0 && website.Status == constant.Running {
|
||||
nginxInstall, err := getNginxFull(&website)
|
||||
if err != nil {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
||||
}
|
||||
config := nginxInstall.SiteConfig.Config
|
||||
servers := config.FindServers()
|
||||
if len(servers) == 0 {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, errors.New("nginx config is not valid"))
|
||||
}
|
||||
server := servers[0]
|
||||
proxy := fmt.Sprintf("http://127.0.0.1:%d", installed.HttpPort)
|
||||
server.UpdateRootProxy([]string{proxy})
|
||||
|
||||
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
||||
}
|
||||
if err := nginxCheckAndReload(nginxInstall.SiteConfig.OldContent, config.FilePath, nginxInstall.Install.ContainerName); err != nil {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
||||
}
|
||||
}
|
||||
if changePort {
|
||||
go func() {
|
||||
_ = OperateFirewallPort(oldPorts, newPorts)
|
||||
nginxInstall, err := getNginxFull(&website)
|
||||
if err != nil {
|
||||
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, err).Error())
|
||||
return
|
||||
}
|
||||
config := nginxInstall.SiteConfig.Config
|
||||
servers := config.FindServers()
|
||||
if len(servers) == 0 {
|
||||
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, errors.New("nginx config is not valid")).Error())
|
||||
return
|
||||
}
|
||||
server := servers[0]
|
||||
proxy := fmt.Sprintf("http://127.0.0.1:%d", installed.HttpPort)
|
||||
server.UpdateRootProxy([]string{proxy})
|
||||
|
||||
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, err).Error())
|
||||
return
|
||||
}
|
||||
if err := nginxCheckAndReload(nginxInstall.SiteConfig.OldContent, config.FilePath, nginxInstall.Install.ContainerName); err != nil {
|
||||
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, err).Error())
|
||||
return
|
||||
}
|
||||
}()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AppInstallService) IgnoreUpgrade(req request.AppInstalledIgnoreUpgrade) error {
|
||||
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(req.DetailID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appDetail.IgnoreUpgrade = req.Operate == "ignore"
|
||||
return appDetailRepo.Update(context.Background(), appDetail)
|
||||
}
|
||||
|
||||
func (a *AppInstallService) SyncAll(systemInit bool) error {
|
||||
allList, err := appInstallRepo.ListBy()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, i := range allList {
|
||||
if i.Status == constant.Installing {
|
||||
if i.Status == constant.Installing || i.Status == constant.Upgrading {
|
||||
if systemInit {
|
||||
i.Status = constant.Error
|
||||
i.Message = "System restart causes application exception"
|
||||
i.Message = "1Panel restart causes the task to terminate"
|
||||
_ = appInstallRepo.Save(context.Background(), &i)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err := syncById(i.ID); err != nil {
|
||||
global.LOG.Errorf("sync install app[%s] error,mgs: %s", i.Name, err.Error())
|
||||
if !systemInit {
|
||||
if err := syncById(i.ID); err != nil {
|
||||
global.LOG.Errorf("sync install app[%s] error,mgs: %s", i.Name, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -366,6 +425,12 @@ func (a *AppInstallService) GetUpdateVersions(installId uint) ([]dto.AppVersion,
|
||||
return versions, err
|
||||
}
|
||||
for _, detail := range details {
|
||||
if detail.IgnoreUpgrade {
|
||||
continue
|
||||
}
|
||||
if common.IsCrossVersion(install.Version, detail.Version) && !app.CrossVersionUpdate {
|
||||
continue
|
||||
}
|
||||
if common.CompareVersion(detail.Version, install.Version) {
|
||||
versions = append(versions, dto.AppVersion{
|
||||
Version: detail.Version,
|
||||
@@ -401,10 +466,6 @@ func (a *AppInstallService) ChangeAppPort(req request.PortUpdate) error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := OperateFirewallPort([]int{int(appInstall.Port)}, []int{int(req.Port)}); err != nil {
|
||||
global.LOG.Errorf("allow firewall failed, err: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -452,7 +513,16 @@ func (a *AppInstallService) GetDefaultConfigByKey(key string) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
filePath := path.Join(constant.AppResourceDir, appInstall.App.Key, "versions", appInstall.Version, "conf")
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
filePath := path.Join(constant.AppResourceDir, "remote", appInstall.App.Key, appInstall.Version, "conf")
|
||||
if !fileOp.Stat(filePath) {
|
||||
filePath = path.Join(constant.AppResourceDir, appInstall.App.Key, "versions", appInstall.Version, "conf")
|
||||
}
|
||||
if !fileOp.Stat(filePath) {
|
||||
return "", buserr.New(constant.ErrPathNotFound)
|
||||
}
|
||||
|
||||
if key == constant.AppMysql {
|
||||
filePath = path.Join(filePath, "my.cnf")
|
||||
}
|
||||
@@ -469,11 +539,12 @@ func (a *AppInstallService) GetDefaultConfigByKey(key string) (string, error) {
|
||||
return string(contentByte), nil
|
||||
}
|
||||
|
||||
func (a *AppInstallService) GetParams(id uint) ([]response.AppParam, error) {
|
||||
func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
||||
var (
|
||||
res []response.AppParam
|
||||
params []response.AppParam
|
||||
appForm dto.AppForm
|
||||
envs = make(map[string]interface{})
|
||||
res response.AppConfig
|
||||
)
|
||||
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(id))
|
||||
if err != nil {
|
||||
@@ -515,10 +586,18 @@ func (a *AppInstallService) GetParams(id uint) ([]response.AppParam, error) {
|
||||
}
|
||||
appParam.Values = form.Values
|
||||
}
|
||||
res = append(res, appParam)
|
||||
params = append(params, appParam)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
|
||||
config := getAppCommonConfig(envs)
|
||||
config.DockerCompose = install.DockerCompose
|
||||
res.Params = params
|
||||
if config.ContainerName == "" {
|
||||
config.ContainerName = install.ContainerName
|
||||
}
|
||||
res.AppContainerConfig = config
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func syncById(installId uint) error {
|
||||
@@ -529,12 +608,10 @@ func syncById(installId uint) error {
|
||||
if appInstall.Status == constant.Installing {
|
||||
return nil
|
||||
}
|
||||
|
||||
containerNames, err := getContainerNames(appInstall)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cli, err := docker.NewClient()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -560,16 +637,16 @@ func syncById(installId uint) error {
|
||||
errorContainers = append(errorContainers, n.Names[0])
|
||||
}
|
||||
}
|
||||
for _, old := range containerNames {
|
||||
for _, name := range containerNames {
|
||||
exist := false
|
||||
for _, new := range containers {
|
||||
if common.ExistWithStrArray(old, new.Names) {
|
||||
for _, container := range containers {
|
||||
if common.ExistWithStrArray(name, container.Names) {
|
||||
exist = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exist {
|
||||
notFoundContainers = append(notFoundContainers, old)
|
||||
notFoundContainers = append(notFoundContainers, name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -582,10 +659,10 @@ func syncById(installId uint) error {
|
||||
|
||||
if containerCount == 0 {
|
||||
appInstall.Status = constant.Error
|
||||
appInstall.Message = "container is not found"
|
||||
appInstall.Message = i18n.GetMsgWithMap("ErrContainerNotFound", map[string]interface{}{"name": strings.Join(containerNames, ",")})
|
||||
return appInstallRepo.Save(context.Background(), &appInstall)
|
||||
}
|
||||
if errCount == 0 && existedCount == 0 {
|
||||
if errCount == 0 && existedCount == 0 && notFoundCount == 0 {
|
||||
appInstall.Status = constant.Running
|
||||
return appInstallRepo.Save(context.Background(), &appInstall)
|
||||
}
|
||||
@@ -600,22 +677,14 @@ func syncById(installId uint) error {
|
||||
appInstall.Status = constant.UnHealthy
|
||||
}
|
||||
|
||||
var errMsg strings.Builder
|
||||
var errMsg string
|
||||
if errCount > 0 {
|
||||
errMsg.Write([]byte(string(rune(errCount)) + " error containers:"))
|
||||
for _, e := range errorContainers {
|
||||
errMsg.Write([]byte(e))
|
||||
}
|
||||
errMsg.Write([]byte("\n"))
|
||||
errMsg += i18n.GetMsgWithMap("ErrContainerMsg", map[string]interface{}{"name": strings.Join(errorContainers, ",")})
|
||||
}
|
||||
if notFoundCount > 0 {
|
||||
errMsg.Write([]byte(string(rune(notFoundCount)) + " not found containers:"))
|
||||
for _, e := range notFoundContainers {
|
||||
errMsg.Write([]byte(e))
|
||||
}
|
||||
errMsg.Write([]byte("\n"))
|
||||
errMsg += i18n.GetMsgWithMap("ErrContainerNotFound", map[string]interface{}{"name": strings.Join(notFoundContainers, ",")})
|
||||
}
|
||||
appInstall.Message = errMsg.String()
|
||||
appInstall.Message = errMsg
|
||||
return appInstallRepo.Save(context.Background(), &appInstall)
|
||||
}
|
||||
|
||||
|
@@ -2,16 +2,21 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||
"github.com/subosito/gotenv"
|
||||
"gopkg.in/yaml.v3"
|
||||
"math"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -29,6 +34,7 @@ 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"
|
||||
)
|
||||
|
||||
@@ -42,7 +48,19 @@ var (
|
||||
func checkPort(key string, params map[string]interface{}) (int, error) {
|
||||
port, ok := params[key]
|
||||
if ok {
|
||||
portN := int(math.Ceil(port.(float64)))
|
||||
portN := 0
|
||||
var err error
|
||||
switch p := port.(type) {
|
||||
case string:
|
||||
portN, err = strconv.Atoi(p)
|
||||
if err != nil {
|
||||
return portN, nil
|
||||
}
|
||||
case float64:
|
||||
portN = int(math.Ceil(p))
|
||||
case int:
|
||||
portN = p
|
||||
}
|
||||
|
||||
oldInstalled, _ := appInstallRepo.ListBy(appInstallRepo.WithPort(portN))
|
||||
if len(oldInstalled) > 0 {
|
||||
@@ -225,40 +243,121 @@ func upgradeInstall(installId uint, detailId uint) error {
|
||||
return err
|
||||
}
|
||||
|
||||
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Key, "versions", detail.Version)
|
||||
if install.App.Resource == constant.AppResourceLocal {
|
||||
detailDir = path.Join(constant.ResourceDir, "localApps", strings.TrimPrefix(install.App.Key, "local"), "versions", detail.Version)
|
||||
}
|
||||
install.Status = constant.Upgrading
|
||||
|
||||
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rf %s/* %s", detailDir, install.GetPath()))
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if stdout != nil {
|
||||
return errors.New(string(stdout))
|
||||
}
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
var upErr error
|
||||
defer func() {
|
||||
if upErr != nil {
|
||||
install.Status = constant.UpgradeErr
|
||||
install.Message = upErr.Error()
|
||||
_ = appInstallRepo.Save(context.Background(), &install)
|
||||
}
|
||||
}()
|
||||
|
||||
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
return errors.New(out)
|
||||
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Resource, install.App.Key, detail.Version)
|
||||
if install.App.Resource == constant.AppResourceRemote {
|
||||
if upErr = downloadApp(install.App, detail, &install); upErr != nil {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
_, _ = http.Get(detail.DownloadCallBackUrl)
|
||||
}()
|
||||
}
|
||||
if install.App.Resource == constant.AppResourceLocal {
|
||||
detailDir = path.Join(constant.ResourceDir, "apps", "local", strings.TrimPrefix(install.App.Key, "local"), detail.Version)
|
||||
}
|
||||
return err
|
||||
}
|
||||
install.DockerCompose = detail.DockerCompose
|
||||
install.Version = detail.Version
|
||||
install.AppDetailId = detailId
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
if err := fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); err != nil {
|
||||
return err
|
||||
}
|
||||
if out, err := compose.Up(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
return errors.New(out)
|
||||
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rf %s/* %s", detailDir, install.GetPath()))
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if stdout != nil {
|
||||
upErr = errors.New(string(stdout))
|
||||
return
|
||||
}
|
||||
upErr = err
|
||||
return
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
composeMap := make(map[string]interface{})
|
||||
if upErr = yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); upErr != nil {
|
||||
return
|
||||
}
|
||||
value, ok := composeMap["services"]
|
||||
if !ok {
|
||||
upErr = buserr.New(constant.ErrFileParse)
|
||||
return
|
||||
}
|
||||
servicesMap := value.(map[string]interface{})
|
||||
index := 0
|
||||
oldServiceName := ""
|
||||
for k := range servicesMap {
|
||||
oldServiceName = k
|
||||
index++
|
||||
if index > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
servicesMap[install.ServiceName] = servicesMap[oldServiceName]
|
||||
if install.ServiceName != oldServiceName {
|
||||
delete(servicesMap, oldServiceName)
|
||||
}
|
||||
|
||||
envs := make(map[string]interface{})
|
||||
if upErr = json.Unmarshal([]byte(install.Env), &envs); upErr != nil {
|
||||
return
|
||||
}
|
||||
config := getAppCommonConfig(envs)
|
||||
if config.ContainerName == "" {
|
||||
config.ContainerName = install.ContainerName
|
||||
envs[constant.ContainerName] = install.ContainerName
|
||||
}
|
||||
config.Advanced = true
|
||||
if upErr = addDockerComposeCommonParam(composeMap, install.ServiceName, config, envs); upErr != nil {
|
||||
return
|
||||
}
|
||||
paramByte, upErr := json.Marshal(envs)
|
||||
if upErr != nil {
|
||||
return
|
||||
}
|
||||
install.Env = string(paramByte)
|
||||
composeByte, upErr := yaml.Marshal(composeMap)
|
||||
if upErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
install.DockerCompose = string(composeByte)
|
||||
install.Version = detail.Version
|
||||
install.AppDetailId = detailId
|
||||
|
||||
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
upErr = errors.New(out)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
envParams := make(map[string]string, len(envs))
|
||||
handleMap(envs, envParams)
|
||||
if err = env.Write(envParams, install.GetEnvPath()); err != nil {
|
||||
return
|
||||
}
|
||||
if upErr = fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); upErr != nil {
|
||||
return
|
||||
}
|
||||
if out, err := compose.Up(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
upErr = errors.New(out)
|
||||
return
|
||||
}
|
||||
upErr = err
|
||||
return
|
||||
}
|
||||
install.Status = constant.Running
|
||||
_ = appInstallRepo.Save(context.Background(), &install)
|
||||
}()
|
||||
|
||||
return appInstallRepo.Save(context.Background(), &install)
|
||||
}
|
||||
|
||||
@@ -272,7 +371,6 @@ func getContainerNames(install model.AppInstall) ([]string, error) {
|
||||
return nil, err
|
||||
}
|
||||
containerMap := make(map[string]struct{})
|
||||
containerMap[install.ContainerName] = struct{}{}
|
||||
for _, service := range project.AllServices() {
|
||||
if service.ContainerName == "${CONTAINER_NAME}" || service.ContainerName == "" {
|
||||
continue
|
||||
@@ -283,6 +381,9 @@ func getContainerNames(install model.AppInstall) ([]string, error) {
|
||||
for k := range containerMap {
|
||||
containerNames = append(containerNames, k)
|
||||
}
|
||||
if len(containerNames) == 0 {
|
||||
containerNames = append(containerNames, install.ContainerName)
|
||||
}
|
||||
return containerNames, nil
|
||||
}
|
||||
|
||||
@@ -315,34 +416,6 @@ func checkRequiredAndLimit(app model.App) error {
|
||||
if err := checkLimit(app); err != nil {
|
||||
return err
|
||||
}
|
||||
if app.Required != "" {
|
||||
var requiredArray []string
|
||||
if err := json.Unmarshal([]byte(app.Required), &requiredArray); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, key := range requiredArray {
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
requireApp, err := appRepo.GetFirst(appRepo.WithKey(key))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
details, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(requireApp.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var detailIds []uint
|
||||
for _, d := range details {
|
||||
detailIds = append(detailIds, d.ID)
|
||||
}
|
||||
|
||||
_, err = appInstallRepo.GetFirst(appInstallRepo.WithDetailIdsIn(detailIds))
|
||||
if err != nil {
|
||||
return buserr.WithDetail(constant.ErrAppRequired, requireApp.Name, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -359,24 +432,74 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
|
||||
}
|
||||
}
|
||||
|
||||
func copyAppData(key, version, installName string, params map[string]interface{}, isLocal bool) (err error) {
|
||||
func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall) (err error) {
|
||||
appResourceDir := path.Join(constant.AppResourceDir, app.Resource)
|
||||
appDownloadDir := path.Join(appResourceDir, app.Key)
|
||||
appVersionDir := path.Join(appDownloadDir, appDetail.Version)
|
||||
fileOp := files.NewFileOp()
|
||||
appResourceDir := constant.AppResourceDir
|
||||
installAppDir := path.Join(constant.AppInstallDir, key)
|
||||
appKey := key
|
||||
if isLocal {
|
||||
if !appDetail.Update && fileOp.Stat(appVersionDir) {
|
||||
return
|
||||
}
|
||||
if !fileOp.Stat(appDownloadDir) {
|
||||
_ = fileOp.CreateDir(appDownloadDir, 0755)
|
||||
}
|
||||
if !fileOp.Stat(appVersionDir) {
|
||||
_ = fileOp.CreateDir(appVersionDir, 0755)
|
||||
}
|
||||
global.LOG.Infof("download app[%s] from %s", app.Name, appDetail.DownloadUrl)
|
||||
filePath := path.Join(appVersionDir, appDetail.Version+".tar.gz")
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if appInstall != nil {
|
||||
appInstall.Status = constant.DownloadErr
|
||||
appInstall.Message = err.Error()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err = fileOp.DownloadFile(appDetail.DownloadUrl, filePath); err != nil {
|
||||
global.LOG.Errorf("download app[%s] error %v", app.Name, err)
|
||||
return
|
||||
}
|
||||
if err = fileOp.Decompress(filePath, appVersionDir, files.TarGz); err != nil {
|
||||
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err)
|
||||
return
|
||||
}
|
||||
_ = fileOp.DeleteFile(filePath)
|
||||
appDetail.Update = false
|
||||
_ = appDetailRepo.Update(context.Background(), appDetail)
|
||||
return
|
||||
}
|
||||
|
||||
func copyData(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall, req request.AppInstallCreate) (err error) {
|
||||
fileOp := files.NewFileOp()
|
||||
appResourceDir := path.Join(constant.AppResourceDir, app.Resource)
|
||||
|
||||
if app.Resource == constant.AppResourceRemote {
|
||||
err = downloadApp(app, appDetail, appInstall)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
_, _ = http.Get(appDetail.DownloadCallBackUrl)
|
||||
}()
|
||||
}
|
||||
appKey := app.Key
|
||||
installAppDir := path.Join(constant.AppInstallDir, app.Key)
|
||||
if app.Resource == constant.AppResourceLocal {
|
||||
appResourceDir = constant.LocalAppResourceDir
|
||||
appKey = strings.TrimPrefix(key, "local")
|
||||
appKey = strings.TrimPrefix(app.Key, "local")
|
||||
installAppDir = path.Join(constant.LocalAppInstallDir, appKey)
|
||||
}
|
||||
resourceDir := path.Join(appResourceDir, appKey, "versions", version)
|
||||
resourceDir := path.Join(appResourceDir, appKey, appDetail.Version)
|
||||
|
||||
if !fileOp.Stat(installAppDir) {
|
||||
if err = fileOp.CreateDir(installAppDir, 0755); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
appDir := path.Join(installAppDir, installName)
|
||||
appDir := path.Join(installAppDir, req.Name)
|
||||
if fileOp.Stat(appDir) {
|
||||
if err = fileOp.DeleteDir(appDir); err != nil {
|
||||
return
|
||||
@@ -385,17 +508,20 @@ func copyAppData(key, version, installName string, params map[string]interface{}
|
||||
if err = fileOp.Copy(resourceDir, installAppDir); err != nil {
|
||||
return
|
||||
}
|
||||
versionDir := path.Join(installAppDir, version)
|
||||
versionDir := path.Join(installAppDir, appDetail.Version)
|
||||
if err = fileOp.Rename(versionDir, appDir); err != nil {
|
||||
return
|
||||
}
|
||||
envPath := path.Join(appDir, ".env")
|
||||
|
||||
envParams := make(map[string]string, len(params))
|
||||
handleMap(params, envParams)
|
||||
envParams := make(map[string]string, len(req.Params))
|
||||
handleMap(req.Params, envParams)
|
||||
if err = env.Write(envParams, envPath); err != nil {
|
||||
return
|
||||
}
|
||||
if err := fileOp.WriteFile(appInstall.GetComposePath(), strings.NewReader(appInstall.DockerCompose), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -410,37 +536,58 @@ func upAppPre(app model.App, appInstall *model.AppInstall) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getServiceFromInstall(appInstall *model.AppInstall) (service *composeV2.ComposeService, err error) {
|
||||
var (
|
||||
project *types.Project
|
||||
envStr string
|
||||
)
|
||||
envStr, err = coverEnvJsonToStr(appInstall.Env)
|
||||
func checkContainerNameIsExist(containerName, appDir string) (bool, error) {
|
||||
client, err := composeV2.NewDockerClient()
|
||||
if err != nil {
|
||||
return
|
||||
return false, err
|
||||
}
|
||||
project, err = composeV2.GetComposeProject(appInstall.Name, appInstall.GetPath(), []byte(appInstall.DockerCompose), []byte(envStr), true)
|
||||
var options dockerTypes.ContainerListOptions
|
||||
list, err := client.ContainerList(context.Background(), options)
|
||||
if err != nil {
|
||||
return
|
||||
return false, err
|
||||
}
|
||||
service, err = composeV2.NewComposeService()
|
||||
if err != nil {
|
||||
return
|
||||
for _, container := range list {
|
||||
if containerName == container.Names[0][1:] {
|
||||
if workDir, ok := container.Labels[composeWorkdirLabel]; ok {
|
||||
if workDir != appDir {
|
||||
return true, nil
|
||||
}
|
||||
} else {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
service.SetProject(project)
|
||||
return
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func upApp(appInstall *model.AppInstall) {
|
||||
upProject := func(appInstall *model.AppInstall) (err error) {
|
||||
if err == nil {
|
||||
var composeService *composeV2.ComposeService
|
||||
composeService, err = getServiceFromInstall(appInstall)
|
||||
if err != nil {
|
||||
return err
|
||||
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
|
||||
}
|
||||
}
|
||||
err = composeService.ComposeUp()
|
||||
out, err = compose.Up(appInstall.GetComposePath())
|
||||
if err != nil {
|
||||
if out != "" {
|
||||
appInstall.Message = errMsg + out
|
||||
}
|
||||
return err
|
||||
}
|
||||
return
|
||||
@@ -450,7 +597,6 @@ func upApp(appInstall *model.AppInstall) {
|
||||
}
|
||||
if err := upProject(appInstall); err != nil {
|
||||
appInstall.Status = constant.Error
|
||||
appInstall.Message = err.Error()
|
||||
} else {
|
||||
appInstall.Status = constant.Running
|
||||
}
|
||||
@@ -473,21 +619,21 @@ func rebuildApp(appInstall model.AppInstall) error {
|
||||
return syncById(appInstall.ID)
|
||||
}
|
||||
|
||||
func getAppDetails(details []model.AppDetail, versions []string) map[string]model.AppDetail {
|
||||
func getAppDetails(details []model.AppDetail, versions []dto.AppConfigVersion) map[string]model.AppDetail {
|
||||
appDetails := make(map[string]model.AppDetail, len(details))
|
||||
for _, old := range details {
|
||||
old.Status = constant.AppTakeDown
|
||||
appDetails[old.Version] = old
|
||||
}
|
||||
|
||||
for _, v := range versions {
|
||||
detail, ok := appDetails[v]
|
||||
version := v.Name
|
||||
detail, ok := appDetails[version]
|
||||
if ok {
|
||||
detail.Status = constant.AppNormal
|
||||
appDetails[v] = detail
|
||||
appDetails[version] = detail
|
||||
} else {
|
||||
appDetails[v] = model.AppDetail{
|
||||
Version: v,
|
||||
appDetails[version] = model.AppDetail{
|
||||
Version: version,
|
||||
Status: constant.AppNormal,
|
||||
}
|
||||
}
|
||||
@@ -495,43 +641,110 @@ func getAppDetails(details []model.AppDetail, versions []string) map[string]mode
|
||||
return appDetails
|
||||
}
|
||||
|
||||
func getApps(oldApps []model.App, items []dto.AppDefine, isLocal bool) map[string]model.App {
|
||||
func getApps(oldApps []model.App, items []dto.AppDefine) map[string]model.App {
|
||||
apps := make(map[string]model.App, len(oldApps))
|
||||
for _, old := range oldApps {
|
||||
old.Status = constant.AppTakeDown
|
||||
apps[old.Key] = old
|
||||
}
|
||||
for _, item := range items {
|
||||
key := item.Key
|
||||
if isLocal {
|
||||
key = "local" + key
|
||||
}
|
||||
config := item.AppProperty
|
||||
key := config.Key
|
||||
app, ok := apps[key]
|
||||
if !ok {
|
||||
app = model.App{}
|
||||
}
|
||||
if isLocal {
|
||||
app.Resource = constant.AppResourceLocal
|
||||
} else {
|
||||
app.Resource = constant.AppResourceRemote
|
||||
}
|
||||
app.Resource = constant.AppResourceRemote
|
||||
app.Name = item.Name
|
||||
app.Limit = item.Limit
|
||||
app.Limit = config.Limit
|
||||
app.Key = key
|
||||
app.ShortDescZh = item.ShortDescZh
|
||||
app.ShortDescEn = item.ShortDescEn
|
||||
app.Website = item.Website
|
||||
app.Document = item.Document
|
||||
app.Github = item.Github
|
||||
app.Type = item.Type
|
||||
app.CrossVersionUpdate = item.CrossVersionUpdate
|
||||
app.Required = item.GetRequired()
|
||||
app.ShortDescZh = config.ShortDescZh
|
||||
app.ShortDescEn = config.ShortDescEn
|
||||
app.Website = config.Website
|
||||
app.Document = config.Document
|
||||
app.Github = config.Github
|
||||
app.Type = config.Type
|
||||
app.CrossVersionUpdate = config.CrossVersionUpdate
|
||||
app.Status = constant.AppNormal
|
||||
app.LastModified = item.LastModified
|
||||
app.ReadMe = item.ReadMe
|
||||
apps[key] = app
|
||||
}
|
||||
return apps
|
||||
}
|
||||
|
||||
func handleLocalAppDetail(versionDir string, appDetail *model.AppDetail) error {
|
||||
fileOp := files.NewFileOp()
|
||||
dockerComposePath := path.Join(versionDir, "docker-compose.yml")
|
||||
if !fileOp.Stat(dockerComposePath) {
|
||||
return errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "docker-compose.yml"}))
|
||||
}
|
||||
dockerComposeByte, _ := fileOp.GetContent(dockerComposePath)
|
||||
if dockerComposeByte == nil {
|
||||
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "docker-compose.yml"}))
|
||||
}
|
||||
appDetail.DockerCompose = string(dockerComposeByte)
|
||||
paramPath := path.Join(versionDir, "data.yml")
|
||||
if !fileOp.Stat(paramPath) {
|
||||
return errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "data.yml"}))
|
||||
}
|
||||
paramByte, _ := fileOp.GetContent(paramPath)
|
||||
if paramByte == nil {
|
||||
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml"}))
|
||||
}
|
||||
appParamConfig := dto.LocalAppParam{}
|
||||
if err := yaml.Unmarshal(paramByte, &appParamConfig); err != nil {
|
||||
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml"}))
|
||||
}
|
||||
dataJson, err := json.Marshal(appParamConfig.AppParams)
|
||||
if err != nil {
|
||||
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
|
||||
}
|
||||
appDetail.Params = string(dataJson)
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleLocalApp(appDir string) (app *model.App, err error) {
|
||||
fileOp := files.NewFileOp()
|
||||
configYamlPath := path.Join(appDir, "data.yml")
|
||||
if !fileOp.Stat(configYamlPath) {
|
||||
err = errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "data.yml"}))
|
||||
return
|
||||
}
|
||||
iconPath := path.Join(appDir, "logo.png")
|
||||
if !fileOp.Stat(iconPath) {
|
||||
err = errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "logo.png"}))
|
||||
return
|
||||
}
|
||||
configYamlByte, err := fileOp.GetContent(configYamlPath)
|
||||
if err != nil {
|
||||
err = errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
|
||||
return
|
||||
}
|
||||
localAppDefine := dto.LocalAppAppDefine{}
|
||||
if err = yaml.Unmarshal(configYamlByte, &localAppDefine); err != nil {
|
||||
err = errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
|
||||
return
|
||||
}
|
||||
app = &localAppDefine.AppProperty
|
||||
app.Resource = constant.AppResourceLocal
|
||||
app.Status = constant.AppNormal
|
||||
app.Recommend = 9999
|
||||
app.TagsKey = append(app.TagsKey, "Local")
|
||||
app.Key = "local" + app.Key
|
||||
readMePath := path.Join(appDir, "README.md")
|
||||
readMeByte, err := fileOp.GetContent(readMePath)
|
||||
if err == nil {
|
||||
app.ReadMe = string(readMeByte)
|
||||
}
|
||||
iconByte, _ := fileOp.GetContent(iconPath)
|
||||
if iconByte != nil {
|
||||
iconStr := base64.StdEncoding.EncodeToString(iconByte)
|
||||
app.Icon = iconStr
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func handleErr(install model.AppInstall, err error, out string) error {
|
||||
reErr := err
|
||||
install.Message = err.Error()
|
||||
@@ -544,37 +757,15 @@ func handleErr(install model.AppInstall, err error, out string) error {
|
||||
return reErr
|
||||
}
|
||||
|
||||
func getAppFromRepo(downloadPath, version string) error {
|
||||
downloadUrl := downloadPath
|
||||
appDir := constant.AppResourceDir
|
||||
|
||||
global.LOG.Infof("download file from %s", downloadUrl)
|
||||
fileOp := files.NewFileOp()
|
||||
if _, err := fileOp.CopyAndBackup(appDir); err != nil {
|
||||
return err
|
||||
}
|
||||
packagePath := path.Join(constant.ResourceDir, path.Base(downloadUrl))
|
||||
if err := fileOp.DownloadFile(downloadUrl, packagePath); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.Decompress(packagePath, constant.ResourceDir, files.TarGz); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = NewISettingService().Update("AppStoreVersion", version)
|
||||
defer func() {
|
||||
_ = fileOp.DeleteFile(packagePath)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]response.AppInstalledDTO, error) {
|
||||
var res []response.AppInstalledDTO
|
||||
for _, installed := range appInstallList {
|
||||
if updated && installed.App.Type == "php" {
|
||||
if updated && (installed.App.Type == "php" || installed.Status == constant.Installing || (installed.App.Key == constant.AppMysql && installed.Version == "5.6.51")) {
|
||||
continue
|
||||
}
|
||||
installDTO := response.AppInstalledDTO{
|
||||
AppInstall: installed,
|
||||
Path: installed.GetPath(),
|
||||
}
|
||||
app, err := appRepo.GetFirst(commonRepo.WithByID(installed.AppId))
|
||||
if err != nil {
|
||||
@@ -586,9 +777,18 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]respons
|
||||
}
|
||||
var versions []string
|
||||
for _, detail := range details {
|
||||
if detail.IgnoreUpgrade {
|
||||
continue
|
||||
}
|
||||
if common.IsCrossVersion(installed.Version, detail.Version) && !app.CrossVersionUpdate {
|
||||
continue
|
||||
}
|
||||
versions = append(versions, detail.Version)
|
||||
}
|
||||
versions = common.GetSortedVersions(versions)
|
||||
if len(versions) == 0 {
|
||||
continue
|
||||
}
|
||||
lastVersion := versions[0]
|
||||
if common.IsCrossVersion(installed.Version, lastVersion) {
|
||||
installDTO.CanUpdate = app.CrossVersionUpdate
|
||||
@@ -667,3 +867,108 @@ func updateToolApp(installed *model.AppInstall) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName string, req request.AppContainerConfig, params map[string]interface{}) error {
|
||||
services, serviceValid := composeMap["services"].(map[string]interface{})
|
||||
if !serviceValid {
|
||||
return buserr.New(constant.ErrFileParse)
|
||||
}
|
||||
service, serviceExist := services[serviceName]
|
||||
if !serviceExist {
|
||||
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}",
|
||||
},
|
||||
},
|
||||
}
|
||||
serviceValue["deploy"] = deploy
|
||||
|
||||
ports, ok := serviceValue["ports"].([]interface{})
|
||||
if ok {
|
||||
for i, port := range ports {
|
||||
portStr, portOK := port.(string)
|
||||
if !portOK {
|
||||
continue
|
||||
}
|
||||
portArray := strings.Split(portStr, ":")
|
||||
if len(portArray) == 2 {
|
||||
portArray = append([]string{"${HOST_IP}"}, portArray...)
|
||||
}
|
||||
ports[i] = strings.Join(portArray, ":")
|
||||
}
|
||||
serviceValue["ports"] = ports
|
||||
}
|
||||
|
||||
params[constant.CPUS] = "0"
|
||||
params[constant.MemoryLimit] = "0"
|
||||
if req.Advanced {
|
||||
if req.CpuQuota > 0 {
|
||||
params[constant.CPUS] = req.CpuQuota
|
||||
}
|
||||
if req.MemoryLimit > 0 {
|
||||
params[constant.MemoryLimit] = strconv.FormatFloat(req.MemoryLimit, 'f', -1, 32) + req.MemoryUnit
|
||||
}
|
||||
}
|
||||
_, portExist := serviceValue["ports"].([]interface{})
|
||||
if portExist {
|
||||
allowHost := "127.0.0.1"
|
||||
if req.Advanced && req.AllowPort {
|
||||
allowHost = "0.0.0.0"
|
||||
}
|
||||
params[constant.HostIP] = allowHost
|
||||
}
|
||||
services[serviceName] = serviceValue
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAppCommonConfig(envs map[string]interface{}) request.AppContainerConfig {
|
||||
config := request.AppContainerConfig{}
|
||||
|
||||
if hostIp, ok := envs[constant.HostIP]; ok {
|
||||
config.AllowPort = hostIp.(string) == "0.0.0.0"
|
||||
} else {
|
||||
config.AllowPort = true
|
||||
}
|
||||
if cpuCore, ok := envs[constant.CPUS]; ok {
|
||||
numStr, ok := cpuCore.(string)
|
||||
if ok {
|
||||
num, err := strconv.ParseFloat(numStr, 64)
|
||||
if err == nil {
|
||||
config.CpuQuota = num
|
||||
}
|
||||
} else {
|
||||
num64, flOk := cpuCore.(float64)
|
||||
if flOk {
|
||||
config.CpuQuota = num64
|
||||
}
|
||||
}
|
||||
} else {
|
||||
config.CpuQuota = 0
|
||||
}
|
||||
if memLimit, ok := envs[constant.MemoryLimit]; ok {
|
||||
re := regexp.MustCompile(`(\d+)([A-Za-z]+)`)
|
||||
matches := re.FindStringSubmatch(memLimit.(string))
|
||||
if len(matches) == 3 {
|
||||
num, err := strconv.ParseFloat(matches[1], 64)
|
||||
if err == nil {
|
||||
unit := matches[2]
|
||||
config.MemoryLimit = num
|
||||
config.MemoryUnit = unit
|
||||
}
|
||||
}
|
||||
} else {
|
||||
config.MemoryLimit = 0
|
||||
config.MemoryUnit = "M"
|
||||
}
|
||||
|
||||
if containerName, ok := envs[constant.ContainerName]; ok {
|
||||
config.ContainerName = containerName.(string)
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ import (
|
||||
type AuthService struct{}
|
||||
|
||||
type IAuthService interface {
|
||||
CheckIsSafety(code string) bool
|
||||
CheckIsSafety(code string) (string, error)
|
||||
VerifyCode(code string) (bool, error)
|
||||
Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error)
|
||||
LogOut(c *gin.Context) error
|
||||
@@ -33,11 +33,11 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo,
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
|
||||
}
|
||||
passwrodSetting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
|
||||
passwordSetting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
|
||||
}
|
||||
pass, err := encrypt.StringDecrypt(passwrodSetting.Value)
|
||||
pass, err := encrypt.StringDecrypt(passwordSetting.Value)
|
||||
if err != nil {
|
||||
return nil, constant.ErrAuth
|
||||
}
|
||||
@@ -60,11 +60,11 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin) (*dto.UserLogi
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
|
||||
}
|
||||
passwrodSetting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
|
||||
passwordSetting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
|
||||
}
|
||||
pass, err := encrypt.StringDecrypt(passwrodSetting.Value)
|
||||
pass, err := encrypt.StringDecrypt(passwordSetting.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -76,7 +76,11 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin) (*dto.UserLogi
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
success := mfa.ValidCode(info.Code, mfaSecret.Value)
|
||||
mfaInterval, err := settingRepo.Get(settingRepo.WithByKey("MFAInterval"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
success := mfa.ValidCode(info.Code, mfaInterval.Value, mfaSecret.Value)
|
||||
if !success {
|
||||
return nil, constant.ErrAuth
|
||||
}
|
||||
@@ -109,7 +113,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, 604800, "", "", false, false)
|
||||
c.SetCookie(constant.SessionName, sID, 0, "", "", false, false)
|
||||
err := global.SESSION.Set(sID, sessionUser, lifeTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -143,13 +147,16 @@ func (u *AuthService) VerifyCode(code string) (bool, error) {
|
||||
return setting.Value == code, nil
|
||||
}
|
||||
|
||||
func (u *AuthService) CheckIsSafety(code string) bool {
|
||||
func (u *AuthService) CheckIsSafety(code string) (string, error) {
|
||||
status, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
|
||||
if err != nil {
|
||||
return false
|
||||
return "", err
|
||||
}
|
||||
if len(status.Value) == 0 {
|
||||
return true
|
||||
return "disable", nil
|
||||
}
|
||||
return status.Value == code
|
||||
if status.Value == code {
|
||||
return "pass", nil
|
||||
}
|
||||
return "unpass", nil
|
||||
}
|
||||
|
@@ -2,8 +2,12 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
@@ -24,6 +28,7 @@ type BackupService struct{}
|
||||
type IBackupService interface {
|
||||
List() ([]dto.BackupInfo, error)
|
||||
SearchRecordsWithPage(search dto.RecordSearch) (int64, []dto.BackupRecords, error)
|
||||
LoadOneDriveInfo() (string, error)
|
||||
DownloadRecord(info dto.DownloadRecord) (string, error)
|
||||
Create(backupDto dto.BackupOperate) error
|
||||
GetBuckets(backupDto dto.ForBuckets) ([]interface{}, error)
|
||||
@@ -62,6 +67,7 @@ func (u *BackupService) List() ([]dto.BackupInfo, error) {
|
||||
dtobas = append(dtobas, u.loadByType("MINIO", ops))
|
||||
dtobas = append(dtobas, u.loadByType("COS", ops))
|
||||
dtobas = append(dtobas, u.loadByType("KODO", ops))
|
||||
dtobas = append(dtobas, u.loadByType("OneDrive", ops))
|
||||
return dtobas, err
|
||||
}
|
||||
|
||||
@@ -84,6 +90,18 @@ func (u *BackupService) SearchRecordsWithPage(search dto.RecordSearch) (int64, [
|
||||
return total, dtobas, err
|
||||
}
|
||||
|
||||
func (u *BackupService) LoadOneDriveInfo() (string, error) {
|
||||
OneDriveID, err := settingRepo.Get(settingRepo.WithByKey("OneDriveID"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
idItem, err := base64.StdEncoding.DecodeString(OneDriveID.Value)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(idItem), err
|
||||
}
|
||||
|
||||
func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error) {
|
||||
if info.Source == "LOCAL" {
|
||||
return info.FileDir + "/" + info.FileName, nil
|
||||
@@ -96,7 +114,6 @@ func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error)
|
||||
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
|
||||
return "", err
|
||||
}
|
||||
varMap["type"] = backup.Type
|
||||
varMap["bucket"] = backup.Bucket
|
||||
switch backup.Type {
|
||||
case constant.Sftp:
|
||||
@@ -105,8 +122,10 @@ func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error)
|
||||
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
|
||||
varMap["accessKey"] = backup.AccessKey
|
||||
varMap["secretKey"] = backup.Credential
|
||||
case constant.OneDrive:
|
||||
varMap["accessToken"] = backup.Credential
|
||||
}
|
||||
backClient, err := cloud_storage.NewCloudStorageClient(varMap)
|
||||
backClient, err := cloud_storage.NewCloudStorageClient(backup.Type, varMap)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new cloud storage client failed, err: %v", err)
|
||||
}
|
||||
@@ -117,6 +136,11 @@ func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error)
|
||||
}
|
||||
}
|
||||
srcPath := fmt.Sprintf("%s/%s", info.FileDir, info.FileName)
|
||||
if len(backup.BackupPath) != 0 {
|
||||
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
|
||||
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
|
||||
srcPath = itemPath + srcPath
|
||||
}
|
||||
if exist, _ := backClient.Exist(srcPath); exist {
|
||||
isOK, err := backClient.Download(srcPath, targetPath)
|
||||
if !isOK {
|
||||
@@ -134,6 +158,12 @@ func (u *BackupService) Create(backupDto dto.BackupOperate) error {
|
||||
if err := copier.Copy(&backup, &backupDto); err != nil {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
|
||||
if backupDto.Type == constant.OneDrive {
|
||||
if err := u.loadAccessToken(&backup); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := backupRepo.Create(&backup); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -145,7 +175,6 @@ func (u *BackupService) GetBuckets(backupDto dto.ForBuckets) ([]interface{}, err
|
||||
if err := json.Unmarshal([]byte(backupDto.Vars), &varMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
varMap["type"] = backupDto.Type
|
||||
switch backupDto.Type {
|
||||
case constant.Sftp:
|
||||
varMap["username"] = backupDto.AccessKey
|
||||
@@ -154,7 +183,7 @@ func (u *BackupService) GetBuckets(backupDto dto.ForBuckets) ([]interface{}, err
|
||||
varMap["accessKey"] = backupDto.AccessKey
|
||||
varMap["secretKey"] = backupDto.Credential
|
||||
}
|
||||
client, err := cloud_storage.NewCloudStorageClient(varMap)
|
||||
client, err := cloud_storage.NewCloudStorageClient(backupDto.Type, varMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -214,7 +243,17 @@ func (u *BackupService) Update(req dto.BackupOperate) error {
|
||||
upMap := make(map[string]interface{})
|
||||
upMap["bucket"] = req.Bucket
|
||||
upMap["credential"] = req.Credential
|
||||
upMap["backup_path"] = req.BackupPath
|
||||
upMap["vars"] = req.Vars
|
||||
backup.Vars = req.Vars
|
||||
|
||||
if req.Type == constant.OneDrive {
|
||||
if err := u.loadAccessToken(&backup); err != nil {
|
||||
return err
|
||||
}
|
||||
upMap["credential"] = backup.Credential
|
||||
upMap["vars"] = backup.Vars
|
||||
}
|
||||
if err := backupRepo.Update(req.ID, upMap); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -251,7 +290,6 @@ func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.Cl
|
||||
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
varMap["type"] = backup.Type
|
||||
if backup.Type == "LOCAL" {
|
||||
return nil, errors.New("not support")
|
||||
}
|
||||
@@ -263,9 +301,11 @@ func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.Cl
|
||||
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
|
||||
varMap["accessKey"] = backup.AccessKey
|
||||
varMap["secretKey"] = backup.Credential
|
||||
case constant.OneDrive:
|
||||
varMap["accessToken"] = backup.Credential
|
||||
}
|
||||
|
||||
backClient, err := cloud_storage.NewCloudStorageClient(varMap)
|
||||
backClient, err := cloud_storage.NewCloudStorageClient(backup.Type, varMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -286,6 +326,53 @@ func (u *BackupService) loadByType(accountType string, accounts []model.BackupAc
|
||||
return dto.BackupInfo{Type: accountType}
|
||||
}
|
||||
|
||||
func (u *BackupService) loadAccessToken(backup *model.BackupAccount) error {
|
||||
varMap := make(map[string]interface{})
|
||||
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()))
|
||||
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)
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
||||
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 loadLocalDir() (string, error) {
|
||||
backup, err := backupRepo.Get(commonRepo.WithByType("LOCAL"))
|
||||
if err != nil {
|
||||
|
@@ -99,7 +99,7 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
|
||||
return err
|
||||
}
|
||||
|
||||
appPath := fmt.Sprintf("%s/%s", install.GetPath(), install.Name)
|
||||
appPath := install.GetPath()
|
||||
if err := handleTar(appPath, tmpDir, "app.tar.gz", ""); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -148,7 +148,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
if err := json.Unmarshal(appjson, &oldInstall); err != nil {
|
||||
return fmt.Errorf("unmarshal app.json failed, err: %v", err)
|
||||
}
|
||||
if oldInstall.App.Key != install.App.Key || oldInstall.Name != install.Name || oldInstall.Version != install.Version || oldInstall.ID != install.ID {
|
||||
if oldInstall.App.Key != install.App.Key || oldInstall.Name != install.Name {
|
||||
return errors.New("the current backup file does not match the application")
|
||||
}
|
||||
|
||||
@@ -172,6 +172,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
}()
|
||||
}
|
||||
|
||||
newEnvFile := ""
|
||||
resource, _ := appInstallResourceRepo.GetFirst(appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||
if resource.ID != 0 && install.App.Key != "mysql" {
|
||||
mysqlInfo, err := appInstallRepo.LoadBaseInfo(resource.Key, "")
|
||||
@@ -182,7 +183,22 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handleMysqlRecover(mysqlInfo, tmpPath, db.Name, fmt.Sprintf("%s.sql.gz", install.Name), true); err != nil {
|
||||
|
||||
newDB, envMap, err := reCreateDB(db.ID, oldInstall.Env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldHost := fmt.Sprintf("\"PANEL_DB_HOST\":\"%v\"", envMap["PANEL_DB_HOST"].(string))
|
||||
newHost := fmt.Sprintf("\"PANEL_DB_HOST\":\"%v\"", mysqlInfo.ServiceName)
|
||||
oldInstall.Env = strings.ReplaceAll(oldInstall.Env, oldHost, newHost)
|
||||
envMap["PANEL_DB_HOST"] = mysqlInfo.ServiceName
|
||||
newEnvFile, err = coverEnvJsonToStr(oldInstall.Env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = appInstallResourceRepo.BatchUpdateBy(map[string]interface{}{"resource_id": newDB.ID}, commonRepo.WithByID(resource.ID))
|
||||
|
||||
if err := handleMysqlRecover(mysqlInfo, tmpPath, newDB.Name, fmt.Sprintf("%s.sql.gz", install.Name), true); err != nil {
|
||||
global.LOG.Errorf("handle recover from sql.gz failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
@@ -193,11 +209,50 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
return err
|
||||
}
|
||||
|
||||
oldInstall.Status = constant.Running
|
||||
if err := appInstallRepo.Save(context.Background(), install); err != nil {
|
||||
if len(newEnvFile) != 0 {
|
||||
envPath := fmt.Sprintf("%s/%s/%s/.env", constant.AppInstallDir, install.App.Key, install.Name)
|
||||
file, err := os.OpenFile(envPath, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
_, _ = file.WriteString(newEnvFile)
|
||||
}
|
||||
|
||||
oldInstall.ID = install.ID
|
||||
oldInstall.Status = constant.StatusRunning
|
||||
oldInstall.AppId = install.AppId
|
||||
oldInstall.AppDetailId = install.AppDetailId
|
||||
if err := appInstallRepo.Save(context.Background(), &oldInstall); err != nil {
|
||||
global.LOG.Errorf("save db app install failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
isOk = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func reCreateDB(dbID uint, oldEnv string) (*model.DatabaseMysql, map[string]interface{}, error) {
|
||||
mysqlService := NewIMysqlService()
|
||||
ctx := context.Background()
|
||||
_ = mysqlService.Delete(ctx, dto.MysqlDBDelete{ID: dbID, DeleteBackup: true, ForceDelete: true})
|
||||
|
||||
envMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(oldEnv), &envMap); err != nil {
|
||||
return nil, envMap, err
|
||||
}
|
||||
oldName, _ := envMap["PANEL_DB_NAME"].(string)
|
||||
oldUser, _ := envMap["PANEL_DB_USER"].(string)
|
||||
oldPassword, _ := envMap["PANEL_DB_USER_PASSWORD"].(string)
|
||||
createDB, err := mysqlService.Create(context.Background(), dto.MysqlDBCreate{
|
||||
Name: oldName,
|
||||
Format: "utf8mb4",
|
||||
Username: oldUser,
|
||||
Password: oldPassword,
|
||||
Permission: "%",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, envMap, err
|
||||
}
|
||||
|
||||
return createDB, envMap, nil
|
||||
}
|
||||
|
@@ -37,7 +37,7 @@ func (u *BackupService) RedisBackup() error {
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
fileName := fmt.Sprintf("%s.rdb", timeNow)
|
||||
if appendonly == "yes" {
|
||||
if redisInfo.Version == "6.0.16" {
|
||||
if strings.HasPrefix(redisInfo.Version, "6.") {
|
||||
fileName = fmt.Sprintf("%s.aof", timeNow)
|
||||
} else {
|
||||
fileName = fmt.Sprintf("%s.tar.gz", timeNow)
|
||||
@@ -120,10 +120,10 @@ func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback
|
||||
}
|
||||
|
||||
if appendonly == "yes" {
|
||||
if redisInfo.Version == "6.0.16" && !strings.HasSuffix(recoverFile, ".aof") {
|
||||
if strings.HasPrefix(redisInfo.Version, "6.") && !strings.HasSuffix(recoverFile, ".aof") {
|
||||
return buserr.New(constant.ErrTypeOfRedis)
|
||||
}
|
||||
if redisInfo.Version == "7.0.5" && !strings.HasSuffix(recoverFile, ".tar.gz") {
|
||||
if strings.HasPrefix(redisInfo.Version, "7.") && !strings.HasSuffix(recoverFile, ".tar.gz") {
|
||||
return buserr.New(constant.ErrTypeOfRedis)
|
||||
}
|
||||
} else {
|
||||
@@ -137,7 +137,7 @@ func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback
|
||||
if !isRollback {
|
||||
suffix := "rdb"
|
||||
if appendonly == "yes" {
|
||||
if redisInfo.Version == "6.0.16" {
|
||||
if strings.HasPrefix(redisInfo.Version, "6.") {
|
||||
suffix = "aof"
|
||||
} else {
|
||||
suffix = "tar.gz"
|
||||
@@ -165,14 +165,14 @@ func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback
|
||||
if _, err := compose.Down(composeDir + "/docker-compose.yml"); err != nil {
|
||||
return err
|
||||
}
|
||||
if appendonly == "yes" && redisInfo.Version == "7.0.5" {
|
||||
if appendonly == "yes" && strings.HasPrefix(redisInfo.Version, "7.") {
|
||||
redisDataDir := fmt.Sprintf("%s/%s/%s/data", constant.AppInstallDir, "redis", redisInfo.Name)
|
||||
if err := handleUnTar(recoverFile, redisDataDir); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
itemName := "dump.rdb"
|
||||
if appendonly == "yes" && redisInfo.Version == "6.0.16" {
|
||||
if appendonly == "yes" && strings.HasPrefix(redisInfo.Version, "6.") {
|
||||
itemName = "appendonly.aof"
|
||||
}
|
||||
input, err := os.ReadFile(recoverFile)
|
||||
|
@@ -3,10 +3,11 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -25,30 +26,42 @@ import (
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/gorilla/websocket"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/mem"
|
||||
)
|
||||
|
||||
type ContainerService struct{}
|
||||
|
||||
type IContainerService interface {
|
||||
Page(req dto.PageContainer) (int64, interface{}, error)
|
||||
List() ([]string, error)
|
||||
PageNetwork(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
ListNetwork() ([]dto.Options, error)
|
||||
PageVolume(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
ListVolume() ([]dto.Options, error)
|
||||
PageCompose(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
CreateCompose(req dto.ComposeCreate) (string, error)
|
||||
ComposeOperation(req dto.ComposeOperation) error
|
||||
ContainerCreate(req dto.ContainerCreate) error
|
||||
ContainerCreate(req dto.ContainerOperate) error
|
||||
ContainerUpdate(req dto.ContainerOperate) error
|
||||
ContainerUpgrade(req dto.ContainerUpgrade) error
|
||||
ContainerInfo(req dto.OperationWithName) (*dto.ContainerOperate, error)
|
||||
ContainerListStats() ([]dto.ContainerListStats, error)
|
||||
LoadResouceLimit() (*dto.ResourceLimit, error)
|
||||
ContainerLogClean(req dto.OperationWithName) error
|
||||
ContainerOperation(req dto.ContainerOperation) error
|
||||
ContainerLogs(param dto.ContainerLog) (string, error)
|
||||
ContainerStats(id string) (*dto.ContainterStats, error)
|
||||
ContainerLogs(wsConn *websocket.Conn, container, since, tail string, follow bool) error
|
||||
ContainerStats(id string) (*dto.ContainerStats, error)
|
||||
Inspect(req dto.InspectReq) (string, error)
|
||||
DeleteNetwork(req dto.BatchDelete) error
|
||||
CreateNetwork(req dto.NetworkCreat) error
|
||||
CreateNetwork(req dto.NetworkCreate) error
|
||||
DeleteVolume(req dto.BatchDelete) error
|
||||
CreateVolume(req dto.VolumeCreat) error
|
||||
CreateVolume(req dto.VolumeCreate) error
|
||||
TestCompose(req dto.ComposeCreate) (bool, error)
|
||||
ComposeUpdate(req dto.ComposeUpdate) error
|
||||
Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error)
|
||||
}
|
||||
|
||||
func NewIContainerService() IContainerService {
|
||||
@@ -57,9 +70,8 @@ func NewIContainerService() IContainerService {
|
||||
|
||||
func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, error) {
|
||||
var (
|
||||
records []types.Container
|
||||
list []types.Container
|
||||
backDatas []dto.ContainerInfo
|
||||
records []types.Container
|
||||
list []types.Container
|
||||
)
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
@@ -75,19 +87,40 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
return 0, nil, err
|
||||
}
|
||||
if len(req.Name) != 0 {
|
||||
lenth, count := len(list), 0
|
||||
for count < lenth {
|
||||
length, count := len(list), 0
|
||||
for count < length {
|
||||
if !strings.Contains(list[count].Names[0][1:], req.Name) {
|
||||
list = append(list[:count], list[(count+1):]...)
|
||||
lenth--
|
||||
length--
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return list[i].Created > list[j].Created
|
||||
})
|
||||
switch req.OrderBy {
|
||||
case "name":
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
if req.Order == constant.OrderAsc {
|
||||
return list[i].Names[0][1:] < list[j].Names[0][1:]
|
||||
}
|
||||
return list[i].Names[0][1:] > list[j].Names[0][1:]
|
||||
})
|
||||
case "state":
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
if req.Order == constant.OrderAsc {
|
||||
return list[i].State < list[j].State
|
||||
}
|
||||
return list[i].State > list[j].State
|
||||
})
|
||||
default:
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
if req.Order == constant.OrderAsc {
|
||||
return list[i].Created < list[j].Created
|
||||
}
|
||||
return list[i].Created > list[j].Created
|
||||
})
|
||||
}
|
||||
|
||||
total, start, end := len(list), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||
if start > total {
|
||||
records = make([]types.Container, 0)
|
||||
@@ -98,49 +131,87 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
records = list[start:end]
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(records))
|
||||
for _, container := range records {
|
||||
go func(item types.Container) {
|
||||
IsFromCompose := false
|
||||
if _, ok := item.Labels[composeProjectLabel]; ok {
|
||||
IsFromCompose = true
|
||||
}
|
||||
IsFromApp := false
|
||||
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
|
||||
IsFromApp = true
|
||||
}
|
||||
backDatas := make([]dto.ContainerInfo, len(records))
|
||||
for i := 0; i < len(records); i++ {
|
||||
item := records[i]
|
||||
IsFromCompose := false
|
||||
if _, ok := item.Labels[composeProjectLabel]; ok {
|
||||
IsFromCompose = true
|
||||
}
|
||||
IsFromApp := false
|
||||
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
|
||||
IsFromApp = true
|
||||
}
|
||||
|
||||
var ports []string
|
||||
for _, port := range item.Ports {
|
||||
if port.IP == "::" || port.PublicPort == 0 {
|
||||
continue
|
||||
}
|
||||
ports = append(ports, fmt.Sprintf("%v:%v/%s", port.PublicPort, port.PrivatePort, port.Type))
|
||||
var ports []string
|
||||
for _, port := range item.Ports {
|
||||
itemPortStr := fmt.Sprintf("%v/%s", port.PrivatePort, port.Type)
|
||||
if port.PublicPort != 0 {
|
||||
itemPortStr = fmt.Sprintf("%s:%v->%v/%s", port.IP, port.PublicPort, port.PrivatePort, port.Type)
|
||||
}
|
||||
cpu, mem := loadCpuAndMem(client, item.ID)
|
||||
backDatas = append(backDatas, dto.ContainerInfo{
|
||||
ContainerID: item.ID,
|
||||
CreateTime: time.Unix(item.Created, 0).Format("2006-01-02 15:04:05"),
|
||||
Name: item.Names[0][1:],
|
||||
ImageId: strings.Split(item.ImageID, ":")[1],
|
||||
ImageName: item.Image,
|
||||
State: item.State,
|
||||
RunTime: item.Status,
|
||||
CPUPercent: cpu,
|
||||
MemoryPercent: mem,
|
||||
Ports: ports,
|
||||
IsFromApp: IsFromApp,
|
||||
IsFromCompose: IsFromCompose,
|
||||
})
|
||||
wg.Done()
|
||||
}(container)
|
||||
ports = append(ports, itemPortStr)
|
||||
}
|
||||
backDatas[i] = dto.ContainerInfo{
|
||||
ContainerID: item.ID,
|
||||
CreateTime: time.Unix(item.Created, 0).Format("2006-01-02 15:04:05"),
|
||||
Name: item.Names[0][1:],
|
||||
ImageId: strings.Split(item.ImageID, ":")[1],
|
||||
ImageName: item.Image,
|
||||
State: item.State,
|
||||
RunTime: item.Status,
|
||||
Ports: ports,
|
||||
IsFromApp: IsFromApp,
|
||||
IsFromCompose: IsFromCompose,
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return int64(total), backDatas, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) List() ([]string, error) {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containers, err := client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var datas []string
|
||||
for _, container := range containers {
|
||||
for _, name := range container.Names {
|
||||
if len(name) != 0 {
|
||||
datas = append(datas, strings.TrimLeft(name, "/"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return datas, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error) {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list, err := client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var datas []dto.ContainerListStats
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(list))
|
||||
for i := 0; i < len(list); i++ {
|
||||
go func(item types.Container) {
|
||||
cpu, mem := loadCpuAndMem(client, item.ID)
|
||||
datas = append(datas, dto.ContainerListStats{CPUPercent: cpu, MemoryPercent: mem, ContainerID: item.ID})
|
||||
wg.Done()
|
||||
}(list[i])
|
||||
}
|
||||
wg.Wait()
|
||||
return datas, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
@@ -165,63 +236,97 @@ func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
|
||||
portMap, err := checkPortStats(req.ExposedPorts)
|
||||
func (u *ContainerService) Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error) {
|
||||
report := dto.ContainerPruneReport{}
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
return report, err
|
||||
}
|
||||
pruneFilters := filters.NewArgs()
|
||||
if req.WithTagAll {
|
||||
pruneFilters.Add("dangling", "false")
|
||||
if req.PruneType != "image" {
|
||||
pruneFilters.Add("until", "24h")
|
||||
}
|
||||
}
|
||||
switch req.PruneType {
|
||||
case "container":
|
||||
rep, err := client.ContainersPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
report.DeletedNumber = len(rep.ContainersDeleted)
|
||||
report.SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||
case "image":
|
||||
rep, err := client.ImagesPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
report.DeletedNumber = len(rep.ImagesDeleted)
|
||||
report.SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||
case "network":
|
||||
rep, err := client.NetworksPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
report.DeletedNumber = len(rep.NetworksDeleted)
|
||||
case "volume":
|
||||
rep, err := client.VolumesPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
report.DeletedNumber = len(rep.VolumesDeleted)
|
||||
report.SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||
}
|
||||
return report, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) LoadResouceLimit() (*dto.ResourceLimit, error) {
|
||||
cpuCounts, err := cpu.Counts(true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load cpu limit failed, err: %v", err)
|
||||
}
|
||||
memoryInfo, err := mem.VirtualMemory()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load memory limit failed, err: %v", err)
|
||||
}
|
||||
|
||||
data := dto.ResourceLimit{
|
||||
CPU: cpuCounts,
|
||||
Memory: int(memoryInfo.Total),
|
||||
}
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerCreate(req dto.ContainerOperate) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx := context.Background()
|
||||
newContainer, _ := client.ContainerInspect(ctx, req.Name)
|
||||
if newContainer.ContainerJSONBase != nil {
|
||||
return buserr.New(constant.ErrContainerName)
|
||||
}
|
||||
|
||||
exposeds := make(nat.PortSet)
|
||||
for port := range portMap {
|
||||
exposeds[port] = struct{}{}
|
||||
}
|
||||
config := &container.Config{
|
||||
Image: req.Image,
|
||||
Cmd: req.Cmd,
|
||||
Env: req.Env,
|
||||
Labels: stringsToMap(req.Labels),
|
||||
Tty: true,
|
||||
OpenStdin: true,
|
||||
ExposedPorts: exposeds,
|
||||
}
|
||||
hostConf := &container.HostConfig{
|
||||
AutoRemove: req.AutoRemove,
|
||||
PublishAllPorts: req.PublishAllPorts,
|
||||
RestartPolicy: container.RestartPolicy{Name: req.RestartPolicy},
|
||||
}
|
||||
if req.RestartPolicy == "on-failure" {
|
||||
hostConf.RestartPolicy.MaximumRetryCount = 5
|
||||
}
|
||||
if req.NanoCPUs != 0 {
|
||||
hostConf.NanoCPUs = req.NanoCPUs * 1000000000
|
||||
}
|
||||
if req.Memory != 0 {
|
||||
hostConf.Memory = req.Memory
|
||||
}
|
||||
if len(req.ExposedPorts) != 0 {
|
||||
hostConf.PortBindings = portMap
|
||||
}
|
||||
if len(req.Volumes) != 0 {
|
||||
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))
|
||||
}
|
||||
var config container.Config
|
||||
var hostConf container.HostConfig
|
||||
var networkConf network.NetworkingConfig
|
||||
if err := loadConfigInfo(req, &config, &hostConf, &networkConf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
global.LOG.Infof("new container info %s has been made, now start to create", req.Name)
|
||||
|
||||
ctx := context.Background()
|
||||
if !checkImageExist(client, req.Image) {
|
||||
if !checkImageExist(client, req.Image) || req.ForcePull {
|
||||
if err := pullImages(ctx, client, req.Image); err != nil {
|
||||
return err
|
||||
if !req.ForcePull {
|
||||
return err
|
||||
}
|
||||
global.LOG.Errorf("force pull image %s failed, err: %v", req.Image, err)
|
||||
}
|
||||
}
|
||||
container, err := client.ContainerCreate(ctx, config, hostConf, &network.NetworkingConfig{}, &v1.Platform{}, req.Name)
|
||||
container, 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})
|
||||
return err
|
||||
@@ -234,6 +339,159 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.ContainerOperate, error) {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx := context.Background()
|
||||
oldContainer, err := client.ContainerInspect(ctx, req.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var data dto.ContainerOperate
|
||||
data.ContainerID = oldContainer.ID
|
||||
data.Name = strings.ReplaceAll(oldContainer.Name, "/", "")
|
||||
data.Image = oldContainer.Config.Image
|
||||
if oldContainer.NetworkSettings != nil {
|
||||
for network := range oldContainer.NetworkSettings.Networks {
|
||||
data.Network = network
|
||||
break
|
||||
}
|
||||
}
|
||||
data.Cmd = oldContainer.Config.Cmd
|
||||
data.Env = oldContainer.Config.Env
|
||||
data.CPUShares = oldContainer.HostConfig.CPUShares
|
||||
for key, val := range oldContainer.Config.Labels {
|
||||
data.Labels = append(data.Labels, fmt.Sprintf("%s=%s", key, val))
|
||||
}
|
||||
for key, val := range oldContainer.HostConfig.PortBindings {
|
||||
var itemPort dto.PortHelper
|
||||
if !strings.Contains(string(key), "/") {
|
||||
continue
|
||||
}
|
||||
itemPort.ContainerPort = strings.Split(string(key), "/")[0]
|
||||
itemPort.Protocol = strings.Split(string(key), "/")[1]
|
||||
for _, binds := range val {
|
||||
itemPort.HostIP = binds.HostIP
|
||||
itemPort.HostPort = binds.HostPort
|
||||
data.ExposedPorts = append(data.ExposedPorts, itemPort)
|
||||
}
|
||||
}
|
||||
data.AutoRemove = oldContainer.HostConfig.AutoRemove
|
||||
data.PublishAllPorts = oldContainer.HostConfig.PublishAllPorts
|
||||
data.RestartPolicy = 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
|
||||
}
|
||||
for _, bind := range oldContainer.HostConfig.Binds {
|
||||
parts := strings.Split(bind, ":")
|
||||
if len(parts) != 3 {
|
||||
continue
|
||||
}
|
||||
data.Volumes = append(data.Volumes, dto.VolumeHelper{SourceDir: parts[0], ContainerDir: parts[1], Mode: parts[2]})
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx := context.Background()
|
||||
newContainer, _ := client.ContainerInspect(ctx, req.Name)
|
||||
if newContainer.ContainerJSONBase != nil && newContainer.ID != req.ContainerID {
|
||||
return buserr.New(constant.ErrContainerName)
|
||||
}
|
||||
|
||||
oldContainer, err := client.ContainerInspect(ctx, req.ContainerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !checkImageExist(client, req.Image) || req.ForcePull {
|
||||
if err := pullImages(ctx, client, req.Image); err != nil {
|
||||
if !req.ForcePull {
|
||||
return err
|
||||
}
|
||||
global.LOG.Errorf("force pull image %s failed, err: %v", req.Image, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := client.ContainerRemove(ctx, req.ContainerID, types.ContainerRemoveOptions{Force: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config := oldContainer.Config
|
||||
hostConf := oldContainer.HostConfig
|
||||
var networkConf network.NetworkingConfig
|
||||
if err := loadConfigInfo(req, config, hostConf, &networkConf); 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)
|
||||
if err != nil {
|
||||
return fmt.Errorf("recreate contianer 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 {
|
||||
return fmt.Errorf("update successful but start failed, err: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx := context.Background()
|
||||
oldContainer, err := client.ContainerInspect(ctx, req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !checkImageExist(client, req.Image) || req.ForcePull {
|
||||
if err := pullImages(ctx, client, req.Image); err != nil {
|
||||
if !req.ForcePull {
|
||||
return err
|
||||
}
|
||||
global.LOG.Errorf("force pull image %s failed, err: %v", req.Image, err)
|
||||
}
|
||||
}
|
||||
config := oldContainer.Config
|
||||
config.Image = req.Image
|
||||
hostConf := oldContainer.HostConfig
|
||||
var networkConf network.NetworkingConfig
|
||||
if oldContainer.NetworkSettings != nil {
|
||||
for networkKey := range oldContainer.NetworkSettings.Networks {
|
||||
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{networkKey: {}}
|
||||
break
|
||||
}
|
||||
}
|
||||
if err := client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{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, &network.NetworkingConfig{}, &v1.Platform{}, req.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("recreate contianer 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 {
|
||||
return fmt.Errorf("update successful but start failed, err: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error {
|
||||
var err error
|
||||
ctx := context.Background()
|
||||
@@ -256,6 +514,10 @@ func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error
|
||||
case constant.ContainerOpUnpause:
|
||||
err = client.ContainerUnpause(ctx, req.Name)
|
||||
case constant.ContainerOpRename:
|
||||
newContainer, _ := client.ContainerInspect(ctx, req.NewName)
|
||||
if newContainer.ContainerJSONBase != nil {
|
||||
return buserr.New(constant.ErrContainerName)
|
||||
}
|
||||
err = client.ContainerRename(ctx, req.Name, req.NewName)
|
||||
case constant.ContainerOpRemove:
|
||||
err = client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
|
||||
@@ -263,19 +525,72 @@ func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error
|
||||
return err
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerLogs(req dto.ContainerLog) (string, error) {
|
||||
cmd := exec.Command("docker", "logs", req.ContainerID)
|
||||
if req.Mode != "all" {
|
||||
cmd = exec.Command("docker", "logs", req.ContainerID, "--since", req.Mode)
|
||||
}
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return "", errors.New(string(stdout))
|
||||
return err
|
||||
}
|
||||
return string(stdout), nil
|
||||
container, err := client.ContainerInspect(context.Background(), req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file, err := os.OpenFile(container.LogPath, os.O_RDWR|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
if err = file.Truncate(0); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = file.Seek(0, 0)
|
||||
|
||||
files, _ := filepath.Glob(fmt.Sprintf("%s.*", container.LogPath))
|
||||
for _, file := range files {
|
||||
_ = os.Remove(file)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerStats(id string) (*dto.ContainterStats, error) {
|
||||
func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, container, since, tail string, follow bool) error {
|
||||
command := fmt.Sprintf("docker logs %s", container)
|
||||
if tail != "0" {
|
||||
command += " -n " + tail
|
||||
}
|
||||
if since != "all" {
|
||||
command += " --since " + since
|
||||
}
|
||||
if follow {
|
||||
command += " -f"
|
||||
}
|
||||
command += " 2>&1"
|
||||
cmd := exec.Command("bash", "-c", command)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buffer := make([]byte, 1024)
|
||||
for {
|
||||
n, err := stdout.Read(buffer)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
global.LOG.Errorf("read bytes from container log failed, err: %v", err)
|
||||
continue
|
||||
}
|
||||
if err = wsConn.WriteMessage(websocket.TextMessage, buffer[:n]); err != nil {
|
||||
global.LOG.Errorf("send message with container log to ws failed, err: %v", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error) {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -296,7 +611,7 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainterStats, erro
|
||||
if err := json.Unmarshal(body, &stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data dto.ContainterStats
|
||||
var data dto.ContainerStats
|
||||
data.CPUPercent = calculateCPUPercentUnix(stats)
|
||||
data.IORead, data.IOWrite = calculateBlockIO(stats.BlkioStats)
|
||||
data.Memory = float64(stats.MemoryStats.Usage) / 1024 / 1024
|
||||
@@ -460,3 +775,39 @@ func checkPortStats(ports []dto.PortHelper) (nat.PortMap, error) {
|
||||
}
|
||||
return portMap, nil
|
||||
}
|
||||
|
||||
func loadConfigInfo(req dto.ContainerOperate, config *container.Config, hostConf *container.HostConfig, networkConf *network.NetworkingConfig) error {
|
||||
portMap, err := checkPortStats(req.ExposedPorts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
exposeds := make(nat.PortSet)
|
||||
for port := range portMap {
|
||||
exposeds[port] = struct{}{}
|
||||
}
|
||||
config.Image = req.Image
|
||||
config.Cmd = req.Cmd
|
||||
config.Env = req.Env
|
||||
config.Labels = stringsToMap(req.Labels)
|
||||
config.ExposedPorts = exposeds
|
||||
|
||||
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: {}}
|
||||
|
||||
hostConf.AutoRemove = req.AutoRemove
|
||||
hostConf.CPUShares = req.CPUShares
|
||||
hostConf.PublishAllPorts = req.PublishAllPorts
|
||||
hostConf.RestartPolicy = container.RestartPolicy{Name: req.RestartPolicy}
|
||||
if req.RestartPolicy == "on-failure" {
|
||||
hostConf.RestartPolicy.MaximumRetryCount = 5
|
||||
}
|
||||
hostConf.NanoCPUs = int64(req.NanoCPUs * 1000000000)
|
||||
hostConf.Memory = int64(req.Memory * 1024 * 1024)
|
||||
hostConf.PortBindings = portMap
|
||||
hostConf.Binds = []string{}
|
||||
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))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -92,7 +92,7 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
||||
}
|
||||
}
|
||||
for _, item := range composeCreatedByLocal {
|
||||
if err := composeRepo.DeleteRecord(commonRepo.WithByName(item.Name)); err != nil {
|
||||
if err := composeRepo.DeleteRecord(commonRepo.WithByID(item.ID)); err != nil {
|
||||
global.LOG.Error(err)
|
||||
}
|
||||
}
|
||||
@@ -101,11 +101,11 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
||||
records = append(records, value)
|
||||
}
|
||||
if len(req.Info) != 0 {
|
||||
lenth, count := len(records), 0
|
||||
for count < lenth {
|
||||
length, count := len(records), 0
|
||||
for count < length {
|
||||
if !strings.Contains(records[count].Name, req.Info) {
|
||||
records = append(records[:count], records[(count+1):]...)
|
||||
lenth--
|
||||
length--
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
@@ -127,6 +127,10 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
||||
}
|
||||
|
||||
func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) {
|
||||
composeItem, _ := composeRepo.GetRecord(commonRepo.WithByName(req.Name))
|
||||
if composeItem.ID != 0 {
|
||||
return false, constant.ErrRecordExist
|
||||
}
|
||||
if err := u.loadPath(&req); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@@ -24,11 +24,11 @@ func (u *ContainerService) PageNetwork(req dto.SearchWithPage) (int64, interface
|
||||
return 0, nil, err
|
||||
}
|
||||
if len(req.Info) != 0 {
|
||||
lenth, count := len(list), 0
|
||||
for count < lenth {
|
||||
length, count := len(list), 0
|
||||
for count < length {
|
||||
if !strings.Contains(list[count].Name, req.Info) {
|
||||
list = append(list[:count], list[(count+1):]...)
|
||||
lenth--
|
||||
length--
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
@@ -75,6 +75,26 @@ func (u *ContainerService) PageNetwork(req dto.SearchWithPage) (int64, interface
|
||||
|
||||
return int64(total), data, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ListNetwork() ([]dto.Options, error) {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list, err := client.NetworkList(context.TODO(), types.NetworkListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var datas []dto.Options
|
||||
for _, item := range list {
|
||||
datas = append(datas, dto.Options{Option: item.Name})
|
||||
}
|
||||
sort.Slice(datas, func(i, j int) bool {
|
||||
return datas[i].Option < datas[j].Option
|
||||
})
|
||||
return datas, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) DeleteNetwork(req dto.BatchDelete) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
@@ -90,7 +110,7 @@ func (u *ContainerService) DeleteNetwork(req dto.BatchDelete) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (u *ContainerService) CreateNetwork(req dto.NetworkCreat) error {
|
||||
func (u *ContainerService) CreateNetwork(req dto.NetworkCreate) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@@ -24,11 +24,11 @@ func (u *ContainerService) PageVolume(req dto.SearchWithPage) (int64, interface{
|
||||
return 0, nil, err
|
||||
}
|
||||
if len(req.Info) != 0 {
|
||||
lenth, count := len(list.Volumes), 0
|
||||
for count < lenth {
|
||||
length, count := len(list.Volumes), 0
|
||||
for count < length {
|
||||
if !strings.Contains(list.Volumes[count].Name, req.Info) {
|
||||
list.Volumes = append(list.Volumes[:count], list.Volumes[(count+1):]...)
|
||||
lenth--
|
||||
length--
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
@@ -80,13 +80,16 @@ func (u *ContainerService) ListVolume() ([]dto.Options, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data []dto.Options
|
||||
var datas []dto.Options
|
||||
for _, item := range list.Volumes {
|
||||
data = append(data, dto.Options{
|
||||
datas = append(datas, dto.Options{
|
||||
Option: item.Name,
|
||||
})
|
||||
}
|
||||
return data, nil
|
||||
sort.Slice(datas, func(i, j int) bool {
|
||||
return datas[i].Option < datas[j].Option
|
||||
})
|
||||
return datas, nil
|
||||
}
|
||||
func (u *ContainerService) DeleteVolume(req dto.BatchDelete) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
@@ -103,7 +106,7 @@ func (u *ContainerService) DeleteVolume(req dto.BatchDelete) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (u *ContainerService) CreateVolume(req dto.VolumeCreat) error {
|
||||
func (u *ContainerService) CreateVolume(req dto.VolumeCreate) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@@ -36,7 +36,7 @@ func NewICronjobService() ICronjobService {
|
||||
}
|
||||
|
||||
func (u *CronjobService) SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error) {
|
||||
total, cronjobs, err := cronjobRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info))
|
||||
total, cronjobs, err := cronjobRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info), commonRepo.WithOrderRuleBy(search.OrderBy, search.Order))
|
||||
var dtoCronjobs []dto.CronjobInfo
|
||||
for _, cronjob := range cronjobs {
|
||||
var item dto.CronjobInfo
|
||||
@@ -53,9 +53,9 @@ func (u *CronjobService) SearchWithPage(search dto.SearchWithPage) (int64, inter
|
||||
}
|
||||
record, _ := cronjobRepo.RecordFirst(cronjob.ID)
|
||||
if record.ID != 0 {
|
||||
item.LastRecrodTime = record.StartTime.Format("2006-01-02 15:04:05")
|
||||
item.LastRecordTime = record.StartTime.Format("2006-01-02 15:04:05")
|
||||
} else {
|
||||
item.LastRecrodTime = "-"
|
||||
item.LastRecordTime = "-"
|
||||
}
|
||||
dtoCronjobs = append(dtoCronjobs, item)
|
||||
}
|
||||
@@ -85,7 +85,7 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req.CleanData && cronjob.Type != "shell" && cronjob.Type != "curl" {
|
||||
if req.CleanData && (cronjob.Type == "database" || cronjob.Type == "website" || cronjob.Type == "directory") {
|
||||
cronjob.RetainCopies = 0
|
||||
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
|
||||
if err != nil {
|
||||
@@ -100,9 +100,9 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, localDir, &cronjob, client)
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
|
||||
} else {
|
||||
u.HandleRmExpired(backup.Type, "", &cronjob, nil)
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, "", &cronjob, nil)
|
||||
}
|
||||
}
|
||||
delRecords, err := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(req.CronjobID)))
|
||||
@@ -133,7 +133,7 @@ func (u *CronjobService) Download(down dto.CronjobDownload) (string, error) {
|
||||
}
|
||||
if backup.Type == "LOCAL" || record.FromLocal {
|
||||
if _, err := os.Stat(record.File); err != nil && os.IsNotExist(err) {
|
||||
return "", constant.ErrRecordNotFound
|
||||
return "", err
|
||||
}
|
||||
return record.File, nil
|
||||
}
|
||||
@@ -145,7 +145,7 @@ func (u *CronjobService) Download(down dto.CronjobDownload) (string, error) {
|
||||
_ = os.MkdirAll(path.Dir(tempPath), os.ModePerm)
|
||||
isOK, err := client.Download(record.File, tempPath)
|
||||
if !isOK || err != nil {
|
||||
return "", constant.ErrRecordNotFound
|
||||
return "", err
|
||||
}
|
||||
return tempPath, nil
|
||||
}
|
||||
@@ -190,7 +190,6 @@ func (u *CronjobService) StartJob(cronjob *model.Cronjob) (int, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
global.LOG.Debug(global.Cron.Entries())
|
||||
return entryID, nil
|
||||
}
|
||||
|
||||
@@ -222,24 +221,30 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
|
||||
if err != nil {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
upMap := make(map[string]interface{})
|
||||
cronjob.EntryID = cronModel.EntryID
|
||||
cronjob.Type = cronModel.Type
|
||||
cronjob.Spec = loadSpec(cronjob)
|
||||
newEntryID, err := u.StartJob(&cronjob)
|
||||
if err != nil {
|
||||
return err
|
||||
if cronModel.Status == constant.StatusEnable {
|
||||
newEntryID, err := u.StartJob(&cronjob)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
upMap["entry_id"] = newEntryID
|
||||
} else {
|
||||
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
|
||||
}
|
||||
|
||||
upMap := make(map[string]interface{})
|
||||
upMap["entry_id"] = newEntryID
|
||||
upMap["name"] = req.Name
|
||||
upMap["spec"] = cronjob.Spec
|
||||
upMap["script"] = req.Script
|
||||
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["website"] = req.Website
|
||||
upMap["exclusion_rules"] = req.ExclusionRules
|
||||
upMap["db_name"] = req.DBName
|
||||
@@ -322,6 +327,8 @@ func loadSpec(cronjob model.Cronjob) string {
|
||||
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 ""
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
@@ -14,6 +15,8 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -29,31 +32,35 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
if len(cronjob.Script) == 0 {
|
||||
return
|
||||
}
|
||||
stdout, errExec := cmd.ExecCronjobWithTimeOut(cronjob.Script, 5*time.Minute)
|
||||
if errExec != nil {
|
||||
err = errExec
|
||||
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)
|
||||
}
|
||||
message = []byte(stdout)
|
||||
u.HandleRmExpired("LOCAL", "", cronjob, nil)
|
||||
case "website":
|
||||
record.File, err = u.HandleBackup(cronjob, record.StartTime)
|
||||
case "database":
|
||||
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.HandleRmExpired("LOCAL", "", "", cronjob, nil)
|
||||
case "curl":
|
||||
if len(cronjob.URL) == 0 {
|
||||
return
|
||||
}
|
||||
stdout, errCurl := cmd.ExecWithTimeOut("curl "+cronjob.URL, 5*time.Minute)
|
||||
if err != nil {
|
||||
err = errCurl
|
||||
message, err = u.handleShell(cronjob.Type, cronjob.Name, fmt.Sprintf("curl '%s'", cronjob.URL))
|
||||
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
|
||||
case "ntp":
|
||||
err = u.handleNtpSync()
|
||||
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
|
||||
case "website":
|
||||
record.File, err = u.handleBackup(cronjob, record.StartTime)
|
||||
case "database":
|
||||
record.File, err = u.handleBackup(cronjob, record.StartTime)
|
||||
case "directory":
|
||||
if len(cronjob.SourceDir) == 0 {
|
||||
return
|
||||
}
|
||||
record.File, err = u.handleBackup(cronjob, record.StartTime)
|
||||
case "cutWebsiteLog":
|
||||
record.File, err = u.handleCutWebsiteLog(cronjob, record.StartTime)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("cut website log file failed, err: %v", err)
|
||||
}
|
||||
message = []byte(stdout)
|
||||
u.HandleRmExpired("LOCAL", "", cronjob, nil)
|
||||
}
|
||||
if err != nil {
|
||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), string(message))
|
||||
@@ -69,7 +76,36 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
}()
|
||||
}
|
||||
|
||||
func (u *CronjobService) HandleBackup(cronjob *model.Cronjob, startTime time.Time) (string, error) {
|
||||
func (u *CronjobService) handleShell(cronType, cornName, script string) ([]byte, 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
|
||||
}
|
||||
}
|
||||
stdout, err := cmd.ExecCronjobWithTimeOut(script, handleDir, 24*time.Hour)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(stdout), nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleNtpSync() error {
|
||||
ntpServer, err := settingRepo.Get(settingRepo.WithByKey("NtpSite"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ntime, err := ntp.GetRemoteTime(ntpServer.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ntp.UpdateSystemTime(ntime.Format("2006-01-02 15:04:05")); err != nil {
|
||||
return err
|
||||
}
|
||||
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
|
||||
@@ -110,37 +146,55 @@ func (u *CronjobService) HandleBackup(cronjob *model.Cronjob, startTime time.Tim
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(backup.BackupPath) != 0 {
|
||||
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
|
||||
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
|
||||
itemFileDir = itemPath + itemFileDir
|
||||
}
|
||||
if _, err = client.Upload(backupDir+"/"+fileName, itemFileDir+"/"+fileName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, localDir, cronjob, client)
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, cronjob, client)
|
||||
if backup.Type == "LOCAL" || cronjob.KeepLocal {
|
||||
return fmt.Sprintf("%s/%s/%s/%s", localDir, cronjob.Type, cronjob.Name, fileName), nil
|
||||
return fmt.Sprintf("%s/%s", backupDir, fileName), nil
|
||||
} else {
|
||||
return fmt.Sprintf("%s/%s", itemFileDir, fileName), nil
|
||||
}
|
||||
return fmt.Sprintf("%s/%s/%s", cronjob.Type, cronjob.Name, fileName), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (u *CronjobService) HandleRmExpired(backType, localDir string, cronjob *model.Cronjob, backClient cloud_storage.CloudStorageClient) {
|
||||
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) {
|
||||
for i := int(cronjob.RetainCopies); i < len(records); i++ {
|
||||
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 {
|
||||
if backType != "LOCAL" {
|
||||
_, _ = backClient.Delete(strings.ReplaceAll(file, localDir+"/", ""))
|
||||
_ = os.Remove(file)
|
||||
} else {
|
||||
_ = os.Remove(file)
|
||||
}
|
||||
_ = os.Remove(file)
|
||||
_ = backupRepo.DeleteRecord(context.TODO(), backupRepo.WithByFileName(path.Base(file)))
|
||||
}
|
||||
if backType == "LOCAL" {
|
||||
continue
|
||||
}
|
||||
|
||||
_ = cronjobRepo.DeleteRecord(commonRepo.WithByID(uint(records[i].ID)))
|
||||
_ = os.Remove(records[i].Records)
|
||||
fileItem := file
|
||||
if cronjob.KeepLocal {
|
||||
if len(backupPath) != 0 {
|
||||
itemPath := strings.TrimPrefix(backupPath, "/")
|
||||
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
|
||||
fileItem = itemPath + strings.TrimPrefix(file, localDir+"/")
|
||||
} else {
|
||||
fileItem = strings.TrimPrefix(file, localDir+"/")
|
||||
}
|
||||
}
|
||||
_, _ = backClient.Delete(fileItem)
|
||||
}
|
||||
}
|
||||
_ = cronjobRepo.DeleteRecord(commonRepo.WithByID(uint(records[i].ID)))
|
||||
_ = os.Remove(records[i].Records)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,18 +205,21 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
}
|
||||
}
|
||||
|
||||
excludes := strings.Split(exclusionRules, ";")
|
||||
excludes := strings.Split(exclusionRules, ",")
|
||||
excludeRules := ""
|
||||
for _, exclude := range excludes {
|
||||
if len(exclude) == 0 {
|
||||
continue
|
||||
}
|
||||
excludeRules += (" --exclude " + exclude)
|
||||
excludeRules += " --exclude " + exclude
|
||||
}
|
||||
path := ""
|
||||
if strings.Contains(sourceDir, "/") {
|
||||
itemDir := strings.ReplaceAll(sourceDir[strings.LastIndex(sourceDir, "/"):], "/", "")
|
||||
aheadDir := strings.ReplaceAll(sourceDir, itemDir, "")
|
||||
aheadDir := sourceDir[:strings.LastIndex(sourceDir, "/")]
|
||||
if len(aheadDir) == 0 {
|
||||
aheadDir = "/"
|
||||
}
|
||||
path += fmt.Sprintf("-C %s %s", aheadDir, itemDir)
|
||||
} else {
|
||||
path = sourceDir
|
||||
@@ -170,7 +227,7 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
|
||||
commands := fmt.Sprintf("tar zcvf %s %s %s", targetDir+"/"+name, excludeRules, path)
|
||||
global.LOG.Debug(commands)
|
||||
stdout, err := cmd.ExecWithTimeOut(commands, 5*time.Minute)
|
||||
stdout, err := cmd.ExecWithTimeOut(commands, 24*time.Hour)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
|
||||
return errors.New(stdout)
|
||||
@@ -187,7 +244,7 @@ func handleUnTar(sourceFile, targetDir string) error {
|
||||
|
||||
commands := fmt.Sprintf("tar zxvfC %s %s", sourceFile, targetDir)
|
||||
global.LOG.Debug(commands)
|
||||
stdout, err := cmd.ExecWithTimeOut(commands, 5*time.Minute)
|
||||
stdout, err := cmd.ExecWithTimeOut(commands, 24*time.Hour)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do handle untar failed, stdout: %s, err: %v", stdout, err)
|
||||
return errors.New(stdout)
|
||||
@@ -236,12 +293,11 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, app *repo.RootInf
|
||||
}
|
||||
record.DetailName = dbName
|
||||
record.FileDir = backupDir
|
||||
itemFileDir := strings.ReplaceAll(backupDir, localDir+"/", "")
|
||||
itemFileDir := strings.TrimPrefix(backupDir, localDir+"/")
|
||||
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
|
||||
record.Source = backup.Type
|
||||
record.FileDir = itemFileDir
|
||||
}
|
||||
paths = append(paths, fmt.Sprintf("%s/%s", record.FileDir, record.FileName))
|
||||
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
@@ -253,15 +309,91 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, app *repo.RootInf
|
||||
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, record.FileName))
|
||||
}()
|
||||
}
|
||||
if len(backup.BackupPath) != 0 {
|
||||
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
|
||||
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
|
||||
itemFileDir = itemPath + 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, localDir, &cronjob, client)
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime time.Time) (string, error) {
|
||||
var (
|
||||
websites []string
|
||||
err error
|
||||
filePaths []string
|
||||
)
|
||||
if cronjob.Website == "all" {
|
||||
websites, _ = NewIWebsiteService().GetWebsiteOptions()
|
||||
if len(websites) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
} else {
|
||||
websites = append(websites, cronjob.Website)
|
||||
}
|
||||
|
||||
nginx, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return "", 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.PrimaryDomain, "log")
|
||||
srcAccessLogPath := path.Join(websiteLogDir, "access.log")
|
||||
srcErrorLogPath := path.Join(websiteLogDir, "error.log")
|
||||
dstLogDir := path.Join(global.CONF.System.Backup, "log", "website", website.PrimaryDomain)
|
||||
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
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
|
||||
var paths []string
|
||||
localDir, err := loadLocalDir()
|
||||
@@ -299,13 +431,12 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.Backu
|
||||
}
|
||||
backupDir := fmt.Sprintf("%s/website/%s", localDir, website.PrimaryDomain)
|
||||
record.FileDir = backupDir
|
||||
itemFileDir := strings.ReplaceAll(backupDir, localDir+"/", "")
|
||||
itemFileDir := strings.TrimPrefix(backupDir, localDir+"/")
|
||||
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
|
||||
record.Source = backup.Type
|
||||
record.FileDir = strings.ReplaceAll(backupDir, localDir+"/", "")
|
||||
record.FileDir = strings.TrimPrefix(backupDir, localDir+"/")
|
||||
}
|
||||
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
|
||||
paths = append(paths, fmt.Sprintf("%s/%s", record.FileDir, record.FileName))
|
||||
if err := handleWebsiteBackup(&website, backupDir, record.FileName); err != nil {
|
||||
return paths, err
|
||||
}
|
||||
@@ -320,11 +451,21 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.Backu
|
||||
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, record.FileName))
|
||||
}()
|
||||
}
|
||||
if len(backup.BackupPath) != 0 {
|
||||
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
|
||||
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
|
||||
itemFileDir = itemPath + 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, localDir, &cronjob, client)
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
|
||||
return paths, nil
|
||||
}
|
||||
|
@@ -14,7 +14,6 @@ import (
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
@@ -33,7 +32,7 @@ type IMysqlService interface {
|
||||
Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error)
|
||||
ChangeAccess(info dto.ChangeDBInfo) error
|
||||
ChangePassword(info dto.ChangeDBInfo) error
|
||||
UpdateVariables(updatas []dto.MysqlVariablesUpdate) error
|
||||
UpdateVariables(updates []dto.MysqlVariablesUpdate) error
|
||||
UpdateConfByFile(info dto.MysqlConfUpdateByFile) error
|
||||
UpdateDescription(req dto.UpdateDescription) error
|
||||
DeleteCheck(id uint) ([]string, error)
|
||||
@@ -49,7 +48,7 @@ func NewIMysqlService() IMysqlService {
|
||||
}
|
||||
|
||||
func (u *MysqlService) SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error) {
|
||||
total, mysqls, err := mysqlRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info))
|
||||
total, mysqls, err := mysqlRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info), commonRepo.WithOrderRuleBy(search.OrderBy, search.Order))
|
||||
var dtoMysqls []dto.MysqlDBInfo
|
||||
for _, mysql := range mysqls {
|
||||
var item dto.MysqlDBInfo
|
||||
@@ -100,7 +99,7 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if err := u.createUser(app, req); err != nil {
|
||||
if err := u.createUser(app.ContainerName, app.Password, app.Version, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -149,8 +148,14 @@ func (u *MysqlService) Delete(ctx context.Context, req dto.MysqlDBDelete) error
|
||||
return err
|
||||
}
|
||||
|
||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists '%s'@'%s'", db.Username, db.Permission)); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
if strings.HasPrefix(app.Version, "5.6") {
|
||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop user '%s'@'%s'", db.Username, db.Permission)); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists '%s'@'%s'", db.Username, db.Permission)); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop database if exists `%s`", db.Name)); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
@@ -195,7 +200,7 @@ func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error {
|
||||
}
|
||||
|
||||
passwordChangeCMD := fmt.Sprintf("set password for '%s'@'%s' = password('%s')", mysql.Username, mysql.Permission, info.Value)
|
||||
if app.Version != "5.7.39" {
|
||||
if !strings.HasPrefix(app.Version, "5.7") && !strings.HasPrefix(app.Version, "5.6") {
|
||||
passwordChangeCMD = fmt.Sprintf("ALTER USER '%s'@'%s' IDENTIFIED WITH mysql_native_password BY '%s';", mysql.Username, mysql.Permission, info.Value)
|
||||
}
|
||||
if info.ID != 0 {
|
||||
@@ -230,7 +235,7 @@ func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error {
|
||||
for _, host := range hosts {
|
||||
if host == "%" || host == "localhost" {
|
||||
passwordRootChangeCMD := fmt.Sprintf("set password for 'root'@'%s' = password('%s')", host, info.Value)
|
||||
if app.Version != "5.7.39" {
|
||||
if !strings.HasPrefix(app.Version, "5.7") && !strings.HasPrefix(app.Version, "5.6") {
|
||||
passwordRootChangeCMD = fmt.Sprintf("alter user 'root'@'%s' identified with mysql_native_password BY '%s';", host, info.Value)
|
||||
}
|
||||
if err := excuteSql(app.ContainerName, app.Password, passwordRootChangeCMD); err != nil {
|
||||
@@ -281,8 +286,14 @@ func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error {
|
||||
}
|
||||
for _, user := range userlist {
|
||||
if len(user) != 0 {
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists '%s'@'%s'", mysql.Username, user)); err != nil {
|
||||
return err
|
||||
if strings.HasPrefix(app.Version, "5.6") {
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("drop user '%s'@'%s'", mysql.Username, user)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists '%s'@'%s'", mysql.Username, user)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,7 +302,7 @@ func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := u.createUser(app, dto.MysqlDBCreate{
|
||||
if err := u.createUser(app.ContainerName, app.Password, app.Version, dto.MysqlDBCreate{
|
||||
Username: mysql.Username,
|
||||
Name: mysql.Name,
|
||||
Permission: info.Value,
|
||||
@@ -331,7 +342,7 @@ func (u *MysqlService) UpdateConfByFile(info dto.MysqlConfUpdateByFile) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) UpdateVariables(updatas []dto.MysqlVariablesUpdate) error {
|
||||
func (u *MysqlService) UpdateVariables(updates []dto.MysqlVariablesUpdate) error {
|
||||
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -346,8 +357,8 @@ func (u *MysqlService) UpdateVariables(updatas []dto.MysqlVariablesUpdate) error
|
||||
files = strings.Split(string(lineBytes), "\n")
|
||||
|
||||
group := "[mysqld]"
|
||||
for _, info := range updatas {
|
||||
if app.Version != "5.7.39" {
|
||||
for _, info := range updates {
|
||||
if !strings.HasPrefix(app.Version, "5.7") && !strings.HasPrefix(app.Version, "5.6") {
|
||||
if info.Param == "query_cache_size" {
|
||||
continue
|
||||
}
|
||||
@@ -470,7 +481,7 @@ func (u *MysqlService) LoadStatus() (*dto.MysqlStatus, error) {
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) createUser(app *repo.RootInfo, req dto.MysqlDBCreate) error {
|
||||
func (u *MysqlService) createUser(container, password, version string, req dto.MysqlDBCreate) error {
|
||||
var userlist []string
|
||||
if strings.Contains(req.Permission, ",") {
|
||||
ips := strings.Split(req.Permission, ",")
|
||||
@@ -484,32 +495,35 @@ func (u *MysqlService) createUser(app *repo.RootInfo, req dto.MysqlDBCreate) err
|
||||
}
|
||||
|
||||
for _, user := range userlist {
|
||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("create user %s identified by '%s';", user, req.Password)); err != nil {
|
||||
handleCreateError(req.Name, userlist, app)
|
||||
if err := excSQL(container, password, fmt.Sprintf("create user %s identified by '%s';", user, req.Password)); err != nil {
|
||||
if strings.Contains(err.Error(), "ERROR 1396") {
|
||||
handleCreateError(container, password, req.Name, userlist, false)
|
||||
return buserr.New(constant.ErrUserIsExist)
|
||||
}
|
||||
handleCreateError(container, password, req.Name, userlist, true)
|
||||
return err
|
||||
}
|
||||
grantStr := fmt.Sprintf("grant all privileges on `%s`.* to %s", req.Name, user)
|
||||
if req.Name == "*" {
|
||||
grantStr = fmt.Sprintf("grant all privileges on *.* to %s", user)
|
||||
}
|
||||
if app.Version == "5.7.39" {
|
||||
if strings.HasPrefix(version, "5.7") || strings.HasPrefix(version, "5.6") {
|
||||
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, req.Password)
|
||||
}
|
||||
if err := excSQL(app.ContainerName, app.Password, grantStr); err != nil {
|
||||
handleCreateError(req.Name, userlist, app)
|
||||
if err := excSQL(container, password, grantStr); err != nil {
|
||||
handleCreateError(container, password, req.Name, userlist, true)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func handleCreateError(dbName string, userlist []string, app *repo.RootInfo) {
|
||||
_ = excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop database `%s`", dbName))
|
||||
for _, user := range userlist {
|
||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists %s", user)); err != nil {
|
||||
global.LOG.Errorf("drop user failed, err: %v", err)
|
||||
func handleCreateError(contaienr, password, dbName string, userlist []string, dropUser bool) {
|
||||
_ = excSQL(contaienr, password, fmt.Sprintf("drop database `%s`", dbName))
|
||||
if dropUser {
|
||||
for _, user := range userlist {
|
||||
if err := excSQL(contaienr, password, fmt.Sprintf("drop user if exists %s", user)); err != nil {
|
||||
global.LOG.Errorf("drop user failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
@@ -18,7 +19,8 @@ import (
|
||||
type DockerService struct{}
|
||||
|
||||
type IDockerService interface {
|
||||
UpdateConf(req dto.DaemonJsonConf) error
|
||||
UpdateConf(req dto.SettingUpdate) error
|
||||
UpdateLogOption(req dto.LogOption) error
|
||||
UpdateConfByFile(info dto.DaemonJsonUpdateByFile) error
|
||||
LoadDockerStatus() string
|
||||
LoadDockerConf() *dto.DaemonJsonConf
|
||||
@@ -30,46 +32,54 @@ func NewIDockerService() IDockerService {
|
||||
}
|
||||
|
||||
type daemonJsonItem struct {
|
||||
Status string `json:"status"`
|
||||
Mirrors []string `json:"registry-mirrors"`
|
||||
Registries []string `json:"insecure-registries"`
|
||||
LiveRestore bool `json:"live-restore"`
|
||||
IPTables bool `json:"iptables"`
|
||||
ExecOpts []string `json:"exec-opts"`
|
||||
Status string `json:"status"`
|
||||
Mirrors []string `json:"registry-mirrors"`
|
||||
Registries []string `json:"insecure-registries"`
|
||||
LiveRestore bool `json:"live-restore"`
|
||||
IPTables bool `json:"iptables"`
|
||||
ExecOpts []string `json:"exec-opts"`
|
||||
LogOption logOption `json:"log-opts"`
|
||||
}
|
||||
type logOption struct {
|
||||
LogMaxSize string `json:"max-size"`
|
||||
LogMaxFile string `json:"max-file"`
|
||||
}
|
||||
|
||||
func (u *DockerService) LoadDockerStatus() string {
|
||||
status := constant.StatusRunning
|
||||
stdout, err := cmd.Exec("systemctl is-active docker")
|
||||
if string(stdout) != "active\n" || err != nil {
|
||||
status = constant.Stopped
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return constant.Stopped
|
||||
}
|
||||
if _, err := client.Ping(context.Background()); err != nil {
|
||||
return constant.Stopped
|
||||
}
|
||||
|
||||
return status
|
||||
return constant.StatusRunning
|
||||
}
|
||||
|
||||
func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
|
||||
ctx := context.Background()
|
||||
var data dto.DaemonJsonConf
|
||||
data.IPTables = true
|
||||
data.Status = constant.StatusRunning
|
||||
stdout, err := cmd.Exec("systemctl is-active docker")
|
||||
if string(stdout) != "active\n" || err != nil {
|
||||
data.Version = "-"
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
data.Status = constant.Stopped
|
||||
} else {
|
||||
if _, err := client.Ping(ctx); err != nil {
|
||||
data.Status = constant.Stopped
|
||||
}
|
||||
itemVersion, err := client.ServerVersion(ctx)
|
||||
if err == nil {
|
||||
data.Version = itemVersion.Version
|
||||
}
|
||||
}
|
||||
data.IsSwarm = false
|
||||
stdout2, _ := cmd.Exec("docker info | grep Swarm")
|
||||
if string(stdout2) == " Swarm: active\n" {
|
||||
data.IsSwarm = true
|
||||
}
|
||||
data.Version = "-"
|
||||
client, err := docker.NewDockerClient()
|
||||
if err == nil {
|
||||
ctx := context.Background()
|
||||
itemVersion, err := client.ServerVersion(ctx)
|
||||
if err == nil {
|
||||
data.Version = itemVersion.Version
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(constant.DaemonJsonPath); err != nil {
|
||||
return &data
|
||||
}
|
||||
@@ -78,18 +88,19 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
|
||||
return &data
|
||||
}
|
||||
var conf daemonJsonItem
|
||||
deamonMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal(file, &deamonMap); err != nil {
|
||||
daemonMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal(file, &daemonMap); err != nil {
|
||||
return &data
|
||||
}
|
||||
arr, err := json.Marshal(deamonMap)
|
||||
arr, err := json.Marshal(daemonMap)
|
||||
if err != nil {
|
||||
return &data
|
||||
}
|
||||
if err := json.Unmarshal(arr, &conf); err != nil {
|
||||
fmt.Println(err)
|
||||
return &data
|
||||
}
|
||||
if _, ok := deamonMap["iptables"]; !ok {
|
||||
if _, ok := daemonMap["iptables"]; !ok {
|
||||
conf.IPTables = true
|
||||
}
|
||||
data.CgroupDriver = "cgroupfs"
|
||||
@@ -99,6 +110,8 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
|
||||
break
|
||||
}
|
||||
}
|
||||
data.LogMaxSize = conf.LogOption.LogMaxSize
|
||||
data.LogMaxFile = conf.LogOption.LogMaxFile
|
||||
data.Mirrors = conf.Mirrors
|
||||
data.Registries = conf.Registries
|
||||
data.IPTables = conf.IPTables
|
||||
@@ -106,7 +119,7 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
|
||||
return &data
|
||||
}
|
||||
|
||||
func (u *DockerService) UpdateConf(req dto.DaemonJsonConf) error {
|
||||
func (u *DockerService) UpdateConf(req dto.SettingUpdate) error {
|
||||
if _, err := os.Stat(constant.DaemonJsonPath); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(path.Dir(constant.DaemonJsonPath), os.ModePerm); err != nil {
|
||||
return err
|
||||
@@ -118,50 +131,98 @@ func (u *DockerService) UpdateConf(req dto.DaemonJsonConf) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deamonMap := make(map[string]interface{})
|
||||
_ = json.Unmarshal(file, &deamonMap)
|
||||
daemonMap := make(map[string]interface{})
|
||||
_ = json.Unmarshal(file, &daemonMap)
|
||||
|
||||
if len(req.Registries) == 0 {
|
||||
delete(deamonMap, "insecure-registries")
|
||||
} else {
|
||||
deamonMap["insecure-registries"] = req.Registries
|
||||
}
|
||||
if len(req.Mirrors) == 0 {
|
||||
delete(deamonMap, "registry-mirrors")
|
||||
} else {
|
||||
deamonMap["registry-mirrors"] = req.Mirrors
|
||||
}
|
||||
if !req.LiveRestore {
|
||||
delete(deamonMap, "live-restore")
|
||||
} else {
|
||||
deamonMap["live-restore"] = req.LiveRestore
|
||||
}
|
||||
if req.IPTables {
|
||||
delete(deamonMap, "iptables")
|
||||
} else {
|
||||
deamonMap["iptables"] = false
|
||||
}
|
||||
if opts, ok := deamonMap["exec-opts"]; ok {
|
||||
if optsValue, isArray := opts.([]interface{}); isArray {
|
||||
for i := 0; i < len(optsValue); i++ {
|
||||
if opt, isStr := optsValue[i].(string); isStr {
|
||||
if strings.HasPrefix(opt, "native.cgroupdriver=") {
|
||||
optsValue[i] = "native.cgroupdriver=" + req.CgroupDriver
|
||||
break
|
||||
switch req.Key {
|
||||
case "Registries":
|
||||
req.Value = strings.TrimRight(req.Value, ",")
|
||||
if len(req.Value) == 0 {
|
||||
delete(daemonMap, "insecure-registries")
|
||||
} else {
|
||||
daemonMap["insecure-registries"] = strings.Split(req.Value, ",")
|
||||
}
|
||||
case "Mirrors":
|
||||
req.Value = strings.TrimRight(req.Value, ",")
|
||||
if len(req.Value) == 0 {
|
||||
delete(daemonMap, "registry-mirrors")
|
||||
} else {
|
||||
daemonMap["registry-mirrors"] = strings.Split(req.Value, ",")
|
||||
}
|
||||
case "LogOption":
|
||||
if req.Value == "disable" {
|
||||
delete(daemonMap, "log-opts")
|
||||
}
|
||||
case "LiveRestore":
|
||||
if req.Value == "disable" {
|
||||
delete(daemonMap, "live-restore")
|
||||
} else {
|
||||
daemonMap["live-restore"] = true
|
||||
}
|
||||
case "IPtables":
|
||||
if req.Value == "enable" {
|
||||
delete(daemonMap, "iptables")
|
||||
} else {
|
||||
daemonMap["iptables"] = false
|
||||
}
|
||||
case "Dirver":
|
||||
if opts, ok := daemonMap["exec-opts"]; ok {
|
||||
if optsValue, isArray := opts.([]interface{}); isArray {
|
||||
for i := 0; i < len(optsValue); i++ {
|
||||
if opt, isStr := optsValue[i].(string); isStr {
|
||||
if strings.HasPrefix(opt, "native.cgroupdriver=") {
|
||||
optsValue[i] = "native.cgroupdriver=" + req.Value
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if req.CgroupDriver == "systemd" {
|
||||
deamonMap["exec-opts"] = []string{"native.cgroupdriver=systemd"}
|
||||
} else {
|
||||
if req.Value == "systemd" {
|
||||
daemonMap["exec-opts"] = []string{"native.cgroupdriver=systemd"}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(deamonMap) == 0 {
|
||||
if len(daemonMap) == 0 {
|
||||
_ = os.Remove(constant.DaemonJsonPath)
|
||||
return nil
|
||||
}
|
||||
newJson, err := json.MarshalIndent(deamonMap, "", "\t")
|
||||
newJson, err := json.MarshalIndent(daemonMap, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(constant.DaemonJsonPath, newJson, 0640); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stdout, err := cmd.Exec("systemctl restart docker")
|
||||
if err != nil {
|
||||
return errors.New(string(stdout))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *DockerService) UpdateLogOption(req dto.LogOption) error {
|
||||
if _, err := os.Stat(constant.DaemonJsonPath); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(path.Dir(constant.DaemonJsonPath), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = os.Create(constant.DaemonJsonPath)
|
||||
}
|
||||
|
||||
file, err := os.ReadFile(constant.DaemonJsonPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
daemonMap := make(map[string]interface{})
|
||||
_ = json.Unmarshal(file, &daemonMap)
|
||||
|
||||
changeLogOption(daemonMap, req.LogMaxFile, req.LogMaxSize)
|
||||
if len(daemonMap) == 0 {
|
||||
_ = os.Remove(constant.DaemonJsonPath)
|
||||
return nil
|
||||
}
|
||||
newJson, err := json.MarshalIndent(daemonMap, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -206,10 +267,7 @@ func (u *DockerService) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error {
|
||||
func (u *DockerService) OperateDocker(req dto.DockerOperation) error {
|
||||
service := "docker"
|
||||
if req.Operation == "stop" {
|
||||
service = "docker.service"
|
||||
if req.StopSocket {
|
||||
service = "docker.socket"
|
||||
}
|
||||
service = "docker.socket"
|
||||
}
|
||||
stdout, err := cmd.Execf("systemctl %s %s ", req.Operation, service)
|
||||
if err != nil {
|
||||
@@ -217,3 +275,52 @@ func (u *DockerService) OperateDocker(req dto.DockerOperation) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func changeLogOption(daemonMap map[string]interface{}, logMaxFile, logMaxSize string) {
|
||||
if opts, ok := daemonMap["log-opts"]; ok {
|
||||
if len(logMaxFile) != 0 || len(logMaxSize) != 0 {
|
||||
daemonMap["log-driver"] = "json-file"
|
||||
}
|
||||
optsMap, isMap := opts.(map[string]interface{})
|
||||
if isMap {
|
||||
if len(logMaxFile) != 0 {
|
||||
optsMap["max-file"] = logMaxFile
|
||||
} else {
|
||||
delete(optsMap, "max-file")
|
||||
}
|
||||
if len(logMaxSize) != 0 {
|
||||
optsMap["max-size"] = logMaxSize
|
||||
} else {
|
||||
delete(optsMap, "max-size")
|
||||
}
|
||||
if len(optsMap) == 0 {
|
||||
delete(daemonMap, "log-opts")
|
||||
}
|
||||
} else {
|
||||
optsMap := make(map[string]interface{})
|
||||
if len(logMaxFile) != 0 {
|
||||
optsMap["max-file"] = logMaxFile
|
||||
}
|
||||
if len(logMaxSize) != 0 {
|
||||
optsMap["max-size"] = logMaxSize
|
||||
}
|
||||
if len(optsMap) != 0 {
|
||||
daemonMap["log-opts"] = optsMap
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if len(logMaxFile) != 0 || len(logMaxSize) != 0 {
|
||||
daemonMap["log-driver"] = "json-file"
|
||||
}
|
||||
optsMap := make(map[string]interface{})
|
||||
if len(logMaxFile) != 0 {
|
||||
optsMap["max-file"] = logMaxFile
|
||||
}
|
||||
if len(logMaxSize) != 0 {
|
||||
optsMap["max-size"] = logMaxSize
|
||||
}
|
||||
if len(optsMap) != 0 {
|
||||
daemonMap["log-opts"] = optsMap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -27,7 +27,7 @@ type IFirewallService interface {
|
||||
OperateAddressRule(req dto.AddrRuleOperate, reload bool) error
|
||||
UpdatePortRule(req dto.PortRuleUpdate) error
|
||||
UpdateAddrRule(req dto.AddrRuleUpdate) error
|
||||
BacthOperateRule(req dto.BatchRuleOperate) error
|
||||
BatchOperateRule(req dto.BatchRuleOperate) error
|
||||
}
|
||||
|
||||
func NewIFirewallService() IFirewallService {
|
||||
@@ -276,7 +276,7 @@ func (u *FirewallService) UpdateAddrRule(req dto.AddrRuleUpdate) error {
|
||||
return client.Reload()
|
||||
}
|
||||
|
||||
func (u *FirewallService) BacthOperateRule(req dto.BatchRuleOperate) error {
|
||||
func (u *FirewallService) BatchOperateRule(req dto.BatchRuleOperate) error {
|
||||
client, err := firewall.NewFirewallClient()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -368,18 +368,16 @@ func (u *FirewallService) pingStatus() string {
|
||||
if _, err := os.Stat("/etc/sysctl.conf"); err != nil {
|
||||
return constant.StatusNone
|
||||
}
|
||||
commond := "cat /etc/sysctl.conf | grep net/ipv4/icmp_echo_ignore_all= "
|
||||
if cmd.HasNoPasswordSudo() {
|
||||
commond = "sudo cat /etc/sysctl.conf | grep net/ipv4/icmp_echo_ignore_all= "
|
||||
}
|
||||
stdout, _ := cmd.Exec(commond)
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
command := fmt.Sprintf("%s cat /etc/sysctl.conf | grep net/ipv4/icmp_echo_ignore_all= ", sudo)
|
||||
stdout, _ := cmd.Exec(command)
|
||||
if stdout == "net/ipv4/icmp_echo_ignore_all=1\n" {
|
||||
return constant.StatusEnable
|
||||
}
|
||||
return constant.StatusDisable
|
||||
}
|
||||
|
||||
func (u *FirewallService) updatePingStatus(enabel string) error {
|
||||
func (u *FirewallService) updatePingStatus(enable string) error {
|
||||
lineBytes, err := os.ReadFile(confPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -389,14 +387,14 @@ func (u *FirewallService) updatePingStatus(enabel string) error {
|
||||
hasLine := false
|
||||
for _, line := range files {
|
||||
if strings.Contains(line, "net/ipv4/icmp_echo_ignore_all") || strings.HasPrefix(line, "net/ipv4/icmp_echo_ignore_all") {
|
||||
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enabel)
|
||||
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enable)
|
||||
hasLine = true
|
||||
} else {
|
||||
newFiles = append(newFiles, line)
|
||||
}
|
||||
}
|
||||
if !hasLine {
|
||||
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enabel)
|
||||
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enable)
|
||||
}
|
||||
file, err := os.OpenFile(confPath, os.O_WRONLY|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
@@ -408,11 +406,9 @@ func (u *FirewallService) updatePingStatus(enabel string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
commond := "sysctl -p"
|
||||
if cmd.HasNoPasswordSudo() {
|
||||
commond = "sudo sysctl -p"
|
||||
}
|
||||
stdout, err := cmd.Exec(commond)
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
command := fmt.Sprintf("%s sysctl -p", sudo)
|
||||
stdout, err := cmd.Exec(command)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update ping status failed, err: %v", stdout)
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ssh"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
@@ -89,8 +90,25 @@ func (u *HostService) TestLocalConn(id uint) bool {
|
||||
if err := copier.Copy(&connInfo, &host); err != nil {
|
||||
return false
|
||||
}
|
||||
connInfo.PrivateKey = []byte(host.PrivateKey)
|
||||
if len(host.Password) != 0 {
|
||||
host.Password, err = encrypt.StringDecrypt(host.Password)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
connInfo.Password = host.Password
|
||||
}
|
||||
if len(host.PrivateKey) != 0 {
|
||||
host.PrivateKey, err = encrypt.StringDecrypt(host.PrivateKey)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
connInfo.PrivateKey = []byte(host.PrivateKey)
|
||||
}
|
||||
if len(host.PassPhrase) != 0 {
|
||||
host.PassPhrase, err = encrypt.StringDecrypt(host.PassPhrase)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
connInfo.PassPhrase = []byte(host.PassPhrase)
|
||||
}
|
||||
client, err := connInfo.NewClient()
|
||||
@@ -107,6 +125,25 @@ func (u *HostService) GetHostInfo(id uint) (*model.Host, error) {
|
||||
if err != nil {
|
||||
return nil, constant.ErrRecordNotFound
|
||||
}
|
||||
if len(host.Password) != 0 {
|
||||
host.Password, err = encrypt.StringDecrypt(host.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(host.PrivateKey) != 0 {
|
||||
host.PrivateKey, err = encrypt.StringDecrypt(host.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(host.PassPhrase) != 0 {
|
||||
host.PassPhrase, err = encrypt.StringDecrypt(host.PassPhrase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &host, err
|
||||
}
|
||||
|
||||
@@ -127,6 +164,25 @@ func (u *HostService) SearchWithPage(search dto.SearchHostWithPage) (int64, inte
|
||||
item.Password = ""
|
||||
item.PrivateKey = ""
|
||||
item.PassPhrase = ""
|
||||
} else {
|
||||
if len(host.Password) != 0 {
|
||||
item.Password, err = encrypt.StringDecrypt(host.Password)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
}
|
||||
if len(host.PrivateKey) != 0 {
|
||||
item.PrivateKey, err = encrypt.StringDecrypt(host.PrivateKey)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
}
|
||||
if len(host.PassPhrase) != 0 {
|
||||
item.PassPhrase, err = encrypt.StringDecrypt(host.PassPhrase)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
dtoHosts = append(dtoHosts, item)
|
||||
}
|
||||
|
@@ -3,12 +3,14 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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/cmd"
|
||||
@@ -50,9 +52,11 @@ func (u *ImageRepoService) Login(req dto.OperateByID) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := u.CheckConn(repo.DownloadUrl, repo.Username, repo.Password); err != nil {
|
||||
_ = imageRepoRepo.Update(repo.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error})
|
||||
return err
|
||||
if repo.Auth {
|
||||
if err := u.CheckConn(repo.DownloadUrl, repo.Username, repo.Password); err != nil {
|
||||
_ = imageRepoRepo.Update(repo.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||
return err
|
||||
}
|
||||
}
|
||||
_ = imageRepoRepo.Update(repo.ID, map[string]interface{}{"status": constant.StatusSuccess})
|
||||
return nil
|
||||
@@ -74,6 +78,9 @@ func (u *ImageRepoService) List() ([]dto.ImageRepoOption, error) {
|
||||
}
|
||||
|
||||
func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
|
||||
if cmd.CheckIllegal(req.Username, req.Password, req.DownloadUrl) {
|
||||
return buserr.New(constant.ErrRepoConn)
|
||||
}
|
||||
imageRepo, _ := imageRepoRepo.Get(commonRepo.WithByName(req.Name))
|
||||
if imageRepo.ID != 0 {
|
||||
return constant.ErrRecordExist
|
||||
@@ -85,12 +92,12 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
|
||||
return errors.New(string(stdout))
|
||||
}
|
||||
ticker := time.NewTicker(3 * time.Second)
|
||||
ctx, cancle := context.WithTimeout(context.Background(), time.Second*20)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
if err := func() error {
|
||||
for range ticker.C {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
cancle()
|
||||
cancel()
|
||||
return errors.New("the docker service cannot be restarted")
|
||||
default:
|
||||
stdout, err := cmd.Exec("systemctl is-active docker")
|
||||
@@ -111,9 +118,11 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
|
||||
}
|
||||
|
||||
imageRepo.Status = constant.StatusSuccess
|
||||
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
|
||||
imageRepo.Status = constant.StatusFailed
|
||||
imageRepo.Message = err.Error()
|
||||
if req.Auth {
|
||||
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
|
||||
imageRepo.Status = constant.StatusFailed
|
||||
imageRepo.Message = err.Error()
|
||||
}
|
||||
}
|
||||
if err := imageRepoRepo.Create(&imageRepo); err != nil {
|
||||
return err
|
||||
@@ -138,14 +147,17 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
|
||||
if req.ID == 1 {
|
||||
return errors.New("The default value cannot be deleted !")
|
||||
}
|
||||
if cmd.CheckIllegal(req.Username, req.Password, req.DownloadUrl) {
|
||||
return buserr.New(constant.ErrRepoConn)
|
||||
}
|
||||
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if repo.DownloadUrl != req.DownloadUrl {
|
||||
if repo.DownloadUrl != req.DownloadUrl || (!repo.Auth && req.Auth) {
|
||||
_ = u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update")
|
||||
if repo.Auth {
|
||||
_, _ = cmd.Execf("docker logout %s", repo.DownloadUrl)
|
||||
_, _ = cmd.ExecWithCheck("docker", "logout", repo.DownloadUrl)
|
||||
}
|
||||
stdout, err := cmd.Exec("systemctl restart docker")
|
||||
if err != nil {
|
||||
@@ -162,17 +174,19 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
|
||||
|
||||
upMap["status"] = constant.StatusSuccess
|
||||
upMap["message"] = ""
|
||||
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
|
||||
upMap["status"] = constant.StatusFailed
|
||||
upMap["message"] = err.Error()
|
||||
if req.Auth {
|
||||
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
|
||||
upMap["status"] = constant.StatusFailed
|
||||
upMap["message"] = err.Error()
|
||||
}
|
||||
}
|
||||
return imageRepoRepo.Update(req.ID, upMap)
|
||||
}
|
||||
|
||||
func (u *ImageRepoService) CheckConn(host, user, password string) error {
|
||||
stdout, err := cmd.Execf("docker login -u %s -p %s %s", user, password, host)
|
||||
stdout, err := cmd.ExecWithCheck("docker", "login", "-u", user, "-p", password, host)
|
||||
if err != nil {
|
||||
return errors.New(string(stdout))
|
||||
return fmt.Errorf("stdout: %s, stderr: %v", stdout, err)
|
||||
}
|
||||
if strings.Contains(string(stdout), "Login Succeeded") {
|
||||
return nil
|
||||
@@ -188,16 +202,16 @@ func (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) err
|
||||
_, _ = os.Create(constant.DaemonJsonPath)
|
||||
}
|
||||
|
||||
deamonMap := make(map[string]interface{})
|
||||
daemonMap := make(map[string]interface{})
|
||||
file, err := os.ReadFile(constant.DaemonJsonPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(file, &deamonMap); err != nil {
|
||||
if err := json.Unmarshal(file, &daemonMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iRegistries := deamonMap["insecure-registries"]
|
||||
iRegistries := daemonMap["insecure-registries"]
|
||||
registries, _ := iRegistries.([]interface{})
|
||||
switch handle {
|
||||
case "create":
|
||||
@@ -217,11 +231,11 @@ func (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) err
|
||||
}
|
||||
}
|
||||
if len(registries) == 0 {
|
||||
delete(deamonMap, "insecure-registries")
|
||||
delete(daemonMap, "insecure-registries")
|
||||
} else {
|
||||
deamonMap["insecure-registries"] = registries
|
||||
daemonMap["insecure-registries"] = registries
|
||||
}
|
||||
newJson, err := json.MarshalIndent(deamonMap, "", "\t")
|
||||
newJson, err := json.MarshalIndent(daemonMap, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -1,12 +1,13 @@
|
||||
package job
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/robfig/cron/v3"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"github.com/shirou/gopsutil/v3/load"
|
||||
@@ -14,14 +15,17 @@ import (
|
||||
"github.com/shirou/gopsutil/v3/net"
|
||||
)
|
||||
|
||||
type monitor struct{}
|
||||
type MonitorService struct{}
|
||||
|
||||
func NewMonitorJob() *monitor {
|
||||
return &monitor{}
|
||||
type IMonitorService interface {
|
||||
Run()
|
||||
}
|
||||
|
||||
func (m *monitor) Run() {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
func NewIMonitorService() IMonitorService {
|
||||
return &MonitorService{}
|
||||
}
|
||||
|
||||
func (m *MonitorService) Run() {
|
||||
monitorStatus, _ := settingRepo.Get(settingRepo.WithByKey("MonitorStatus"))
|
||||
if monitorStatus.Value == "disable" {
|
||||
return
|
||||
@@ -42,7 +46,7 @@ func (m *monitor) Run() {
|
||||
memoryInfo, _ := mem.VirtualMemory()
|
||||
itemModel.Memory = memoryInfo.UsedPercent
|
||||
|
||||
if err := global.DB.Create(&itemModel).Error; err != nil {
|
||||
if err := settingRepo.CreateMonitorBase(itemModel); err != nil {
|
||||
global.LOG.Errorf("Insert basic monitoring data failed, err: %v", err)
|
||||
}
|
||||
|
||||
@@ -55,9 +59,9 @@ func (m *monitor) Run() {
|
||||
}
|
||||
storeDays, _ := strconv.Atoi(MonitorStoreDays.Value)
|
||||
timeForDelete := time.Now().AddDate(0, 0, -storeDays)
|
||||
_ = global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorBase{}).Error
|
||||
_ = global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorIO{}).Error
|
||||
_ = global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error
|
||||
_ = settingRepo.DelMonitorBase(timeForDelete)
|
||||
_ = settingRepo.DelMonitorIO(timeForDelete)
|
||||
_ = settingRepo.DelMonitorNet(timeForDelete)
|
||||
}
|
||||
|
||||
func loadDiskIO() {
|
||||
@@ -72,17 +76,31 @@ func loadDiskIO() {
|
||||
if io2.Name == io1.Name {
|
||||
var itemIO model.MonitorIO
|
||||
itemIO.Name = io1.Name
|
||||
itemIO.Read = uint64(float64(io2.ReadBytes-io1.ReadBytes) / 60)
|
||||
itemIO.Write = uint64(float64(io2.WriteBytes-io1.WriteBytes) / 60)
|
||||
if io2.ReadBytes != 0 && io1.ReadBytes != 0 {
|
||||
itemIO.Read = uint64(float64(io2.ReadBytes-io1.ReadBytes) / 60)
|
||||
}
|
||||
if io2.WriteBytes != 0 && io1.WriteBytes != 0 {
|
||||
itemIO.Write = uint64(float64(io2.WriteBytes-io1.WriteBytes) / 60)
|
||||
}
|
||||
|
||||
itemIO.Count = uint64(float64(io2.ReadCount-io1.ReadCount) / 60)
|
||||
writeCount := uint64(float64(io2.WriteCount-io1.WriteCount) / 60)
|
||||
if io2.ReadCount != 0 && io1.ReadCount != 0 {
|
||||
itemIO.Count = uint64(float64(io2.ReadCount-io1.ReadCount) / 60)
|
||||
}
|
||||
writeCount := uint64(0)
|
||||
if io2.WriteCount != 0 && io1.WriteCount != 0 {
|
||||
writeCount = uint64(float64(io2.WriteCount-io1.WriteCount) / 60)
|
||||
}
|
||||
if writeCount > itemIO.Count {
|
||||
itemIO.Count = writeCount
|
||||
}
|
||||
|
||||
itemIO.Time = uint64(float64(io2.ReadTime-io1.ReadTime) / 60)
|
||||
writeTime := uint64(float64(io2.WriteTime-io1.WriteTime) / 60)
|
||||
if io2.ReadTime != 0 && io1.ReadTime != 0 {
|
||||
itemIO.Time = uint64(float64(io2.ReadTime-io1.ReadTime) / 60)
|
||||
}
|
||||
writeTime := uint64(0)
|
||||
if io2.WriteTime != 0 && io1.WriteTime != 0 {
|
||||
writeTime = uint64(float64(io2.WriteTime-io1.WriteTime) / 60)
|
||||
}
|
||||
if writeTime > itemIO.Time {
|
||||
itemIO.Time = writeTime
|
||||
}
|
||||
@@ -91,7 +109,7 @@ func loadDiskIO() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := global.DB.CreateInBatches(ioList, len(ioList)).Error; err != nil {
|
||||
if err := settingRepo.BatchCreateMonitorIO(ioList); err != nil {
|
||||
global.LOG.Errorf("Insert io monitoring data failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -109,8 +127,13 @@ func loadNetIO() {
|
||||
if net2.Name == net1.Name {
|
||||
var itemNet model.MonitorNetwork
|
||||
itemNet.Name = net1.Name
|
||||
itemNet.Up = float64(net2.BytesSent-net1.BytesSent) / 1024 / 60
|
||||
itemNet.Down = float64(net2.BytesRecv-net1.BytesRecv) / 1024 / 60
|
||||
|
||||
if net2.BytesSent != 0 && net1.BytesSent != 0 {
|
||||
itemNet.Up = float64(net2.BytesSent-net1.BytesSent) / 1024 / 60
|
||||
}
|
||||
if net2.BytesRecv != 0 && net1.BytesRecv != 0 {
|
||||
itemNet.Down = float64(net2.BytesRecv-net1.BytesRecv) / 1024 / 60
|
||||
}
|
||||
netList = append(netList, itemNet)
|
||||
break
|
||||
}
|
||||
@@ -119,21 +142,43 @@ func loadNetIO() {
|
||||
netStatAll2, _ := net.IOCounters(false)
|
||||
for _, net2 := range netStatAll2 {
|
||||
for _, net1 := range netStatAll {
|
||||
if net1.BytesSent == 0 || net1.BytesRecv == 0 {
|
||||
continue
|
||||
}
|
||||
if net2.Name == net1.Name {
|
||||
var itemNet model.MonitorNetwork
|
||||
itemNet.Name = net1.Name
|
||||
itemNet.Up = float64(net2.BytesSent-net1.BytesSent) / 1024 / 60
|
||||
itemNet.Down = float64(net2.BytesRecv-net1.BytesRecv) / 1024 / 60
|
||||
if net2.BytesSent != 0 && net1.BytesSent != 0 {
|
||||
itemNet.Up = float64(net2.BytesSent-net1.BytesSent) / 1024 / 60
|
||||
}
|
||||
if itemNet.Up > 10485760 {
|
||||
itemNet.Up = 0
|
||||
global.LOG.Errorf("net2: %v, net1: %v, BytesSent: %v \n", net2.BytesSent, net1.BytesSent, float64(net2.BytesSent-net1.BytesSent)/1024/60)
|
||||
}
|
||||
|
||||
if net2.BytesRecv != 0 && net1.BytesRecv != 0 {
|
||||
itemNet.Down = float64(net2.BytesRecv-net1.BytesRecv) / 1024 / 60
|
||||
}
|
||||
if itemNet.Down > 10485760 {
|
||||
itemNet.Down = 0
|
||||
global.LOG.Errorf("net2: %v, net1: %v, BytesRecv: %v \n", net2.BytesRecv, net1.BytesRecv, float64(net2.BytesRecv-net1.BytesRecv)/1024/60)
|
||||
}
|
||||
netList = append(netList, itemNet)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := global.DB.CreateInBatches(netList, len(netList)).Error; err != nil {
|
||||
if err := settingRepo.BatchCreateMonitorNet(netList); err != nil {
|
||||
global.LOG.Errorf("Insert network monitoring data failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func StartMonitor(removeBefore bool, interval string) error {
|
||||
if removeBefore {
|
||||
global.Cron.Remove(cron.EntryID(global.MonitorCronID))
|
||||
}
|
||||
monitorID, err := global.Cron.AddJob(fmt.Sprintf("@every %sm", interval), NewIMonitorService())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
global.MonitorCronID = int(monitorID)
|
||||
return nil
|
||||
}
|
27
backend/app/service/process.go
Normal file
27
backend/app/service/process.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/shirou/gopsutil/v3/process"
|
||||
)
|
||||
|
||||
type ProcessService struct{}
|
||||
|
||||
type IProcessService interface {
|
||||
StopProcess(req request.ProcessReq) error
|
||||
}
|
||||
|
||||
func NewIProcessService() IProcessService {
|
||||
return &ProcessService{}
|
||||
}
|
||||
|
||||
func (p *ProcessService) StopProcess(req request.ProcessReq) error {
|
||||
proc, err := process.NewProcess(req.PID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := proc.Kill(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -64,7 +64,13 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
|
||||
return err
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
buildDir := path.Join(constant.AppResourceDir, app.Key, "versions", appDetail.Version, "build")
|
||||
appVersionDir := path.Join(constant.AppResourceDir, app.Resource, app.Key, appDetail.Version)
|
||||
if !fileOp.Stat(appVersionDir) || appDetail.Update {
|
||||
if err := downloadApp(app, appDetail, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
buildDir := path.Join(appVersionDir, "build")
|
||||
if !fileOp.Stat(buildDir) {
|
||||
return buserr.New(constant.ErrDirNotFound)
|
||||
}
|
||||
|
@@ -62,7 +62,7 @@ func handleParams(image, runtimeType, runtimeDir string, params map[string]inter
|
||||
if extendsArray, ok := extends.([]interface{}); ok {
|
||||
strArray := make([]string, len(extendsArray))
|
||||
for i, v := range extendsArray {
|
||||
strArray[i] = fmt.Sprintf("%v", v)
|
||||
strArray[i] = strings.ToLower(fmt.Sprintf("%v", v))
|
||||
}
|
||||
params["PHP_EXTENSIONS"] = strings.Join(strArray, ",")
|
||||
}
|
||||
|
@@ -19,20 +19,24 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ssl"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
type SettingService struct{}
|
||||
|
||||
type ISettingService interface {
|
||||
GetSettingInfo() (*dto.SettingInfo, error)
|
||||
LoadTimeZone() ([]string, error)
|
||||
Update(key, value string) error
|
||||
UpdatePassword(c *gin.Context, old, new string) error
|
||||
UpdatePort(port uint) error
|
||||
UpdateSSL(c *gin.Context, req dto.SSLUpdate) error
|
||||
LoadFromCert() (*dto.SSLInfo, error)
|
||||
HandlePasswordExpired(c *gin.Context, old, new string) error
|
||||
SyncTime(req dto.SyncTime) error
|
||||
}
|
||||
|
||||
func NewISettingService() ISettingService {
|
||||
@@ -60,18 +64,85 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
|
||||
return &info, err
|
||||
}
|
||||
|
||||
func (u *SettingService) LoadTimeZone() ([]string, error) {
|
||||
std, err := cmd.Exec("timedatectl list-timezones")
|
||||
if err != nil {
|
||||
return []string{}, nil
|
||||
}
|
||||
return strings.Split(std, "\n"), err
|
||||
}
|
||||
|
||||
func (u *SettingService) Update(key, value string) error {
|
||||
if key == "ExpirationDays" {
|
||||
switch key {
|
||||
case "MonitorStatus":
|
||||
if value == "enable" && global.MonitorCronID == 0 {
|
||||
interval, err := settingRepo.Get(settingRepo.WithByKey("MonitorInterval"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := StartMonitor(false, interval.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if value == "disable" && global.MonitorCronID != 0 {
|
||||
global.Cron.Remove(cron.EntryID(global.MonitorCronID))
|
||||
global.MonitorCronID = 0
|
||||
}
|
||||
case "MonitorInterval":
|
||||
status, err := settingRepo.Get(settingRepo.WithByKey("MonitorStatus"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status.Value == "enable" && global.MonitorCronID != 0 {
|
||||
if err := StartMonitor(true, value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case "TimeZone":
|
||||
if err := ntp.UpdateSystemTimeZone(value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := settingRepo.Update(key, value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch key {
|
||||
case "ExpirationDays":
|
||||
timeout, _ := strconv.Atoi(value)
|
||||
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006-01-02 15:04:05")); err != nil {
|
||||
return err
|
||||
}
|
||||
case "TimeZone":
|
||||
go func() {
|
||||
_, err := cmd.Exec("systemctl restart 1panel.service")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("restart system for new time zone failed, err: %v", err)
|
||||
}
|
||||
}()
|
||||
case "BindDomain":
|
||||
if len(value) != 0 {
|
||||
_ = global.SESSION.Clean()
|
||||
}
|
||||
case "UserName", "Password":
|
||||
_ = global.SESSION.Clean()
|
||||
}
|
||||
if err := settingRepo.Update(key, value); err != nil {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SettingService) SyncTime(req dto.SyncTime) error {
|
||||
if err := settingRepo.Update("NtpSite", req.NtpSite); err != nil {
|
||||
return err
|
||||
}
|
||||
if key == "UserName" {
|
||||
_ = global.SESSION.Clean()
|
||||
ntime, err := ntp.GetRemoteTime(req.NtpSite)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ts := ntime.Format("2006-01-02 15:04:05")
|
||||
if err := ntp.UpdateSystemTime(ts); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -56,6 +56,12 @@ func (u *SnapshotService) SnapshotImport(req dto.SnapshotImport) error {
|
||||
if len(req.Names) == 0 {
|
||||
return fmt.Errorf("incorrect snapshot request body: %v", req.Names)
|
||||
}
|
||||
for _, snapName := range req.Names {
|
||||
snap, _ := snapshotRepo.Get(commonRepo.WithByName(strings.ReplaceAll(snapName, ".tar.gz", "")))
|
||||
if snap.ID != 0 {
|
||||
return constant.ErrRecordExist
|
||||
}
|
||||
}
|
||||
for _, snap := range req.Names {
|
||||
nameItems := strings.Split(snap, "_")
|
||||
if !strings.HasPrefix(snap, "1panel_v") || !strings.HasSuffix(snap, ".tar.gz") || len(nameItems) != 3 {
|
||||
@@ -93,7 +99,7 @@ func (u *SnapshotService) UpdateDescription(req dto.UpdateDescription) error {
|
||||
type SnapshotJson struct {
|
||||
OldBaseDir string `json:"oldBaseDir"`
|
||||
OldDockerDataDir string `json:"oldDockerDataDir"`
|
||||
OldBackupDataDir string `json:"oldDackupDataDir"`
|
||||
OldBackupDataDir string `json:"oldBackupDataDir"`
|
||||
OldPanelDataDir string `json:"oldPanelDataDir"`
|
||||
|
||||
BaseDir string `json:"baseDir"`
|
||||
@@ -113,7 +119,7 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
backupAccont, err := NewIBackupService().NewClient(&backup)
|
||||
backupAccount, err := NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -202,7 +208,9 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
|
||||
global.LOG.Infof("start to upload snapshot to %s, please wait", backup.Type)
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusUploading})
|
||||
localPath := fmt.Sprintf("%s/system/1panel_%s_%s.tar.gz", localDir, versionItem.Value, timeNow)
|
||||
if ok, err := backupAccont.Upload(localPath, fmt.Sprintf("system_snapshot/1panel_%s_%s.tar.gz", versionItem.Value, timeNow)); err != nil || !ok {
|
||||
itemBackupPath := strings.TrimLeft(backup.BackupPath, "/")
|
||||
itemBackupPath = strings.TrimRight(itemBackupPath, "/")
|
||||
if ok, err := backupAccount.Upload(localPath, fmt.Sprintf("%s/system_snapshot/1panel_%s_%s.tar.gz", itemBackupPath, versionItem.Value, timeNow)); err != nil || !ok {
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||
global.LOG.Errorf("upload snapshot to %s failed, err: %v", backup.Type, err)
|
||||
return
|
||||
@@ -253,7 +261,9 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
|
||||
operation = "re-recover"
|
||||
}
|
||||
if !isReTry || snap.InterruptStep == "Download" || (isReTry && req.ReDownload) {
|
||||
ok, err := client.Download(fmt.Sprintf("system_snapshot/%s.tar.gz", snap.Name), fmt.Sprintf("%s/%s.tar.gz", baseDir, snap.Name))
|
||||
itemBackupPath := strings.TrimLeft(backup.BackupPath, "/")
|
||||
itemBackupPath = strings.TrimRight(itemBackupPath, "/")
|
||||
ok, err := client.Download(fmt.Sprintf("%s/system_snapshot/%s.tar.gz", itemBackupPath, snap.Name), fmt.Sprintf("%s/%s.tar.gz", baseDir, snap.Name))
|
||||
if err != nil || !ok {
|
||||
if req.ReDownload {
|
||||
updateRecoverStatus(snap.ID, snap.InterruptStep, constant.StatusFailed, fmt.Sprintf("download file %s from %s failed, err: %v", snap.Name, backup.Type, err))
|
||||
@@ -535,7 +545,7 @@ func (u *SnapshotService) handleDaemonJson(fileOp files.FileOp, operation string
|
||||
if operation == "snapshot" || operation == "recover" {
|
||||
_, err := os.Stat(daemonJsonPath)
|
||||
if os.IsNotExist(err) {
|
||||
global.LOG.Info("no daemon.josn in snapshot and system now, nothing happened")
|
||||
global.LOG.Info("no daemon.json in snapshot and system now, nothing happened")
|
||||
}
|
||||
if err == nil {
|
||||
if err := fileOp.CopyFile(daemonJsonPath, target); err != nil {
|
||||
@@ -658,7 +668,7 @@ func (u *SnapshotService) handleBackupDatas(fileOp files.FileOp, operation strin
|
||||
func (u *SnapshotService) handlePanelDatas(snapID uint, fileOp files.FileOp, operation string, source, target, backupDir, dockerDir string) error {
|
||||
switch operation {
|
||||
case "snapshot":
|
||||
exclusionRules := "./tmp;./cache;"
|
||||
exclusionRules := "./tmp;./cache;./db/1Panel.db-*;"
|
||||
if strings.Contains(backupDir, source) {
|
||||
exclusionRules += ("." + strings.ReplaceAll(backupDir, source, "") + ";")
|
||||
}
|
||||
@@ -796,15 +806,15 @@ func (u *SnapshotService) updateLiveRestore(enabled bool) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deamonMap := make(map[string]interface{})
|
||||
_ = json.Unmarshal(file, &deamonMap)
|
||||
daemonMap := make(map[string]interface{})
|
||||
_ = json.Unmarshal(file, &daemonMap)
|
||||
|
||||
if !enabled {
|
||||
delete(deamonMap, "live-restore")
|
||||
delete(daemonMap, "live-restore")
|
||||
} else {
|
||||
deamonMap["live-restore"] = enabled
|
||||
daemonMap["live-restore"] = enabled
|
||||
}
|
||||
newJson, err := json.MarshalIndent(deamonMap, "", "\t")
|
||||
newJson, err := json.MarshalIndent(daemonMap, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -818,8 +828,8 @@ func (u *SnapshotService) updateLiveRestore(enabled bool) error {
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(3 * time.Second)
|
||||
ctx, cancle := context.WithTimeout(context.Background(), time.Second*30)
|
||||
defer cancle()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||
defer cancel()
|
||||
for range ticker.C {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
451
backend/app/service/ssh.go
Normal file
451
backend/app/service/ssh.go
Normal file
@@ -0,0 +1,451 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/qqwry"
|
||||
)
|
||||
|
||||
const sshPath = "/etc/ssh/sshd_config"
|
||||
|
||||
type SSHService struct{}
|
||||
|
||||
type ISSHService interface {
|
||||
GetSSHInfo() (*dto.SSHInfo, error)
|
||||
OperateSSH(operation string) error
|
||||
UpdateByFile(value string) error
|
||||
Update(key, value string) error
|
||||
GenerateSSH(req dto.GenerateSSH) error
|
||||
LoadSSHSecret(mode string) (string, error)
|
||||
LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error)
|
||||
}
|
||||
|
||||
func NewISSHService() ISSHService {
|
||||
return &SSHService{}
|
||||
}
|
||||
|
||||
func (u *SSHService) GetSSHInfo() (*dto.SSHInfo, error) {
|
||||
data := dto.SSHInfo{
|
||||
Status: constant.StatusEnable,
|
||||
Message: "",
|
||||
Port: "22",
|
||||
ListenAddress: "0.0.0.0",
|
||||
PasswordAuthentication: "yes",
|
||||
PubkeyAuthentication: "yes",
|
||||
PermitRootLogin: "yes",
|
||||
UseDNS: "yes",
|
||||
}
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
stdout, err := cmd.Execf("%s systemctl status sshd", sudo)
|
||||
if err != nil {
|
||||
data.Message = stdout
|
||||
data.Status = constant.StatusDisable
|
||||
}
|
||||
stdLines := strings.Split(stdout, "\n")
|
||||
for _, stdline := range stdLines {
|
||||
if strings.Contains(stdline, "active (running)") {
|
||||
data.Status = constant.StatusEnable
|
||||
break
|
||||
}
|
||||
}
|
||||
sshConf, err := os.ReadFile(sshPath)
|
||||
if err != nil {
|
||||
data.Message = err.Error()
|
||||
data.Status = constant.StatusDisable
|
||||
}
|
||||
lines := strings.Split(string(sshConf), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "Port ") {
|
||||
data.Port = strings.ReplaceAll(line, "Port ", "")
|
||||
}
|
||||
if strings.HasPrefix(line, "ListenAddress ") {
|
||||
data.ListenAddress = strings.ReplaceAll(line, "ListenAddress ", "")
|
||||
}
|
||||
if strings.HasPrefix(line, "PasswordAuthentication ") {
|
||||
data.PasswordAuthentication = strings.ReplaceAll(line, "PasswordAuthentication ", "")
|
||||
}
|
||||
if strings.HasPrefix(line, "PubkeyAuthentication ") {
|
||||
data.PubkeyAuthentication = strings.ReplaceAll(line, "PubkeyAuthentication ", "")
|
||||
}
|
||||
if strings.HasPrefix(line, "PermitRootLogin ") {
|
||||
data.PermitRootLogin = strings.ReplaceAll(line, "PermitRootLogin ", "")
|
||||
}
|
||||
if strings.HasPrefix(line, "UseDNS ") {
|
||||
data.UseDNS = strings.ReplaceAll(line, "UseDNS ", "")
|
||||
}
|
||||
}
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func (u *SSHService) OperateSSH(operation string) error {
|
||||
if operation == "start" || operation == "stop" || operation == "restart" {
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
stdout, err := cmd.Execf("%s systemctl %s sshd", sudo, operation)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s sshd failed, stdout: %s, err: %v", operation, stdout, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("not support such operation: %s", operation)
|
||||
}
|
||||
|
||||
func (u *SSHService) Update(key, value string) error {
|
||||
sshConf, err := os.ReadFile(sshPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lines := strings.Split(string(sshConf), "\n")
|
||||
newFiles := updateSSHConf(lines, key, value)
|
||||
if err := settingRepo.Update(key, value); err != nil {
|
||||
return err
|
||||
}
|
||||
file, err := os.OpenFile(sshPath, os.O_WRONLY|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
if _, err = file.WriteString(strings.Join(newFiles, "\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
if key == "Port" {
|
||||
stdout, _ := cmd.Execf("%s getenforce", sudo)
|
||||
if stdout == "Enforcing\n" {
|
||||
_, _ = cmd.Execf("%s semanage port -a -t ssh_port_t -p tcp %s", sudo, value)
|
||||
}
|
||||
}
|
||||
_, _ = cmd.Execf("%s systemctl restart sshd", sudo)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SSHService) UpdateByFile(value string) error {
|
||||
file, err := os.OpenFile(sshPath, os.O_WRONLY|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
if _, err = file.WriteString(value); err != nil {
|
||||
return err
|
||||
}
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
_, _ = cmd.Execf("%s systemctl restart sshd", sudo)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SSHService) GenerateSSH(req dto.GenerateSSH) error {
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
return fmt.Errorf("load current user failed, err: %v", err)
|
||||
}
|
||||
secretFile := fmt.Sprintf("%s/.ssh/id_item_%s", currentUser.HomeDir, req.EncryptionMode)
|
||||
secretPubFile := fmt.Sprintf("%s/.ssh/id_item_%s.pub", currentUser.HomeDir, req.EncryptionMode)
|
||||
authFile := currentUser.HomeDir + "/.ssh/authorized_keys"
|
||||
|
||||
command := fmt.Sprintf("ssh-keygen -t %s -f %s/.ssh/id_item_%s | echo y", req.EncryptionMode, currentUser.HomeDir, req.EncryptionMode)
|
||||
if len(req.Password) != 0 {
|
||||
command = fmt.Sprintf("ssh-keygen -t %s -P %s -f %s/.ssh/id_item_%s | echo y", req.EncryptionMode, req.Password, currentUser.HomeDir, req.EncryptionMode)
|
||||
}
|
||||
stdout, err := cmd.Exec(command)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generate failed, err: %v, message: %s", err, stdout)
|
||||
}
|
||||
defer func() {
|
||||
_ = os.Remove(secretFile)
|
||||
}()
|
||||
defer func() {
|
||||
_ = os.Remove(secretPubFile)
|
||||
}()
|
||||
|
||||
if _, err := os.Stat(authFile); err != nil {
|
||||
_, _ = os.Create(authFile)
|
||||
}
|
||||
stdout1, err := cmd.Execf("cat %s >> %s/.ssh/authorized_keys", secretPubFile, currentUser.HomeDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generate failed, err: %v, message: %s", err, stdout1)
|
||||
}
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
if err := fileOp.Rename(secretFile, fmt.Sprintf("%s/.ssh/id_%s", currentUser.HomeDir, req.EncryptionMode)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.Rename(secretPubFile, fmt.Sprintf("%s/.ssh/id_%s.pub", currentUser.HomeDir, req.EncryptionMode)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SSHService) LoadSSHSecret(mode string) (string, error) {
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("load current user failed, err: %v", err)
|
||||
}
|
||||
|
||||
homeDir := currentUser.HomeDir
|
||||
if _, err := os.Stat(fmt.Sprintf("%s/.ssh/id_%s", homeDir, mode)); err != nil {
|
||||
return "", nil
|
||||
}
|
||||
file, err := os.ReadFile(fmt.Sprintf("%s/.ssh/id_%s", homeDir, mode))
|
||||
return string(file), err
|
||||
}
|
||||
|
||||
func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
|
||||
var fileList []string
|
||||
var data dto.SSHLog
|
||||
baseDir := "/var/log"
|
||||
if err := filepath.Walk(baseDir, func(pathItem string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() && strings.HasPrefix(info.Name(), "secure") || strings.HasPrefix(info.Name(), "auth") {
|
||||
if strings.HasSuffix(info.Name(), ".gz") {
|
||||
if err := handleGunzip(pathItem); err == nil {
|
||||
fileList = append(fileList, strings.ReplaceAll(pathItem, ".gz", ""))
|
||||
}
|
||||
} else {
|
||||
fileList = append(fileList, pathItem)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fileList = sortFileList(fileList)
|
||||
|
||||
command := ""
|
||||
if len(req.Info) != 0 {
|
||||
command = fmt.Sprintf(" | grep '%s'", req.Info)
|
||||
}
|
||||
|
||||
for i := 0; i < len(fileList); i++ {
|
||||
withAppend := len(data.Logs) < req.Page*req.PageSize
|
||||
if req.Status != constant.StatusSuccess {
|
||||
if strings.HasPrefix(path.Base(fileList[i]), "secure") {
|
||||
commandItem := fmt.Sprintf("cat %s | grep -a 'Failed password for' | grep -v 'invalid' %s", fileList[i], command)
|
||||
dataItem, itemTotal := loadFailedSecureDatas(commandItem, withAppend)
|
||||
data.FailedCount += itemTotal
|
||||
data.TotalCount += itemTotal
|
||||
data.Logs = append(data.Logs, dataItem...)
|
||||
}
|
||||
if strings.HasPrefix(path.Base(fileList[i]), "auth.log") {
|
||||
commandItem := fmt.Sprintf("cat %s | grep -a 'Connection closed by authenticating user' | grep -a 'preauth' %s", fileList[i], command)
|
||||
dataItem, itemTotal := loadFailedAuthDatas(commandItem, withAppend)
|
||||
data.FailedCount += itemTotal
|
||||
data.TotalCount += itemTotal
|
||||
data.Logs = append(data.Logs, dataItem...)
|
||||
}
|
||||
}
|
||||
if req.Status != constant.StatusFailed {
|
||||
commandItem := fmt.Sprintf("cat %s | grep -a Accepted %s", fileList[i], command)
|
||||
dataItem, itemTotal := loadSuccessDatas(commandItem, withAppend)
|
||||
data.TotalCount += itemTotal
|
||||
data.Logs = append(data.Logs, dataItem...)
|
||||
}
|
||||
}
|
||||
data.SuccessfulCount = data.TotalCount - data.FailedCount
|
||||
if len(data.Logs) < 1 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var itemDatas []dto.SSHHistory
|
||||
total, start, end := len(data.Logs), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||
if start > total {
|
||||
itemDatas = make([]dto.SSHHistory, 0)
|
||||
} else {
|
||||
if end >= total {
|
||||
end = total
|
||||
}
|
||||
itemDatas = data.Logs[start:end]
|
||||
}
|
||||
data.Logs = itemDatas
|
||||
|
||||
timeNow := time.Now()
|
||||
nyc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||
qqWry, err := qqwry.NewQQwry()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load qqwry datas failed: %s", err)
|
||||
}
|
||||
var itemLogs []dto.SSHHistory
|
||||
for i := 0; i < len(data.Logs); i++ {
|
||||
data.Logs[i].Area = qqWry.Find(data.Logs[i].Address).Area
|
||||
data.Logs[i].Date, _ = time.ParseInLocation("2006 Jan 2 15:04:05", fmt.Sprintf("%d %s", timeNow.Year(), data.Logs[i].DateStr), nyc)
|
||||
itemLogs = append(itemLogs, data.Logs[i])
|
||||
}
|
||||
data.Logs = itemLogs
|
||||
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func sortFileList(fileNames []string) []string {
|
||||
if len(fileNames) < 2 {
|
||||
return fileNames
|
||||
}
|
||||
if strings.HasPrefix(path.Base(fileNames[0]), "secure") {
|
||||
var itemFile []string
|
||||
sort.Slice(fileNames, func(i, j int) bool {
|
||||
return fileNames[i] > fileNames[j]
|
||||
})
|
||||
itemFile = append(itemFile, fileNames[len(fileNames)-1])
|
||||
itemFile = append(itemFile, fileNames[:len(fileNames)-2]...)
|
||||
return itemFile
|
||||
}
|
||||
sort.Slice(fileNames, func(i, j int) bool {
|
||||
return fileNames[i] < fileNames[j]
|
||||
})
|
||||
return fileNames
|
||||
}
|
||||
|
||||
func updateSSHConf(oldFiles []string, param string, value interface{}) []string {
|
||||
hasKey := false
|
||||
var newFiles []string
|
||||
for _, line := range oldFiles {
|
||||
if strings.HasPrefix(line, param+" ") {
|
||||
newFiles = append(newFiles, fmt.Sprintf("%s %v", param, value))
|
||||
hasKey = true
|
||||
continue
|
||||
}
|
||||
newFiles = append(newFiles, line)
|
||||
}
|
||||
if !hasKey {
|
||||
newFiles = []string{}
|
||||
for _, line := range oldFiles {
|
||||
if strings.HasPrefix(line, fmt.Sprintf("#%s ", param)) && !hasKey {
|
||||
newFiles = append(newFiles, fmt.Sprintf("%s %v", param, value))
|
||||
hasKey = true
|
||||
continue
|
||||
}
|
||||
newFiles = append(newFiles, line)
|
||||
}
|
||||
}
|
||||
if !hasKey {
|
||||
newFiles = []string{}
|
||||
newFiles = append(newFiles, oldFiles...)
|
||||
newFiles = append(newFiles, fmt.Sprintf("%s %v", param, value))
|
||||
}
|
||||
return newFiles
|
||||
}
|
||||
|
||||
func loadSuccessDatas(command string, withAppend bool) ([]dto.SSHHistory, int) {
|
||||
var (
|
||||
datas []dto.SSHHistory
|
||||
totalNum int
|
||||
)
|
||||
stdout2, err := cmd.Exec(command)
|
||||
if err == nil {
|
||||
lines := strings.Split(string(stdout2), "\n")
|
||||
if len(lines) == 0 {
|
||||
return datas, 0
|
||||
}
|
||||
for i := len(lines) - 1; i >= 0; i-- {
|
||||
parts := strings.Fields(lines[i])
|
||||
if len(parts) < 14 {
|
||||
continue
|
||||
}
|
||||
totalNum++
|
||||
if withAppend {
|
||||
historyItem := dto.SSHHistory{
|
||||
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
||||
AuthMode: parts[6],
|
||||
User: parts[8],
|
||||
Address: parts[10],
|
||||
Port: parts[12],
|
||||
Status: constant.StatusSuccess,
|
||||
}
|
||||
datas = append(datas, historyItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
return datas, totalNum
|
||||
}
|
||||
|
||||
func loadFailedAuthDatas(command string, withAppend bool) ([]dto.SSHHistory, int) {
|
||||
var (
|
||||
datas []dto.SSHHistory
|
||||
totalNum int
|
||||
)
|
||||
stdout2, err := cmd.Exec(command)
|
||||
if err == nil {
|
||||
lines := strings.Split(string(stdout2), "\n")
|
||||
if len(lines) == 0 {
|
||||
return datas, 0
|
||||
}
|
||||
for i := len(lines) - 1; i >= 0; i-- {
|
||||
parts := strings.Fields(lines[i])
|
||||
if len(parts) < 14 {
|
||||
continue
|
||||
}
|
||||
totalNum++
|
||||
if withAppend {
|
||||
historyItem := dto.SSHHistory{
|
||||
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
||||
AuthMode: parts[8],
|
||||
User: parts[10],
|
||||
Address: parts[11],
|
||||
Port: parts[13],
|
||||
Status: constant.StatusFailed,
|
||||
}
|
||||
if strings.Contains(lines[i], ": ") {
|
||||
historyItem.Message = strings.Split(lines[i], ": ")[1]
|
||||
}
|
||||
datas = append(datas, historyItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
return datas, totalNum
|
||||
}
|
||||
|
||||
func loadFailedSecureDatas(command string, withAppend bool) ([]dto.SSHHistory, int) {
|
||||
var (
|
||||
datas []dto.SSHHistory
|
||||
totalNum int
|
||||
)
|
||||
stdout2, err := cmd.Exec(command)
|
||||
if err == nil {
|
||||
lines := strings.Split(string(stdout2), "\n")
|
||||
if len(lines) == 0 {
|
||||
return datas, 0
|
||||
}
|
||||
for i := len(lines) - 1; i >= 0; i-- {
|
||||
parts := strings.Fields(lines[i])
|
||||
if len(parts) < 14 {
|
||||
continue
|
||||
}
|
||||
totalNum++
|
||||
if withAppend {
|
||||
historyItem := dto.SSHHistory{
|
||||
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
||||
AuthMode: parts[6],
|
||||
User: parts[8],
|
||||
Address: parts[10],
|
||||
Port: parts[12],
|
||||
Status: constant.StatusFailed,
|
||||
}
|
||||
if strings.Contains(lines[i], ": ") {
|
||||
historyItem.Message = strings.Split(lines[i], ": ")[1]
|
||||
}
|
||||
datas = append(datas, historyItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
return datas, totalNum
|
||||
}
|
||||
|
||||
func handleGunzip(path string) error {
|
||||
if _, err := cmd.Execf("gunzip %s", path); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -2,7 +2,6 @@ package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -12,6 +11,8 @@ import (
|
||||
"time"
|
||||
|
||||
"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/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
@@ -67,7 +68,7 @@ func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) {
|
||||
notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, global.CONF.System.Mode, itemVersion, itemVersion))
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load relase-notes of version %s failed, err: %v", latestVersion, err)
|
||||
return nil, fmt.Errorf("load releases-notes of version %s failed, err: %v", latestVersion, err)
|
||||
}
|
||||
upgrade.ReleaseNote = notes
|
||||
return &upgrade, nil
|
||||
@@ -76,7 +77,7 @@ func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) {
|
||||
func (u *UpgradeService) LoadNotes(req dto.Upgrade) (string, error) {
|
||||
notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, global.CONF.System.Mode, req.Version, req.Version))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("load relase-notes of version %s failed, err: %v", req.Version, err)
|
||||
return "", fmt.Errorf("load releases-notes of version %s failed, err: %v", req.Version, err)
|
||||
}
|
||||
return notes, nil
|
||||
}
|
||||
@@ -93,9 +94,13 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
|
||||
if err := os.MkdirAll(originalDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
itemArch, err := loadArch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
downloadPath := fmt.Sprintf("%s/%s/%s/release", global.CONF.System.RepoUrl, global.CONF.System.Mode, req.Version)
|
||||
fileName := fmt.Sprintf("1panel-%s-%s-%s.tar.gz", req.Version, "linux", runtime.GOARCH)
|
||||
fileName := fmt.Sprintf("1panel-%s-%s-%s.tar.gz", req.Version, "linux", itemArch)
|
||||
_ = settingRepo.Update("SystemStatus", "Upgrading")
|
||||
go func() {
|
||||
if err := fileOp.DownloadFile(downloadPath+"/"+fileName, rootDir+"/"+fileName); err != nil {
|
||||
@@ -200,12 +205,12 @@ func (u *UpgradeService) loadVersion(isLatest bool, currentVersion string) (stri
|
||||
}
|
||||
latestVersionRes, err := http.Get(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", buserr.New(constant.ErrOSSConn)
|
||||
}
|
||||
defer latestVersionRes.Body.Close()
|
||||
version, err := io.ReadAll(latestVersionRes.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", buserr.New(constant.ErrOSSConn)
|
||||
}
|
||||
if isLatest {
|
||||
return string(version), nil
|
||||
@@ -213,7 +218,7 @@ func (u *UpgradeService) loadVersion(isLatest bool, currentVersion string) (stri
|
||||
|
||||
versionMap := make(map[string]string)
|
||||
if err := json.Unmarshal(version, &versionMap); err != nil {
|
||||
return "", fmt.Errorf("load version map failed, err: %v", err)
|
||||
return "", buserr.New(constant.ErrOSSConn)
|
||||
}
|
||||
|
||||
if len(currentVersion) < 4 {
|
||||
@@ -222,7 +227,7 @@ func (u *UpgradeService) loadVersion(isLatest bool, currentVersion string) (stri
|
||||
if version, ok := versionMap[currentVersion[0:4]]; ok {
|
||||
return version, nil
|
||||
}
|
||||
return "", errors.New("load version failed in latest.current")
|
||||
return "", buserr.New(constant.ErrOSSConn)
|
||||
}
|
||||
|
||||
func (u *UpgradeService) loadReleaseNotes(path string) (string, error) {
|
||||
@@ -237,3 +242,21 @@ func (u *UpgradeService) loadReleaseNotes(path string) (string, error) {
|
||||
}
|
||||
return string(release), nil
|
||||
}
|
||||
|
||||
func loadArch() (string, error) {
|
||||
switch runtime.GOARCH {
|
||||
case "amd64", "ppc64le", "s390x", "arm64":
|
||||
return runtime.GOARCH, nil
|
||||
case "arm":
|
||||
std, err := cmd.Exec("uname -m")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("std: %s, err: %s", std, err.Error())
|
||||
}
|
||||
if std == "armv7l\n" {
|
||||
return "armv7", nil
|
||||
}
|
||||
return "", fmt.Errorf("unsupport such arch: arm-%s", std)
|
||||
default:
|
||||
return "", fmt.Errorf("unsupport such arch: %s", runtime.GOARCH)
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,14 @@ import (
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
@@ -16,14 +24,8 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser"
|
||||
"github.com/1Panel-dev/1Panel/cmd/server/nginx_conf"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gopkg.in/ini.v1"
|
||||
"gorm.io/gorm"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||
@@ -56,7 +58,7 @@ type IWebsiteService interface {
|
||||
UpdateNginxConfigByScope(req request.NginxConfigUpdate) error
|
||||
GetWebsiteNginxConfig(websiteId uint, configType string) (response.FileInfo, error)
|
||||
GetWebsiteHTTPS(websiteId uint) (response.WebsiteHTTPS, error)
|
||||
OpWebsiteHTTPS(ctx context.Context, req request.WebsiteHTTPSOp) (response.WebsiteHTTPS, error)
|
||||
OpWebsiteHTTPS(ctx context.Context, req request.WebsiteHTTPSOp) (*response.WebsiteHTTPS, error)
|
||||
PreInstallCheck(req request.WebsiteInstallCheckReq) ([]response.WebsitePreInstallCheck, error)
|
||||
GetWafConfig(req request.WebsiteWafReq) (response.WebsiteWafConfig, error)
|
||||
UpdateWafConfig(req request.WebsiteWafUpdate) error
|
||||
@@ -75,6 +77,8 @@ type IWebsiteService interface {
|
||||
UpdateProxyFile(req request.NginxProxyUpdate) (err error)
|
||||
GetAuthBasics(req request.NginxAuthReq) (res response.NginxAuthRes, err error)
|
||||
UpdateAuthBasic(req request.NginxAuthUpdate) (err error)
|
||||
GetAntiLeech(id uint) (*response.NginxAntiLeechRes, error)
|
||||
UpdateAntiLeech(req request.NginxAntiLeechUpdate) (err error)
|
||||
}
|
||||
|
||||
func NewIWebsiteService() IWebsiteService {
|
||||
@@ -93,7 +97,7 @@ func (w WebsiteService) PageWebsite(req request.WebsiteSearch) (int64, []respons
|
||||
}
|
||||
return 0, nil, err
|
||||
}
|
||||
opts = append(opts, commonRepo.WithOrderBy("created_at desc"))
|
||||
opts = append(opts, commonRepo.WithOrderRuleBy(req.OrderBy, req.Order))
|
||||
if req.Name != "" {
|
||||
opts = append(opts, websiteRepo.WithDomainLike(req.Name))
|
||||
}
|
||||
@@ -173,6 +177,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
||||
SiteDir: "/",
|
||||
AccessLog: true,
|
||||
ErrorLog: true,
|
||||
IPV6: create.IPV6,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -206,6 +211,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
||||
req.Name = create.AppInstall.Name
|
||||
req.AppDetailId = create.AppInstall.AppDetailId
|
||||
req.Params = create.AppInstall.Params
|
||||
req.AppContainerConfig = create.AppInstall.AppContainerConfig
|
||||
tx, installCtx := getTxAndContext()
|
||||
install, err = NewIAppService().Install(installCtx, req)
|
||||
if err != nil {
|
||||
@@ -243,6 +249,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
||||
req.AppDetailId = create.AppInstall.AppDetailId
|
||||
req.Params = create.AppInstall.Params
|
||||
req.Params["IMAGE_NAME"] = runtime.Image
|
||||
req.AppContainerConfig = create.AppInstall.AppContainerConfig
|
||||
nginxInstall, err = getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -335,6 +342,8 @@ func (w WebsiteService) UpdateWebsite(req request.WebsiteUpdate) error {
|
||||
website.PrimaryDomain = req.PrimaryDomain
|
||||
website.WebsiteGroupID = req.WebsiteGroupID
|
||||
website.Remark = req.Remark
|
||||
website.IPV6 = req.IPV6
|
||||
|
||||
if req.ExpireDate != "" {
|
||||
expireDate, err := time.Parse(constant.DateLayout, req.ExpireDate)
|
||||
if err != nil {
|
||||
@@ -610,10 +619,10 @@ func (w WebsiteService) GetWebsiteHTTPS(websiteId uint) (response.WebsiteHTTPS,
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteHTTPSOp) (response.WebsiteHTTPS, error) {
|
||||
func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteHTTPSOp) (*response.WebsiteHTTPS, error) {
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
return response.WebsiteHTTPS{}, err
|
||||
return nil, err
|
||||
}
|
||||
var (
|
||||
res response.WebsiteHTTPS
|
||||
@@ -626,7 +635,7 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
|
||||
website.Protocol = constant.ProtocolHTTP
|
||||
website.WebsiteSSLID = 0
|
||||
if err := deleteListenAndServerName(website, []string{"443", "[::]:443"}, []string{}); err != nil {
|
||||
return response.WebsiteHTTPS{}, err
|
||||
return nil, err
|
||||
}
|
||||
nginxParams := getNginxParamsFromStaticFile(dto.SSL, nil)
|
||||
nginxParams = append(nginxParams,
|
||||
@@ -647,68 +656,99 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
|
||||
Name: "ssl_ciphers",
|
||||
},
|
||||
)
|
||||
if err := deleteNginxConfig(constant.NginxScopeServer, nginxParams, &website); err != nil {
|
||||
return response.WebsiteHTTPS{}, err
|
||||
if err = deleteNginxConfig(constant.NginxScopeServer, nginxParams, &website); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := websiteRepo.Save(ctx, &website); err != nil {
|
||||
return response.WebsiteHTTPS{}, err
|
||||
if err = websiteRepo.Save(ctx, &website); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if req.Type == constant.SSLExisted {
|
||||
websiteSSL, err = websiteSSLRepo.GetFirst(commonRepo.WithByID(req.WebsiteSSLID))
|
||||
if err != nil {
|
||||
return response.WebsiteHTTPS{}, err
|
||||
return nil, err
|
||||
}
|
||||
website.WebsiteSSLID = websiteSSL.ID
|
||||
res.SSL = websiteSSL
|
||||
}
|
||||
if req.Type == constant.SSLManual {
|
||||
certBlock, _ := pem.Decode([]byte(req.Certificate))
|
||||
var (
|
||||
certificate string
|
||||
privateKey string
|
||||
)
|
||||
switch req.ImportType {
|
||||
case "paste":
|
||||
certificate = req.Certificate
|
||||
privateKey = req.PrivateKey
|
||||
case "local":
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(req.PrivateKeyPath) {
|
||||
return nil, buserr.New("ErrSSLKeyNotFound")
|
||||
}
|
||||
if !fileOp.Stat(req.CertificatePath) {
|
||||
return nil, buserr.New("ErrSSLCertificateNotFound")
|
||||
}
|
||||
if content, err := fileOp.GetContent(req.PrivateKeyPath); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
privateKey = string(content)
|
||||
}
|
||||
if content, err := fileOp.GetContent(req.CertificatePath); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
certificate = string(content)
|
||||
}
|
||||
}
|
||||
|
||||
privateKeyCertBlock, _ := pem.Decode([]byte(privateKey))
|
||||
if privateKeyCertBlock == nil {
|
||||
return nil, buserr.New("ErrSSLKeyFormat")
|
||||
}
|
||||
|
||||
certBlock, _ := pem.Decode([]byte(certificate))
|
||||
if certBlock == nil {
|
||||
return nil, buserr.New("ErrSSLCertificateFormat")
|
||||
}
|
||||
cert, err := x509.ParseCertificate(certBlock.Bytes)
|
||||
if err != nil {
|
||||
return response.WebsiteHTTPS{}, err
|
||||
return nil, err
|
||||
}
|
||||
websiteSSL.ExpireDate = cert.NotAfter
|
||||
websiteSSL.StartDate = cert.NotBefore
|
||||
websiteSSL.Type = cert.Issuer.CommonName
|
||||
websiteSSL.Organization = cert.Issuer.Organization[0]
|
||||
websiteSSL.PrimaryDomain = cert.Subject.CommonName
|
||||
if len(cert.Subject.Names) > 0 {
|
||||
var domains []string
|
||||
for _, name := range cert.Subject.Names {
|
||||
if v, ok := name.Value.(string); ok {
|
||||
if v != cert.Subject.CommonName {
|
||||
domains = append(domains, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(domains) > 0 {
|
||||
websiteSSL.Domains = strings.Join(domains, "")
|
||||
}
|
||||
if len(cert.Issuer.Organization) > 0 {
|
||||
websiteSSL.Organization = cert.Issuer.Organization[0]
|
||||
} else {
|
||||
websiteSSL.Organization = cert.Issuer.CommonName
|
||||
}
|
||||
if len(cert.DNSNames) > 0 {
|
||||
websiteSSL.PrimaryDomain = cert.DNSNames[0]
|
||||
websiteSSL.Domains = strings.Join(cert.DNSNames, ",")
|
||||
}
|
||||
websiteSSL.Provider = constant.Manual
|
||||
websiteSSL.PrivateKey = req.PrivateKey
|
||||
websiteSSL.Pem = req.Certificate
|
||||
websiteSSL.PrivateKey = privateKey
|
||||
websiteSSL.Pem = certificate
|
||||
|
||||
res.SSL = websiteSSL
|
||||
}
|
||||
website.Protocol = constant.ProtocolHTTPS
|
||||
if err := applySSL(website, websiteSSL, req); err != nil {
|
||||
return response.WebsiteHTTPS{}, err
|
||||
return nil, err
|
||||
}
|
||||
website.HttpConfig = req.HttpConfig
|
||||
|
||||
if websiteSSL.ID == 0 {
|
||||
if err := websiteSSLRepo.Create(ctx, &websiteSSL); err != nil {
|
||||
return response.WebsiteHTTPS{}, err
|
||||
return nil, err
|
||||
}
|
||||
website.WebsiteSSLID = websiteSSL.ID
|
||||
}
|
||||
if err := websiteRepo.Save(ctx, &website); err != nil {
|
||||
return response.WebsiteHTTPS{}, err
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (w WebsiteService) PreInstallCheck(req request.WebsiteInstallCheckReq) ([]response.WebsitePreInstallCheck, error) {
|
||||
@@ -859,7 +899,7 @@ func (w WebsiteService) OpWebsiteLog(req request.WebsiteLogReq) (*response.Websi
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fileInfo.Size() > 10<<20 {
|
||||
if fileInfo.Size() > 20<<20 {
|
||||
return nil, buserr.New(constant.ErrFileToLarge)
|
||||
}
|
||||
fileInfo.Size()
|
||||
@@ -905,6 +945,11 @@ func (w WebsiteService) OpWebsiteLog(req request.WebsiteLogReq) (*response.Websi
|
||||
if err := websiteRepo.Save(context.Background(), &website); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case constant.DeleteLog:
|
||||
logPath := path.Join(nginx.Install.GetPath(), "www", "sites", website.Alias, "log", req.LogType)
|
||||
if err := files.NewFileOp().WriteFile(logPath, strings.NewReader(""), 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
@@ -1002,7 +1047,27 @@ func (w WebsiteService) GetPHPConfig(id uint) (*response.PHPConfig, error) {
|
||||
params[matches[1]] = matches[2]
|
||||
}
|
||||
}
|
||||
return &response.PHPConfig{Params: params}, nil
|
||||
cfg, err := ini.Load(phpConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
phpConfig, err := cfg.GetSection("PHP")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disableFunctionStr := phpConfig.Key("disable_functions").Value()
|
||||
res := &response.PHPConfig{Params: params}
|
||||
if disableFunctionStr != "" {
|
||||
disableFunctions := strings.Split(disableFunctionStr, ",")
|
||||
if len(disableFunctions) > 0 {
|
||||
res.DisableFunctions = disableFunctions
|
||||
}
|
||||
}
|
||||
uploadMaxSize := phpConfig.Key("upload_max_filesize").Value()
|
||||
if uploadMaxSize != "" {
|
||||
res.UploadMaxSize = uploadMaxSize
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (w WebsiteService) UpdatePHPConfig(req request.WebsitePHPConfigUpdate) (err error) {
|
||||
@@ -1026,16 +1091,38 @@ func (w WebsiteService) UpdatePHPConfig(req request.WebsitePHPConfigUpdate) (err
|
||||
defer configFile.Close()
|
||||
|
||||
contentBytes, err := fileOp.GetContent(phpConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
content := string(contentBytes)
|
||||
lines := strings.Split(content, "\n")
|
||||
for i, line := range lines {
|
||||
if strings.HasPrefix(line, ";") {
|
||||
continue
|
||||
}
|
||||
for key, value := range req.Params {
|
||||
pattern := "^" + regexp.QuoteMeta(key) + "\\s*=\\s*.*$"
|
||||
switch req.Scope {
|
||||
case "params":
|
||||
for key, value := range req.Params {
|
||||
pattern := "^" + regexp.QuoteMeta(key) + "\\s*=\\s*.*$"
|
||||
if matched, _ := regexp.MatchString(pattern, line); matched {
|
||||
lines[i] = key + " = " + value
|
||||
}
|
||||
}
|
||||
case "disable_functions":
|
||||
pattern := "^" + regexp.QuoteMeta("disable_functions") + "\\s*=\\s*.*$"
|
||||
if matched, _ := regexp.MatchString(pattern, line); matched {
|
||||
lines[i] = key + " = " + value
|
||||
lines[i] = "disable_functions" + " = " + strings.Join(req.DisableFunctions, ",")
|
||||
break
|
||||
}
|
||||
case "upload_max_filesize":
|
||||
pattern := "^" + regexp.QuoteMeta("post_max_size") + "\\s*=\\s*.*$"
|
||||
if matched, _ := regexp.MatchString(pattern, line); matched {
|
||||
lines[i] = "post_max_size" + " = " + req.UploadMaxSize
|
||||
}
|
||||
patternUpload := "^" + regexp.QuoteMeta("upload_max_filesize") + "\\s*=\\s*.*$"
|
||||
if matched, _ := regexp.MatchString(patternUpload, line); matched {
|
||||
lines[i] = "upload_max_filesize" + " = " + req.UploadMaxSize
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1043,6 +1130,7 @@ func (w WebsiteService) UpdatePHPConfig(req request.WebsitePHPConfigUpdate) (err
|
||||
if err := fileOp.WriteFile(phpConfigPath, strings.NewReader(updatedContent), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
appInstallReq := request.AppInstalledOperate{
|
||||
InstallId: appInstall.ID,
|
||||
Operate: constant.Restart,
|
||||
@@ -1051,6 +1139,7 @@ func (w WebsiteService) UpdatePHPConfig(req request.WebsitePHPConfigUpdate) (err
|
||||
_ = fileOp.WriteFile(phpConfigPath, strings.NewReader(string(contentBytes)), 0755)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1443,14 +1532,6 @@ func (w WebsiteService) UpdateAuthBasic(req request.NginxAuthUpdate) (err error)
|
||||
if !fileOp.Stat(absoluteAuthPath) {
|
||||
_ = fileOp.CreateFile(absoluteAuthPath)
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
switch req.Operate {
|
||||
case "create":
|
||||
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
params = append(params, dto.NginxParam{Name: "auth_basic", Params: []string{`"Authentication"`}})
|
||||
params = append(params, dto.NginxParam{Name: "auth_basic_user_file", Params: []string{authPath}})
|
||||
@@ -1458,7 +1539,9 @@ func (w WebsiteService) UpdateAuthBasic(req request.NginxAuthUpdate) (err error)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
authArray = strings.Split(string(authContent), "\n")
|
||||
if len(authContent) > 0 {
|
||||
authArray = strings.Split(string(authContent), "\n")
|
||||
}
|
||||
switch req.Operate {
|
||||
case "disable":
|
||||
return deleteNginxConfig(constant.NginxScopeServer, params, &website)
|
||||
@@ -1529,6 +1612,9 @@ func (w WebsiteService) UpdateAuthBasic(req request.NginxAuthUpdate) (err error)
|
||||
defer passFile.Close()
|
||||
writer := bufio.NewWriter(passFile)
|
||||
for _, line := range authArray {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
_, err = writer.WriteString(line + "\n")
|
||||
if err != nil {
|
||||
return
|
||||
@@ -1538,6 +1624,15 @@ func (w WebsiteService) UpdateAuthBasic(req request.NginxAuthUpdate) (err error)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
authContent, err = fileOp.GetContent(absoluteAuthPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(authContent) == 0 {
|
||||
if err = deleteNginxConfig(constant.NginxScopeServer, params, &website); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1584,3 +1679,168 @@ func (w WebsiteService) GetAuthBasics(req request.NginxAuthReq) (res response.Ng
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w WebsiteService) UpdateAntiLeech(req request.NginxAntiLeechUpdate) (err error) {
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nginxFull, err := getNginxFull(&website)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
backpContent, err := fileOp.GetContent(nginxFull.SiteConfig.Config.FilePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
block := nginxFull.SiteConfig.Config.FindServers()[0]
|
||||
locations := block.FindDirectives("location")
|
||||
for _, location := range locations {
|
||||
loParams := location.GetParameters()
|
||||
if len(loParams) > 1 || loParams[0] == "~" {
|
||||
extendStr := loParams[1]
|
||||
if strings.HasPrefix(extendStr, `.*\.(`) && strings.HasSuffix(extendStr, `)$`) {
|
||||
block.RemoveDirective("location", loParams)
|
||||
}
|
||||
}
|
||||
}
|
||||
if req.Enable {
|
||||
exts := strings.Split(req.Extends, ",")
|
||||
newDirective := components.Directive{
|
||||
Name: "location",
|
||||
Parameters: []string{"~", fmt.Sprintf(`.*\.(%s)$`, strings.Join(exts, "|"))},
|
||||
}
|
||||
|
||||
newBlock := &components.Block{}
|
||||
newBlock.Directives = make([]components.IDirective, 0)
|
||||
if req.Cache {
|
||||
newBlock.Directives = append(newBlock.Directives, &components.Directive{
|
||||
Name: "expires",
|
||||
Parameters: []string{strconv.Itoa(req.CacheTime) + req.CacheUint},
|
||||
})
|
||||
}
|
||||
newBlock.Directives = append(newBlock.Directives, &components.Directive{
|
||||
Name: "log_not_found",
|
||||
Parameters: []string{"off"},
|
||||
})
|
||||
validDir := &components.Directive{
|
||||
Name: "valid_referers",
|
||||
Parameters: []string{},
|
||||
}
|
||||
if req.NoneRef {
|
||||
validDir.Parameters = append(validDir.Parameters, "none")
|
||||
}
|
||||
if len(req.ServerNames) > 0 {
|
||||
validDir.Parameters = append(validDir.Parameters, strings.Join(req.ServerNames, " "))
|
||||
}
|
||||
newBlock.Directives = append(newBlock.Directives, validDir)
|
||||
|
||||
ifDir := &components.Directive{
|
||||
Name: "if",
|
||||
Parameters: []string{"($invalid_referer)"},
|
||||
}
|
||||
ifDir.Block = &components.Block{
|
||||
Directives: []components.IDirective{
|
||||
&components.Directive{
|
||||
Name: "return",
|
||||
Parameters: []string{req.Return},
|
||||
},
|
||||
&components.Directive{
|
||||
Name: "access_log",
|
||||
Parameters: []string{"off"},
|
||||
},
|
||||
},
|
||||
}
|
||||
newBlock.Directives = append(newBlock.Directives, ifDir)
|
||||
newDirective.Block = newBlock
|
||||
block.Directives = append(block.Directives, &newDirective)
|
||||
}
|
||||
|
||||
if err = nginx.WriteConfig(nginxFull.SiteConfig.Config, nginx.IndentedStyle); err != nil {
|
||||
return
|
||||
}
|
||||
if err = updateNginxConfig(constant.NginxScopeServer, nil, &website); err != nil {
|
||||
_ = fileOp.WriteFile(nginxFull.SiteConfig.Config.FilePath, bytes.NewReader(backpContent), 0755)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w WebsiteService) GetAntiLeech(id uint) (*response.NginxAntiLeechRes, error) {
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nginxFull, err := getNginxFull(&website)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := &response.NginxAntiLeechRes{
|
||||
LogEnable: true,
|
||||
ServerNames: []string{},
|
||||
}
|
||||
block := nginxFull.SiteConfig.Config.FindServers()[0]
|
||||
locations := block.FindDirectives("location")
|
||||
for _, location := range locations {
|
||||
loParams := location.GetParameters()
|
||||
if len(loParams) > 1 || loParams[0] == "~" {
|
||||
extendStr := loParams[1]
|
||||
if strings.HasPrefix(extendStr, `.*\.(`) && strings.HasSuffix(extendStr, `)$`) {
|
||||
str1 := strings.TrimPrefix(extendStr, `.*\.(`)
|
||||
str2 := strings.TrimSuffix(str1, ")$")
|
||||
res.Extends = strings.Join(strings.Split(str2, "|"), ",")
|
||||
}
|
||||
}
|
||||
lDirectives := location.GetBlock().GetDirectives()
|
||||
for _, lDir := range lDirectives {
|
||||
if lDir.GetName() == "valid_referers" {
|
||||
res.Enable = true
|
||||
params := lDir.GetParameters()
|
||||
for _, param := range params {
|
||||
if param == "none" {
|
||||
res.NoneRef = true
|
||||
continue
|
||||
}
|
||||
if param == "blocked" {
|
||||
res.Blocked = true
|
||||
continue
|
||||
}
|
||||
if param == "server_names" {
|
||||
continue
|
||||
}
|
||||
res.ServerNames = append(res.ServerNames, param)
|
||||
}
|
||||
}
|
||||
if lDir.GetName() == "if" && lDir.GetParameters()[0] == "($invalid_referer)" {
|
||||
directives := lDir.GetBlock().GetDirectives()
|
||||
for _, dir := range directives {
|
||||
if dir.GetName() == "return" {
|
||||
res.Return = strings.Join(dir.GetParameters(), " ")
|
||||
}
|
||||
if dir.GetName() == "access_log" {
|
||||
if strings.Join(dir.GetParameters(), "") == "off" {
|
||||
res.LogEnable = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if lDir.GetName() == "expires" {
|
||||
res.Cache = true
|
||||
re := regexp.MustCompile(`^(\d+)(\w+)$`)
|
||||
matches := re.FindStringSubmatch(lDir.GetParameters()[0])
|
||||
if matches == nil {
|
||||
continue
|
||||
}
|
||||
cacheTime, err := strconv.Atoi(matches[1])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
unit := matches[2]
|
||||
res.CacheUint = unit
|
||||
res.CacheTime = cacheTime
|
||||
}
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
@@ -7,11 +7,13 @@ 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/app/repo"
|
||||
"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/ssl"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -21,7 +23,7 @@ type WebsiteSSLService struct {
|
||||
type IWebsiteSSLService interface {
|
||||
Page(search request.WebsiteSSLSearch) (int64, []response.WebsiteSSLDTO, error)
|
||||
GetSSL(id uint) (*response.WebsiteSSLDTO, error)
|
||||
Search() ([]response.WebsiteSSLDTO, error)
|
||||
Search(req request.WebsiteSSLSearch) ([]response.WebsiteSSLDTO, error)
|
||||
Create(create request.WebsiteSSLCreate) (request.WebsiteSSLCreate, error)
|
||||
Renew(sslId uint) error
|
||||
GetDNSResolve(req request.WebsiteDNSReq) ([]response.WebsiteDNSRes, error)
|
||||
@@ -35,17 +37,19 @@ func NewIWebsiteSSLService() IWebsiteSSLService {
|
||||
}
|
||||
|
||||
func (w WebsiteSSLService) Page(search request.WebsiteSSLSearch) (int64, []response.WebsiteSSLDTO, error) {
|
||||
var (
|
||||
result []response.WebsiteSSLDTO
|
||||
)
|
||||
total, sslList, err := websiteSSLRepo.Page(search.Page, search.PageSize, commonRepo.WithOrderBy("created_at desc"))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
var sslDTOs []response.WebsiteSSLDTO
|
||||
for _, ssl := range sslList {
|
||||
sslDTOs = append(sslDTOs, response.WebsiteSSLDTO{
|
||||
WebsiteSSL: ssl,
|
||||
for _, sslModel := range sslList {
|
||||
result = append(result, response.WebsiteSSLDTO{
|
||||
WebsiteSSL: sslModel,
|
||||
})
|
||||
}
|
||||
return total, sslDTOs, err
|
||||
return total, result, err
|
||||
}
|
||||
|
||||
func (w WebsiteSSLService) GetSSL(id uint) (*response.WebsiteSSLDTO, error) {
|
||||
@@ -58,18 +62,29 @@ func (w WebsiteSSLService) GetSSL(id uint) (*response.WebsiteSSLDTO, error) {
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (w WebsiteSSLService) Search() ([]response.WebsiteSSLDTO, error) {
|
||||
sslList, err := websiteSSLRepo.List()
|
||||
func (w WebsiteSSLService) Search(search request.WebsiteSSLSearch) ([]response.WebsiteSSLDTO, error) {
|
||||
var (
|
||||
opts []repo.DBOption
|
||||
result []response.WebsiteSSLDTO
|
||||
)
|
||||
opts = append(opts, commonRepo.WithOrderBy("created_at desc"))
|
||||
if search.AcmeAccountID != "" {
|
||||
acmeAccountID, err := strconv.ParseUint(search.AcmeAccountID, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts = append(opts, websiteSSLRepo.WithByAcmeAccountId(uint(acmeAccountID)))
|
||||
}
|
||||
sslList, err := websiteSSLRepo.List(opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var sslDTOs []response.WebsiteSSLDTO
|
||||
for _, ssl := range sslList {
|
||||
sslDTOs = append(sslDTOs, response.WebsiteSSLDTO{
|
||||
WebsiteSSL: ssl,
|
||||
for _, sslModel := range sslList {
|
||||
result = append(result, response.WebsiteSSLDTO{
|
||||
WebsiteSSL: sslModel,
|
||||
})
|
||||
}
|
||||
return sslDTOs, err
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.WebsiteSSLCreate, error) {
|
||||
|
@@ -190,6 +190,9 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
|
||||
for _, domain := range domains {
|
||||
serverNames = append(serverNames, domain.Domain)
|
||||
server.UpdateListen(strconv.Itoa(domain.Port), false)
|
||||
if website.IPV6 {
|
||||
server.UpdateListen("[::]:"+strconv.Itoa(domain.Port), false)
|
||||
}
|
||||
}
|
||||
server.UpdateServerName(serverNames)
|
||||
|
||||
@@ -291,6 +294,9 @@ func addListenAndServerName(website model.Website, ports []int, domains []string
|
||||
server := config.FindServers()[0]
|
||||
for _, port := range ports {
|
||||
server.AddListen(strconv.Itoa(port), false)
|
||||
if website.IPV6 {
|
||||
server.UpdateListen("[::]:"+strconv.Itoa(port), false)
|
||||
}
|
||||
}
|
||||
for _, domain := range domains {
|
||||
server.AddServerName(domain)
|
||||
@@ -311,6 +317,7 @@ func deleteListenAndServerName(website model.Website, binds []string, domains []
|
||||
server := config.FindServers()[0]
|
||||
for _, bind := range binds {
|
||||
server.DeleteListen(bind)
|
||||
server.DeleteListen("[::]:" + bind)
|
||||
}
|
||||
for _, domain := range domains {
|
||||
server.DeleteServerName(domain)
|
||||
@@ -371,7 +378,10 @@ func applySSL(website model.Website, websiteSSL model.WebsiteSSL, req request.We
|
||||
}
|
||||
config := nginxFull.SiteConfig.Config
|
||||
server := config.FindServers()[0]
|
||||
server.UpdateListen("443", website.DefaultServer, "ssl")
|
||||
server.UpdateListen("443", website.DefaultServer, "ssl", "http2")
|
||||
if website.IPV6 {
|
||||
server.UpdateListen("[::]:443", website.DefaultServer, "ssl", "http2")
|
||||
}
|
||||
|
||||
switch req.HttpConfig {
|
||||
case constant.HTTPSOnly:
|
||||
@@ -380,10 +390,16 @@ func applySSL(website model.Website, websiteSSL model.WebsiteSSL, req request.We
|
||||
server.RemoveDirective("if", []string{"($scheme"})
|
||||
case constant.HTTPToHTTPS:
|
||||
server.UpdateListen("80", website.DefaultServer)
|
||||
if website.IPV6 {
|
||||
server.UpdateListen("[::]:80", website.DefaultServer)
|
||||
}
|
||||
server.AddHTTP2HTTPS()
|
||||
case constant.HTTPAlso:
|
||||
server.UpdateListen("80", website.DefaultServer)
|
||||
server.RemoveDirective("if", []string{"($scheme"})
|
||||
if website.IPV6 {
|
||||
server.UpdateListen("[::]:80", website.DefaultServer)
|
||||
}
|
||||
}
|
||||
|
||||
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
|
@@ -19,5 +19,8 @@ type System struct {
|
||||
Password string `mapstructure:"password"`
|
||||
Entrance string `mapstructure:"entrance"`
|
||||
IsDemo bool `mapstructure:"is_demo"`
|
||||
AppRepo string `mapstructure:"app_repo"`
|
||||
ChangeUserInfo bool `mapstructure:"change_user_info"`
|
||||
OneDriveID string `mapstructure:"one_drive_id"`
|
||||
OneDriveSc string `mapstructure:"one_drive_sc"`
|
||||
}
|
||||
|
@@ -1,12 +1,17 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
Running = "Running"
|
||||
UnHealthy = "UnHealthy"
|
||||
Error = "Error"
|
||||
Stopped = "Stopped"
|
||||
Installing = "Installing"
|
||||
Syncing = "Syncing"
|
||||
Running = "Running"
|
||||
UnHealthy = "UnHealthy"
|
||||
Error = "Error"
|
||||
Stopped = "Stopped"
|
||||
Installing = "Installing"
|
||||
Syncing = "Syncing"
|
||||
DownloadErr = "DownloadErr"
|
||||
DirNotFound = "DirNotFound"
|
||||
Upgrading = "Upgrading"
|
||||
UpgradeErr = "UpgradeErr"
|
||||
PullErr = "PullErr"
|
||||
|
||||
ContainerPrefix = "1Panel-"
|
||||
|
||||
@@ -19,6 +24,11 @@ const (
|
||||
|
||||
AppResourceLocal = "local"
|
||||
AppResourceRemote = "remote"
|
||||
|
||||
CPUS = "CPUS"
|
||||
MemoryLimit = "MEMORY_LIMIT"
|
||||
HostIP = "HOST_IP"
|
||||
ContainerName = "CONTAINER_NAME"
|
||||
)
|
||||
|
||||
type AppOperate string
|
||||
|
@@ -7,7 +7,10 @@ const (
|
||||
S3 = "S3"
|
||||
OSS = "OSS"
|
||||
Sftp = "SFTP"
|
||||
OneDrive = "OneDrive"
|
||||
MinIo = "MINIO"
|
||||
Cos = "COS"
|
||||
Kodo = "KODO"
|
||||
|
||||
OneDriveRedirectURI = "http://localhost/login/authorized"
|
||||
)
|
||||
|
@@ -7,11 +7,12 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
DataDir = global.CONF.System.DataDir
|
||||
ResourceDir = path.Join(DataDir, "resource")
|
||||
AppResourceDir = path.Join(ResourceDir, "apps")
|
||||
AppInstallDir = path.Join(DataDir, "apps")
|
||||
LocalAppResourceDir = path.Join(ResourceDir, "localApps")
|
||||
LocalAppInstallDir = path.Join(DataDir, "localApps")
|
||||
RuntimeDir = path.Join(DataDir, "runtime")
|
||||
DataDir = global.CONF.System.DataDir
|
||||
ResourceDir = path.Join(DataDir, "resource")
|
||||
AppResourceDir = path.Join(ResourceDir, "apps")
|
||||
AppInstallDir = path.Join(DataDir, "apps")
|
||||
LocalAppResourceDir = path.Join(AppResourceDir, "local")
|
||||
LocalAppInstallDir = path.Join(AppInstallDir, "local")
|
||||
RemoteAppResourceDir = path.Join(AppResourceDir, "remote")
|
||||
RuntimeDir = path.Join(DataDir, "runtime")
|
||||
)
|
||||
|
@@ -14,6 +14,8 @@ const (
|
||||
CodePasswordExpired = 405
|
||||
CodeAuth = 406
|
||||
CodeGlobalLoading = 407
|
||||
CodeErrIP = 408
|
||||
CodeErrDomain = 409
|
||||
CodeErrInternalServer = 500
|
||||
CodeErrHeader = 406
|
||||
)
|
||||
@@ -30,45 +32,39 @@ var (
|
||||
ErrInvalidParams = errors.New("ErrInvalidParams")
|
||||
|
||||
ErrTokenParse = errors.New("ErrTokenParse")
|
||||
|
||||
ErrPageGenerate = errors.New("generate page info failed")
|
||||
ErrRepoNotValid = "ErrRepoNotValid"
|
||||
)
|
||||
|
||||
// api
|
||||
var (
|
||||
ErrTypeInternalServer = "ErrInternalServer"
|
||||
ErrTypeInvalidParams = "ErrInvalidParams"
|
||||
ErrTypeToken = "ErrToken"
|
||||
ErrTypeTokenTimeOut = "ErrTokenTimeOut"
|
||||
ErrTypeNotLogin = "ErrNotLogin"
|
||||
ErrTypePasswordExpired = "ErrPasswordExpired"
|
||||
ErrTypeNotSafety = "ErrNotSafety"
|
||||
ErrNameIsExist = "ErrNameIsExist"
|
||||
ErrDemoEnvironment = "ErrDemoEnvironment"
|
||||
ErrInitUser = "ErrInitUser"
|
||||
)
|
||||
|
||||
// app
|
||||
var (
|
||||
ErrPortInUsed = "ErrPortInUsed"
|
||||
ErrAppLimit = "ErrAppLimit"
|
||||
ErrAppRequired = "ErrAppRequired"
|
||||
ErrFileCanNotRead = "ErrFileCanNotRead"
|
||||
ErrFileToLarge = "ErrFileToLarge"
|
||||
ErrFileCanNotRead = "ErrFileCanNotRead"
|
||||
ErrNotInstall = "ErrNotInstall"
|
||||
ErrPortInOtherApp = "ErrPortInOtherApp"
|
||||
ErrDbUserNotValid = "ErrDbUserNotValid"
|
||||
ErrUpdateBuWebsite = "ErrUpdateBuWebsite"
|
||||
Err1PanelNetworkFailed = "Err1PanelNetworkFailed"
|
||||
ErrCmdTimeout = "ErrCmdTimeout"
|
||||
ErrFileParse = "ErrFileParse"
|
||||
ErrInstallDirNotFound = "ErrInstallDirNotFound"
|
||||
ErrContainerName = "ErrContainerName"
|
||||
)
|
||||
|
||||
// website
|
||||
var (
|
||||
ErrDomainIsExist = "ErrDomainIsExist"
|
||||
ErrAliasIsExist = "ErrAliasIsExist"
|
||||
ErrAppDelete = "ErrAppDelete"
|
||||
ErrGroupIsUsed = "ErrGroupIsUsed"
|
||||
ErrUsernameIsExist = "ErrUsernameIsExist"
|
||||
ErrUsernameIsNotExist = "ErrUsernameIsNotExist"
|
||||
@@ -89,6 +85,7 @@ var (
|
||||
ErrLinkPathNotFound = "ErrLinkPathNotFound"
|
||||
ErrFileIsExit = "ErrFileIsExit"
|
||||
ErrFileUpload = "ErrFileUpload"
|
||||
ErrFileDownloadDir = "ErrFileDownloadDir"
|
||||
)
|
||||
|
||||
// mysql
|
||||
@@ -108,6 +105,7 @@ var (
|
||||
ErrInUsed = "ErrInUsed"
|
||||
ErrObjectInUsed = "ErrObjectInUsed"
|
||||
ErrPortRules = "ErrPortRules"
|
||||
ErrRepoConn = "ErrRepoConn"
|
||||
)
|
||||
|
||||
// runtime
|
||||
@@ -121,4 +119,5 @@ var (
|
||||
|
||||
var (
|
||||
ErrBackupInUsed = "ErrBackupInUsed"
|
||||
ErrOSSConn = "ErrOSSConn"
|
||||
)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user