Compare commits
144 Commits
v1.3.2
...
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 |
6
.github/workflows/build-test.yml
vendored
6
.github/workflows/build-test.yml
vendored
@@ -12,9 +12,7 @@ jobs:
|
|||||||
build-linux-binary:
|
build-linux-binary:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Install cross-compilers
|
- name: Checkout Code
|
||||||
run: sudo apt-get update && sudo apt-get -y install gcc-x86-64-linux-gnu gcc-aarch64-linux-gnu gcc-arm-linux-gnueabi gcc-powerpc64le-linux-gnu gcc-s390x-linux-gnu
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
@@ -23,7 +21,7 @@ jobs:
|
|||||||
- name: Build Web
|
- name: Build Web
|
||||||
id: build_frontend
|
id: build_frontend
|
||||||
run: |
|
run: |
|
||||||
cd frontend && npm install && npm run build:dev
|
cd frontend && npm install && npm run build:pro
|
||||||
env:
|
env:
|
||||||
NODE_OPTIONS: --max-old-space-size=8192
|
NODE_OPTIONS: --max-old-space-size=8192
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
|
28
.github/workflows/release-drafter.yml
vendored
28
.github/workflows/release-drafter.yml
vendored
@@ -10,9 +10,7 @@ jobs:
|
|||||||
create-release:
|
create-release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Install cross-compilers
|
- name: Checkout Code
|
||||||
run: sudo apt-get update && sudo apt-get -y install gcc-x86-64-linux-gnu gcc-aarch64-linux-gnu gcc-arm-linux-gnueabi gcc-powerpc64le-linux-gnu gcc-s390x-linux-gnu
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
@@ -20,7 +18,7 @@ jobs:
|
|||||||
node-version: '18.14'
|
node-version: '18.14'
|
||||||
- name: Build Web
|
- name: Build Web
|
||||||
run: |
|
run: |
|
||||||
cd frontend && npm install && npm run build:dev
|
cd frontend && npm install && npm run build:pro
|
||||||
env:
|
env:
|
||||||
NODE_OPTIONS: --max-old-space-size=8192
|
NODE_OPTIONS: --max-old-space-size=8192
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
@@ -30,17 +28,33 @@ jobs:
|
|||||||
- name: Build Release
|
- name: Build Release
|
||||||
uses: goreleaser/goreleaser-action@v4
|
uses: goreleaser/goreleaser-action@v4
|
||||||
with:
|
with:
|
||||||
|
distribution: goreleaser
|
||||||
|
version: latest
|
||||||
args: release --skip-publish --clean
|
args: release --skip-publish --clean
|
||||||
- name: Upload Assets
|
- name: Upload Assets
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
with:
|
with:
|
||||||
draft: true
|
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: |
|
files: |
|
||||||
dist/*.tar.gz
|
dist/*.tar.gz
|
||||||
dist/checksums.txt
|
dist/checksums.txt
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Setup OSSUTIL
|
- name: Setup OSSUTIL
|
||||||
uses: yizhoumo/setup-ossutil@v1
|
uses: yizhoumo/setup-ossutil@v1
|
||||||
with:
|
with:
|
||||||
@@ -49,4 +63,4 @@ jobs:
|
|||||||
access-key-secret: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
|
access-key-secret: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
|
||||||
ossutil-version: '1.7.14'
|
ossutil-version: '1.7.14'
|
||||||
- name: Upload Assets to OSS
|
- 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
|
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
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -30,5 +30,6 @@ dist/
|
|||||||
1pctl
|
1pctl
|
||||||
1panel.service
|
1panel.service
|
||||||
install.sh
|
install.sh
|
||||||
|
quick_start.sh
|
||||||
cmd/server/web/.DS_Store
|
cmd/server/web/.DS_Store
|
||||||
cmd/server/.DS_Store
|
cmd/server/.DS_Store
|
||||||
|
@@ -16,18 +16,8 @@ builds:
|
|||||||
- -trimpath
|
- -trimpath
|
||||||
ldflags:
|
ldflags:
|
||||||
- -w -s
|
- -w -s
|
||||||
- --extldflags "-static -fpic"
|
|
||||||
tags:
|
|
||||||
- osusergo
|
|
||||||
env:
|
env:
|
||||||
- CGO_ENABLED=1
|
- CGO_ENABLED=0
|
||||||
- >-
|
|
||||||
{{- if eq .Arch "amd64"}}CC=x86_64-linux-gnu-gcc{{- end }}
|
|
||||||
{{- if eq .Arch "arm64"}}CC=aarch64-linux-gnu-gcc{{- end }}
|
|
||||||
{{- if eq .Arch "arm"}}CC=arm-linux-gnueabi-gcc{{- end }}
|
|
||||||
{{- if eq .Arch "loong64"}}CC=loongarch64-linux-gnu-gcc{{- end }}
|
|
||||||
{{- if eq .Arch "ppc64le"}}CC=powerpc64le-linux-gnu-gcc{{- end }}
|
|
||||||
{{- if eq .Arch "s390x"}}CC=s390x-linux-gnu-gcc{{- end }}
|
|
||||||
goos:
|
goos:
|
||||||
- linux
|
- linux
|
||||||
goarm:
|
goarm:
|
||||||
|
34
Dockerfile
34
Dockerfile
@@ -1,34 +0,0 @@
|
|||||||
FROM node:18.14 as build_web
|
|
||||||
ARG TARGETARCH
|
|
||||||
ARG NPM_REGISTRY="https://registry.npmmirror.com"
|
|
||||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
|
||||||
|
|
||||||
WORKDIR /data
|
|
||||||
|
|
||||||
RUN set -ex \
|
|
||||||
&& npm config set registry ${NPM_REGISTRY}
|
|
||||||
|
|
||||||
ADD . /data
|
|
||||||
|
|
||||||
RUN set -ex \
|
|
||||||
&& cd /data/frontend \
|
|
||||||
&& npm install
|
|
||||||
|
|
||||||
RUN set -ex \
|
|
||||||
&& cd /data/frontend \
|
|
||||||
&& npm run build:dev
|
|
||||||
|
|
||||||
FROM golang:1.20
|
|
||||||
ARG TARGETARCH
|
|
||||||
ARG GOPROXY="https://goproxy.cn,direct"
|
|
||||||
|
|
||||||
COPY --from=build_web /data /data
|
|
||||||
|
|
||||||
WORKDIR /data
|
|
||||||
|
|
||||||
RUN set -ex \
|
|
||||||
&& go env -w GOPROXY=${GOPROXY} \
|
|
||||||
&& go install github.com/goreleaser/goreleaser@latest \
|
|
||||||
&& goreleaser build --single-target --snapshot --clean
|
|
||||||
|
|
||||||
CMD ["/bin/bash"]
|
|
10
Makefile
10
Makefile
@@ -23,16 +23,16 @@ build_frontend:
|
|||||||
|
|
||||||
build_backend_on_linux:
|
build_backend_on_linux:
|
||||||
cd $(SERVER_PATH) \
|
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:
|
build_backend_on_darwin:
|
||||||
cd $(SERVER_PATH) \
|
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:
|
build_backend_on_archlinux:
|
||||||
cd $(SERVER_PATH) \
|
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
|
build_on_local: clean_assets build_frontend build_backend_on_darwin upx_bin
|
||||||
|
@@ -130,6 +130,22 @@ func (b *BaseApi) GetAppDetailByID(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, appDetailDTO)
|
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
|
// @Tags App
|
||||||
// @Summary Install app
|
// @Summary Install app
|
||||||
// @Description 安装应用
|
// @Description 安装应用
|
||||||
|
@@ -118,7 +118,7 @@ func (b *BaseApi) LoadConnInfo(c *gin.Context) {
|
|||||||
// @Description 删除前检查
|
// @Description 删除前检查
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param appInstallId path integer true "App install id"
|
// @Param appInstallId path integer true "App install id"
|
||||||
// @Success 200 {anrry} dto.AppResource
|
// @Success 200 {array} dto.AppResource
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /apps/installed/delete/check/:appInstallId [get]
|
// @Router /apps/installed/delete/check/:appInstallId [get]
|
||||||
func (b *BaseApi) DeleteCheck(c *gin.Context) {
|
func (b *BaseApi) DeleteCheck(c *gin.Context) {
|
||||||
@@ -178,7 +178,7 @@ func (b *BaseApi) OperateInstalled(c *gin.Context) {
|
|||||||
// @Description 通过 key 获取应用 service
|
// @Description 通过 key 获取应用 service
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param key path string true "request"
|
// @Param key path string true "request"
|
||||||
// @Success 200 {anrry} response.AppService
|
// @Success 200 {array} response.AppService
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /apps/services/:key [get]
|
// @Router /apps/services/:key [get]
|
||||||
func (b *BaseApi) GetServices(c *gin.Context) {
|
func (b *BaseApi) GetServices(c *gin.Context) {
|
||||||
@@ -196,7 +196,7 @@ func (b *BaseApi) GetServices(c *gin.Context) {
|
|||||||
// @Description 通过 install id 获取应用更新版本
|
// @Description 通过 install id 获取应用更新版本
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param appInstallId path integer true "request"
|
// @Param appInstallId path integer true "request"
|
||||||
// @Success 200 {anrry} dto.AppVersion
|
// @Success 200 {array} dto.AppVersion
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /apps/installed/:appInstallId/versions [get]
|
// @Router /apps/installed/:appInstallId/versions [get]
|
||||||
func (b *BaseApi) GetUpdateVersions(c *gin.Context) {
|
func (b *BaseApi) GetUpdateVersions(c *gin.Context) {
|
||||||
@@ -305,3 +305,25 @@ func (b *BaseApi) UpdateInstalled(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
helper.SuccessWithData(c, nil)
|
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)
|
||||||
|
}
|
||||||
|
@@ -60,7 +60,7 @@ func (b *BaseApi) CreateBackup(c *gin.Context) {
|
|||||||
// @Description 获取 bucket 列表
|
// @Description 获取 bucket 列表
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.ForBuckets true "request"
|
// @Param request body dto.ForBuckets true "request"
|
||||||
// @Success 200 {anrry} string
|
// @Success 200 {array} string
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /settings/backup/search [post]
|
// @Router /settings/backup/search [post]
|
||||||
func (b *BaseApi) ListBuckets(c *gin.Context) {
|
func (b *BaseApi) ListBuckets(c *gin.Context) {
|
||||||
@@ -98,6 +98,22 @@ func (b *BaseApi) ListBuckets(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, buckets)
|
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
|
// @Tags Backup Account
|
||||||
// @Summary Delete backup account
|
// @Summary Delete backup account
|
||||||
// @Description 删除备份账号
|
// @Description 删除备份账号
|
||||||
@@ -253,7 +269,7 @@ func (b *BaseApi) UpdateBackup(c *gin.Context) {
|
|||||||
// @Tags Backup Account
|
// @Tags Backup Account
|
||||||
// @Summary List backup accounts
|
// @Summary List backup accounts
|
||||||
// @Description 获取备份账号列表
|
// @Description 获取备份账号列表
|
||||||
// @Success 200 {anrry} dto.BackupInfo
|
// @Success 200 {array} dto.BackupInfo
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /settings/backup/search [get]
|
// @Router /settings/backup/search [get]
|
||||||
func (b *BaseApi) ListBackup(c *gin.Context) {
|
func (b *BaseApi) ListBackup(c *gin.Context) {
|
||||||
@@ -271,7 +287,7 @@ func (b *BaseApi) ListBackup(c *gin.Context) {
|
|||||||
// @Description 获取备份账号内文件列表
|
// @Description 获取备份账号内文件列表
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.BackupSearchFile true "request"
|
// @Param request body dto.BackupSearchFile true "request"
|
||||||
// @Success 200 {anrry} string
|
// @Success 200 {array} string
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /settings/backup/search/files [post]
|
// @Router /settings/backup/search/files [post]
|
||||||
func (b *BaseApi) LoadFilesFromBackup(c *gin.Context) {
|
func (b *BaseApi) LoadFilesFromBackup(c *gin.Context) {
|
||||||
|
@@ -66,7 +66,7 @@ func (b *BaseApi) SearchComposeTemplate(c *gin.Context) {
|
|||||||
// @Summary List compose templates
|
// @Summary List compose templates
|
||||||
// @Description 获取容器编排模版列表
|
// @Description 获取容器编排模版列表
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {anrry} dto.ComposeTemplateInfo
|
// @Success 200 {array} dto.ComposeTemplateInfo
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /containers/template [get]
|
// @Router /containers/template [get]
|
||||||
func (b *BaseApi) ListComposeTemplate(c *gin.Context) {
|
func (b *BaseApi) ListComposeTemplate(c *gin.Context) {
|
||||||
|
@@ -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
|
// @Tags Container Compose
|
||||||
// @Summary Page composes
|
// @Summary Page composes
|
||||||
// @Description 获取编排列表分页
|
// @Description 获取编排列表分页
|
||||||
@@ -153,17 +170,97 @@ func (b *BaseApi) OperatorCompose(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, nil)
|
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
|
// @Tags Container
|
||||||
// @Summary Create container
|
// @Summary Create container
|
||||||
// @Description 创建容器
|
// @Description 创建容器
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.ContainerCreate true "request"
|
// @Param request body dto.ContainerOperate true "request"
|
||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /containers [post]
|
// @Router /containers [post]
|
||||||
// @x-panel-log {"bodyKeys":["name","image"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建容器 [name][image]","formatEN":"create container [name][image]"}
|
// @x-panel-log {"bodyKeys":["name","image"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建容器 [name][image]","formatEN":"create container [name][image]"}
|
||||||
func (b *BaseApi) ContainerCreate(c *gin.Context) {
|
func (b *BaseApi) ContainerCreate(c *gin.Context) {
|
||||||
var req dto.ContainerCreate
|
var req dto.ContainerOperate
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
return
|
return
|
||||||
@@ -179,6 +276,32 @@ func (b *BaseApi) ContainerCreate(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, nil)
|
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
|
// @Tags Container
|
||||||
// @Summary Clean container
|
// @Summary Clean container
|
||||||
// @Description 容器清理
|
// @Description 容器清理
|
||||||
@@ -262,7 +385,7 @@ func (b *BaseApi) ContainerOperation(c *gin.Context) {
|
|||||||
// @Summary Container stats
|
// @Summary Container stats
|
||||||
// @Description 容器监控信息
|
// @Description 容器监控信息
|
||||||
// @Param id path integer true "容器id"
|
// @Param id path integer true "容器id"
|
||||||
// @Success 200 {object} dto.ContainterStats
|
// @Success 200 {object} dto.ContainerStats
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /containers/stats/:id [get]
|
// @Router /containers/stats/:id [get]
|
||||||
func (b *BaseApi) ContainerStats(c *gin.Context) {
|
func (b *BaseApi) ContainerStats(c *gin.Context) {
|
||||||
@@ -310,27 +433,29 @@ func (b *BaseApi) Inspect(c *gin.Context) {
|
|||||||
// @Tags Container
|
// @Tags Container
|
||||||
// @Summary Container logs
|
// @Summary Container logs
|
||||||
// @Description 容器日志
|
// @Description 容器日志
|
||||||
// @Accept json
|
// @Param container query string false "容器名称"
|
||||||
// @Param request body dto.ContainerLog true "request"
|
// @Param since query string false "时间筛选"
|
||||||
// @Success 200 {string} logs
|
// @Param follow query string false "是否追踪"
|
||||||
|
// @Param tail query string false "显示行号"
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /containers/search/log [post]
|
// @Router /containers/search/log [post]
|
||||||
func (b *BaseApi) ContainerLogs(c *gin.Context) {
|
func (b *BaseApi) ContainerLogs(c *gin.Context) {
|
||||||
var req dto.ContainerLog
|
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||||
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)
|
|
||||||
if err != 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
|
return
|
||||||
}
|
}
|
||||||
helper.SuccessWithData(c, logs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Tags Container Network
|
// @Tags Container Network
|
||||||
@@ -364,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
|
// @Tags Container Network
|
||||||
// @Summary Delete network
|
// @Summary Delete network
|
||||||
// @Description 删除容器网络
|
// @Description 删除容器网络
|
||||||
@@ -453,11 +595,10 @@ func (b *BaseApi) SearchVolume(c *gin.Context) {
|
|||||||
// @Summary List volumes
|
// @Summary List volumes
|
||||||
// @Description 获取容器存储卷列表
|
// @Description 获取容器存储卷列表
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.PageInfo true "request"
|
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} dto.PageResult
|
// @Success 200 {array} dto.Options
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /containers/volume/search [get]
|
// @Router /containers/volume [get]
|
||||||
func (b *BaseApi) ListVolume(c *gin.Context) {
|
func (b *BaseApi) ListVolume(c *gin.Context) {
|
||||||
list, err := containerService.ListVolume()
|
list, err := containerService.ListVolume()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -216,7 +216,7 @@ func (b *BaseApi) SearchMysql(c *gin.Context) {
|
|||||||
// @Description 获取 mysql 数据库列表
|
// @Description 获取 mysql 数据库列表
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.PageInfo true "request"
|
// @Param request body dto.PageInfo true "request"
|
||||||
// @Success 200 {anrry} string
|
// @Success 200 {array} string
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /databases/options [get]
|
// @Router /databases/options [get]
|
||||||
func (b *BaseApi) ListDBName(c *gin.Context) {
|
func (b *BaseApi) ListDBName(c *gin.Context) {
|
||||||
@@ -234,7 +234,7 @@ func (b *BaseApi) ListDBName(c *gin.Context) {
|
|||||||
// @Description Mysql 数据库删除前检查
|
// @Description Mysql 数据库删除前检查
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.OperateByID true "request"
|
// @Param request body dto.OperateByID true "request"
|
||||||
// @Success 200 {anrry} string
|
// @Success 200 {array} string
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /databases/del/check [post]
|
// @Router /databases/del/check [post]
|
||||||
func (b *BaseApi) DeleteCheckMysql(c *gin.Context) {
|
func (b *BaseApi) DeleteCheckMysql(c *gin.Context) {
|
||||||
|
@@ -49,4 +49,5 @@ var (
|
|||||||
upgradeService = service.NewIUpgradeService()
|
upgradeService = service.NewIUpgradeService()
|
||||||
|
|
||||||
runtimeService = service.NewRuntimeService()
|
runtimeService = service.NewRuntimeService()
|
||||||
|
processService = service.NewIProcessService()
|
||||||
)
|
)
|
||||||
|
@@ -52,7 +52,7 @@ func (b *BaseApi) ListFiles(c *gin.Context) {
|
|||||||
// @Description 分页获取上传文件
|
// @Description 分页获取上传文件
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body request.SearchUploadWithPage true "request"
|
// @Param request body request.SearchUploadWithPage true "request"
|
||||||
// @Success 200 {anrry} response.FileInfo
|
// @Success 200 {array} response.FileInfo
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /files/upload/search [post]
|
// @Router /files/upload/search [post]
|
||||||
func (b *BaseApi) SearchUploadWithPage(c *gin.Context) {
|
func (b *BaseApi) SearchUploadWithPage(c *gin.Context) {
|
||||||
@@ -81,7 +81,7 @@ func (b *BaseApi) SearchUploadWithPage(c *gin.Context) {
|
|||||||
// @Description 加载文件树
|
// @Description 加载文件树
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body request.FileOption true "request"
|
// @Param request body request.FileOption true "request"
|
||||||
// @Success 200 {anrry} response.FileTree
|
// @Success 200 {array} response.FileTree
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /files/tree [post]
|
// @Router /files/tree [post]
|
||||||
func (b *BaseApi) GetFileTree(c *gin.Context) {
|
func (b *BaseApi) GetFileTree(c *gin.Context) {
|
||||||
@@ -532,7 +532,7 @@ func (b *BaseApi) DownloadChunkFiles(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
file.Seek(startPos, 0)
|
_, _ = file.Seek(startPos, 0)
|
||||||
reader := io.LimitReader(file, endPos-startPos+1)
|
reader := io.LimitReader(file, endPos-startPos+1)
|
||||||
_, err = io.CopyBuffer(c.Writer, reader, buffer)
|
_, err = io.CopyBuffer(c.Writer, reader, buffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -683,7 +683,12 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
filename := c.PostForm("filename")
|
filename := c.PostForm("filename")
|
||||||
fileDir := filepath.Join(tmpDir, 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)
|
filePath := filepath.Join(fileDir, filename)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -734,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) {
|
func (b *BaseApi) Ws(c *gin.Context) {
|
||||||
ws, err := wsUpgrade.Upgrade(c.Writer, c.Request, nil)
|
ws, err := wsUpgrade.Upgrade(c.Writer, c.Request, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
wsClient := websocket2.NewWsClient("wsClient", ws)
|
wsClient := websocket2.NewWsClient("fileClient", ws)
|
||||||
go wsClient.Read()
|
go wsClient.Read()
|
||||||
go wsClient.Write()
|
go wsClient.Write()
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
"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"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,7 +36,14 @@ func (b *BaseApi) CreateHost(c *gin.Context) {
|
|||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
return
|
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 {
|
if req.AuthMode == "key" && len(req.PrivateKey) != 0 {
|
||||||
privateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)
|
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)
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
return
|
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)
|
host, err := hostService.Create(req)
|
||||||
@@ -102,7 +124,7 @@ func (b *BaseApi) TestByID(c *gin.Context) {
|
|||||||
// @Description 加载主机树
|
// @Description 加载主机树
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.SearchForTree true "request"
|
// @Param request body dto.SearchForTree true "request"
|
||||||
// @Success 200 {anrry} dto.HostTree
|
// @Success 200 {array} dto.HostTree
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /hosts/tree [post]
|
// @Router /hosts/tree [post]
|
||||||
func (b *BaseApi) HostTree(c *gin.Context) {
|
func (b *BaseApi) HostTree(c *gin.Context) {
|
||||||
@@ -126,7 +148,7 @@ func (b *BaseApi) HostTree(c *gin.Context) {
|
|||||||
// @Description 获取主机列表分页
|
// @Description 获取主机列表分页
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.SearchHostWithPage true "request"
|
// @Param request body dto.SearchHostWithPage true "request"
|
||||||
// @Success 200 {anrry} dto.HostTree
|
// @Success 200 {array} dto.HostTree
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /hosts/search [post]
|
// @Router /hosts/search [post]
|
||||||
func (b *BaseApi) SearchHost(c *gin.Context) {
|
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
|
// @Tags Host
|
||||||
// @Summary Delete host
|
// @Summary Delete host
|
||||||
// @Description 删除主机
|
// @Description 删除主机
|
||||||
@@ -227,7 +222,12 @@ func (b *BaseApi) UpdateHost(c *gin.Context) {
|
|||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
return
|
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 {
|
if req.AuthMode == "key" && len(req.PrivateKey) != 0 {
|
||||||
privateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)
|
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)
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
return
|
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{})
|
upMap := make(map[string]interface{})
|
||||||
@@ -246,10 +260,12 @@ func (b *BaseApi) UpdateHost(c *gin.Context) {
|
|||||||
upMap["user"] = req.User
|
upMap["user"] = req.User
|
||||||
upMap["auth_mode"] = req.AuthMode
|
upMap["auth_mode"] = req.AuthMode
|
||||||
upMap["remember_password"] = req.RememberPassword
|
upMap["remember_password"] = req.RememberPassword
|
||||||
if len(req.Password) != 0 {
|
if req.AuthMode == "password" {
|
||||||
upMap["password"] = req.Password
|
upMap["password"] = req.Password
|
||||||
}
|
upMap["private_key"] = ""
|
||||||
if len(req.PrivateKey) != 0 {
|
upMap["pass_phrase"] = ""
|
||||||
|
} else {
|
||||||
|
upMap["password"] = ""
|
||||||
upMap["private_key"] = req.PrivateKey
|
upMap["private_key"] = req.PrivateKey
|
||||||
upMap["pass_phrase"] = req.PassPhrase
|
upMap["pass_phrase"] = req.PassPhrase
|
||||||
}
|
}
|
||||||
|
@@ -44,7 +44,7 @@ func (b *BaseApi) SearchImage(c *gin.Context) {
|
|||||||
// @Summary List images
|
// @Summary List images
|
||||||
// @Description 获取镜像列表
|
// @Description 获取镜像列表
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {anrry} dto.Options
|
// @Success 200 {array} dto.Options
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /containers/image [get]
|
// @Router /containers/image [get]
|
||||||
func (b *BaseApi) ListImage(c *gin.Context) {
|
func (b *BaseApi) ListImage(c *gin.Context) {
|
||||||
|
@@ -44,7 +44,7 @@ func (b *BaseApi) SearchRepo(c *gin.Context) {
|
|||||||
// @Summary List image repos
|
// @Summary List image repos
|
||||||
// @Description 获取镜像仓库列表
|
// @Description 获取镜像仓库列表
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {anrry} dto.ImageRepoOption
|
// @Success 200 {array} dto.ImageRepoOption
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /containers/repo [get]
|
// @Router /containers/repo [get]
|
||||||
func (b *BaseApi) ListRepo(c *gin.Context) {
|
func (b *BaseApi) ListRepo(c *gin.Context) {
|
||||||
|
@@ -27,7 +27,7 @@ func (b *BaseApi) GetNginx(c *gin.Context) {
|
|||||||
// @Description 获取部分 OpenResty 配置信息
|
// @Description 获取部分 OpenResty 配置信息
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body request.NginxScopeReq true "request"
|
// @Param request body request.NginxScopeReq true "request"
|
||||||
// @Success 200 {anrry} response.NginxParam
|
// @Success 200 {array} response.NginxParam
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /openResty/scope [post]
|
// @Router /openResty/scope [post]
|
||||||
func (b *BaseApi) GetNginxConfigByScope(c *gin.Context) {
|
func (b *BaseApi) GetNginxConfigByScope(c *gin.Context) {
|
||||||
|
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,6 +2,8 @@ package v1
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
@@ -260,11 +262,23 @@ func (b *BaseApi) CleanMonitor(c *gin.Context) {
|
|||||||
// @Tags System Setting
|
// @Tags System Setting
|
||||||
// @Summary Load mfa info
|
// @Summary Load mfa info
|
||||||
// @Description 获取 mfa 信息
|
// @Description 获取 mfa 信息
|
||||||
|
// @Param interval path string true "request"
|
||||||
// @Success 200 {object} mfa.Otp
|
// @Success 200 {object} mfa.Otp
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /settings/mfa [get]
|
// @Router /settings/mfa/:interval [get]
|
||||||
func (b *BaseApi) GetMFA(c *gin.Context) {
|
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 {
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
@@ -288,12 +302,17 @@ func (b *BaseApi) MFABind(c *gin.Context) {
|
|||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
success := mfa.ValidCode(req.Code, req.Secret)
|
success := mfa.ValidCode(req.Code, req.Interval, req.Secret)
|
||||||
if !success {
|
if !success {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, errors.New("code is not valid"))
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, errors.New("code is not valid"))
|
||||||
return
|
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 {
|
if err := settingService.Update("MFAStatus", "enable"); err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
|
@@ -9,8 +9,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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/global"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/copier"
|
"github.com/1Panel-dev/1Panel/backend/utils/copier"
|
||||||
@@ -22,24 +20,27 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (b *BaseApi) WsSsh(c *gin.Context) {
|
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 {
|
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
|
return
|
||||||
}
|
}
|
||||||
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
||||||
if err != nil {
|
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
||||||
if err != nil {
|
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
host, err := hostService.GetHostInfo(uint(id))
|
host, err := hostService.GetHostInfo(uint(id))
|
||||||
if err != nil {
|
if wshandleError(wsConn, errors.WithMessage(err, "load host info by id failed")) {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var connInfo ssh.ConnInfo
|
var connInfo ssh.ConnInfo
|
||||||
@@ -49,13 +50,6 @@ func (b *BaseApi) WsSsh(c *gin.Context) {
|
|||||||
connInfo.PassPhrase = []byte(host.PassPhrase)
|
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()
|
client, err := connInfo.NewClient()
|
||||||
if wshandleError(wsConn, errors.WithMessage(err, "failed to set up the connection. Please check the host information")) {
|
if wshandleError(wsConn, errors.WithMessage(err, "failed to set up the connection. Please check the host information")) {
|
||||||
return
|
return
|
||||||
@@ -85,28 +79,25 @@ func (b *BaseApi) WsSsh(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseApi) RedisWsSsh(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)
|
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||||
return
|
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()
|
defer wsConn.Close()
|
||||||
commands := "redis-cli"
|
commands := "redis-cli"
|
||||||
if len(redisConf.Requirepass) != 0 {
|
if len(redisConf.Requirepass) != 0 {
|
||||||
@@ -138,24 +129,6 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseApi) ContainerWsSsh(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)
|
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||||
@@ -163,11 +136,33 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
defer wsConn.Close()
|
defer wsConn.Close()
|
||||||
|
|
||||||
cmds := fmt.Sprintf("docker exec %s %s", containerID, command)
|
containerID := c.Query("containerid")
|
||||||
if len(user) != 0 {
|
command := c.Query("command")
|
||||||
cmds = fmt.Sprintf("docker exec -u %s %s %s", user, containerID, 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)) {
|
if wshandleError(wsConn, errors.WithMessage(err, stdout)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@@ -36,7 +36,7 @@ func (b *BaseApi) PageWebsite(c *gin.Context) {
|
|||||||
// @Tags Website
|
// @Tags Website
|
||||||
// @Summary List websites
|
// @Summary List websites
|
||||||
// @Description 获取网站列表
|
// @Description 获取网站列表
|
||||||
// @Success 200 {anrry} response.WebsiteDTO
|
// @Success 200 {array} response.WebsiteDTO
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /websites/list [get]
|
// @Router /websites/list [get]
|
||||||
func (b *BaseApi) GetWebsites(c *gin.Context) {
|
func (b *BaseApi) GetWebsites(c *gin.Context) {
|
||||||
@@ -51,7 +51,7 @@ func (b *BaseApi) GetWebsites(c *gin.Context) {
|
|||||||
// @Tags Website
|
// @Tags Website
|
||||||
// @Summary List website names
|
// @Summary List website names
|
||||||
// @Description 获取网站列表
|
// @Description 获取网站列表
|
||||||
// @Success 200 {anrry} string
|
// @Success 200 {array} string
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /websites/options [get]
|
// @Router /websites/options [get]
|
||||||
func (b *BaseApi) GetWebsiteOptions(c *gin.Context) {
|
func (b *BaseApi) GetWebsiteOptions(c *gin.Context) {
|
||||||
@@ -207,7 +207,7 @@ func (b *BaseApi) GetWebsiteNginx(c *gin.Context) {
|
|||||||
// @Description 通过网站 id 查询域名
|
// @Description 通过网站 id 查询域名
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param websiteId path integer true "request"
|
// @Param websiteId path integer true "request"
|
||||||
// @Success 200 {anrry} model.WebsiteDomain
|
// @Success 200 {array} model.WebsiteDomain
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /websites/domains/:websiteId [get]
|
// @Router /websites/domains/:websiteId [get]
|
||||||
func (b *BaseApi) GetWebDomains(c *gin.Context) {
|
func (b *BaseApi) GetWebDomains(c *gin.Context) {
|
||||||
@@ -367,7 +367,7 @@ func (b *BaseApi) UpdateHTTPSConfig(c *gin.Context) {
|
|||||||
// @Description 网站创建前检查
|
// @Description 网站创建前检查
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body request.WebsiteInstallCheckReq true "request"
|
// @Param request body request.WebsiteInstallCheckReq true "request"
|
||||||
// @Success 200 {anrry} request.WebsitePreInstallCheck
|
// @Success 200 {array} response.WebsitePreInstallCheck
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /websites/check [post]
|
// @Router /websites/check [post]
|
||||||
func (b *BaseApi) CreateWebsiteCheck(c *gin.Context) {
|
func (b *BaseApi) CreateWebsiteCheck(c *gin.Context) {
|
||||||
@@ -541,7 +541,7 @@ func (b *BaseApi) UpdateWebsitePHPConfig(c *gin.Context) {
|
|||||||
|
|
||||||
// @Tags Website PHP
|
// @Tags Website PHP
|
||||||
// @Summary Update php conf
|
// @Summary Update php conf
|
||||||
// @Description 更新 php 配置
|
// @Description 更新 php 配置文件
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body request.WebsitePHPFileUpdate true "request"
|
// @Param request body request.WebsitePHPFileUpdate true "request"
|
||||||
// @Success 200
|
// @Success 200
|
||||||
|
@@ -35,7 +35,7 @@ func (b *BaseApi) PageWebsiteSSL(c *gin.Context) {
|
|||||||
Items: accounts,
|
Items: accounts,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
list, err := websiteSSLService.Search()
|
list, err := websiteSSLService.Search(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
@@ -94,7 +94,7 @@ func (b *BaseApi) RenewWebsiteSSL(c *gin.Context) {
|
|||||||
// @Description 解析网站 ssl
|
// @Description 解析网站 ssl
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body request.WebsiteDNSReq true "request"
|
// @Param request body request.WebsiteDNSReq true "request"
|
||||||
// @Success 200 {anrry} response.WebsiteDNSRes
|
// @Success 200 {array} response.WebsiteDNSRes
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /websites/ssl/resolve [post]
|
// @Router /websites/ssl/resolve [post]
|
||||||
func (b *BaseApi) GetDNSResolve(c *gin.Context) {
|
func (b *BaseApi) GetDNSResolve(c *gin.Context) {
|
||||||
|
@@ -12,8 +12,9 @@ type UserLoginInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MfaCredential struct {
|
type MfaCredential struct {
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
|
Interval string `json:"interval"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Login struct {
|
type Login struct {
|
||||||
|
@@ -8,15 +8,17 @@ type BackupOperate struct {
|
|||||||
Bucket string `json:"bucket"`
|
Bucket string `json:"bucket"`
|
||||||
AccessKey string `json:"accessKey"`
|
AccessKey string `json:"accessKey"`
|
||||||
Credential string `json:"credential"`
|
Credential string `json:"credential"`
|
||||||
|
BackupPath string `json:"backupPath"`
|
||||||
Vars string `json:"vars" validate:"required"`
|
Vars string `json:"vars" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BackupInfo struct {
|
type BackupInfo struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Bucket string `json:"bucket"`
|
Bucket string `json:"bucket"`
|
||||||
Vars string `json:"vars"`
|
BackupPath string `json:"backupPath"`
|
||||||
|
Vars string `json:"vars"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BackupSearch struct {
|
type BackupSearch struct {
|
||||||
@@ -36,7 +38,7 @@ type CommonBackup struct {
|
|||||||
DetailName string `json:"detailName"`
|
DetailName string `json:"detailName"`
|
||||||
}
|
}
|
||||||
type CommonRecover struct {
|
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"`
|
Type string `json:"type" validate:"required,oneof=app mysql redis website"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
DetailName string `json:"detailName"`
|
DetailName string `json:"detailName"`
|
||||||
@@ -60,7 +62,7 @@ type BackupRecords struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DownloadRecord 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"`
|
FileDir string `json:"fileDir" validate:"required"`
|
||||||
FileName string `json:"fileName" validate:"required"`
|
FileName string `json:"fileName" validate:"required"`
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,9 @@ package dto
|
|||||||
|
|
||||||
type SearchWithPage struct {
|
type SearchWithPage struct {
|
||||||
PageInfo
|
PageInfo
|
||||||
Info string `json:"info"`
|
Info string `json:"info"`
|
||||||
|
OrderBy string `json:"orderBy"`
|
||||||
|
Order string `json:"order"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PageInfo struct {
|
type PageInfo struct {
|
||||||
|
@@ -5,6 +5,8 @@ import "time"
|
|||||||
type PageContainer struct {
|
type PageContainer struct {
|
||||||
PageInfo
|
PageInfo
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
OrderBy string `json:"orderBy"`
|
||||||
|
Order string `json:"order"`
|
||||||
Filters string `json:"filters"`
|
Filters string `json:"filters"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,22 +24,29 @@ type ContainerInfo struct {
|
|||||||
State string `json:"state"`
|
State string `json:"state"`
|
||||||
RunTime string `json:"runTime"`
|
RunTime string `json:"runTime"`
|
||||||
|
|
||||||
CPUPercent float64 `json:"cpuPercent"`
|
Ports []string `json:"ports"`
|
||||||
MemoryPercent float64 `json:"memoryPercent"`
|
|
||||||
Ports []string `json:"ports"`
|
|
||||||
|
|
||||||
IsFromApp bool `json:"isFromApp"`
|
IsFromApp bool `json:"isFromApp"`
|
||||||
IsFromCompose bool `json:"isFromCompose"`
|
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"`
|
Name string `json:"name"`
|
||||||
Image string `json:"image"`
|
Image string `json:"image"`
|
||||||
|
Network string `json:"network"`
|
||||||
PublishAllPorts bool `json:"publishAllPorts"`
|
PublishAllPorts bool `json:"publishAllPorts"`
|
||||||
ExposedPorts []PortHelper `json:"exposedPorts"`
|
ExposedPorts []PortHelper `json:"exposedPorts"`
|
||||||
Cmd []string `json:"cmd"`
|
Cmd []string `json:"cmd"`
|
||||||
NanoCPUs int64 `json:"nanoCPUs"`
|
CPUShares int64 `json:"cpuShares"`
|
||||||
Memory int64 `json:"memory"`
|
NanoCPUs float64 `json:"nanoCPUs"`
|
||||||
|
Memory float64 `json:"memory"`
|
||||||
AutoRemove bool `json:"autoRemove"`
|
AutoRemove bool `json:"autoRemove"`
|
||||||
Volumes []VolumeHelper `json:"volumes"`
|
Volumes []VolumeHelper `json:"volumes"`
|
||||||
Labels []string `json:"labels"`
|
Labels []string `json:"labels"`
|
||||||
@@ -45,7 +54,19 @@ type ContainerCreate struct {
|
|||||||
RestartPolicy string `json:"restartPolicy"`
|
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"`
|
CPUPercent float64 `json:"cpuPercent"`
|
||||||
Memory float64 `json:"memory"`
|
Memory float64 `json:"memory"`
|
||||||
Cache float64 `json:"cache"`
|
Cache float64 `json:"cache"`
|
||||||
@@ -69,11 +90,6 @@ type PortHelper struct {
|
|||||||
Protocol string `json:"protocol"`
|
Protocol string `json:"protocol"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContainerLog struct {
|
|
||||||
ContainerID string `json:"containerID" validate:"required"`
|
|
||||||
Mode string `json:"mode" validate:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ContainerOperation struct {
|
type ContainerOperation struct {
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
Operation string `json:"operation" validate:"required,oneof=start stop restart kill pause unpause rename remove"`
|
Operation string `json:"operation" validate:"required,oneof=start stop restart kill pause unpause rename remove"`
|
||||||
@@ -82,7 +98,7 @@ type ContainerOperation struct {
|
|||||||
|
|
||||||
type ContainerPrune struct {
|
type ContainerPrune struct {
|
||||||
PruneType string `json:"pruneType" validate:"required,oneof=container image volume network"`
|
PruneType string `json:"pruneType" validate:"required,oneof=container image volume network"`
|
||||||
WithTagAll bool `josn:"withTagAll"`
|
WithTagAll bool `json:"withTagAll"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContainerPruneReport struct {
|
type ContainerPruneReport struct {
|
||||||
|
@@ -6,13 +6,14 @@ type CronjobCreate struct {
|
|||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
Type string `json:"type" validate:"required"`
|
Type string `json:"type" validate:"required"`
|
||||||
SpecType string `json:"specType" 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"`
|
Day int `json:"day" validate:"number"`
|
||||||
Hour int `json:"hour" validate:"number"`
|
Hour int `json:"hour" validate:"number"`
|
||||||
Minute int `json:"minute" validate:"number"`
|
Minute int `json:"minute" validate:"number"`
|
||||||
Second int `json:"second" validate:"number"`
|
Second int `json:"second" validate:"number"`
|
||||||
|
|
||||||
Script string `json:"script"`
|
Script string `json:"script"`
|
||||||
|
ContainerName string `json:"containerName"`
|
||||||
Website string `json:"website"`
|
Website string `json:"website"`
|
||||||
ExclusionRules string `json:"exclusionRules"`
|
ExclusionRules string `json:"exclusionRules"`
|
||||||
DBName string `json:"dbName"`
|
DBName string `json:"dbName"`
|
||||||
@@ -27,13 +28,14 @@ type CronjobUpdate struct {
|
|||||||
ID uint `json:"id" validate:"required"`
|
ID uint `json:"id" validate:"required"`
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
SpecType string `json:"specType" 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"`
|
Day int `json:"day" validate:"number"`
|
||||||
Hour int `json:"hour" validate:"number"`
|
Hour int `json:"hour" validate:"number"`
|
||||||
Minute int `json:"minute" validate:"number"`
|
Minute int `json:"minute" validate:"number"`
|
||||||
Second int `json:"second" validate:"number"`
|
Second int `json:"second" validate:"number"`
|
||||||
|
|
||||||
Script string `json:"script"`
|
Script string `json:"script"`
|
||||||
|
ContainerName string `json:"containerName"`
|
||||||
Website string `json:"website"`
|
Website string `json:"website"`
|
||||||
ExclusionRules string `json:"exclusionRules"`
|
ExclusionRules string `json:"exclusionRules"`
|
||||||
DBName string `json:"dbName"`
|
DBName string `json:"dbName"`
|
||||||
@@ -76,6 +78,7 @@ type CronjobInfo struct {
|
|||||||
Second int `json:"second"`
|
Second int `json:"second"`
|
||||||
|
|
||||||
Script string `json:"script"`
|
Script string `json:"script"`
|
||||||
|
ContainerName string `json:"containerName"`
|
||||||
Website string `json:"website"`
|
Website string `json:"website"`
|
||||||
ExclusionRules string `json:"exclusionRules"`
|
ExclusionRules string `json:"exclusionRules"`
|
||||||
DBName string `json:"dbName"`
|
DBName string `json:"dbName"`
|
||||||
|
@@ -10,35 +10,35 @@ type ImageInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ImageLoad struct {
|
type ImageLoad struct {
|
||||||
Path string `josn:"path" validate:"required"`
|
Path string `json:"path" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImageBuild struct {
|
type ImageBuild struct {
|
||||||
From string `josn:"from" validate:"required"`
|
From string `json:"from" validate:"required"`
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
Dockerfile string `josn:"dockerfile" validate:"required"`
|
Dockerfile string `json:"dockerfile" validate:"required"`
|
||||||
Tags []string `josn:"tags"`
|
Tags []string `json:"tags"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImagePull struct {
|
type ImagePull struct {
|
||||||
RepoID uint `josn:"repoID"`
|
RepoID uint `json:"repoID"`
|
||||||
ImageName string `josn:"imageName" validate:"required"`
|
ImageName string `json:"imageName" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImageTag struct {
|
type ImageTag struct {
|
||||||
RepoID uint `josn:"repoID"`
|
RepoID uint `json:"repoID"`
|
||||||
SourceID string `json:"sourceID" validate:"required"`
|
SourceID string `json:"sourceID" validate:"required"`
|
||||||
TargetName string `josn:"targetName" validate:"required"`
|
TargetName string `json:"targetName" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImagePush struct {
|
type ImagePush struct {
|
||||||
RepoID uint `josn:"repoID" validate:"required"`
|
RepoID uint `json:"repoID" validate:"required"`
|
||||||
TagName string `json:"tagName" validate:"required"`
|
TagName string `json:"tagName" validate:"required"`
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImageSave struct {
|
type ImageSave struct {
|
||||||
TagName string `json:"tagName" validate:"required"`
|
TagName string `json:"tagName" validate:"required"`
|
||||||
Path string `josn:"path" validate:"required"`
|
Path string `json:"path" validate:"required"`
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
}
|
}
|
||||||
|
@@ -66,6 +66,11 @@ type AppInstalledUpdate struct {
|
|||||||
AppContainerConfig
|
AppContainerConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AppInstalledIgnoreUpgrade struct {
|
||||||
|
DetailID uint `json:"detailID" validate:"required"`
|
||||||
|
Operate string `json:"operate" validate:"required,oneof=cancel ignore"`
|
||||||
|
}
|
||||||
|
|
||||||
type PortUpdate struct {
|
type PortUpdate struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
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 {
|
type WebsiteSearch struct {
|
||||||
dto.PageInfo
|
dto.PageInfo
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
OrderBy string `json:"orderBy"`
|
||||||
|
Order string `json:"order"`
|
||||||
WebsiteGroupID uint `json:"websiteGroupId"`
|
WebsiteGroupID uint `json:"websiteGroupId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,15 +115,18 @@ type WebsiteDomainDelete struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WebsiteHTTPSOp struct {
|
type WebsiteHTTPSOp struct {
|
||||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||||
Enable bool `json:"enable" validate:"required"`
|
Enable bool `json:"enable" validate:"required"`
|
||||||
WebsiteSSLID uint `json:"websiteSSLId"`
|
WebsiteSSLID uint `json:"websiteSSLId"`
|
||||||
Type string `json:"type" validate:"oneof=existed auto manual"`
|
Type string `json:"type" validate:"oneof=existed auto manual"`
|
||||||
PrivateKey string `json:"privateKey"`
|
PrivateKey string `json:"privateKey"`
|
||||||
Certificate string `json:"certificate"`
|
Certificate string `json:"certificate"`
|
||||||
HttpConfig string `json:"HttpConfig" validate:"oneof=HTTPSOnly HTTPAlso HTTPToHTTPS"`
|
PrivateKeyPath string `json:"privateKeyPath"`
|
||||||
SSLProtocol []string `json:"SSLProtocol"`
|
CertificatePath string `json:"certificatePath"`
|
||||||
Algorithm string `json:"algorithm"`
|
ImportType string `json:"importType"`
|
||||||
|
HttpConfig string `json:"httpConfig" validate:"oneof=HTTPSOnly HTTPAlso HTTPToHTTPS"`
|
||||||
|
SSLProtocol []string `json:"SSLProtocol"`
|
||||||
|
Algorithm string `json:"algorithm"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebsiteNginxUpdate struct {
|
type WebsiteNginxUpdate struct {
|
||||||
|
@@ -4,6 +4,7 @@ import "github.com/1Panel-dev/1Panel/backend/app/dto"
|
|||||||
|
|
||||||
type WebsiteSSLSearch struct {
|
type WebsiteSSLSearch struct {
|
||||||
dto.PageInfo
|
dto.PageInfo
|
||||||
|
AcmeAccountID string `json:"acmeAccountID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebsiteSSLCreate struct {
|
type WebsiteSSLCreate struct {
|
||||||
|
@@ -48,6 +48,13 @@ type AppDetailDTO struct {
|
|||||||
Image string `json:"image"`
|
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 {
|
type AppInstalledDTO struct {
|
||||||
model.AppInstall
|
model.AppInstall
|
||||||
Total int `json:"total"`
|
Total int `json:"total"`
|
||||||
|
@@ -5,6 +5,7 @@ import "time"
|
|||||||
type SettingInfo struct {
|
type SettingInfo struct {
|
||||||
UserName string `json:"userName"`
|
UserName string `json:"userName"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
|
SystemIP string `json:"systemIP"`
|
||||||
SystemVersion string `json:"systemVersion"`
|
SystemVersion string `json:"systemVersion"`
|
||||||
|
|
||||||
SessionTimeout string `json:"sessionTimeout"`
|
SessionTimeout string `json:"sessionTimeout"`
|
||||||
@@ -28,6 +29,7 @@ type SettingInfo struct {
|
|||||||
ComplexityVerification string `json:"complexityVerification"`
|
ComplexityVerification string `json:"complexityVerification"`
|
||||||
MFAStatus string `json:"mfaStatus"`
|
MFAStatus string `json:"mfaStatus"`
|
||||||
MFASecret string `json:"mfaSecret"`
|
MFASecret string `json:"mfaSecret"`
|
||||||
|
MFAInterval string `json:"mfaInterval"`
|
||||||
|
|
||||||
MonitorStatus string `json:"monitorStatus"`
|
MonitorStatus string `json:"monitorStatus"`
|
||||||
MonitorInterval string `json:"monitorInterval"`
|
MonitorInterval string `json:"monitorInterval"`
|
||||||
@@ -74,7 +76,7 @@ type PortUpdate struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SnapshotCreate 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"`
|
Description string `json:"description" validate:"max=256"`
|
||||||
}
|
}
|
||||||
type SnapshotRecover struct {
|
type SnapshotRecover struct {
|
||||||
|
@@ -12,4 +12,5 @@ type AppDetail struct {
|
|||||||
DownloadUrl string `json:"downloadUrl" gorm:"type:varchar;"`
|
DownloadUrl string `json:"downloadUrl" gorm:"type:varchar;"`
|
||||||
DownloadCallBackUrl string `json:"downloadCallBackUrl" gorm:"type:longtext;"`
|
DownloadCallBackUrl string `json:"downloadCallBackUrl" gorm:"type:longtext;"`
|
||||||
Update bool `json:"update"`
|
Update bool `json:"update"`
|
||||||
|
IgnoreUpgrade bool `json:"ignoreUpgrade"`
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ type BackupAccount struct {
|
|||||||
Bucket string `gorm:"type:varchar(256)" json:"bucket"`
|
Bucket string `gorm:"type:varchar(256)" json:"bucket"`
|
||||||
AccessKey string `gorm:"type:varchar(256)" json:"accessKey"`
|
AccessKey string `gorm:"type:varchar(256)" json:"accessKey"`
|
||||||
Credential string `gorm:"type:varchar(256)" json:"credential"`
|
Credential string `gorm:"type:varchar(256)" json:"credential"`
|
||||||
|
BackupPath string `gorm:"type:varchar(256)" json:"backupPath"`
|
||||||
Vars string `gorm:"type:longText" json:"vars"`
|
Vars string `gorm:"type:longText" json:"vars"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,6 +15,7 @@ type Cronjob struct {
|
|||||||
Minute uint64 `gorm:"type:decimal" json:"minute"`
|
Minute uint64 `gorm:"type:decimal" json:"minute"`
|
||||||
Second uint64 `gorm:"type:decimal" json:"second"`
|
Second uint64 `gorm:"type:decimal" json:"second"`
|
||||||
|
|
||||||
|
ContainerName string `gorm:"type:varchar(64)" json:"containerName"`
|
||||||
Script string `gorm:"longtext" json:"script"`
|
Script string `gorm:"longtext" json:"script"`
|
||||||
Website string `gorm:"type:varchar(64)" json:"website"`
|
Website string `gorm:"type:varchar(64)" json:"website"`
|
||||||
DBName string `gorm:"type:varchar(64)" json:"dbName"`
|
DBName string `gorm:"type:varchar(64)" json:"dbName"`
|
||||||
|
@@ -13,6 +13,7 @@ type AppDetailRepo struct {
|
|||||||
type IAppDetailRepo interface {
|
type IAppDetailRepo interface {
|
||||||
WithVersion(version string) DBOption
|
WithVersion(version string) DBOption
|
||||||
WithAppId(id uint) DBOption
|
WithAppId(id uint) DBOption
|
||||||
|
WithIgnored() DBOption
|
||||||
GetFirst(opts ...DBOption) (model.AppDetail, error)
|
GetFirst(opts ...DBOption) (model.AppDetail, error)
|
||||||
Update(ctx context.Context, detail model.AppDetail) error
|
Update(ctx context.Context, detail model.AppDetail) error
|
||||||
BatchCreate(ctx context.Context, details []model.AppDetail) error
|
BatchCreate(ctx context.Context, details []model.AppDetail) error
|
||||||
@@ -31,12 +32,19 @@ func (a AppDetailRepo) WithVersion(version string) DBOption {
|
|||||||
return g.Where("version = ?", version)
|
return g.Where("version = ?", version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AppDetailRepo) WithAppId(id uint) DBOption {
|
func (a AppDetailRepo) WithAppId(id uint) DBOption {
|
||||||
return func(g *gorm.DB) *gorm.DB {
|
return func(g *gorm.DB) *gorm.DB {
|
||||||
return g.Where("app_id = ?", id)
|
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) {
|
func (a AppDetailRepo) GetFirst(opts ...DBOption) (model.AppDetail, error) {
|
||||||
var detail model.AppDetail
|
var detail model.AppDetail
|
||||||
err := getDb(opts...).Model(&model.AppDetail{}).Find(&detail).Error
|
err := getDb(opts...).Model(&model.AppDetail{}).Find(&detail).Error
|
||||||
|
@@ -19,6 +19,7 @@ type IAppInstallResourceRpo interface {
|
|||||||
GetFirst(opts ...DBOption) (model.AppInstallResource, error)
|
GetFirst(opts ...DBOption) (model.AppInstallResource, error)
|
||||||
Create(ctx context.Context, resource *model.AppInstallResource) error
|
Create(ctx context.Context, resource *model.AppInstallResource) error
|
||||||
DeleteBy(ctx context.Context, opts ...DBOption) error
|
DeleteBy(ctx context.Context, opts ...DBOption) error
|
||||||
|
BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIAppInstallResourceRpo() IAppInstallResourceRpo {
|
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 {
|
func (a AppInstallResourceRpo) DeleteBy(ctx context.Context, opts ...DBOption) error {
|
||||||
return getTx(ctx, opts...).Delete(&model.AppInstallResource{}).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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
@@ -16,6 +17,7 @@ type ICommonRepo interface {
|
|||||||
WithByName(name string) DBOption
|
WithByName(name string) DBOption
|
||||||
WithByType(tp string) DBOption
|
WithByType(tp string) DBOption
|
||||||
WithOrderBy(orderStr string) DBOption
|
WithOrderBy(orderStr string) DBOption
|
||||||
|
WithOrderRuleBy(orderBy, order string) DBOption
|
||||||
WithByGroupID(groupID uint) DBOption
|
WithByGroupID(groupID uint) DBOption
|
||||||
WithLikeName(name string) DBOption
|
WithLikeName(name string) DBOption
|
||||||
WithIdsIn(ids []uint) 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 {
|
func (c *CommonRepo) WithIdsIn(ids []uint) DBOption {
|
||||||
return func(g *gorm.DB) *gorm.DB {
|
return func(g *gorm.DB) *gorm.DB {
|
||||||
return g.Where("id in (?)", ids)
|
return g.Where("id in (?)", ids)
|
||||||
|
@@ -15,6 +15,7 @@ type IComposeTemplateRepo interface {
|
|||||||
Update(id uint, vars map[string]interface{}) error
|
Update(id uint, vars map[string]interface{}) error
|
||||||
Delete(opts ...DBOption) error
|
Delete(opts ...DBOption) error
|
||||||
|
|
||||||
|
GetRecord(opts ...DBOption) (model.Compose, error)
|
||||||
CreateRecord(compose *model.Compose) error
|
CreateRecord(compose *model.Compose) error
|
||||||
DeleteRecord(opts ...DBOption) error
|
DeleteRecord(opts ...DBOption) error
|
||||||
ListRecord() ([]model.Compose, error)
|
ListRecord() ([]model.Compose, error)
|
||||||
@@ -72,6 +73,16 @@ func (u *ComposeTemplateRepo) Delete(opts ...DBOption) error {
|
|||||||
return db.Delete(&model.ComposeTemplate{}).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) {
|
func (u *ComposeTemplateRepo) ListRecord() ([]model.Compose, error) {
|
||||||
var composes []model.Compose
|
var composes []model.Compose
|
||||||
if err := global.DB.Find(&composes).Error; err != nil {
|
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)
|
count := int64(0)
|
||||||
db = db.Count(&count)
|
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
|
return count, cronjobs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,6 +24,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AppService struct {
|
type AppService struct {
|
||||||
@@ -39,6 +40,7 @@ type IAppService interface {
|
|||||||
GetAppUpdate() (*response.AppUpdateRes, error)
|
GetAppUpdate() (*response.AppUpdateRes, error)
|
||||||
GetAppDetailByID(id uint) (*response.AppDetailDTO, error)
|
GetAppDetailByID(id uint) (*response.AppDetailDTO, error)
|
||||||
SyncAppListFromLocal()
|
SyncAppListFromLocal()
|
||||||
|
GetIgnoredApp() ([]response.IgnoredApp, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIAppService() IAppService {
|
func NewIAppService() IAppService {
|
||||||
@@ -82,12 +84,16 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var appDTOs []*response.AppDTO
|
var appDTOs []*response.AppDTO
|
||||||
for _, a := range apps {
|
for _, ap := range apps {
|
||||||
|
ap.ReadMe = ""
|
||||||
|
ap.Website = ""
|
||||||
|
ap.Document = ""
|
||||||
|
ap.Github = ""
|
||||||
appDTO := &response.AppDTO{
|
appDTO := &response.AppDTO{
|
||||||
App: a,
|
App: ap,
|
||||||
}
|
}
|
||||||
appDTOs = append(appDTOs, appDTO)
|
appDTOs = append(appDTOs, appDTO)
|
||||||
appTags, err := appTagRepo.GetByAppId(a.ID)
|
appTags, err := appTagRepo.GetByAppId(ap.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -100,7 +106,7 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
appDTO.Tags = tags
|
appDTO.Tags = tags
|
||||||
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(a.ID))
|
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(ap.ID))
|
||||||
appDTO.Installed = len(installs) > 0
|
appDTO.Installed = len(installs) > 0
|
||||||
}
|
}
|
||||||
res.Items = appDTOs
|
res.Items = appDTOs
|
||||||
@@ -163,7 +169,7 @@ func (a AppService) GetAppDetail(appId uint, version, appType string) (response.
|
|||||||
}
|
}
|
||||||
fileOp := files.NewFileOp()
|
fileOp := files.NewFileOp()
|
||||||
versionPath := path.Join(constant.AppResourceDir, app.Resource, app.Key, detail.Version)
|
versionPath := path.Join(constant.AppResourceDir, app.Resource, app.Key, detail.Version)
|
||||||
if !fileOp.Stat(versionPath) {
|
if !fileOp.Stat(versionPath) || detail.Update {
|
||||||
if err = downloadApp(app, detail, nil); err != nil {
|
if err = downloadApp(app, detail, nil); err != nil {
|
||||||
return appDetailDTO, err
|
return appDetailDTO, err
|
||||||
}
|
}
|
||||||
@@ -232,6 +238,27 @@ func (a AppService) GetAppDetailByID(id uint) (*response.AppDetailDTO, error) {
|
|||||||
return res, nil
|
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) {
|
func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (appInstall *model.AppInstall, err error) {
|
||||||
if err = docker.CreateDefaultDockerNetwork(); err != nil {
|
if err = docker.CreateDefaultDockerNetwork(); err != nil {
|
||||||
err = buserr.WithDetail(constant.Err1PanelNetworkFailed, err.Error(), nil)
|
err = buserr.WithDetail(constant.Err1PanelNetworkFailed, err.Error(), nil)
|
||||||
@@ -247,14 +274,23 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
|||||||
appDetail model.AppDetail
|
appDetail model.AppDetail
|
||||||
app model.App
|
app model.App
|
||||||
)
|
)
|
||||||
httpPort, err = checkPort("PANEL_APP_PORT_HTTP", req.Params)
|
for key := range req.Params {
|
||||||
if err != nil {
|
if !strings.Contains(key, "PANEL_APP_PORT") {
|
||||||
return
|
continue
|
||||||
}
|
}
|
||||||
httpsPort, err = checkPort("PANEL_APP_PORT_HTTPS", req.Params)
|
var port int
|
||||||
if err != nil {
|
if port, err = checkPort(key, req.Params); err == nil {
|
||||||
return
|
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))
|
appDetail, err = appDetailRepo.GetFirst(commonRepo.WithByID(req.AppDetailId))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -470,9 +506,13 @@ func (a AppService) SyncAppListFromLocal() {
|
|||||||
oldDetail, exist := appDetails[version]
|
oldDetail, exist := appDetails[version]
|
||||||
if exist {
|
if exist {
|
||||||
newDetail.ID = oldDetail.ID
|
newDetail.ID = oldDetail.ID
|
||||||
|
delete(appDetails, version)
|
||||||
}
|
}
|
||||||
app.Details[i] = newDetail
|
app.Details[i] = newDetail
|
||||||
}
|
}
|
||||||
|
for _, v := range appDetails {
|
||||||
|
app.Details = append(app.Details, v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
app.TagsKey = append(app.TagsKey, constant.AppResourceLocal)
|
app.TagsKey = append(app.TagsKey, constant.AppResourceLocal)
|
||||||
apps[app.Key] = app
|
apps[app.Key] = app
|
||||||
@@ -730,10 +770,8 @@ func (a AppService) SyncAppListFromRemote() error {
|
|||||||
detail.Params = string(paramByte)
|
detail.Params = string(paramByte)
|
||||||
detail.DownloadUrl = v.DownloadUrl
|
detail.DownloadUrl = v.DownloadUrl
|
||||||
detail.DownloadCallBackUrl = v.DownloadCallBackUrl
|
detail.DownloadCallBackUrl = v.DownloadCallBackUrl
|
||||||
if v.LastModified > detail.LastModified {
|
detail.Update = true
|
||||||
detail.Update = true
|
detail.LastModified = v.LastModified
|
||||||
detail.LastModified = v.LastModified
|
|
||||||
}
|
|
||||||
detailsMap[version] = detail
|
detailsMap[version] = detail
|
||||||
}
|
}
|
||||||
var newDetails []model.AppDetail
|
var newDetails []model.AppDetail
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
@@ -45,6 +46,7 @@ type IAppInstallService interface {
|
|||||||
SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstalledDTO, error)
|
SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstalledDTO, error)
|
||||||
Operate(req request.AppInstalledOperate) error
|
Operate(req request.AppInstalledOperate) error
|
||||||
Update(req request.AppInstalledUpdate) error
|
Update(req request.AppInstalledUpdate) error
|
||||||
|
IgnoreUpgrade(req request.AppInstalledIgnoreUpgrade) error
|
||||||
SyncAll(systemInit bool) error
|
SyncAll(systemInit bool) error
|
||||||
GetServices(key string) ([]response.AppService, error)
|
GetServices(key string) ([]response.AppService, error)
|
||||||
GetUpdateVersions(installId uint) ([]dto.AppVersion, error)
|
GetUpdateVersions(installId uint) ([]dto.AppVersion, error)
|
||||||
@@ -352,6 +354,15 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
|||||||
return nil
|
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 {
|
func (a *AppInstallService) SyncAll(systemInit bool) error {
|
||||||
allList, err := appInstallRepo.ListBy()
|
allList, err := appInstallRepo.ListBy()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -366,8 +377,10 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := syncById(i.ID); err != nil {
|
if !systemInit {
|
||||||
global.LOG.Errorf("sync install app[%s] error,mgs: %s", i.Name, err.Error())
|
if err := syncById(i.ID); err != nil {
|
||||||
|
global.LOG.Errorf("sync install app[%s] error,mgs: %s", i.Name, err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -412,6 +425,9 @@ func (a *AppInstallService) GetUpdateVersions(installId uint) ([]dto.AppVersion,
|
|||||||
return versions, err
|
return versions, err
|
||||||
}
|
}
|
||||||
for _, detail := range details {
|
for _, detail := range details {
|
||||||
|
if detail.IgnoreUpgrade {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if common.IsCrossVersion(install.Version, detail.Version) && !app.CrossVersionUpdate {
|
if common.IsCrossVersion(install.Version, detail.Version) && !app.CrossVersionUpdate {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -577,6 +593,9 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
|||||||
config := getAppCommonConfig(envs)
|
config := getAppCommonConfig(envs)
|
||||||
config.DockerCompose = install.DockerCompose
|
config.DockerCompose = install.DockerCompose
|
||||||
res.Params = params
|
res.Params = params
|
||||||
|
if config.ContainerName == "" {
|
||||||
|
config.ContainerName = install.ContainerName
|
||||||
|
}
|
||||||
res.AppContainerConfig = config
|
res.AppContainerConfig = config
|
||||||
return &res, nil
|
return &res, nil
|
||||||
}
|
}
|
||||||
@@ -589,12 +608,10 @@ func syncById(installId uint) error {
|
|||||||
if appInstall.Status == constant.Installing {
|
if appInstall.Status == constant.Installing {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
containerNames, err := getContainerNames(appInstall)
|
containerNames, err := getContainerNames(appInstall)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cli, err := docker.NewClient()
|
cli, err := docker.NewClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -620,16 +637,16 @@ func syncById(installId uint) error {
|
|||||||
errorContainers = append(errorContainers, n.Names[0])
|
errorContainers = append(errorContainers, n.Names[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, old := range containerNames {
|
for _, name := range containerNames {
|
||||||
exist := false
|
exist := false
|
||||||
for _, new := range containers {
|
for _, container := range containers {
|
||||||
if common.ExistWithStrArray(old, new.Names) {
|
if common.ExistWithStrArray(name, container.Names) {
|
||||||
exist = true
|
exist = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !exist {
|
if !exist {
|
||||||
notFoundContainers = append(notFoundContainers, old)
|
notFoundContainers = append(notFoundContainers, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -642,10 +659,10 @@ func syncById(installId uint) error {
|
|||||||
|
|
||||||
if containerCount == 0 {
|
if containerCount == 0 {
|
||||||
appInstall.Status = constant.Error
|
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)
|
return appInstallRepo.Save(context.Background(), &appInstall)
|
||||||
}
|
}
|
||||||
if errCount == 0 && existedCount == 0 {
|
if errCount == 0 && existedCount == 0 && notFoundCount == 0 {
|
||||||
appInstall.Status = constant.Running
|
appInstall.Status = constant.Running
|
||||||
return appInstallRepo.Save(context.Background(), &appInstall)
|
return appInstallRepo.Save(context.Background(), &appInstall)
|
||||||
}
|
}
|
||||||
@@ -660,22 +677,14 @@ func syncById(installId uint) error {
|
|||||||
appInstall.Status = constant.UnHealthy
|
appInstall.Status = constant.UnHealthy
|
||||||
}
|
}
|
||||||
|
|
||||||
var errMsg strings.Builder
|
var errMsg string
|
||||||
if errCount > 0 {
|
if errCount > 0 {
|
||||||
errMsg.Write([]byte(string(rune(errCount)) + " error containers:"))
|
errMsg += i18n.GetMsgWithMap("ErrContainerMsg", map[string]interface{}{"name": strings.Join(errorContainers, ",")})
|
||||||
for _, e := range errorContainers {
|
|
||||||
errMsg.Write([]byte(e))
|
|
||||||
}
|
|
||||||
errMsg.Write([]byte("\n"))
|
|
||||||
}
|
}
|
||||||
if notFoundCount > 0 {
|
if notFoundCount > 0 {
|
||||||
errMsg.Write([]byte(string(rune(notFoundCount)) + " not found containers:"))
|
errMsg += i18n.GetMsgWithMap("ErrContainerNotFound", map[string]interface{}{"name": strings.Join(notFoundContainers, ",")})
|
||||||
for _, e := range notFoundContainers {
|
|
||||||
errMsg.Write([]byte(e))
|
|
||||||
}
|
|
||||||
errMsg.Write([]byte("\n"))
|
|
||||||
}
|
}
|
||||||
appInstall.Message = errMsg.String()
|
appInstall.Message = errMsg
|
||||||
return appInstallRepo.Save(context.Background(), &appInstall)
|
return appInstallRepo.Save(context.Background(), &appInstall)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,7 +8,6 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
"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/app/dto/request"
|
||||||
"github.com/1Panel-dev/1Panel/backend/i18n"
|
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||||
"github.com/compose-spec/compose-go/types"
|
|
||||||
"github.com/subosito/gotenv"
|
"github.com/subosito/gotenv"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"math"
|
"math"
|
||||||
@@ -49,7 +48,20 @@ var (
|
|||||||
func checkPort(key string, params map[string]interface{}) (int, error) {
|
func checkPort(key string, params map[string]interface{}) (int, error) {
|
||||||
port, ok := params[key]
|
port, ok := params[key]
|
||||||
if ok {
|
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))
|
oldInstalled, _ := appInstallRepo.ListBy(appInstallRepo.WithPort(portN))
|
||||||
if len(oldInstalled) > 0 {
|
if len(oldInstalled) > 0 {
|
||||||
var apps []string
|
var apps []string
|
||||||
@@ -359,7 +371,6 @@ func getContainerNames(install model.AppInstall) ([]string, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
containerMap := make(map[string]struct{})
|
containerMap := make(map[string]struct{})
|
||||||
containerMap[install.ContainerName] = struct{}{}
|
|
||||||
for _, service := range project.AllServices() {
|
for _, service := range project.AllServices() {
|
||||||
if service.ContainerName == "${CONTAINER_NAME}" || service.ContainerName == "" {
|
if service.ContainerName == "${CONTAINER_NAME}" || service.ContainerName == "" {
|
||||||
continue
|
continue
|
||||||
@@ -370,6 +381,9 @@ func getContainerNames(install model.AppInstall) ([]string, error) {
|
|||||||
for k := range containerMap {
|
for k := range containerMap {
|
||||||
containerNames = append(containerNames, k)
|
containerNames = append(containerNames, k)
|
||||||
}
|
}
|
||||||
|
if len(containerNames) == 0 {
|
||||||
|
containerNames = append(containerNames, install.ContainerName)
|
||||||
|
}
|
||||||
return containerNames, nil
|
return containerNames, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,27 +536,6 @@ func upAppPre(app model.App, appInstall *model.AppInstall) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getServiceFromInstall(appInstall *model.AppInstall) (service *composeV2.ComposeService, err error) {
|
|
||||||
var (
|
|
||||||
project *types.Project
|
|
||||||
envStr string
|
|
||||||
)
|
|
||||||
envStr, err = coverEnvJsonToStr(appInstall.Env)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
project, err = composeV2.GetComposeProject(appInstall.Name, appInstall.GetPath(), []byte(appInstall.DockerCompose), []byte(envStr), true)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
service, err = composeV2.NewComposeService()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
service.SetProject(project)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkContainerNameIsExist(containerName, appDir string) (bool, error) {
|
func checkContainerNameIsExist(containerName, appDir string) (bool, error) {
|
||||||
client, err := composeV2.NewDockerClient()
|
client, err := composeV2.NewDockerClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -571,13 +564,30 @@ func checkContainerNameIsExist(containerName, appDir string) (bool, error) {
|
|||||||
func upApp(appInstall *model.AppInstall) {
|
func upApp(appInstall *model.AppInstall) {
|
||||||
upProject := func(appInstall *model.AppInstall) (err error) {
|
upProject := func(appInstall *model.AppInstall) (err error) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
var composeService *composeV2.ComposeService
|
var (
|
||||||
composeService, err = getServiceFromInstall(appInstall)
|
out string
|
||||||
if err != nil {
|
errMsg string
|
||||||
return err
|
)
|
||||||
|
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 err != nil {
|
||||||
|
if out != "" {
|
||||||
|
appInstall.Message = errMsg + out
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -587,7 +597,6 @@ func upApp(appInstall *model.AppInstall) {
|
|||||||
}
|
}
|
||||||
if err := upProject(appInstall); err != nil {
|
if err := upProject(appInstall); err != nil {
|
||||||
appInstall.Status = constant.Error
|
appInstall.Status = constant.Error
|
||||||
appInstall.Message = err.Error()
|
|
||||||
} else {
|
} else {
|
||||||
appInstall.Status = constant.Running
|
appInstall.Status = constant.Running
|
||||||
}
|
}
|
||||||
@@ -751,7 +760,7 @@ func handleErr(install model.AppInstall, err error, out string) error {
|
|||||||
func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]response.AppInstalledDTO, error) {
|
func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]response.AppInstalledDTO, error) {
|
||||||
var res []response.AppInstalledDTO
|
var res []response.AppInstalledDTO
|
||||||
for _, installed := range appInstallList {
|
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
|
continue
|
||||||
}
|
}
|
||||||
installDTO := response.AppInstalledDTO{
|
installDTO := response.AppInstalledDTO{
|
||||||
@@ -768,12 +777,18 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]respons
|
|||||||
}
|
}
|
||||||
var versions []string
|
var versions []string
|
||||||
for _, detail := range details {
|
for _, detail := range details {
|
||||||
|
if detail.IgnoreUpgrade {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if common.IsCrossVersion(installed.Version, detail.Version) && !app.CrossVersionUpdate {
|
if common.IsCrossVersion(installed.Version, detail.Version) && !app.CrossVersionUpdate {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
versions = append(versions, detail.Version)
|
versions = append(versions, detail.Version)
|
||||||
}
|
}
|
||||||
versions = common.GetSortedVersions(versions)
|
versions = common.GetSortedVersions(versions)
|
||||||
|
if len(versions) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
lastVersion := versions[0]
|
lastVersion := versions[0]
|
||||||
if common.IsCrossVersion(installed.Version, lastVersion) {
|
if common.IsCrossVersion(installed.Version, lastVersion) {
|
||||||
installDTO.CanUpdate = app.CrossVersionUpdate
|
installDTO.CanUpdate = app.CrossVersionUpdate
|
||||||
|
@@ -76,7 +76,11 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin) (*dto.UserLogi
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
if !success {
|
||||||
return nil, constant.ErrAuth
|
return nil, constant.ErrAuth
|
||||||
}
|
}
|
||||||
|
@@ -2,8 +2,12 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -24,6 +28,7 @@ type BackupService struct{}
|
|||||||
type IBackupService interface {
|
type IBackupService interface {
|
||||||
List() ([]dto.BackupInfo, error)
|
List() ([]dto.BackupInfo, error)
|
||||||
SearchRecordsWithPage(search dto.RecordSearch) (int64, []dto.BackupRecords, error)
|
SearchRecordsWithPage(search dto.RecordSearch) (int64, []dto.BackupRecords, error)
|
||||||
|
LoadOneDriveInfo() (string, error)
|
||||||
DownloadRecord(info dto.DownloadRecord) (string, error)
|
DownloadRecord(info dto.DownloadRecord) (string, error)
|
||||||
Create(backupDto dto.BackupOperate) error
|
Create(backupDto dto.BackupOperate) error
|
||||||
GetBuckets(backupDto dto.ForBuckets) ([]interface{}, 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("MINIO", ops))
|
||||||
dtobas = append(dtobas, u.loadByType("COS", ops))
|
dtobas = append(dtobas, u.loadByType("COS", ops))
|
||||||
dtobas = append(dtobas, u.loadByType("KODO", ops))
|
dtobas = append(dtobas, u.loadByType("KODO", ops))
|
||||||
|
dtobas = append(dtobas, u.loadByType("OneDrive", ops))
|
||||||
return dtobas, err
|
return dtobas, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,6 +90,18 @@ func (u *BackupService) SearchRecordsWithPage(search dto.RecordSearch) (int64, [
|
|||||||
return total, dtobas, err
|
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) {
|
func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error) {
|
||||||
if info.Source == "LOCAL" {
|
if info.Source == "LOCAL" {
|
||||||
return info.FileDir + "/" + info.FileName, nil
|
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 {
|
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
varMap["type"] = backup.Type
|
|
||||||
varMap["bucket"] = backup.Bucket
|
varMap["bucket"] = backup.Bucket
|
||||||
switch backup.Type {
|
switch backup.Type {
|
||||||
case constant.Sftp:
|
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:
|
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
|
||||||
varMap["accessKey"] = backup.AccessKey
|
varMap["accessKey"] = backup.AccessKey
|
||||||
varMap["secretKey"] = backup.Credential
|
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 {
|
if err != nil {
|
||||||
return "", fmt.Errorf("new cloud storage client failed, err: %v", err)
|
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)
|
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 {
|
if exist, _ := backClient.Exist(srcPath); exist {
|
||||||
isOK, err := backClient.Download(srcPath, targetPath)
|
isOK, err := backClient.Download(srcPath, targetPath)
|
||||||
if !isOK {
|
if !isOK {
|
||||||
@@ -134,6 +158,12 @@ func (u *BackupService) Create(backupDto dto.BackupOperate) error {
|
|||||||
if err := copier.Copy(&backup, &backupDto); err != nil {
|
if err := copier.Copy(&backup, &backupDto); err != nil {
|
||||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
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 {
|
if err := backupRepo.Create(&backup); err != nil {
|
||||||
return err
|
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 {
|
if err := json.Unmarshal([]byte(backupDto.Vars), &varMap); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
varMap["type"] = backupDto.Type
|
|
||||||
switch backupDto.Type {
|
switch backupDto.Type {
|
||||||
case constant.Sftp:
|
case constant.Sftp:
|
||||||
varMap["username"] = backupDto.AccessKey
|
varMap["username"] = backupDto.AccessKey
|
||||||
@@ -154,7 +183,7 @@ func (u *BackupService) GetBuckets(backupDto dto.ForBuckets) ([]interface{}, err
|
|||||||
varMap["accessKey"] = backupDto.AccessKey
|
varMap["accessKey"] = backupDto.AccessKey
|
||||||
varMap["secretKey"] = backupDto.Credential
|
varMap["secretKey"] = backupDto.Credential
|
||||||
}
|
}
|
||||||
client, err := cloud_storage.NewCloudStorageClient(varMap)
|
client, err := cloud_storage.NewCloudStorageClient(backupDto.Type, varMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -214,7 +243,17 @@ func (u *BackupService) Update(req dto.BackupOperate) error {
|
|||||||
upMap := make(map[string]interface{})
|
upMap := make(map[string]interface{})
|
||||||
upMap["bucket"] = req.Bucket
|
upMap["bucket"] = req.Bucket
|
||||||
upMap["credential"] = req.Credential
|
upMap["credential"] = req.Credential
|
||||||
|
upMap["backup_path"] = req.BackupPath
|
||||||
upMap["vars"] = req.Vars
|
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 {
|
if err := backupRepo.Update(req.ID, upMap); err != nil {
|
||||||
return err
|
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 {
|
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
varMap["type"] = backup.Type
|
|
||||||
if backup.Type == "LOCAL" {
|
if backup.Type == "LOCAL" {
|
||||||
return nil, errors.New("not support")
|
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:
|
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
|
||||||
varMap["accessKey"] = backup.AccessKey
|
varMap["accessKey"] = backup.AccessKey
|
||||||
varMap["secretKey"] = backup.Credential
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -286,6 +326,53 @@ func (u *BackupService) loadByType(accountType string, accounts []model.BackupAc
|
|||||||
return dto.BackupInfo{Type: accountType}
|
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) {
|
func loadLocalDir() (string, error) {
|
||||||
backup, err := backupRepo.Get(commonRepo.WithByType("LOCAL"))
|
backup, err := backupRepo.Get(commonRepo.WithByType("LOCAL"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -148,7 +148,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
|||||||
if err := json.Unmarshal(appjson, &oldInstall); err != nil {
|
if err := json.Unmarshal(appjson, &oldInstall); err != nil {
|
||||||
return fmt.Errorf("unmarshal app.json failed, err: %v", err)
|
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")
|
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))
|
resource, _ := appInstallResourceRepo.GetFirst(appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||||
if resource.ID != 0 && install.App.Key != "mysql" {
|
if resource.ID != 0 && install.App.Key != "mysql" {
|
||||||
mysqlInfo, err := appInstallRepo.LoadBaseInfo(resource.Key, "")
|
mysqlInfo, err := appInstallRepo.LoadBaseInfo(resource.Key, "")
|
||||||
@@ -182,7 +183,22 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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)
|
global.LOG.Errorf("handle recover from sql.gz failed, err: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -193,11 +209,50 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
oldInstall.Status = constant.Running
|
if len(newEnvFile) != 0 {
|
||||||
if err := appInstallRepo.Save(context.Background(), install); err != nil {
|
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)
|
global.LOG.Errorf("save db app install failed, err: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
isOk = true
|
isOk = true
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
@@ -3,11 +3,11 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -26,24 +26,34 @@ import (
|
|||||||
"github.com/docker/docker/api/types/network"
|
"github.com/docker/docker/api/types/network"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
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 ContainerService struct{}
|
||||||
|
|
||||||
type IContainerService interface {
|
type IContainerService interface {
|
||||||
Page(req dto.PageContainer) (int64, interface{}, error)
|
Page(req dto.PageContainer) (int64, interface{}, error)
|
||||||
|
List() ([]string, error)
|
||||||
PageNetwork(req dto.SearchWithPage) (int64, interface{}, error)
|
PageNetwork(req dto.SearchWithPage) (int64, interface{}, error)
|
||||||
|
ListNetwork() ([]dto.Options, error)
|
||||||
PageVolume(req dto.SearchWithPage) (int64, interface{}, error)
|
PageVolume(req dto.SearchWithPage) (int64, interface{}, error)
|
||||||
ListVolume() ([]dto.Options, error)
|
ListVolume() ([]dto.Options, error)
|
||||||
PageCompose(req dto.SearchWithPage) (int64, interface{}, error)
|
PageCompose(req dto.SearchWithPage) (int64, interface{}, error)
|
||||||
CreateCompose(req dto.ComposeCreate) (string, error)
|
CreateCompose(req dto.ComposeCreate) (string, error)
|
||||||
ComposeOperation(req dto.ComposeOperation) 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
|
ContainerLogClean(req dto.OperationWithName) error
|
||||||
ContainerOperation(req dto.ContainerOperation) error
|
ContainerOperation(req dto.ContainerOperation) error
|
||||||
ContainerLogs(param dto.ContainerLog) (string, error)
|
ContainerLogs(wsConn *websocket.Conn, container, since, tail string, follow bool) error
|
||||||
ContainerStats(id string) (*dto.ContainterStats, error)
|
ContainerStats(id string) (*dto.ContainerStats, error)
|
||||||
Inspect(req dto.InspectReq) (string, error)
|
Inspect(req dto.InspectReq) (string, error)
|
||||||
DeleteNetwork(req dto.BatchDelete) error
|
DeleteNetwork(req dto.BatchDelete) error
|
||||||
CreateNetwork(req dto.NetworkCreate) error
|
CreateNetwork(req dto.NetworkCreate) error
|
||||||
@@ -60,9 +70,8 @@ func NewIContainerService() IContainerService {
|
|||||||
|
|
||||||
func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, error) {
|
func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, error) {
|
||||||
var (
|
var (
|
||||||
records []types.Container
|
records []types.Container
|
||||||
list []types.Container
|
list []types.Container
|
||||||
backDatas []dto.ContainerInfo
|
|
||||||
)
|
)
|
||||||
client, err := docker.NewDockerClient()
|
client, err := docker.NewDockerClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -88,9 +97,30 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Slice(list, func(i, j int) bool {
|
switch req.OrderBy {
|
||||||
return list[i].Created > list[j].Created
|
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
|
total, start, end := len(list), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||||
if start > total {
|
if start > total {
|
||||||
records = make([]types.Container, 0)
|
records = make([]types.Container, 0)
|
||||||
@@ -101,49 +131,87 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
|||||||
records = list[start:end]
|
records = list[start:end]
|
||||||
}
|
}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
backDatas := make([]dto.ContainerInfo, len(records))
|
||||||
wg.Add(len(records))
|
for i := 0; i < len(records); i++ {
|
||||||
for _, container := range records {
|
item := records[i]
|
||||||
go func(item types.Container) {
|
IsFromCompose := false
|
||||||
IsFromCompose := false
|
if _, ok := item.Labels[composeProjectLabel]; ok {
|
||||||
if _, ok := item.Labels[composeProjectLabel]; ok {
|
IsFromCompose = true
|
||||||
IsFromCompose = true
|
}
|
||||||
}
|
IsFromApp := false
|
||||||
IsFromApp := false
|
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
|
||||||
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
|
IsFromApp = true
|
||||||
IsFromApp = true
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var ports []string
|
var ports []string
|
||||||
for _, port := range item.Ports {
|
for _, port := range item.Ports {
|
||||||
if port.IP == "::" || port.PublicPort == 0 {
|
itemPortStr := fmt.Sprintf("%v/%s", port.PrivatePort, port.Type)
|
||||||
continue
|
if port.PublicPort != 0 {
|
||||||
}
|
itemPortStr = fmt.Sprintf("%s:%v->%v/%s", port.IP, port.PublicPort, port.PrivatePort, port.Type)
|
||||||
ports = append(ports, fmt.Sprintf("%v:%v/%s", port.PublicPort, port.PrivatePort, port.Type))
|
|
||||||
}
|
}
|
||||||
cpu, mem := loadCpuAndMem(client, item.ID)
|
ports = append(ports, itemPortStr)
|
||||||
backDatas = append(backDatas, dto.ContainerInfo{
|
}
|
||||||
ContainerID: item.ID,
|
backDatas[i] = dto.ContainerInfo{
|
||||||
CreateTime: time.Unix(item.Created, 0).Format("2006-01-02 15:04:05"),
|
ContainerID: item.ID,
|
||||||
Name: item.Names[0][1:],
|
CreateTime: time.Unix(item.Created, 0).Format("2006-01-02 15:04:05"),
|
||||||
ImageId: strings.Split(item.ImageID, ":")[1],
|
Name: item.Names[0][1:],
|
||||||
ImageName: item.Image,
|
ImageId: strings.Split(item.ImageID, ":")[1],
|
||||||
State: item.State,
|
ImageName: item.Image,
|
||||||
RunTime: item.Status,
|
State: item.State,
|
||||||
CPUPercent: cpu,
|
RunTime: item.Status,
|
||||||
MemoryPercent: mem,
|
Ports: ports,
|
||||||
Ports: ports,
|
IsFromApp: IsFromApp,
|
||||||
IsFromApp: IsFromApp,
|
IsFromCompose: IsFromCompose,
|
||||||
IsFromCompose: IsFromCompose,
|
}
|
||||||
})
|
|
||||||
wg.Done()
|
|
||||||
}(container)
|
|
||||||
}
|
}
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
return int64(total), backDatas, nil
|
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) {
|
func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
|
||||||
client, err := docker.NewDockerClient()
|
client, err := docker.NewDockerClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -213,63 +281,52 @@ func (u *ContainerService) Prune(req dto.ContainerPrune) (dto.ContainerPruneRepo
|
|||||||
return report, nil
|
return report, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
|
func (u *ContainerService) LoadResouceLimit() (*dto.ResourceLimit, error) {
|
||||||
portMap, err := checkPortStats(req.ExposedPorts)
|
cpuCounts, err := cpu.Counts(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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()
|
client, err := docker.NewDockerClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
ctx := context.Background()
|
||||||
|
newContainer, _ := client.ContainerInspect(ctx, req.Name)
|
||||||
|
if newContainer.ContainerJSONBase != nil {
|
||||||
|
return buserr.New(constant.ErrContainerName)
|
||||||
|
}
|
||||||
|
|
||||||
exposeds := make(nat.PortSet)
|
var config container.Config
|
||||||
for port := range portMap {
|
var hostConf container.HostConfig
|
||||||
exposeds[port] = struct{}{}
|
var networkConf network.NetworkingConfig
|
||||||
}
|
if err := loadConfigInfo(req, &config, &hostConf, &networkConf); err != nil {
|
||||||
config := &container.Config{
|
return err
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
global.LOG.Infof("new container info %s has been made, now start to create", req.Name)
|
global.LOG.Infof("new container info %s has been made, now start to create", req.Name)
|
||||||
|
|
||||||
ctx := context.Background()
|
if !checkImageExist(client, req.Image) || req.ForcePull {
|
||||||
if !checkImageExist(client, req.Image) {
|
|
||||||
if err := pullImages(ctx, client, req.Image); err != nil {
|
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 {
|
if err != nil {
|
||||||
_ = client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
|
_ = client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
|
||||||
return err
|
return err
|
||||||
@@ -282,6 +339,159 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
|
|||||||
return nil
|
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 {
|
func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error {
|
||||||
var err error
|
var err error
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
@@ -304,6 +514,10 @@ func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error
|
|||||||
case constant.ContainerOpUnpause:
|
case constant.ContainerOpUnpause:
|
||||||
err = client.ContainerUnpause(ctx, req.Name)
|
err = client.ContainerUnpause(ctx, req.Name)
|
||||||
case constant.ContainerOpRename:
|
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)
|
err = client.ContainerRename(ctx, req.Name, req.NewName)
|
||||||
case constant.ContainerOpRemove:
|
case constant.ContainerOpRemove:
|
||||||
err = client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
|
err = client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
|
||||||
@@ -329,22 +543,54 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, _ = file.Seek(0, 0)
|
_, _ = file.Seek(0, 0)
|
||||||
|
|
||||||
|
files, _ := filepath.Glob(fmt.Sprintf("%s.*", container.LogPath))
|
||||||
|
for _, file := range files {
|
||||||
|
_ = os.Remove(file)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ContainerService) ContainerLogs(req dto.ContainerLog) (string, error) {
|
func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, container, since, tail string, follow bool) error {
|
||||||
cmd := exec.Command("docker", "logs", req.ContainerID)
|
command := fmt.Sprintf("docker logs %s", container)
|
||||||
if req.Mode != "all" {
|
if tail != "0" {
|
||||||
cmd = exec.Command("docker", "logs", req.ContainerID, "--since", req.Mode)
|
command += " -n " + tail
|
||||||
}
|
}
|
||||||
stdout, err := cmd.CombinedOutput()
|
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 {
|
if err != nil {
|
||||||
return "", errors.New(string(stdout))
|
return err
|
||||||
}
|
}
|
||||||
return string(stdout), nil
|
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.ContainterStats, error) {
|
func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error) {
|
||||||
client, err := docker.NewDockerClient()
|
client, err := docker.NewDockerClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -365,7 +611,7 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainterStats, erro
|
|||||||
if err := json.Unmarshal(body, &stats); err != nil {
|
if err := json.Unmarshal(body, &stats); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var data dto.ContainterStats
|
var data dto.ContainerStats
|
||||||
data.CPUPercent = calculateCPUPercentUnix(stats)
|
data.CPUPercent = calculateCPUPercentUnix(stats)
|
||||||
data.IORead, data.IOWrite = calculateBlockIO(stats.BlkioStats)
|
data.IORead, data.IOWrite = calculateBlockIO(stats.BlkioStats)
|
||||||
data.Memory = float64(stats.MemoryStats.Usage) / 1024 / 1024
|
data.Memory = float64(stats.MemoryStats.Usage) / 1024 / 1024
|
||||||
@@ -529,3 +775,39 @@ func checkPortStats(ports []dto.PortHelper) (nat.PortMap, error) {
|
|||||||
}
|
}
|
||||||
return portMap, nil
|
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 {
|
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)
|
global.LOG.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,6 +127,10 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) {
|
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 {
|
if err := u.loadPath(&req); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@@ -75,6 +75,26 @@ func (u *ContainerService) PageNetwork(req dto.SearchWithPage) (int64, interface
|
|||||||
|
|
||||||
return int64(total), data, nil
|
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 {
|
func (u *ContainerService) DeleteNetwork(req dto.BatchDelete) error {
|
||||||
client, err := docker.NewDockerClient()
|
client, err := docker.NewDockerClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -80,13 +80,16 @@ func (u *ContainerService) ListVolume() ([]dto.Options, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var data []dto.Options
|
var datas []dto.Options
|
||||||
for _, item := range list.Volumes {
|
for _, item := range list.Volumes {
|
||||||
data = append(data, dto.Options{
|
datas = append(datas, dto.Options{
|
||||||
Option: item.Name,
|
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 {
|
func (u *ContainerService) DeleteVolume(req dto.BatchDelete) error {
|
||||||
client, err := docker.NewDockerClient()
|
client, err := docker.NewDockerClient()
|
||||||
|
@@ -36,7 +36,7 @@ func NewICronjobService() ICronjobService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *CronjobService) SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error) {
|
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
|
var dtoCronjobs []dto.CronjobInfo
|
||||||
for _, cronjob := range cronjobs {
|
for _, cronjob := range cronjobs {
|
||||||
var item dto.CronjobInfo
|
var item dto.CronjobInfo
|
||||||
@@ -100,9 +100,9 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
u.HandleRmExpired(backup.Type, localDir, &cronjob, client)
|
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
|
||||||
} else {
|
} else {
|
||||||
u.HandleRmExpired(backup.Type, "", &cronjob, nil)
|
u.HandleRmExpired(backup.Type, backup.BackupPath, "", &cronjob, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delRecords, err := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(req.CronjobID)))
|
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 backup.Type == "LOCAL" || record.FromLocal {
|
||||||
if _, err := os.Stat(record.File); err != nil && os.IsNotExist(err) {
|
if _, err := os.Stat(record.File); err != nil && os.IsNotExist(err) {
|
||||||
return "", constant.ErrRecordNotFound
|
return "", err
|
||||||
}
|
}
|
||||||
return record.File, nil
|
return record.File, nil
|
||||||
}
|
}
|
||||||
@@ -145,7 +145,7 @@ func (u *CronjobService) Download(down dto.CronjobDownload) (string, error) {
|
|||||||
_ = os.MkdirAll(path.Dir(tempPath), os.ModePerm)
|
_ = os.MkdirAll(path.Dir(tempPath), os.ModePerm)
|
||||||
isOK, err := client.Download(record.File, tempPath)
|
isOK, err := client.Download(record.File, tempPath)
|
||||||
if !isOK || err != nil {
|
if !isOK || err != nil {
|
||||||
return "", constant.ErrRecordNotFound
|
return "", err
|
||||||
}
|
}
|
||||||
return tempPath, nil
|
return tempPath, nil
|
||||||
}
|
}
|
||||||
@@ -238,6 +238,7 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
|
|||||||
upMap["name"] = req.Name
|
upMap["name"] = req.Name
|
||||||
upMap["spec"] = cronjob.Spec
|
upMap["spec"] = cronjob.Spec
|
||||||
upMap["script"] = req.Script
|
upMap["script"] = req.Script
|
||||||
|
upMap["container_name"] = req.ContainerName
|
||||||
upMap["spec_type"] = req.SpecType
|
upMap["spec_type"] = req.SpecType
|
||||||
upMap["week"] = req.Week
|
upMap["week"] = req.Week
|
||||||
upMap["day"] = req.Day
|
upMap["day"] = req.Day
|
||||||
|
@@ -32,17 +32,21 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
|||||||
if len(cronjob.Script) == 0 {
|
if len(cronjob.Script) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
message, err = u.handleShell(cronjob.Type, cronjob.Name, cronjob.Script)
|
if len(cronjob.ContainerName) != 0 {
|
||||||
u.HandleRmExpired("LOCAL", "", cronjob, nil)
|
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)
|
||||||
|
}
|
||||||
|
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
|
||||||
case "curl":
|
case "curl":
|
||||||
if len(cronjob.URL) == 0 {
|
if len(cronjob.URL) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
message, err = u.handleShell(cronjob.Type, cronjob.Name, fmt.Sprintf("curl '%s'", cronjob.URL))
|
message, err = u.handleShell(cronjob.Type, cronjob.Name, fmt.Sprintf("curl '%s'", cronjob.URL))
|
||||||
u.HandleRmExpired("LOCAL", "", cronjob, nil)
|
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
|
||||||
case "ntp":
|
case "ntp":
|
||||||
err = u.handleNtpSync()
|
err = u.handleNtpSync()
|
||||||
u.HandleRmExpired("LOCAL", "", cronjob, nil)
|
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
|
||||||
case "website":
|
case "website":
|
||||||
record.File, err = u.handleBackup(cronjob, record.StartTime)
|
record.File, err = u.handleBackup(cronjob, record.StartTime)
|
||||||
case "database":
|
case "database":
|
||||||
@@ -142,39 +146,55 @@ func (u *CronjobService) handleBackup(cronjob *model.Cronjob, startTime time.Tim
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
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 {
|
if _, err = client.Upload(backupDir+"/"+fileName, itemFileDir+"/"+fileName); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
u.HandleRmExpired(backup.Type, localDir, cronjob, client)
|
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, cronjob, client)
|
||||||
if backup.Type == "LOCAL" || cronjob.KeepLocal {
|
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)
|
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"))
|
records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), commonRepo.WithOrderBy("created_at desc"))
|
||||||
if len(records) > int(cronjob.RetainCopies) {
|
if len(records) <= int(cronjob.RetainCopies) {
|
||||||
for i := int(cronjob.RetainCopies); i < len(records); i++ {
|
return
|
||||||
if len(records[i].File) != 0 {
|
}
|
||||||
files := strings.Split(records[i].File, ",")
|
for i := int(cronjob.RetainCopies); i < len(records); i++ {
|
||||||
for _, file := range files {
|
if len(records[i].File) != 0 {
|
||||||
if backType != "LOCAL" {
|
files := strings.Split(records[i].File, ",")
|
||||||
_, _ = backClient.Delete(strings.ReplaceAll(file, localDir+"/", ""))
|
for _, file := range files {
|
||||||
_ = os.Remove(file)
|
_ = os.Remove(file)
|
||||||
} else {
|
_ = backupRepo.DeleteRecord(context.TODO(), backupRepo.WithByFileName(path.Base(file)))
|
||||||
_ = os.Remove(file)
|
if backType == "LOCAL" {
|
||||||
}
|
continue
|
||||||
_ = backupRepo.DeleteRecord(context.TODO(), backupRepo.WithByFileName(path.Base(file)))
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
_ = cronjobRepo.DeleteRecord(commonRepo.WithByID(uint(records[i].ID)))
|
fileItem := file
|
||||||
_ = os.Remove(records[i].Records)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +217,9 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
|
|||||||
if strings.Contains(sourceDir, "/") {
|
if strings.Contains(sourceDir, "/") {
|
||||||
itemDir := strings.ReplaceAll(sourceDir[strings.LastIndex(sourceDir, "/"):], "/", "")
|
itemDir := strings.ReplaceAll(sourceDir[strings.LastIndex(sourceDir, "/"):], "/", "")
|
||||||
aheadDir := sourceDir[:strings.LastIndex(sourceDir, "/")]
|
aheadDir := sourceDir[:strings.LastIndex(sourceDir, "/")]
|
||||||
|
if len(aheadDir) == 0 {
|
||||||
|
aheadDir = "/"
|
||||||
|
}
|
||||||
path += fmt.Sprintf("-C %s %s", aheadDir, itemDir)
|
path += fmt.Sprintf("-C %s %s", aheadDir, itemDir)
|
||||||
} else {
|
} else {
|
||||||
path = sourceDir
|
path = sourceDir
|
||||||
@@ -204,7 +227,7 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
|
|||||||
|
|
||||||
commands := fmt.Sprintf("tar zcvf %s %s %s", targetDir+"/"+name, excludeRules, path)
|
commands := fmt.Sprintf("tar zcvf %s %s %s", targetDir+"/"+name, excludeRules, path)
|
||||||
global.LOG.Debug(commands)
|
global.LOG.Debug(commands)
|
||||||
stdout, err := cmd.ExecWithTimeOut(commands, 5*time.Minute)
|
stdout, err := cmd.ExecWithTimeOut(commands, 24*time.Hour)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
|
global.LOG.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
|
||||||
return errors.New(stdout)
|
return errors.New(stdout)
|
||||||
@@ -221,7 +244,7 @@ func handleUnTar(sourceFile, targetDir string) error {
|
|||||||
|
|
||||||
commands := fmt.Sprintf("tar zxvfC %s %s", sourceFile, targetDir)
|
commands := fmt.Sprintf("tar zxvfC %s %s", sourceFile, targetDir)
|
||||||
global.LOG.Debug(commands)
|
global.LOG.Debug(commands)
|
||||||
stdout, err := cmd.ExecWithTimeOut(commands, 5*time.Minute)
|
stdout, err := cmd.ExecWithTimeOut(commands, 24*time.Hour)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("do handle untar failed, stdout: %s, err: %v", stdout, err)
|
global.LOG.Errorf("do handle untar failed, stdout: %s, err: %v", stdout, err)
|
||||||
return errors.New(stdout)
|
return errors.New(stdout)
|
||||||
@@ -270,12 +293,11 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, app *repo.RootInf
|
|||||||
}
|
}
|
||||||
record.DetailName = dbName
|
record.DetailName = dbName
|
||||||
record.FileDir = backupDir
|
record.FileDir = backupDir
|
||||||
itemFileDir := strings.ReplaceAll(backupDir, localDir+"/", "")
|
itemFileDir := strings.TrimPrefix(backupDir, localDir+"/")
|
||||||
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
|
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
|
||||||
record.Source = backup.Type
|
record.Source = backup.Type
|
||||||
record.FileDir = itemFileDir
|
record.FileDir = itemFileDir
|
||||||
}
|
}
|
||||||
paths = append(paths, fmt.Sprintf("%s/%s", record.FileDir, record.FileName))
|
|
||||||
|
|
||||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||||
@@ -287,12 +309,22 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, app *repo.RootInf
|
|||||||
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, record.FileName))
|
_ = 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 {
|
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
|
||||||
return paths, err
|
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
|
return paths, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,7 +370,7 @@ func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime t
|
|||||||
dstName := fmt.Sprintf("%s_log_%s.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
|
dstName := fmt.Sprintf("%s_log_%s.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
|
||||||
filePaths = append(filePaths, path.Join(dstLogDir, dstName))
|
filePaths = append(filePaths, path.Join(dstLogDir, dstName))
|
||||||
if err = fileOp.Compress([]string{srcAccessLogPath, srcErrorLogPath}, dstLogDir, dstName, files.Gz); err != nil {
|
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", website.PrimaryDomain)
|
global.LOG.Errorf("There was an error in compressing the website[%s] access.log, err: %v", website.PrimaryDomain, err)
|
||||||
} else {
|
} else {
|
||||||
_ = fileOp.WriteFile(srcAccessLogPath, strings.NewReader(""), 0755)
|
_ = fileOp.WriteFile(srcAccessLogPath, strings.NewReader(""), 0755)
|
||||||
_ = fileOp.WriteFile(srcErrorLogPath, strings.NewReader(""), 0755)
|
_ = fileOp.WriteFile(srcErrorLogPath, strings.NewReader(""), 0755)
|
||||||
@@ -358,7 +390,7 @@ func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime t
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
u.HandleRmExpired("LOCAL", "", cronjob, nil)
|
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
|
||||||
return strings.Join(filePaths, ","), nil
|
return strings.Join(filePaths, ","), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,13 +431,12 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.Backu
|
|||||||
}
|
}
|
||||||
backupDir := fmt.Sprintf("%s/website/%s", localDir, website.PrimaryDomain)
|
backupDir := fmt.Sprintf("%s/website/%s", localDir, website.PrimaryDomain)
|
||||||
record.FileDir = backupDir
|
record.FileDir = backupDir
|
||||||
itemFileDir := strings.ReplaceAll(backupDir, localDir+"/", "")
|
itemFileDir := strings.TrimPrefix(backupDir, localDir+"/")
|
||||||
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
|
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
|
||||||
record.Source = backup.Type
|
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"))
|
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 {
|
if err := handleWebsiteBackup(&website, backupDir, record.FileName); err != nil {
|
||||||
return paths, err
|
return paths, err
|
||||||
}
|
}
|
||||||
@@ -420,11 +451,21 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.Backu
|
|||||||
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, record.FileName))
|
_ = 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 {
|
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
|
||||||
return paths, err
|
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
|
return paths, nil
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,6 @@ import (
|
|||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
"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/buserr"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
@@ -49,7 +48,7 @@ func NewIMysqlService() IMysqlService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *MysqlService) SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error) {
|
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
|
var dtoMysqls []dto.MysqlDBInfo
|
||||||
for _, mysql := range mysqls {
|
for _, mysql := range mysqls {
|
||||||
var item dto.MysqlDBInfo
|
var item dto.MysqlDBInfo
|
||||||
@@ -100,7 +99,7 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
|
|||||||
}
|
}
|
||||||
return nil, err
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,8 +148,14 @@ func (u *MysqlService) Delete(ctx context.Context, req dto.MysqlDBDelete) error
|
|||||||
return err
|
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 {
|
if strings.HasPrefix(app.Version, "5.6") {
|
||||||
return err
|
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 {
|
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop database if exists `%s`", db.Name)); err != nil && !req.ForceDelete {
|
||||||
return err
|
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)
|
passwordChangeCMD := fmt.Sprintf("set password for '%s'@'%s' = password('%s')", mysql.Username, mysql.Permission, info.Value)
|
||||||
if !strings.HasPrefix(app.Version, "5.7") {
|
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)
|
passwordChangeCMD = fmt.Sprintf("ALTER USER '%s'@'%s' IDENTIFIED WITH mysql_native_password BY '%s';", mysql.Username, mysql.Permission, info.Value)
|
||||||
}
|
}
|
||||||
if info.ID != 0 {
|
if info.ID != 0 {
|
||||||
@@ -230,7 +235,7 @@ func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error {
|
|||||||
for _, host := range hosts {
|
for _, host := range hosts {
|
||||||
if host == "%" || host == "localhost" {
|
if host == "%" || host == "localhost" {
|
||||||
passwordRootChangeCMD := fmt.Sprintf("set password for 'root'@'%s' = password('%s')", host, info.Value)
|
passwordRootChangeCMD := fmt.Sprintf("set password for 'root'@'%s' = password('%s')", host, info.Value)
|
||||||
if !strings.HasPrefix(app.Version, "5.7") {
|
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)
|
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 {
|
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 {
|
for _, user := range userlist {
|
||||||
if len(user) != 0 {
|
if len(user) != 0 {
|
||||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists '%s'@'%s'", mysql.Username, user)); err != nil {
|
if strings.HasPrefix(app.Version, "5.6") {
|
||||||
return err
|
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,
|
Username: mysql.Username,
|
||||||
Name: mysql.Name,
|
Name: mysql.Name,
|
||||||
Permission: info.Value,
|
Permission: info.Value,
|
||||||
@@ -347,7 +358,7 @@ func (u *MysqlService) UpdateVariables(updates []dto.MysqlVariablesUpdate) error
|
|||||||
|
|
||||||
group := "[mysqld]"
|
group := "[mysqld]"
|
||||||
for _, info := range updates {
|
for _, info := range updates {
|
||||||
if !strings.HasPrefix(app.Version, "5.7") {
|
if !strings.HasPrefix(app.Version, "5.7") && !strings.HasPrefix(app.Version, "5.6") {
|
||||||
if info.Param == "query_cache_size" {
|
if info.Param == "query_cache_size" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -470,7 +481,7 @@ func (u *MysqlService) LoadStatus() (*dto.MysqlStatus, error) {
|
|||||||
return &info, nil
|
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
|
var userlist []string
|
||||||
if strings.Contains(req.Permission, ",") {
|
if strings.Contains(req.Permission, ",") {
|
||||||
ips := strings.Split(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 {
|
for _, user := range userlist {
|
||||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("create user %s identified by '%s';", user, req.Password)); err != nil {
|
if err := excSQL(container, password, fmt.Sprintf("create user %s identified by '%s';", user, req.Password)); err != nil {
|
||||||
handleCreateError(req.Name, userlist, app)
|
|
||||||
if strings.Contains(err.Error(), "ERROR 1396") {
|
if strings.Contains(err.Error(), "ERROR 1396") {
|
||||||
|
handleCreateError(container, password, req.Name, userlist, false)
|
||||||
return buserr.New(constant.ErrUserIsExist)
|
return buserr.New(constant.ErrUserIsExist)
|
||||||
}
|
}
|
||||||
|
handleCreateError(container, password, req.Name, userlist, true)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
grantStr := fmt.Sprintf("grant all privileges on `%s`.* to %s", req.Name, user)
|
grantStr := fmt.Sprintf("grant all privileges on `%s`.* to %s", req.Name, user)
|
||||||
if req.Name == "*" {
|
if req.Name == "*" {
|
||||||
grantStr = fmt.Sprintf("grant all privileges on *.* to %s", user)
|
grantStr = fmt.Sprintf("grant all privileges on *.* to %s", user)
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(app.Version, "5.7") {
|
if strings.HasPrefix(version, "5.7") || strings.HasPrefix(version, "5.6") {
|
||||||
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, req.Password)
|
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, req.Password)
|
||||||
}
|
}
|
||||||
if err := excSQL(app.ContainerName, app.Password, grantStr); err != nil {
|
if err := excSQL(container, password, grantStr); err != nil {
|
||||||
handleCreateError(req.Name, userlist, app)
|
handleCreateError(container, password, req.Name, userlist, true)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func handleCreateError(dbName string, userlist []string, app *repo.RootInfo) {
|
func handleCreateError(contaienr, password, dbName string, userlist []string, dropUser bool) {
|
||||||
_ = excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop database `%s`", dbName))
|
_ = excSQL(contaienr, password, fmt.Sprintf("drop database `%s`", dbName))
|
||||||
for _, user := range userlist {
|
if dropUser {
|
||||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists %s", user)); err != nil {
|
for _, user := range userlist {
|
||||||
global.LOG.Errorf("drop user failed, err: %v", err)
|
if err := excSQL(contaienr, password, fmt.Sprintf("drop user if exists %s", user)); err != nil {
|
||||||
|
global.LOG.Errorf("drop user failed, err: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -136,12 +136,14 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate) error {
|
|||||||
|
|
||||||
switch req.Key {
|
switch req.Key {
|
||||||
case "Registries":
|
case "Registries":
|
||||||
|
req.Value = strings.TrimRight(req.Value, ",")
|
||||||
if len(req.Value) == 0 {
|
if len(req.Value) == 0 {
|
||||||
delete(daemonMap, "insecure-registries")
|
delete(daemonMap, "insecure-registries")
|
||||||
} else {
|
} else {
|
||||||
daemonMap["insecure-registries"] = strings.Split(req.Value, ",")
|
daemonMap["insecure-registries"] = strings.Split(req.Value, ",")
|
||||||
}
|
}
|
||||||
case "Mirrors":
|
case "Mirrors":
|
||||||
|
req.Value = strings.TrimRight(req.Value, ",")
|
||||||
if len(req.Value) == 0 {
|
if len(req.Value) == 0 {
|
||||||
delete(daemonMap, "registry-mirrors")
|
delete(daemonMap, "registry-mirrors")
|
||||||
} else {
|
} else {
|
||||||
|
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"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/1Panel-dev/1Panel/backend/utils/ssh"
|
||||||
"github.com/jinzhu/copier"
|
"github.com/jinzhu/copier"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -89,8 +90,25 @@ func (u *HostService) TestLocalConn(id uint) bool {
|
|||||||
if err := copier.Copy(&connInfo, &host); err != nil {
|
if err := copier.Copy(&connInfo, &host); err != nil {
|
||||||
return false
|
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 {
|
if len(host.PassPhrase) != 0 {
|
||||||
|
host.PassPhrase, err = encrypt.StringDecrypt(host.PassPhrase)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
connInfo.PassPhrase = []byte(host.PassPhrase)
|
connInfo.PassPhrase = []byte(host.PassPhrase)
|
||||||
}
|
}
|
||||||
client, err := connInfo.NewClient()
|
client, err := connInfo.NewClient()
|
||||||
@@ -107,6 +125,25 @@ func (u *HostService) GetHostInfo(id uint) (*model.Host, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, constant.ErrRecordNotFound
|
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
|
return &host, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,6 +164,25 @@ func (u *HostService) SearchWithPage(search dto.SearchHostWithPage) (int64, inte
|
|||||||
item.Password = ""
|
item.Password = ""
|
||||||
item.PrivateKey = ""
|
item.PrivateKey = ""
|
||||||
item.PassPhrase = ""
|
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)
|
dtoHosts = append(dtoHosts, item)
|
||||||
}
|
}
|
||||||
|
@@ -3,12 +3,14 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"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/constant"
|
||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||||
@@ -76,6 +78,9 @@ func (u *ImageRepoService) List() ([]dto.ImageRepoOption, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *ImageRepoService) Create(req dto.ImageRepoCreate) 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))
|
imageRepo, _ := imageRepoRepo.Get(commonRepo.WithByName(req.Name))
|
||||||
if imageRepo.ID != 0 {
|
if imageRepo.ID != 0 {
|
||||||
return constant.ErrRecordExist
|
return constant.ErrRecordExist
|
||||||
@@ -142,6 +147,9 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
|
|||||||
if req.ID == 1 {
|
if req.ID == 1 {
|
||||||
return errors.New("The default value cannot be deleted !")
|
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))
|
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.ID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -149,7 +157,7 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
|
|||||||
if repo.DownloadUrl != req.DownloadUrl || (!repo.Auth && req.Auth) {
|
if repo.DownloadUrl != req.DownloadUrl || (!repo.Auth && req.Auth) {
|
||||||
_ = u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update")
|
_ = u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update")
|
||||||
if repo.Auth {
|
if repo.Auth {
|
||||||
_, _ = cmd.Execf("docker logout %s", repo.DownloadUrl)
|
_, _ = cmd.ExecWithCheck("docker", "logout", repo.DownloadUrl)
|
||||||
}
|
}
|
||||||
stdout, err := cmd.Exec("systemctl restart docker")
|
stdout, err := cmd.Exec("systemctl restart docker")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -176,9 +184,9 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *ImageRepoService) CheckConn(host, user, password string) error {
|
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 {
|
if err != nil {
|
||||||
return errors.New(string(stdout))
|
return fmt.Errorf("stdout: %s, stderr: %v", stdout, err)
|
||||||
}
|
}
|
||||||
if strings.Contains(string(stdout), "Login Succeeded") {
|
if strings.Contains(string(stdout), "Login Succeeded") {
|
||||||
return nil
|
return nil
|
||||||
|
@@ -76,17 +76,31 @@ func loadDiskIO() {
|
|||||||
if io2.Name == io1.Name {
|
if io2.Name == io1.Name {
|
||||||
var itemIO model.MonitorIO
|
var itemIO model.MonitorIO
|
||||||
itemIO.Name = io1.Name
|
itemIO.Name = io1.Name
|
||||||
itemIO.Read = uint64(float64(io2.ReadBytes-io1.ReadBytes) / 60)
|
if io2.ReadBytes != 0 && io1.ReadBytes != 0 {
|
||||||
itemIO.Write = uint64(float64(io2.WriteBytes-io1.WriteBytes) / 60)
|
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)
|
if io2.ReadCount != 0 && io1.ReadCount != 0 {
|
||||||
writeCount := uint64(float64(io2.WriteCount-io1.WriteCount) / 60)
|
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 {
|
if writeCount > itemIO.Count {
|
||||||
itemIO.Count = writeCount
|
itemIO.Count = writeCount
|
||||||
}
|
}
|
||||||
|
|
||||||
itemIO.Time = uint64(float64(io2.ReadTime-io1.ReadTime) / 60)
|
if io2.ReadTime != 0 && io1.ReadTime != 0 {
|
||||||
writeTime := uint64(float64(io2.WriteTime-io1.WriteTime) / 60)
|
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 {
|
if writeTime > itemIO.Time {
|
||||||
itemIO.Time = writeTime
|
itemIO.Time = writeTime
|
||||||
}
|
}
|
||||||
@@ -113,8 +127,13 @@ func loadNetIO() {
|
|||||||
if net2.Name == net1.Name {
|
if net2.Name == net1.Name {
|
||||||
var itemNet model.MonitorNetwork
|
var itemNet model.MonitorNetwork
|
||||||
itemNet.Name = net1.Name
|
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)
|
netList = append(netList, itemNet)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -123,14 +142,24 @@ func loadNetIO() {
|
|||||||
netStatAll2, _ := net.IOCounters(false)
|
netStatAll2, _ := net.IOCounters(false)
|
||||||
for _, net2 := range netStatAll2 {
|
for _, net2 := range netStatAll2 {
|
||||||
for _, net1 := range netStatAll {
|
for _, net1 := range netStatAll {
|
||||||
if net1.BytesSent == 0 || net1.BytesRecv == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if net2.Name == net1.Name {
|
if net2.Name == net1.Name {
|
||||||
var itemNet model.MonitorNetwork
|
var itemNet model.MonitorNetwork
|
||||||
itemNet.Name = net1.Name
|
itemNet.Name = net1.Name
|
||||||
itemNet.Up = float64(net2.BytesSent-net1.BytesSent) / 1024 / 60
|
if net2.BytesSent != 0 && net1.BytesSent != 0 {
|
||||||
itemNet.Down = float64(net2.BytesRecv-net1.BytesRecv) / 1024 / 60
|
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)
|
netList = append(netList, itemNet)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
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
|
||||||
|
}
|
@@ -65,7 +65,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
|
|||||||
}
|
}
|
||||||
fileOp := files.NewFileOp()
|
fileOp := files.NewFileOp()
|
||||||
appVersionDir := path.Join(constant.AppResourceDir, app.Resource, app.Key, appDetail.Version)
|
appVersionDir := path.Join(constant.AppResourceDir, app.Resource, app.Key, appDetail.Version)
|
||||||
if !fileOp.Stat(appVersionDir) {
|
if !fileOp.Stat(appVersionDir) || appDetail.Update {
|
||||||
if err := downloadApp(app, appDetail, nil); err != nil {
|
if err := downloadApp(app, appDetail, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -62,7 +62,7 @@ func handleParams(image, runtimeType, runtimeDir string, params map[string]inter
|
|||||||
if extendsArray, ok := extends.([]interface{}); ok {
|
if extendsArray, ok := extends.([]interface{}); ok {
|
||||||
strArray := make([]string, len(extendsArray))
|
strArray := make([]string, len(extendsArray))
|
||||||
for i, v := range 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, ",")
|
params["PHP_EXTENSIONS"] = strings.Join(strArray, ",")
|
||||||
}
|
}
|
||||||
|
@@ -56,6 +56,12 @@ func (u *SnapshotService) SnapshotImport(req dto.SnapshotImport) error {
|
|||||||
if len(req.Names) == 0 {
|
if len(req.Names) == 0 {
|
||||||
return fmt.Errorf("incorrect snapshot request body: %v", req.Names)
|
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 {
|
for _, snap := range req.Names {
|
||||||
nameItems := strings.Split(snap, "_")
|
nameItems := strings.Split(snap, "_")
|
||||||
if !strings.HasPrefix(snap, "1panel_v") || !strings.HasSuffix(snap, ".tar.gz") || len(nameItems) != 3 {
|
if !strings.HasPrefix(snap, "1panel_v") || !strings.HasSuffix(snap, ".tar.gz") || len(nameItems) != 3 {
|
||||||
@@ -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)
|
global.LOG.Infof("start to upload snapshot to %s, please wait", backup.Type)
|
||||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusUploading})
|
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusUploading})
|
||||||
localPath := fmt.Sprintf("%s/system/1panel_%s_%s.tar.gz", localDir, versionItem.Value, timeNow)
|
localPath := fmt.Sprintf("%s/system/1panel_%s_%s.tar.gz", localDir, versionItem.Value, timeNow)
|
||||||
if ok, err := backupAccount.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()})
|
_ = 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)
|
global.LOG.Errorf("upload snapshot to %s failed, err: %v", backup.Type, err)
|
||||||
return
|
return
|
||||||
@@ -253,7 +261,9 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
|
|||||||
operation = "re-recover"
|
operation = "re-recover"
|
||||||
}
|
}
|
||||||
if !isReTry || snap.InterruptStep == "Download" || (isReTry && req.ReDownload) {
|
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 err != nil || !ok {
|
||||||
if req.ReDownload {
|
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))
|
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" {
|
if operation == "snapshot" || operation == "recover" {
|
||||||
_, err := os.Stat(daemonJsonPath)
|
_, err := os.Stat(daemonJsonPath)
|
||||||
if os.IsNotExist(err) {
|
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 == nil {
|
||||||
if err := fileOp.CopyFile(daemonJsonPath, target); 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 {
|
func (u *SnapshotService) handlePanelDatas(snapID uint, fileOp files.FileOp, operation string, source, target, backupDir, dockerDir string) error {
|
||||||
switch operation {
|
switch operation {
|
||||||
case "snapshot":
|
case "snapshot":
|
||||||
exclusionRules := "./tmp;./cache;"
|
exclusionRules := "./tmp;./cache;./db/1Panel.db-*;"
|
||||||
if strings.Contains(backupDir, source) {
|
if strings.Contains(backupDir, source) {
|
||||||
exclusionRules += ("." + strings.ReplaceAll(backupDir, source, "") + ";")
|
exclusionRules += ("." + strings.ReplaceAll(backupDir, source, "") + ";")
|
||||||
}
|
}
|
||||||
|
@@ -229,29 +229,29 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
|
|||||||
if len(req.Info) != 0 {
|
if len(req.Info) != 0 {
|
||||||
command = fmt.Sprintf(" | grep '%s'", req.Info)
|
command = fmt.Sprintf(" | grep '%s'", req.Info)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len(fileList); i++ {
|
for i := 0; i < len(fileList); i++ {
|
||||||
if strings.HasPrefix(path.Base(fileList[i]), "secure") {
|
withAppend := len(data.Logs) < req.Page*req.PageSize
|
||||||
commandItem := fmt.Sprintf("cat %s | grep -a 'Failed password for' | grep -v 'invalid' %s", fileList[i], command)
|
if req.Status != constant.StatusSuccess {
|
||||||
dataItem := loadFailedSecureDatas(commandItem)
|
if strings.HasPrefix(path.Base(fileList[i]), "secure") {
|
||||||
data.FailedCount += len(dataItem)
|
commandItem := fmt.Sprintf("cat %s | grep -a 'Failed password for' | grep -v 'invalid' %s", fileList[i], command)
|
||||||
data.TotalCount += len(dataItem)
|
dataItem, itemTotal := loadFailedSecureDatas(commandItem, withAppend)
|
||||||
if req.Status != constant.StatusSuccess {
|
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...)
|
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 := loadFailedAuthDatas(commandItem)
|
|
||||||
data.FailedCount += len(dataItem)
|
|
||||||
data.TotalCount += len(dataItem)
|
|
||||||
if req.Status != constant.StatusSuccess {
|
|
||||||
data.Logs = append(data.Logs, dataItem...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
commandItem := fmt.Sprintf("cat %s | grep Accepted %s", fileList[i], command)
|
|
||||||
dataItem := loadSuccessDatas(commandItem)
|
|
||||||
data.TotalCount += len(dataItem)
|
|
||||||
if req.Status != constant.StatusFailed {
|
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.Logs = append(data.Logs, dataItem...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,7 +279,7 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
|
|||||||
global.LOG.Errorf("load qqwry datas failed: %s", err)
|
global.LOG.Errorf("load qqwry datas failed: %s", err)
|
||||||
}
|
}
|
||||||
var itemLogs []dto.SSHHistory
|
var itemLogs []dto.SSHHistory
|
||||||
for i := len(data.Logs) - 1; i >= 0; i-- {
|
for i := 0; i < len(data.Logs); i++ {
|
||||||
data.Logs[i].Area = qqWry.Find(data.Logs[i].Address).Area
|
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)
|
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])
|
itemLogs = append(itemLogs, data.Logs[i])
|
||||||
@@ -293,18 +293,17 @@ func sortFileList(fileNames []string) []string {
|
|||||||
if len(fileNames) < 2 {
|
if len(fileNames) < 2 {
|
||||||
return fileNames
|
return fileNames
|
||||||
}
|
}
|
||||||
var itemFile []string
|
if strings.HasPrefix(path.Base(fileNames[0]), "secure") {
|
||||||
if strings.Contains(fileNames[0], "secure") {
|
var itemFile []string
|
||||||
sort.Slice(fileNames, func(i, j int) bool {
|
sort.Slice(fileNames, func(i, j int) bool {
|
||||||
return fileNames[i] < fileNames[j]
|
return fileNames[i] > fileNames[j]
|
||||||
})
|
})
|
||||||
itemFile = append(itemFile, fileNames[1:]...)
|
itemFile = append(itemFile, fileNames[len(fileNames)-1])
|
||||||
itemFile = append(itemFile, fileNames[0])
|
itemFile = append(itemFile, fileNames[:len(fileNames)-2]...)
|
||||||
return itemFile
|
return itemFile
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Slice(fileNames, func(i, j int) bool {
|
sort.Slice(fileNames, func(i, j int) bool {
|
||||||
return fileNames[i] > fileNames[j]
|
return fileNames[i] < fileNames[j]
|
||||||
})
|
})
|
||||||
return fileNames
|
return fileNames
|
||||||
}
|
}
|
||||||
@@ -339,82 +338,109 @@ func updateSSHConf(oldFiles []string, param string, value interface{}) []string
|
|||||||
return newFiles
|
return newFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadSuccessDatas(command string) []dto.SSHHistory {
|
func loadSuccessDatas(command string, withAppend bool) ([]dto.SSHHistory, int) {
|
||||||
var datas []dto.SSHHistory
|
var (
|
||||||
|
datas []dto.SSHHistory
|
||||||
|
totalNum int
|
||||||
|
)
|
||||||
stdout2, err := cmd.Exec(command)
|
stdout2, err := cmd.Exec(command)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
lines := strings.Split(string(stdout2), "\n")
|
lines := strings.Split(string(stdout2), "\n")
|
||||||
for _, line := range lines {
|
if len(lines) == 0 {
|
||||||
parts := strings.Fields(line)
|
return datas, 0
|
||||||
|
}
|
||||||
|
for i := len(lines) - 1; i >= 0; i-- {
|
||||||
|
parts := strings.Fields(lines[i])
|
||||||
if len(parts) < 14 {
|
if len(parts) < 14 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
historyItem := dto.SSHHistory{
|
totalNum++
|
||||||
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
if withAppend {
|
||||||
AuthMode: parts[6],
|
historyItem := dto.SSHHistory{
|
||||||
User: parts[8],
|
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
||||||
Address: parts[10],
|
AuthMode: parts[6],
|
||||||
Port: parts[12],
|
User: parts[8],
|
||||||
Status: constant.StatusSuccess,
|
Address: parts[10],
|
||||||
|
Port: parts[12],
|
||||||
|
Status: constant.StatusSuccess,
|
||||||
|
}
|
||||||
|
datas = append(datas, historyItem)
|
||||||
}
|
}
|
||||||
datas = append(datas, historyItem)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return datas
|
return datas, totalNum
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadFailedAuthDatas(command string) []dto.SSHHistory {
|
func loadFailedAuthDatas(command string, withAppend bool) ([]dto.SSHHistory, int) {
|
||||||
var datas []dto.SSHHistory
|
var (
|
||||||
|
datas []dto.SSHHistory
|
||||||
|
totalNum int
|
||||||
|
)
|
||||||
stdout2, err := cmd.Exec(command)
|
stdout2, err := cmd.Exec(command)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
lines := strings.Split(string(stdout2), "\n")
|
lines := strings.Split(string(stdout2), "\n")
|
||||||
for _, line := range lines {
|
if len(lines) == 0 {
|
||||||
parts := strings.Fields(line)
|
return datas, 0
|
||||||
|
}
|
||||||
|
for i := len(lines) - 1; i >= 0; i-- {
|
||||||
|
parts := strings.Fields(lines[i])
|
||||||
if len(parts) < 14 {
|
if len(parts) < 14 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
historyItem := dto.SSHHistory{
|
totalNum++
|
||||||
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
if withAppend {
|
||||||
AuthMode: parts[8],
|
historyItem := dto.SSHHistory{
|
||||||
User: parts[10],
|
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
||||||
Address: parts[11],
|
AuthMode: parts[8],
|
||||||
Port: parts[13],
|
User: parts[10],
|
||||||
Status: constant.StatusFailed,
|
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)
|
||||||
}
|
}
|
||||||
if strings.Contains(line, ": ") {
|
|
||||||
historyItem.Message = strings.Split(line, ": ")[1]
|
|
||||||
}
|
|
||||||
datas = append(datas, historyItem)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return datas
|
return datas, totalNum
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadFailedSecureDatas(command string) []dto.SSHHistory {
|
func loadFailedSecureDatas(command string, withAppend bool) ([]dto.SSHHistory, int) {
|
||||||
var datas []dto.SSHHistory
|
var (
|
||||||
|
datas []dto.SSHHistory
|
||||||
|
totalNum int
|
||||||
|
)
|
||||||
stdout2, err := cmd.Exec(command)
|
stdout2, err := cmd.Exec(command)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
lines := strings.Split(string(stdout2), "\n")
|
lines := strings.Split(string(stdout2), "\n")
|
||||||
for _, line := range lines {
|
if len(lines) == 0 {
|
||||||
parts := strings.Fields(line)
|
return datas, 0
|
||||||
|
}
|
||||||
|
for i := len(lines) - 1; i >= 0; i-- {
|
||||||
|
parts := strings.Fields(lines[i])
|
||||||
if len(parts) < 14 {
|
if len(parts) < 14 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
historyItem := dto.SSHHistory{
|
totalNum++
|
||||||
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
if withAppend {
|
||||||
AuthMode: parts[6],
|
historyItem := dto.SSHHistory{
|
||||||
User: parts[8],
|
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
||||||
Address: parts[10],
|
AuthMode: parts[6],
|
||||||
Port: parts[12],
|
User: parts[8],
|
||||||
Status: constant.StatusFailed,
|
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)
|
||||||
}
|
}
|
||||||
if strings.Contains(line, ": ") {
|
|
||||||
historyItem.Message = strings.Split(line, ": ")[1]
|
|
||||||
}
|
|
||||||
datas = append(datas, historyItem)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return datas
|
return datas, totalNum
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleGunzip(path string) error {
|
func handleGunzip(path string) error {
|
||||||
|
@@ -8,6 +8,14 @@ import (
|
|||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
"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/cmd"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||||
@@ -18,13 +26,6 @@ import (
|
|||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
"gorm.io/gorm"
|
"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/request"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||||
@@ -57,7 +58,7 @@ type IWebsiteService interface {
|
|||||||
UpdateNginxConfigByScope(req request.NginxConfigUpdate) error
|
UpdateNginxConfigByScope(req request.NginxConfigUpdate) error
|
||||||
GetWebsiteNginxConfig(websiteId uint, configType string) (response.FileInfo, error)
|
GetWebsiteNginxConfig(websiteId uint, configType string) (response.FileInfo, error)
|
||||||
GetWebsiteHTTPS(websiteId uint) (response.WebsiteHTTPS, 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)
|
PreInstallCheck(req request.WebsiteInstallCheckReq) ([]response.WebsitePreInstallCheck, error)
|
||||||
GetWafConfig(req request.WebsiteWafReq) (response.WebsiteWafConfig, error)
|
GetWafConfig(req request.WebsiteWafReq) (response.WebsiteWafConfig, error)
|
||||||
UpdateWafConfig(req request.WebsiteWafUpdate) error
|
UpdateWafConfig(req request.WebsiteWafUpdate) error
|
||||||
@@ -96,7 +97,7 @@ func (w WebsiteService) PageWebsite(req request.WebsiteSearch) (int64, []respons
|
|||||||
}
|
}
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
opts = append(opts, commonRepo.WithOrderBy("created_at desc"))
|
opts = append(opts, commonRepo.WithOrderRuleBy(req.OrderBy, req.Order))
|
||||||
if req.Name != "" {
|
if req.Name != "" {
|
||||||
opts = append(opts, websiteRepo.WithDomainLike(req.Name))
|
opts = append(opts, websiteRepo.WithDomainLike(req.Name))
|
||||||
}
|
}
|
||||||
@@ -618,10 +619,10 @@ func (w WebsiteService) GetWebsiteHTTPS(websiteId uint) (response.WebsiteHTTPS,
|
|||||||
return res, nil
|
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))
|
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.WebsiteHTTPS{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
res response.WebsiteHTTPS
|
res response.WebsiteHTTPS
|
||||||
@@ -634,7 +635,7 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
|
|||||||
website.Protocol = constant.ProtocolHTTP
|
website.Protocol = constant.ProtocolHTTP
|
||||||
website.WebsiteSSLID = 0
|
website.WebsiteSSLID = 0
|
||||||
if err := deleteListenAndServerName(website, []string{"443", "[::]:443"}, []string{}); err != nil {
|
if err := deleteListenAndServerName(website, []string{"443", "[::]:443"}, []string{}); err != nil {
|
||||||
return response.WebsiteHTTPS{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
nginxParams := getNginxParamsFromStaticFile(dto.SSL, nil)
|
nginxParams := getNginxParamsFromStaticFile(dto.SSL, nil)
|
||||||
nginxParams = append(nginxParams,
|
nginxParams = append(nginxParams,
|
||||||
@@ -655,28 +656,64 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
|
|||||||
Name: "ssl_ciphers",
|
Name: "ssl_ciphers",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err := deleteNginxConfig(constant.NginxScopeServer, nginxParams, &website); err != nil {
|
if err = deleteNginxConfig(constant.NginxScopeServer, nginxParams, &website); err != nil {
|
||||||
return response.WebsiteHTTPS{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := websiteRepo.Save(ctx, &website); err != nil {
|
if err = websiteRepo.Save(ctx, &website); err != nil {
|
||||||
return response.WebsiteHTTPS{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return res, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Type == constant.SSLExisted {
|
if req.Type == constant.SSLExisted {
|
||||||
websiteSSL, err = websiteSSLRepo.GetFirst(commonRepo.WithByID(req.WebsiteSSLID))
|
websiteSSL, err = websiteSSLRepo.GetFirst(commonRepo.WithByID(req.WebsiteSSLID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.WebsiteHTTPS{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
website.WebsiteSSLID = websiteSSL.ID
|
website.WebsiteSSLID = websiteSSL.ID
|
||||||
res.SSL = websiteSSL
|
res.SSL = websiteSSL
|
||||||
}
|
}
|
||||||
if req.Type == constant.SSLManual {
|
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)
|
cert, err := x509.ParseCertificate(certBlock.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.WebsiteHTTPS{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
websiteSSL.ExpireDate = cert.NotAfter
|
websiteSSL.ExpireDate = cert.NotAfter
|
||||||
websiteSSL.StartDate = cert.NotBefore
|
websiteSSL.StartDate = cert.NotBefore
|
||||||
@@ -690,28 +727,28 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
|
|||||||
websiteSSL.PrimaryDomain = cert.DNSNames[0]
|
websiteSSL.PrimaryDomain = cert.DNSNames[0]
|
||||||
websiteSSL.Domains = strings.Join(cert.DNSNames, ",")
|
websiteSSL.Domains = strings.Join(cert.DNSNames, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
websiteSSL.Provider = constant.Manual
|
websiteSSL.Provider = constant.Manual
|
||||||
websiteSSL.PrivateKey = req.PrivateKey
|
websiteSSL.PrivateKey = privateKey
|
||||||
websiteSSL.Pem = req.Certificate
|
websiteSSL.Pem = certificate
|
||||||
|
|
||||||
res.SSL = websiteSSL
|
res.SSL = websiteSSL
|
||||||
}
|
}
|
||||||
website.Protocol = constant.ProtocolHTTPS
|
website.Protocol = constant.ProtocolHTTPS
|
||||||
if err := applySSL(website, websiteSSL, req); err != nil {
|
if err := applySSL(website, websiteSSL, req); err != nil {
|
||||||
return response.WebsiteHTTPS{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
website.HttpConfig = req.HttpConfig
|
website.HttpConfig = req.HttpConfig
|
||||||
|
|
||||||
if websiteSSL.ID == 0 {
|
if websiteSSL.ID == 0 {
|
||||||
if err := websiteSSLRepo.Create(ctx, &websiteSSL); err != nil {
|
if err := websiteSSLRepo.Create(ctx, &websiteSSL); err != nil {
|
||||||
return response.WebsiteHTTPS{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
website.WebsiteSSLID = websiteSSL.ID
|
website.WebsiteSSLID = websiteSSL.ID
|
||||||
}
|
}
|
||||||
if err := websiteRepo.Save(ctx, &website); err != nil {
|
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) {
|
func (w WebsiteService) PreInstallCheck(req request.WebsiteInstallCheckReq) ([]response.WebsitePreInstallCheck, error) {
|
||||||
@@ -1058,50 +1095,41 @@ func (w WebsiteService) UpdatePHPConfig(req request.WebsitePHPConfigUpdate) (err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Scope == "params" {
|
content := string(contentBytes)
|
||||||
content := string(contentBytes)
|
lines := strings.Split(content, "\n")
|
||||||
lines := strings.Split(content, "\n")
|
for i, line := range lines {
|
||||||
for i, line := range lines {
|
if strings.HasPrefix(line, ";") {
|
||||||
if strings.HasPrefix(line, ";") {
|
continue
|
||||||
continue
|
}
|
||||||
}
|
switch req.Scope {
|
||||||
|
case "params":
|
||||||
for key, value := range req.Params {
|
for key, value := range req.Params {
|
||||||
pattern := "^" + regexp.QuoteMeta(key) + "\\s*=\\s*.*$"
|
pattern := "^" + regexp.QuoteMeta(key) + "\\s*=\\s*.*$"
|
||||||
if matched, _ := regexp.MatchString(pattern, line); matched {
|
if matched, _ := regexp.MatchString(pattern, line); matched {
|
||||||
lines[i] = key + " = " + value
|
lines[i] = key + " = " + value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
case "disable_functions":
|
||||||
updatedContent := strings.Join(lines, "\n")
|
pattern := "^" + regexp.QuoteMeta("disable_functions") + "\\s*=\\s*.*$"
|
||||||
if err := fileOp.WriteFile(phpConfigPath, strings.NewReader(updatedContent), 0755); err != nil {
|
if matched, _ := regexp.MatchString(pattern, line); matched {
|
||||||
return err
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
updatedContent := strings.Join(lines, "\n")
|
||||||
cfg, err := ini.Load(phpConfigPath)
|
if err := fileOp.WriteFile(phpConfigPath, strings.NewReader(updatedContent), 0755); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
phpConfig, err := cfg.GetSection("PHP")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if req.Scope == "disable_functions" {
|
|
||||||
disable := phpConfig.Key("disable_functions")
|
|
||||||
disable.SetValue(strings.Join(req.DisableFunctions, ","))
|
|
||||||
if err = cfg.SaveTo(phpConfigPath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if req.Scope == "upload_max_filesize" {
|
|
||||||
postMaxSize := phpConfig.Key("post_max_size")
|
|
||||||
postMaxSize.SetValue(req.UploadMaxSize)
|
|
||||||
uploadMaxFileSize := phpConfig.Key("upload_max_filesize")
|
|
||||||
uploadMaxFileSize.SetValue(req.UploadMaxSize)
|
|
||||||
if err = cfg.SaveTo(phpConfigPath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
appInstallReq := request.AppInstalledOperate{
|
appInstallReq := request.AppInstalledOperate{
|
||||||
InstallId: appInstall.ID,
|
InstallId: appInstall.ID,
|
||||||
@@ -1504,14 +1532,6 @@ func (w WebsiteService) UpdateAuthBasic(req request.NginxAuthUpdate) (err error)
|
|||||||
if !fileOp.Stat(absoluteAuthPath) {
|
if !fileOp.Stat(absoluteAuthPath) {
|
||||||
_ = fileOp.CreateFile(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", Params: []string{`"Authentication"`}})
|
||||||
params = append(params, dto.NginxParam{Name: "auth_basic_user_file", Params: []string{authPath}})
|
params = append(params, dto.NginxParam{Name: "auth_basic_user_file", Params: []string{authPath}})
|
||||||
@@ -1519,7 +1539,9 @@ func (w WebsiteService) UpdateAuthBasic(req request.NginxAuthUpdate) (err error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
authArray = strings.Split(string(authContent), "\n")
|
if len(authContent) > 0 {
|
||||||
|
authArray = strings.Split(string(authContent), "\n")
|
||||||
|
}
|
||||||
switch req.Operate {
|
switch req.Operate {
|
||||||
case "disable":
|
case "disable":
|
||||||
return deleteNginxConfig(constant.NginxScopeServer, params, &website)
|
return deleteNginxConfig(constant.NginxScopeServer, params, &website)
|
||||||
@@ -1590,6 +1612,9 @@ func (w WebsiteService) UpdateAuthBasic(req request.NginxAuthUpdate) (err error)
|
|||||||
defer passFile.Close()
|
defer passFile.Close()
|
||||||
writer := bufio.NewWriter(passFile)
|
writer := bufio.NewWriter(passFile)
|
||||||
for _, line := range authArray {
|
for _, line := range authArray {
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
_, err = writer.WriteString(line + "\n")
|
_, err = writer.WriteString(line + "\n")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -1599,6 +1624,15 @@ func (w WebsiteService) UpdateAuthBasic(req request.NginxAuthUpdate) (err error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,11 +7,13 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
"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/dto/response"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
"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/buserr"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/ssl"
|
"github.com/1Panel-dev/1Panel/backend/utils/ssl"
|
||||||
"path"
|
"path"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -21,7 +23,7 @@ type WebsiteSSLService struct {
|
|||||||
type IWebsiteSSLService interface {
|
type IWebsiteSSLService interface {
|
||||||
Page(search request.WebsiteSSLSearch) (int64, []response.WebsiteSSLDTO, error)
|
Page(search request.WebsiteSSLSearch) (int64, []response.WebsiteSSLDTO, error)
|
||||||
GetSSL(id uint) (*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)
|
Create(create request.WebsiteSSLCreate) (request.WebsiteSSLCreate, error)
|
||||||
Renew(sslId uint) error
|
Renew(sslId uint) error
|
||||||
GetDNSResolve(req request.WebsiteDNSReq) ([]response.WebsiteDNSRes, 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) {
|
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"))
|
total, sslList, err := websiteSSLRepo.Page(search.Page, search.PageSize, commonRepo.WithOrderBy("created_at desc"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
var sslDTOs []response.WebsiteSSLDTO
|
for _, sslModel := range sslList {
|
||||||
for _, ssl := range sslList {
|
result = append(result, response.WebsiteSSLDTO{
|
||||||
sslDTOs = append(sslDTOs, response.WebsiteSSLDTO{
|
WebsiteSSL: sslModel,
|
||||||
WebsiteSSL: ssl,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return total, sslDTOs, err
|
return total, result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w WebsiteSSLService) GetSSL(id uint) (*response.WebsiteSSLDTO, error) {
|
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
|
return &res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w WebsiteSSLService) Search() ([]response.WebsiteSSLDTO, error) {
|
func (w WebsiteSSLService) Search(search request.WebsiteSSLSearch) ([]response.WebsiteSSLDTO, error) {
|
||||||
sslList, err := websiteSSLRepo.List()
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var sslDTOs []response.WebsiteSSLDTO
|
for _, sslModel := range sslList {
|
||||||
for _, ssl := range sslList {
|
result = append(result, response.WebsiteSSLDTO{
|
||||||
sslDTOs = append(sslDTOs, response.WebsiteSSLDTO{
|
WebsiteSSL: sslModel,
|
||||||
WebsiteSSL: ssl,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return sslDTOs, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.WebsiteSSLCreate, error) {
|
func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.WebsiteSSLCreate, error) {
|
||||||
|
@@ -378,9 +378,9 @@ func applySSL(website model.Website, websiteSSL model.WebsiteSSL, req request.We
|
|||||||
}
|
}
|
||||||
config := nginxFull.SiteConfig.Config
|
config := nginxFull.SiteConfig.Config
|
||||||
server := config.FindServers()[0]
|
server := config.FindServers()[0]
|
||||||
server.UpdateListen("443", website.DefaultServer, "ssl")
|
server.UpdateListen("443", website.DefaultServer, "ssl", "http2")
|
||||||
if website.IPV6 {
|
if website.IPV6 {
|
||||||
server.UpdateListen("[::]:443", website.DefaultServer, "ssl")
|
server.UpdateListen("[::]:443", website.DefaultServer, "ssl", "http2")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch req.HttpConfig {
|
switch req.HttpConfig {
|
||||||
|
@@ -21,4 +21,6 @@ type System struct {
|
|||||||
IsDemo bool `mapstructure:"is_demo"`
|
IsDemo bool `mapstructure:"is_demo"`
|
||||||
AppRepo string `mapstructure:"app_repo"`
|
AppRepo string `mapstructure:"app_repo"`
|
||||||
ChangeUserInfo bool `mapstructure:"change_user_info"`
|
ChangeUserInfo bool `mapstructure:"change_user_info"`
|
||||||
|
OneDriveID string `mapstructure:"one_drive_id"`
|
||||||
|
OneDriveSc string `mapstructure:"one_drive_sc"`
|
||||||
}
|
}
|
||||||
|
@@ -11,6 +11,7 @@ const (
|
|||||||
DirNotFound = "DirNotFound"
|
DirNotFound = "DirNotFound"
|
||||||
Upgrading = "Upgrading"
|
Upgrading = "Upgrading"
|
||||||
UpgradeErr = "UpgradeErr"
|
UpgradeErr = "UpgradeErr"
|
||||||
|
PullErr = "PullErr"
|
||||||
|
|
||||||
ContainerPrefix = "1Panel-"
|
ContainerPrefix = "1Panel-"
|
||||||
|
|
||||||
|
@@ -7,7 +7,10 @@ const (
|
|||||||
S3 = "S3"
|
S3 = "S3"
|
||||||
OSS = "OSS"
|
OSS = "OSS"
|
||||||
Sftp = "SFTP"
|
Sftp = "SFTP"
|
||||||
|
OneDrive = "OneDrive"
|
||||||
MinIo = "MINIO"
|
MinIo = "MINIO"
|
||||||
Cos = "COS"
|
Cos = "COS"
|
||||||
Kodo = "KODO"
|
Kodo = "KODO"
|
||||||
|
|
||||||
|
OneDriveRedirectURI = "http://localhost/login/authorized"
|
||||||
)
|
)
|
||||||
|
@@ -105,6 +105,7 @@ var (
|
|||||||
ErrInUsed = "ErrInUsed"
|
ErrInUsed = "ErrInUsed"
|
||||||
ErrObjectInUsed = "ErrObjectInUsed"
|
ErrObjectInUsed = "ErrObjectInUsed"
|
||||||
ErrPortRules = "ErrPortRules"
|
ErrPortRules = "ErrPortRules"
|
||||||
|
ErrRepoConn = "ErrRepoConn"
|
||||||
)
|
)
|
||||||
|
|
||||||
// runtime
|
// runtime
|
||||||
|
@@ -10,4 +10,7 @@ const (
|
|||||||
StatusEnable = "Enable"
|
StatusEnable = "Enable"
|
||||||
StatusDisable = "Disable"
|
StatusDisable = "Disable"
|
||||||
StatusNone = "None"
|
StatusNone = "None"
|
||||||
|
|
||||||
|
OrderDesc = "descending"
|
||||||
|
OrderAsc = "ascending"
|
||||||
)
|
)
|
||||||
|
@@ -60,7 +60,7 @@ func GinI18nLocalize() gin.HandlerFunc {
|
|||||||
return ginI18n.Localize(
|
return ginI18n.Localize(
|
||||||
ginI18n.WithBundle(&ginI18n.BundleCfg{
|
ginI18n.WithBundle(&ginI18n.BundleCfg{
|
||||||
RootPath: "./lang",
|
RootPath: "./lang",
|
||||||
AcceptLanguage: []language.Tag{language.Chinese, language.English},
|
AcceptLanguage: []language.Tag{language.Chinese, language.English, language.TraditionalChinese},
|
||||||
DefaultLanguage: language.Chinese,
|
DefaultLanguage: language.Chinese,
|
||||||
FormatBundleFile: "yaml",
|
FormatBundleFile: "yaml",
|
||||||
UnmarshalFunc: yaml.Unmarshal,
|
UnmarshalFunc: yaml.Unmarshal,
|
||||||
|
@@ -1,17 +1,13 @@
|
|||||||
ErrInvalidParams: "Request parameter error: {{ .detail }}"
|
ErrInvalidParams: "Request parameter error: {{ .detail }}"
|
||||||
ErrToken: "Token information is incorrect.: {{ .detail }}"
|
|
||||||
ErrTokenParse: "Token generation error: {{ .detail }}"
|
ErrTokenParse: "Token generation error: {{ .detail }}"
|
||||||
ErrTokenTimeOut: "Login information is out of date: {{ .detail }}"
|
|
||||||
ErrInitialPassword: "Initial password error"
|
ErrInitialPassword: "Initial password error"
|
||||||
ErrInternalServer: "Service internal error: {{ .detail }}"
|
ErrInternalServer: "Service internal error: {{ .detail }}"
|
||||||
ErrRecordExist: "Record already exists"
|
ErrRecordExist: "Record already exists"
|
||||||
ErrRecordNotFound: "Records not found"
|
ErrRecordNotFound: "Records not found"
|
||||||
ErrStructTransform: "Type conversion failure: {{ .detail }}"
|
ErrStructTransform: "Type conversion failure: {{ .detail }}"
|
||||||
ErrNotLogin: "User is not Login: {{ .detail }}"
|
ErrNotLogin: "User is not Login: {{ .detail }}"
|
||||||
ErrNotSafety: "The login status of the current user is unsafe: {{ .detail }}"
|
|
||||||
ErrPasswordExpired: "The current password has expired: {{ .detail }}"
|
ErrPasswordExpired: "The current password has expired: {{ .detail }}"
|
||||||
ErrNotSupportType: "The system does not support the current type: {{ .detail }}"
|
ErrNotSupportType: "The system does not support the current type: {{ .detail }}"
|
||||||
ErrRepoNotValid: "Remote repository verification failed!"
|
|
||||||
|
|
||||||
#common
|
#common
|
||||||
ErrNameIsExist: "Name is already exist"
|
ErrNameIsExist: "Name is already exist"
|
||||||
@@ -44,6 +40,9 @@ ErrHttpReqTimeOut: "Request timed out {{.err}}"
|
|||||||
ErrHttpReqFailed: "Request failed {{.err}}"
|
ErrHttpReqFailed: "Request failed {{.err}}"
|
||||||
ErrHttpReqNotFound: "The file does not exist"
|
ErrHttpReqNotFound: "The file does not exist"
|
||||||
ErrNoSuchHost: "Network connection failed"
|
ErrNoSuchHost: "Network connection failed"
|
||||||
|
ErrImagePullTimeOut: 'Image pull timeout'
|
||||||
|
ErrContainerNotFound: '{{ .name }} container does not exist'
|
||||||
|
ErrContainerMsg: '{{ .name }} container is abnormal, please check the log on the container page for details'
|
||||||
|
|
||||||
#file
|
#file
|
||||||
ErrFileCanNotRead: "File can not read"
|
ErrFileCanNotRead: "File can not read"
|
||||||
@@ -66,6 +65,10 @@ ErrSSLCannotDelete: "The certificate is being used by the website and cannot be
|
|||||||
ErrAccountCannotDelete: "The certificate associated with the account cannot be deleted"
|
ErrAccountCannotDelete: "The certificate associated with the account cannot be deleted"
|
||||||
ErrSSLApply: "The certificate continues to be signed successfully, but openresty reload fails, please check the configuration!"
|
ErrSSLApply: "The certificate continues to be signed successfully, but openresty reload fails, please check the configuration!"
|
||||||
ErrEmailIsExist: 'Email is already exist'
|
ErrEmailIsExist: 'Email is already exist'
|
||||||
|
ErrSSLKeyNotFound: 'The private key file does not exist'
|
||||||
|
ErrSSLCertificateNotFound: 'The certificate file does not exist'
|
||||||
|
ErrSSLKeyFormat: 'Private key file verification error'
|
||||||
|
ErrSSLCertificateFormat: 'Certificate file format error, please use pem format'
|
||||||
|
|
||||||
#mysql
|
#mysql
|
||||||
ErrUserIsExist: "The current user already exists. Please enter a new user"
|
ErrUserIsExist: "The current user already exists. Please enter a new user"
|
||||||
@@ -78,6 +81,8 @@ ErrTypeOfRedis: "The recovery file type does not match the current persistence m
|
|||||||
#container
|
#container
|
||||||
ErrInUsed: "{{ .detail }} is in use and cannot be deleted"
|
ErrInUsed: "{{ .detail }} is in use and cannot be deleted"
|
||||||
ErrObjectInUsed: "This object is in use and cannot be deleted"
|
ErrObjectInUsed: "This object is in use and cannot be deleted"
|
||||||
|
ErrRepoConn: "The repository information contains illegal characters"
|
||||||
|
ErrPortRules: "The number of ports does not match, please re-enter!"
|
||||||
|
|
||||||
#runtime
|
#runtime
|
||||||
ErrDirNotFound: "The build folder does not exist! Please check file integrity!"
|
ErrDirNotFound: "The build folder does not exist! Please check file integrity!"
|
||||||
|
97
backend/i18n/lang/zh-Hant.yaml
Normal file
97
backend/i18n/lang/zh-Hant.yaml
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
ErrInvalidParams: "請求參數錯誤: {{ .detail }}"
|
||||||
|
ErrTokenParse: "Token 產生錯誤: {{ .detail }}"
|
||||||
|
ErrInitialPassword: "原密碼錯誤"
|
||||||
|
ErrInternalServer: "伺服器內部錯誤: {{ .detail }}"
|
||||||
|
ErrRecordExist: "記錄已存在"
|
||||||
|
ErrRecordNotFound: "記錄未找到"
|
||||||
|
ErrStructTransform: "類型轉換失敗: {{ .detail }}"
|
||||||
|
ErrNotLogin: "用戶未登入: {{ .detail }}"
|
||||||
|
ErrPasswordExpired: "當前密碼已過期: {{ .detail }}"
|
||||||
|
ErrNotSupportType: "系統暫不支持當前類型: {{ .detail }}"
|
||||||
|
|
||||||
|
#common
|
||||||
|
ErrNameIsExist: "名稱已存在"
|
||||||
|
ErrDemoEnvironment: "演示伺服器,禁止此操作!"
|
||||||
|
ErrCmdTimeout: "指令執行超時!"
|
||||||
|
|
||||||
|
#app
|
||||||
|
ErrPortInUsed: "{{ .detail }} 端口已被佔用!"
|
||||||
|
ErrAppLimit: "應用超出安裝數量限制"
|
||||||
|
ErrAppRequired: "請先安裝 {{ .detail }} 應用"
|
||||||
|
ErrNotInstall: "應用未安裝"
|
||||||
|
ErrPortInOtherApp: "{{ .port }} 端口已被 {{ .apps }}佔用!"
|
||||||
|
ErrDbUserNotValid: "儲存資料庫,用戶名密碼不匹配!"
|
||||||
|
ErrDockerComposeNotValid: "docker-compose 文件格式錯誤"
|
||||||
|
ErrUpdateBuWebsite: '應用更新成功,但是網站配置文件修改失敗,請檢查配置!'
|
||||||
|
Err1PanelNetworkFailed: '默認容器網絡創建失敗!{{ .detail }}'
|
||||||
|
ErrFileParse: '應用 docker-compose 文件解析失敗!'
|
||||||
|
ErrInstallDirNotFound: '安裝目錄不存在'
|
||||||
|
AppStoreIsUpToDate: '應用商店已經是最新版本'
|
||||||
|
LocalAppVersionNull: '{{.name}} 應用未同步到版本!無法添加到應用列表'
|
||||||
|
LocalAppVersionErr: '{{.name}} 同步版本 {{.version}} 失敗!{{.err}}'
|
||||||
|
ErrFileNotFound: '{{.name}} 文件不存在'
|
||||||
|
ErrFileParseApp: '{{.name}} 文件解析失敗 {{.err}}'
|
||||||
|
ErrAppDirNull: '版本資料夾不存在'
|
||||||
|
LocalAppErr: "應用 {{.name}} 同步失敗!{{.err}}"
|
||||||
|
ErrContainerName: "容器名稱已存在"
|
||||||
|
ErrAppSystemRestart: "1Panel 重啟導致任務中斷"
|
||||||
|
ErrCreateHttpClient: "創建HTTP請求失敗 {{.err}}"
|
||||||
|
ErrHttpReqTimeOut: "請求超時 {{.err}}"
|
||||||
|
ErrHttpReqFailed: "請求失敗 {{.err}}"
|
||||||
|
ErrHttpReqNotFound: "文件不存在"
|
||||||
|
ErrNoSuchHost: "網路連接失敗"
|
||||||
|
ErrImagePullTimeOut: "鏡像拉取超時"
|
||||||
|
ErrContainerNotFound: '{{ .name }} 容器不存在'
|
||||||
|
ErrContainerMsg: '{{ .name }} 容器異常,具體請在容器頁面查看日誌'
|
||||||
|
|
||||||
|
#file
|
||||||
|
ErrFileCanNotRead: "此文件不支持預覽"
|
||||||
|
ErrFileToLarge: "文件超過10M,無法打開"
|
||||||
|
ErrPathNotFound: "目錄不存在"
|
||||||
|
ErrMovePathFailed: "目標路徑不能包含原路徑!"
|
||||||
|
ErrLinkPathNotFound: "目標路徑不存在!"
|
||||||
|
ErrFileIsExit: "文件已存在!"
|
||||||
|
ErrFileUpload: "{{ .name }} 上傳文件失敗 {{ .detail}}"
|
||||||
|
ErrFileDownloadDir: "不支持下載文件夾"
|
||||||
|
|
||||||
|
#website
|
||||||
|
ErrDomainIsExist: "域名已存在"
|
||||||
|
ErrAliasIsExist: "代號已存在"
|
||||||
|
ErrAppDelete: '其他網站使用此應用,無法刪除'
|
||||||
|
ErrGroupIsUsed: '分組正在使用中,無法刪除'
|
||||||
|
|
||||||
|
#ssl
|
||||||
|
ErrSSLCannotDelete: "證書正在被網站使用,無法刪除"
|
||||||
|
ErrAccountCannotDelete: "帳號關聯證書,無法刪除"
|
||||||
|
ErrSSLApply: "證書續簽成功,openresty reload失敗,請檢查配置!"
|
||||||
|
ErrEmailIsExist: '郵箱已存在'
|
||||||
|
ErrSSLKeyNotFound: '私鑰文件不存在'
|
||||||
|
ErrSSLCertificateNotFound: '證書文件不存在'
|
||||||
|
ErrSSLKeyFormat: '私鑰文件校驗錯誤'
|
||||||
|
ErrSSLCertificateFormat: '證書文件格式錯誤,請使用 pem 格式'
|
||||||
|
|
||||||
|
#mysql
|
||||||
|
ErrUserIsExist: "當前用戶已存在,請重新輸入"
|
||||||
|
ErrDatabaseIsExist: "當前資料庫已存在,請重新輸入"
|
||||||
|
ErrExecTimeOut: "SQL 執行超時,請檢查{{ .detail }}容器"
|
||||||
|
|
||||||
|
#redis
|
||||||
|
ErrTypeOfRedis: "恢復文件類型與當前持久化方式不符,請修改後重試"
|
||||||
|
|
||||||
|
#container
|
||||||
|
ErrInUsed: "{{ .detail }} 正被使用,無法刪除"
|
||||||
|
ErrObjectInUsed: "該對象正被使用,無法刪除"
|
||||||
|
ErrRepoConn: "倉庫資訊中存在不合法的字符"
|
||||||
|
ErrPortRules: "端口數目不匹配,請重新輸入!"
|
||||||
|
|
||||||
|
#runtime
|
||||||
|
ErrDirNotFound: "build 文件夾不存在!請檢查文件完整性!"
|
||||||
|
ErrFileNotExist: "{{ .detail }} 文件不存在!請檢查源文件完整性!"
|
||||||
|
ErrImageBuildErr: "鏡像 build 失敗"
|
||||||
|
ErrImageExist: "鏡像已存在!"
|
||||||
|
ErrDelWithWebsite: "運行環境已經關聯網站,無法刪除"
|
||||||
|
|
||||||
|
#setting
|
||||||
|
ErrBackupInUsed: "該備份帳號已在計劃任務中使用,無法刪除"
|
||||||
|
|
||||||
|
ErrOSSConn: "無法成功請求最新版本,請檢查伺服器是否能夠連接到外部網絡環境。"
|
@@ -1,17 +1,13 @@
|
|||||||
ErrInvalidParams: "请求参数错误: {{ .detail }}"
|
ErrInvalidParams: "请求参数错误: {{ .detail }}"
|
||||||
ErrToken: "Token 信息错误: {{ .detail }}"
|
|
||||||
ErrTokenParse: "Token 生成错误: {{ .detail }}"
|
ErrTokenParse: "Token 生成错误: {{ .detail }}"
|
||||||
ErrTokenTimeOut: "登陆信息已过期: {{ .detail }}"
|
|
||||||
ErrInitialPassword: "原密码错误"
|
ErrInitialPassword: "原密码错误"
|
||||||
ErrInternalServer: "服务内部错误: {{ .detail }}"
|
ErrInternalServer: "服务内部错误: {{ .detail }}"
|
||||||
ErrRecordExist: "记录已存在"
|
ErrRecordExist: "记录已存在"
|
||||||
ErrRecordNotFound: "记录未能找到"
|
ErrRecordNotFound: "记录未能找到"
|
||||||
ErrStructTransform: "类型转换失败: {{ .detail }}"
|
ErrStructTransform: "类型转换失败: {{ .detail }}"
|
||||||
ErrNotLogin: "用户未登录: {{ .detail }}"
|
ErrNotLogin: "用户未登录: {{ .detail }}"
|
||||||
ErrNotSafety: "当前用户登录状态不安全: {{ .detail }}"
|
|
||||||
ErrPasswordExpired: "当前密码已过期: {{ .detail }}"
|
ErrPasswordExpired: "当前密码已过期: {{ .detail }}"
|
||||||
ErrNotSupportType: "系统暂不支持当前类型: {{ .detail }}"
|
ErrNotSupportType: "系统暂不支持当前类型: {{ .detail }}"
|
||||||
ErrRepoNotValid: "远程仓库校验失败!"
|
|
||||||
|
|
||||||
#common
|
#common
|
||||||
ErrNameIsExist: "名称已存在"
|
ErrNameIsExist: "名称已存在"
|
||||||
@@ -44,6 +40,9 @@ ErrHttpReqTimeOut: "请求超时 {{.err}}"
|
|||||||
ErrHttpReqFailed: "请求失败 {{.err}}"
|
ErrHttpReqFailed: "请求失败 {{.err}}"
|
||||||
ErrHttpReqNotFound: "文件不存在"
|
ErrHttpReqNotFound: "文件不存在"
|
||||||
ErrNoSuchHost: "网络连接失败"
|
ErrNoSuchHost: "网络连接失败"
|
||||||
|
ErrImagePullTimeOut: '镜像拉取超时'
|
||||||
|
ErrContainerNotFound: '{{ .name }} 容器不存在'
|
||||||
|
ErrContainerMsg: '{{ .name }} 容器异常,具体请在容器页面查看日志'
|
||||||
|
|
||||||
#file
|
#file
|
||||||
ErrFileCanNotRead: "此文件不支持预览"
|
ErrFileCanNotRead: "此文件不支持预览"
|
||||||
@@ -66,6 +65,10 @@ ErrSSLCannotDelete: "证书正在被网站使用,无法删除"
|
|||||||
ErrAccountCannotDelete: "账号关联证书,无法删除"
|
ErrAccountCannotDelete: "账号关联证书,无法删除"
|
||||||
ErrSSLApply: "证书续签成功,openresty reload失败,请检查配置!"
|
ErrSSLApply: "证书续签成功,openresty reload失败,请检查配置!"
|
||||||
ErrEmailIsExist: '邮箱已存在'
|
ErrEmailIsExist: '邮箱已存在'
|
||||||
|
ErrSSLKeyNotFound: '私钥文件不存在'
|
||||||
|
ErrSSLCertificateNotFound: '证书文件不存在'
|
||||||
|
ErrSSLKeyFormat: '私钥文件校验失败'
|
||||||
|
ErrSSLCertificateFormat: '证书文件格式错误,请使用 pem 格式'
|
||||||
|
|
||||||
#mysql
|
#mysql
|
||||||
ErrUserIsExist: "当前用户已存在,请重新输入"
|
ErrUserIsExist: "当前用户已存在,请重新输入"
|
||||||
@@ -78,6 +81,8 @@ ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后
|
|||||||
#container
|
#container
|
||||||
ErrInUsed: "{{ .detail }} 正被使用,无法删除"
|
ErrInUsed: "{{ .detail }} 正被使用,无法删除"
|
||||||
ErrObjectInUsed: "该对象正被使用,无法删除"
|
ErrObjectInUsed: "该对象正被使用,无法删除"
|
||||||
|
ErrRepoConn: "仓库信息中存在不合法的字符"
|
||||||
|
ErrPortRules: "端口数目不匹配,请重新输入!"
|
||||||
|
|
||||||
#runtime
|
#runtime
|
||||||
ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!"
|
ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!"
|
||||||
|
@@ -2,12 +2,13 @@ package db
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"gorm.io/gorm/logger"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gorm.io/driver/sqlite"
|
"gorm.io/gorm/logger"
|
||||||
|
|
||||||
|
"github.com/glebarez/sqlite"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
package hook
|
package hook
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||||
@@ -15,17 +17,30 @@ func Init() {
|
|||||||
global.LOG.Errorf("load service port from setting failed, err: %v", err)
|
global.LOG.Errorf("load service port from setting failed, err: %v", err)
|
||||||
}
|
}
|
||||||
global.CONF.System.Port = portSetting.Value
|
global.CONF.System.Port = portSetting.Value
|
||||||
enptrySetting, err := settingRepo.Get(settingRepo.WithByKey("EncryptKey"))
|
encryptSetting, err := settingRepo.Get(settingRepo.WithByKey("EncryptKey"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("load service encrypt key from setting failed, err: %v", err)
|
global.LOG.Errorf("load service encrypt key from setting failed, err: %v", err)
|
||||||
}
|
}
|
||||||
global.CONF.System.EncryptKey = enptrySetting.Value
|
global.CONF.System.EncryptKey = encryptSetting.Value
|
||||||
sslSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
|
sslSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("load service ssl from setting failed, err: %v", err)
|
global.LOG.Errorf("load service ssl from setting failed, err: %v", err)
|
||||||
}
|
}
|
||||||
global.CONF.System.SSL = sslSetting.Value
|
global.CONF.System.SSL = sslSetting.Value
|
||||||
|
|
||||||
|
OneDriveID, err := settingRepo.Get(settingRepo.WithByKey("OneDriveID"))
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("load onedrive info from setting failed, err: %v", err)
|
||||||
|
}
|
||||||
|
idItem, _ := base64.StdEncoding.DecodeString(OneDriveID.Value)
|
||||||
|
global.CONF.System.OneDriveID = string(idItem)
|
||||||
|
OneDriveSc, err := settingRepo.Get(settingRepo.WithByKey("OneDriveSc"))
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("load onedrive info from setting failed, err: %v", err)
|
||||||
|
}
|
||||||
|
scItem, _ := base64.StdEncoding.DecodeString(OneDriveSc.Value)
|
||||||
|
global.CONF.System.OneDriveSc = string(scItem)
|
||||||
|
|
||||||
if _, err := settingRepo.Get(settingRepo.WithByKey("SystemStatus")); err != nil {
|
if _, err := settingRepo.Get(settingRepo.WithByKey("SystemStatus")); err != nil {
|
||||||
_ = settingRepo.Create("SystemStatus", "Free")
|
_ = settingRepo.Create("SystemStatus", "Free")
|
||||||
}
|
}
|
||||||
|
@@ -31,6 +31,10 @@ func Init() {
|
|||||||
migrations.AddBindAndAllowIPs,
|
migrations.AddBindAndAllowIPs,
|
||||||
migrations.UpdateCronjobWithSecond,
|
migrations.UpdateCronjobWithSecond,
|
||||||
migrations.UpdateWebsite,
|
migrations.UpdateWebsite,
|
||||||
|
migrations.AddBackupAccountDir,
|
||||||
|
migrations.AddMfaInterval,
|
||||||
|
migrations.UpdateAppDetail,
|
||||||
|
migrations.EncryptHostPassword,
|
||||||
})
|
})
|
||||||
if err := m.Migrate(); err != nil {
|
if err := m.Migrate(); err != nil {
|
||||||
global.LOG.Error(err)
|
global.LOG.Error(err)
|
||||||
|
@@ -390,3 +390,92 @@ var UpdateWebsite = &gormigrate.Migration{
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var AddBackupAccountDir = &gormigrate.Migration{
|
||||||
|
ID: "20200620-add-backup-dir",
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
if err := tx.AutoMigrate(&model.BackupAccount{}, &model.Cronjob{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var AddMfaInterval = &gormigrate.Migration{
|
||||||
|
ID: "20230625-add-mfa-interval",
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
if err := tx.Create(&model.Setting{Key: "MFAInterval", Value: "30"}).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Create(&model.Setting{Key: "SystemIP", Value: ""}).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Create(&model.Setting{Key: "OneDriveID", Value: "MDEwOTM1YTktMWFhOS00ODU0LWExZGMtNmU0NWZlNjI4YzZi"}).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Create(&model.Setting{Key: "OneDriveSc", Value: "akpuOFF+YkNXOU1OLWRzS1ZSRDdOcG1LT2ZRM0RLNmdvS1RkVWNGRA=="}).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var UpdateAppDetail = &gormigrate.Migration{
|
||||||
|
ID: "20230704-update-app-detail",
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
if err := tx.AutoMigrate(&model.AppDetail{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Model(&model.AppDetail{}).Where("1 = 1").Update("ignore_upgrade", "0").Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var EncryptHostPassword = &gormigrate.Migration{
|
||||||
|
ID: "20230703-encrypt-host-password",
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
var hosts []model.Host
|
||||||
|
if err := tx.Where("1 = 1").Find(&hosts).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var encryptSetting model.Setting
|
||||||
|
if err := tx.Where("key = ?", "EncryptKey").Find(&encryptSetting).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
global.CONF.System.EncryptKey = encryptSetting.Value
|
||||||
|
|
||||||
|
for _, host := range hosts {
|
||||||
|
if len(host.Password) != 0 {
|
||||||
|
pass, err := encrypt.StringEncrypt(host.Password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Model(&model.Host{}).Where("id = ?", host.ID).Update("password", pass).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(host.PrivateKey) != 0 {
|
||||||
|
key, err := encrypt.StringEncrypt(host.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Model(&model.Host{}).Where("id = ?", host.ID).Update("private_key", key).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(host.PassPhrase) != 0 {
|
||||||
|
pass, err := encrypt.StringEncrypt(host.PassPhrase)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Model(&model.Host{}).Where("id = ?", host.ID).Update("pass_phrase", pass).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
@@ -86,6 +86,7 @@ func Routers() *gin.Engine {
|
|||||||
systemRouter.InitWebsiteAcmeAccountRouter(PrivateGroup)
|
systemRouter.InitWebsiteAcmeAccountRouter(PrivateGroup)
|
||||||
systemRouter.InitNginxRouter(PrivateGroup)
|
systemRouter.InitNginxRouter(PrivateGroup)
|
||||||
systemRouter.InitRuntimeRouter(PrivateGroup)
|
systemRouter.InitRuntimeRouter(PrivateGroup)
|
||||||
|
systemRouter.InitProcessRouter(PrivateGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Router
|
return Router
|
||||||
|
@@ -15,7 +15,7 @@ func BindDomain() gin.HandlerFunc {
|
|||||||
settingRepo := repo.NewISettingRepo()
|
settingRepo := repo.NewISettingRepo()
|
||||||
status, err := settingRepo.Get(settingRepo.WithByKey("BindDomain"))
|
status, err := settingRepo.Get(settingRepo.WithByKey("BindDomain"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrDomain, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(status.Value) == 0 {
|
if len(status.Value) == 0 {
|
||||||
|
@@ -15,7 +15,7 @@ func WhiteAllow() gin.HandlerFunc {
|
|||||||
settingRepo := repo.NewISettingRepo()
|
settingRepo := repo.NewISettingRepo()
|
||||||
status, err := settingRepo.Get(settingRepo.WithByKey("AllowIPs"))
|
status, err := settingRepo.Get(settingRepo.WithByKey("AllowIPs"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrIP, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -118,8 +118,8 @@ func OperationLog() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
record.DetailEN = operationDic.FormatEN
|
record.DetailEN = strings.ReplaceAll(operationDic.FormatEN, "[]", "")
|
||||||
record.DetailZH = operationDic.FormatZH
|
record.DetailZH = strings.ReplaceAll(operationDic.FormatZH, "[]", "")
|
||||||
|
|
||||||
writer := responseBodyWriter{
|
writer := responseBodyWriter{
|
||||||
ResponseWriter: c.Writer,
|
ResponseWriter: c.Writer,
|
||||||
|
@@ -16,7 +16,7 @@ func PasswordExpired() gin.HandlerFunc {
|
|||||||
settingRepo := repo.NewISettingRepo()
|
settingRepo := repo.NewISettingRepo()
|
||||||
setting, err := settingRepo.Get(settingRepo.WithByKey("ExpirationDays"))
|
setting, err := settingRepo.Get(settingRepo.WithByKey("ExpirationDays"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodePasswordExpired, constant.ErrTypePasswordExpired, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypePasswordExpired, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
expiredDays, _ := strconv.Atoi(setting.Value)
|
expiredDays, _ := strconv.Atoi(setting.Value)
|
||||||
@@ -27,7 +27,7 @@ func PasswordExpired() gin.HandlerFunc {
|
|||||||
|
|
||||||
extime, err := settingRepo.Get(settingRepo.WithByKey("ExpirationTime"))
|
extime, err := settingRepo.Get(settingRepo.WithByKey("ExpirationTime"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodePasswordExpired, constant.ErrTypePasswordExpired, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypePasswordExpired, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
loc, _ := time.LoadLocation(common.LoadTimeZone())
|
loc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||||
|
@@ -20,6 +20,7 @@ type RouterGroup struct {
|
|||||||
DatabaseRouter
|
DatabaseRouter
|
||||||
NginxRouter
|
NginxRouter
|
||||||
RuntimeRouter
|
RuntimeRouter
|
||||||
|
ProcessRouter
|
||||||
}
|
}
|
||||||
|
|
||||||
var RouterGroupApp = new(RouterGroup)
|
var RouterGroupApp = new(RouterGroup)
|
||||||
|
@@ -36,5 +36,7 @@ func (a *AppRouter) InitAppRouter(Router *gin.RouterGroup) {
|
|||||||
appRouter.GET("/installed/conf/:key", baseApi.GetDefaultConfig)
|
appRouter.GET("/installed/conf/:key", baseApi.GetDefaultConfig)
|
||||||
appRouter.GET("/installed/params/:appInstallId", baseApi.GetParams)
|
appRouter.GET("/installed/params/:appInstallId", baseApi.GetParams)
|
||||||
appRouter.POST("/installed/params/update", baseApi.UpdateInstalled)
|
appRouter.POST("/installed/params/update", baseApi.UpdateInstalled)
|
||||||
|
appRouter.POST("/installed/ignore", baseApi.IgnoreUpgrade)
|
||||||
|
appRouter.GET("/ignored/detail", baseApi.GetIgnoredApp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,8 +19,14 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) {
|
|||||||
baRouter.GET("/stats/:id", baseApi.ContainerStats)
|
baRouter.GET("/stats/:id", baseApi.ContainerStats)
|
||||||
|
|
||||||
baRouter.POST("", baseApi.ContainerCreate)
|
baRouter.POST("", baseApi.ContainerCreate)
|
||||||
|
baRouter.POST("/update", baseApi.ContainerUpdate)
|
||||||
|
baRouter.POST("/upgrade", baseApi.ContainerUpgrade)
|
||||||
|
baRouter.POST("/info", baseApi.ContainerInfo)
|
||||||
baRouter.POST("/search", baseApi.SearchContainer)
|
baRouter.POST("/search", baseApi.SearchContainer)
|
||||||
baRouter.POST("/search/log", baseApi.ContainerLogs)
|
baRouter.POST("/list", baseApi.ListContainer)
|
||||||
|
baRouter.GET("/list/stats", baseApi.ContainerListStats)
|
||||||
|
baRouter.GET("/search/log", baseApi.ContainerLogs)
|
||||||
|
baRouter.GET("/limit", baseApi.LoadResouceLimit)
|
||||||
baRouter.POST("/clean/log", baseApi.CleanContainerLog)
|
baRouter.POST("/clean/log", baseApi.CleanContainerLog)
|
||||||
baRouter.POST("/inspect", baseApi.Inspect)
|
baRouter.POST("/inspect", baseApi.Inspect)
|
||||||
baRouter.POST("/operate", baseApi.ContainerOperation)
|
baRouter.POST("/operate", baseApi.ContainerOperation)
|
||||||
@@ -55,10 +61,11 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) {
|
|||||||
baRouter.POST("/image/tag", baseApi.ImageTag)
|
baRouter.POST("/image/tag", baseApi.ImageTag)
|
||||||
baRouter.POST("/image/build", baseApi.ImageBuild)
|
baRouter.POST("/image/build", baseApi.ImageBuild)
|
||||||
|
|
||||||
baRouter.GET("/volume", baseApi.ListVolume)
|
baRouter.GET("/network", baseApi.ListNetwork)
|
||||||
baRouter.POST("/network/del", baseApi.DeleteNetwork)
|
baRouter.POST("/network/del", baseApi.DeleteNetwork)
|
||||||
baRouter.POST("/network/search", baseApi.SearchNetwork)
|
baRouter.POST("/network/search", baseApi.SearchNetwork)
|
||||||
baRouter.POST("/network", baseApi.CreateNetwork)
|
baRouter.POST("/network", baseApi.CreateNetwork)
|
||||||
|
baRouter.GET("/volume", baseApi.ListVolume)
|
||||||
baRouter.POST("/volume/del", baseApi.DeleteVolume)
|
baRouter.POST("/volume/del", baseApi.DeleteVolume)
|
||||||
baRouter.POST("/volume/search", baseApi.SearchVolume)
|
baRouter.POST("/volume/search", baseApi.SearchVolume)
|
||||||
baRouter.POST("/volume", baseApi.CreateVolume)
|
baRouter.POST("/volume", baseApi.CreateVolume)
|
||||||
|
@@ -24,7 +24,6 @@ func (s *HostRouter) InitHostRouter(Router *gin.RouterGroup) {
|
|||||||
hostRouter.POST("/tree", baseApi.HostTree)
|
hostRouter.POST("/tree", baseApi.HostTree)
|
||||||
hostRouter.POST("/test/byinfo", baseApi.TestByInfo)
|
hostRouter.POST("/test/byinfo", baseApi.TestByInfo)
|
||||||
hostRouter.POST("/test/byid/:id", baseApi.TestByID)
|
hostRouter.POST("/test/byid/:id", baseApi.TestByID)
|
||||||
hostRouter.GET(":id", baseApi.GetHostInfo)
|
|
||||||
|
|
||||||
hostRouter.GET("/firewall/base", baseApi.LoadFirewallBaseInfo)
|
hostRouter.GET("/firewall/base", baseApi.LoadFirewallBaseInfo)
|
||||||
hostRouter.POST("/firewall/search", baseApi.SearchFirewallRule)
|
hostRouter.POST("/firewall/search", baseApi.SearchFirewallRule)
|
||||||
|
20
backend/router/ro_process.go
Normal file
20
backend/router/ro_process.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/middleware"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProcessRouter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ProcessRouter) InitProcessRouter(Router *gin.RouterGroup) {
|
||||||
|
processRouter := Router.Group("process")
|
||||||
|
processRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.PasswordExpired())
|
||||||
|
baseApi := v1.ApiGroupApp.BaseApi
|
||||||
|
{
|
||||||
|
processRouter.GET("/ws", baseApi.ProcessWs)
|
||||||
|
processRouter.POST("/stop", baseApi.StopProcess)
|
||||||
|
}
|
||||||
|
}
|
@@ -29,7 +29,7 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
|
|||||||
settingRouter.GET("/time/option", baseApi.LoadTimeZone)
|
settingRouter.GET("/time/option", baseApi.LoadTimeZone)
|
||||||
settingRouter.POST("/time/sync", baseApi.SyncTime)
|
settingRouter.POST("/time/sync", baseApi.SyncTime)
|
||||||
settingRouter.POST("/monitor/clean", baseApi.CleanMonitor)
|
settingRouter.POST("/monitor/clean", baseApi.CleanMonitor)
|
||||||
settingRouter.GET("/mfa", baseApi.GetMFA)
|
settingRouter.GET("/mfa/:interval", baseApi.GetMFA)
|
||||||
settingRouter.POST("/mfa/bind", baseApi.MFABind)
|
settingRouter.POST("/mfa/bind", baseApi.MFABind)
|
||||||
|
|
||||||
settingRouter.POST("/snapshot", baseApi.CreateSnapshot)
|
settingRouter.POST("/snapshot", baseApi.CreateSnapshot)
|
||||||
@@ -41,6 +41,7 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
|
|||||||
settingRouter.POST("/snapshot/description/update", baseApi.UpdateSnapDescription)
|
settingRouter.POST("/snapshot/description/update", baseApi.UpdateSnapDescription)
|
||||||
|
|
||||||
settingRouter.GET("/backup/search", baseApi.ListBackup)
|
settingRouter.GET("/backup/search", baseApi.ListBackup)
|
||||||
|
settingRouter.GET("/backup/onedrive", baseApi.LoadOneDriveInfo)
|
||||||
settingRouter.POST("/backup/backup", baseApi.Backup)
|
settingRouter.POST("/backup/backup", baseApi.Backup)
|
||||||
settingRouter.POST("/backup/recover", baseApi.Recover)
|
settingRouter.POST("/backup/recover", baseApi.Recover)
|
||||||
settingRouter.POST("/backup/recover/byupload", baseApi.RecoverByUpload)
|
settingRouter.POST("/backup/recover/byupload", baseApi.RecoverByUpload)
|
||||||
|
@@ -14,6 +14,7 @@ type cosClient struct {
|
|||||||
region string
|
region string
|
||||||
accessKey string
|
accessKey string
|
||||||
secretKey string
|
secretKey string
|
||||||
|
scType string
|
||||||
Vars map[string]interface{}
|
Vars map[string]interface{}
|
||||||
client *cosSDK.Client
|
client *cosSDK.Client
|
||||||
}
|
}
|
||||||
@@ -21,6 +22,7 @@ type cosClient struct {
|
|||||||
func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
||||||
var accessKey string
|
var accessKey string
|
||||||
var secretKey string
|
var secretKey string
|
||||||
|
var scType string
|
||||||
var region string
|
var region string
|
||||||
if _, ok := vars["region"]; ok {
|
if _, ok := vars["region"]; ok {
|
||||||
region = vars["region"].(string)
|
region = vars["region"].(string)
|
||||||
@@ -32,6 +34,11 @@ func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
|||||||
} else {
|
} else {
|
||||||
return nil, constant.ErrInvalidParams
|
return nil, constant.ErrInvalidParams
|
||||||
}
|
}
|
||||||
|
if _, ok := vars["scType"]; ok {
|
||||||
|
scType = vars["scType"].(string)
|
||||||
|
} else {
|
||||||
|
scType = "Standard"
|
||||||
|
}
|
||||||
if _, ok := vars["secretKey"]; ok {
|
if _, ok := vars["secretKey"]; ok {
|
||||||
secretKey = vars["secretKey"].(string)
|
secretKey = vars["secretKey"].(string)
|
||||||
} else {
|
} else {
|
||||||
@@ -47,7 +54,7 @@ func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return &cosClient{Vars: vars, client: client, accessKey: accessKey, secretKey: secretKey, region: region}, nil
|
return &cosClient{Vars: vars, client: client, accessKey: accessKey, secretKey: secretKey, scType: scType, region: region}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cos cosClient) ListBuckets() ([]interface{}, error) {
|
func (cos cosClient) ListBuckets() ([]interface{}, error) {
|
||||||
@@ -90,7 +97,12 @@ func (cos cosClient) Upload(src, target string) (bool, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if _, err := client.Object.PutFromFile(context.Background(), target, src, &cosSDK.ObjectPutOptions{}); err != nil {
|
if _, err := client.Object.PutFromFile(context.Background(), target, src, &cosSDK.ObjectPutOptions{
|
||||||
|
ACLHeaderOptions: nil,
|
||||||
|
ObjectPutHeaderOptions: &cosSDK.ObjectPutHeaderOptions{
|
||||||
|
XCosStorageClass: cos.scType,
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
|
331
backend/utils/cloud_storage/client/onedrive.go
Normal file
331
backend/utils/cloud_storage/client/onedrive.go
Normal file
@@ -0,0 +1,331 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
|
odsdk "github.com/goh-chunlin/go-onedrive/onedrive"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type oneDriveClient struct {
|
||||||
|
Vars map[string]interface{}
|
||||||
|
client odsdk.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOneDriveClient(vars map[string]interface{}) (*oneDriveClient, error) {
|
||||||
|
token := ""
|
||||||
|
if _, ok := vars["accessToken"]; ok {
|
||||||
|
token = vars["accessToken"].(string)
|
||||||
|
} else {
|
||||||
|
return nil, constant.ErrInvalidParams
|
||||||
|
}
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
newToken, err := refreshToken(token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_ = global.DB.Model(&model.Group{}).Where("type = ?", "OneDrive").Updates(map[string]interface{}{"credential": newToken}).Error
|
||||||
|
|
||||||
|
ts := oauth2.StaticTokenSource(
|
||||||
|
&oauth2.Token{AccessToken: newToken},
|
||||||
|
)
|
||||||
|
tc := oauth2.NewClient(ctx, ts)
|
||||||
|
|
||||||
|
client := odsdk.NewClient(tc)
|
||||||
|
return &oneDriveClient{client: *client}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (onedrive oneDriveClient) ListBuckets() ([]interface{}, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (onedrive oneDriveClient) Exist(path string) (bool, error) {
|
||||||
|
path = "/" + strings.TrimPrefix(path, "/")
|
||||||
|
fileID, err := onedrive.loadIDByPath(path)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(fileID) != 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (onedrive oneDriveClient) Delete(path string) (bool, error) {
|
||||||
|
path = "/" + strings.TrimPrefix(path, "/")
|
||||||
|
req, err := onedrive.client.NewRequest("DELETE", fmt.Sprintf("me/drive/root:%s", path), nil)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("new request for delete file failed, err: %v \n", err)
|
||||||
|
}
|
||||||
|
if err := onedrive.client.Do(context.Background(), req, false, nil); err != nil {
|
||||||
|
return false, fmt.Errorf("do request for delete file failed, err: %v \n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (onedrive oneDriveClient) Upload(src, target string) (bool, error) {
|
||||||
|
target = "/" + strings.TrimPrefix(target, "/")
|
||||||
|
if _, err := onedrive.loadIDByPath(path.Dir(target)); err != nil {
|
||||||
|
if !strings.Contains(err.Error(), "itemNotFound") {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if err := onedrive.createFolder(path.Dir(target)); err != nil {
|
||||||
|
return false, fmt.Errorf("create dir before upload failed, err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
file, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
fileInfo, err := file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if fileInfo.IsDir() {
|
||||||
|
return false, errors.New("Only file is allowed to be uploaded here.")
|
||||||
|
}
|
||||||
|
fileName := fileInfo.Name()
|
||||||
|
fileSize := fileInfo.Size()
|
||||||
|
|
||||||
|
folderID, err := onedrive.loadIDByPath(path.Dir(target))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
apiURL := fmt.Sprintf("me/drive/items/%s:/%s:/createUploadSession", url.PathEscape(folderID), fileName)
|
||||||
|
sessionCreationRequestInside := NewUploadSessionCreationRequest{
|
||||||
|
ConflictBehavior: "rename",
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionCreationRequest := struct {
|
||||||
|
Item NewUploadSessionCreationRequest `json:"item"`
|
||||||
|
DeferCommit bool `json:"deferCommit"`
|
||||||
|
}{sessionCreationRequestInside, false}
|
||||||
|
|
||||||
|
sessionCreationReq, err := onedrive.client.NewRequest("POST", apiURL, sessionCreationRequest)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var sessionCreationResp *NewUploadSessionCreationResponse
|
||||||
|
err = onedrive.client.Do(ctx, sessionCreationReq, false, &sessionCreationResp)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("session creation failed %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileSessionUploadUrl := sessionCreationResp.UploadURL
|
||||||
|
|
||||||
|
sizePerSplit := int64(3200 * 1024)
|
||||||
|
buffer := make([]byte, 3200*1024)
|
||||||
|
splitCount := fileSize / sizePerSplit
|
||||||
|
if fileSize%sizePerSplit != 0 {
|
||||||
|
splitCount += 1
|
||||||
|
}
|
||||||
|
bfReader := bufio.NewReader(file)
|
||||||
|
var fileUploadResp *UploadSessionUploadResponse
|
||||||
|
for splitNow := int64(0); splitNow < splitCount; splitNow++ {
|
||||||
|
length, err := bfReader.Read(buffer)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if int64(length) < sizePerSplit {
|
||||||
|
bufferLast := buffer[:length]
|
||||||
|
buffer = bufferLast
|
||||||
|
}
|
||||||
|
sessionFileUploadReq, err := onedrive.NewSessionFileUploadRequest(fileSessionUploadUrl, splitNow*sizePerSplit, fileSize, bytes.NewReader(buffer))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if err := onedrive.client.Do(ctx, sessionFileUploadReq, false, &fileUploadResp); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if fileUploadResp.Id == "" {
|
||||||
|
return false, errors.New("something went wrong. file upload incomplete. consider upload the file in a step-by-step manner")
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (onedrive oneDriveClient) Download(src, target string) (bool, error) {
|
||||||
|
src = "/" + strings.TrimPrefix(src, "/")
|
||||||
|
req, err := onedrive.client.NewRequest("GET", fmt.Sprintf("me/drive/root:%s", src), nil)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("new request for file id failed, err: %v", err)
|
||||||
|
}
|
||||||
|
var driveItem *odsdk.DriveItem
|
||||||
|
if err := onedrive.client.Do(context.Background(), req, false, &driveItem); err != nil {
|
||||||
|
return false, fmt.Errorf("do request for file id failed, err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.Get(driveItem.DownloadURL)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
out, err := os.Create(target)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
buffer := make([]byte, 2*1024*1024)
|
||||||
|
|
||||||
|
_, err = io.CopyBuffer(out, resp.Body, buffer)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (onedrive *oneDriveClient) ListObjects(prefix string) ([]interface{}, error) {
|
||||||
|
prefix = "/" + strings.TrimPrefix(prefix, "/")
|
||||||
|
folderID, err := onedrive.loadIDByPath(prefix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := onedrive.client.NewRequest("GET", fmt.Sprintf("me/drive/items/%s/children", folderID), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("new request for list failed, err: %v", err)
|
||||||
|
}
|
||||||
|
var driveItems *odsdk.OneDriveDriveItemsResponse
|
||||||
|
if err := onedrive.client.Do(context.Background(), req, false, &driveItems); err != nil {
|
||||||
|
return nil, fmt.Errorf("do request for list failed, err: %v", err)
|
||||||
|
}
|
||||||
|
for _, item := range driveItems.DriveItems {
|
||||||
|
return nil, fmt.Errorf("id: %v, name: %s \n", item.Id, item.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
var itemList []interface{}
|
||||||
|
for _, item := range driveItems.DriveItems {
|
||||||
|
itemList = append(itemList, item.Name)
|
||||||
|
}
|
||||||
|
return itemList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (onedrive *oneDriveClient) loadIDByPath(path string) (string, error) {
|
||||||
|
pathItem := "root:" + path
|
||||||
|
if path == "/" {
|
||||||
|
pathItem = "root"
|
||||||
|
}
|
||||||
|
req, err := onedrive.client.NewRequest("GET", fmt.Sprintf("me/drive/%s", pathItem), nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("new request for file id failed, err: %v", err)
|
||||||
|
}
|
||||||
|
var driveItem *odsdk.DriveItem
|
||||||
|
if err := onedrive.client.Do(context.Background(), req, false, &driveItem); err != nil {
|
||||||
|
return "", fmt.Errorf("do request for file id failed, err: %v", err)
|
||||||
|
}
|
||||||
|
return driveItem.Id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func refreshToken(oldToken string) (string, error) {
|
||||||
|
data := url.Values{}
|
||||||
|
data.Set("client_id", global.CONF.System.OneDriveID)
|
||||||
|
data.Set("client_secret", global.CONF.System.OneDriveSc)
|
||||||
|
data.Set("grant_type", "refresh_token")
|
||||||
|
data.Set("refresh_token", oldToken)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
|
||||||
|
tokenMap := map[string]interface{}{}
|
||||||
|
if err := json.Unmarshal(respBody, &tokenMap); err != nil {
|
||||||
|
return "", fmt.Errorf("unmarshal data from response body failed, err: %v", err)
|
||||||
|
}
|
||||||
|
accessToken, ok := tokenMap["access_token"].(string)
|
||||||
|
if !ok {
|
||||||
|
return "", errors.New("no such access token in response")
|
||||||
|
}
|
||||||
|
return accessToken, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (onedrive *oneDriveClient) createFolder(parent string) error {
|
||||||
|
if _, err := onedrive.loadIDByPath(path.Dir(parent)); err != nil {
|
||||||
|
if !strings.Contains(err.Error(), "itemNotFound") {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_ = onedrive.createFolder(path.Dir(parent))
|
||||||
|
}
|
||||||
|
item2, err := onedrive.loadIDByPath(path.Dir(parent))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := onedrive.client.DriveItems.CreateNewFolder(context.Background(), "", item2, path.Base(parent)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type NewUploadSessionCreationRequest struct {
|
||||||
|
ConflictBehavior string `json:"@microsoft.graph.conflictBehavior,omitempty"`
|
||||||
|
}
|
||||||
|
type NewUploadSessionCreationResponse struct {
|
||||||
|
UploadURL string `json:"uploadUrl"`
|
||||||
|
ExpirationDateTime string `json:"expirationDateTime"`
|
||||||
|
}
|
||||||
|
type UploadSessionUploadResponse struct {
|
||||||
|
ExpirationDateTime string `json:"expirationDateTime"`
|
||||||
|
NextExpectedRanges []string `json:"nextExpectedRanges"`
|
||||||
|
DriveItem
|
||||||
|
}
|
||||||
|
type DriveItem struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Id string `json:"id"`
|
||||||
|
DownloadURL string `json:"@microsoft.graph.downloadUrl"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
WebURL string `json:"webUrl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (onedrive *oneDriveClient) NewSessionFileUploadRequest(absoluteUrl string, grandOffset, grandTotalSize int64, byteReader *bytes.Reader) (*http.Request, error) {
|
||||||
|
apiUrl, err := onedrive.client.BaseURL.Parse(absoluteUrl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
absoluteUrl = apiUrl.String()
|
||||||
|
contentLength := byteReader.Size()
|
||||||
|
req, err := http.NewRequest("PUT", absoluteUrl, byteReader)
|
||||||
|
req.Header.Set("Content-Length", strconv.FormatInt(contentLength, 10))
|
||||||
|
preliminaryLength := grandOffset
|
||||||
|
preliminaryRange := grandOffset + contentLength - 1
|
||||||
|
if preliminaryRange >= grandTotalSize {
|
||||||
|
preliminaryRange = grandTotalSize - 1
|
||||||
|
preliminaryLength = preliminaryRange - grandOffset + 1
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", preliminaryLength, preliminaryRange, grandTotalSize))
|
||||||
|
|
||||||
|
return req, err
|
||||||
|
}
|
@@ -6,6 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ossClient struct {
|
type ossClient struct {
|
||||||
|
scType string
|
||||||
Vars map[string]interface{}
|
Vars map[string]interface{}
|
||||||
client osssdk.Client
|
client osssdk.Client
|
||||||
}
|
}
|
||||||
@@ -14,6 +15,7 @@ func NewOssClient(vars map[string]interface{}) (*ossClient, error) {
|
|||||||
var endpoint string
|
var endpoint string
|
||||||
var accessKey string
|
var accessKey string
|
||||||
var secretKey string
|
var secretKey string
|
||||||
|
var scType string
|
||||||
if _, ok := vars["endpoint"]; ok {
|
if _, ok := vars["endpoint"]; ok {
|
||||||
endpoint = vars["endpoint"].(string)
|
endpoint = vars["endpoint"].(string)
|
||||||
} else {
|
} else {
|
||||||
@@ -24,6 +26,11 @@ func NewOssClient(vars map[string]interface{}) (*ossClient, error) {
|
|||||||
} else {
|
} else {
|
||||||
return nil, constant.ErrInvalidParams
|
return nil, constant.ErrInvalidParams
|
||||||
}
|
}
|
||||||
|
if _, ok := vars["scType"]; ok {
|
||||||
|
scType = vars["scType"].(string)
|
||||||
|
} else {
|
||||||
|
scType = "Standard"
|
||||||
|
}
|
||||||
if _, ok := vars["secretKey"]; ok {
|
if _, ok := vars["secretKey"]; ok {
|
||||||
secretKey = vars["secretKey"].(string)
|
secretKey = vars["secretKey"].(string)
|
||||||
} else {
|
} else {
|
||||||
@@ -34,6 +41,7 @@ func NewOssClient(vars map[string]interface{}) (*ossClient, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &ossClient{
|
return &ossClient{
|
||||||
|
scType: scType,
|
||||||
Vars: vars,
|
Vars: vars,
|
||||||
client: *client,
|
client: *client,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -77,7 +85,7 @@ func (oss ossClient) Upload(src, target string) (bool, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
err = bucket.UploadFile(target, src, 200*1024*1024, osssdk.Routines(5), osssdk.Checkpoint(true, ""))
|
err = bucket.UploadFile(target, src, 200*1024*1024, osssdk.Routines(5), osssdk.Checkpoint(true, ""), osssdk.ObjectStorageClass(osssdk.StorageClassType(oss.scType)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@@ -13,14 +13,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type s3Client struct {
|
type s3Client struct {
|
||||||
Vars map[string]interface{}
|
scType string
|
||||||
Sess session.Session
|
Vars map[string]interface{}
|
||||||
|
Sess session.Session
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewS3Client(vars map[string]interface{}) (*s3Client, error) {
|
func NewS3Client(vars map[string]interface{}) (*s3Client, error) {
|
||||||
var accessKey string
|
var accessKey string
|
||||||
var secretKey string
|
var secretKey string
|
||||||
var endpoint string
|
var endpoint string
|
||||||
|
var scType string
|
||||||
var region string
|
var region string
|
||||||
if _, ok := vars["accessKey"]; ok {
|
if _, ok := vars["accessKey"]; ok {
|
||||||
accessKey = vars["accessKey"].(string)
|
accessKey = vars["accessKey"].(string)
|
||||||
@@ -32,6 +34,11 @@ func NewS3Client(vars map[string]interface{}) (*s3Client, error) {
|
|||||||
} else {
|
} else {
|
||||||
return nil, constant.ErrInvalidParams
|
return nil, constant.ErrInvalidParams
|
||||||
}
|
}
|
||||||
|
if _, ok := vars["scType"]; ok {
|
||||||
|
scType = vars["scType"].(string)
|
||||||
|
} else {
|
||||||
|
scType = "Standard"
|
||||||
|
}
|
||||||
if _, ok := vars["endpoint"]; ok {
|
if _, ok := vars["endpoint"]; ok {
|
||||||
endpoint = vars["endpoint"].(string)
|
endpoint = vars["endpoint"].(string)
|
||||||
} else {
|
} else {
|
||||||
@@ -53,8 +60,9 @@ func NewS3Client(vars map[string]interface{}) (*s3Client, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &s3Client{
|
return &s3Client{
|
||||||
Vars: vars,
|
scType: scType,
|
||||||
Sess: *sess,
|
Vars: vars,
|
||||||
|
Sess: *sess,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,9 +134,10 @@ func (s3C s3Client) Upload(src, target string) (bool, error) {
|
|||||||
|
|
||||||
uploader := s3manager.NewUploader(&s3C.Sess)
|
uploader := s3manager.NewUploader(&s3C.Sess)
|
||||||
_, err = uploader.Upload(&s3manager.UploadInput{
|
_, err = uploader.Upload(&s3manager.UploadInput{
|
||||||
Bucket: aws.String(bucket),
|
Bucket: aws.String(bucket),
|
||||||
Key: aws.String(target),
|
Key: aws.String(target),
|
||||||
Body: file,
|
Body: file,
|
||||||
|
StorageClass: &s3C.scType,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
@@ -14,24 +14,23 @@ type CloudStorageClient interface {
|
|||||||
Download(src, target string) (bool, error)
|
Download(src, target string) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCloudStorageClient(vars map[string]interface{}) (CloudStorageClient, error) {
|
func NewCloudStorageClient(backupType string, vars map[string]interface{}) (CloudStorageClient, error) {
|
||||||
if vars["type"] == constant.S3 {
|
switch backupType {
|
||||||
|
case constant.S3:
|
||||||
return client.NewS3Client(vars)
|
return client.NewS3Client(vars)
|
||||||
}
|
case constant.OSS:
|
||||||
if vars["type"] == constant.OSS {
|
|
||||||
return client.NewOssClient(vars)
|
return client.NewOssClient(vars)
|
||||||
}
|
case constant.Sftp:
|
||||||
if vars["type"] == constant.Sftp {
|
|
||||||
return client.NewSftpClient(vars)
|
return client.NewSftpClient(vars)
|
||||||
}
|
case constant.MinIo:
|
||||||
if vars["type"] == constant.MinIo {
|
|
||||||
return client.NewMinIoClient(vars)
|
return client.NewMinIoClient(vars)
|
||||||
}
|
case constant.Cos:
|
||||||
if vars["type"] == constant.Cos {
|
|
||||||
return client.NewCosClient(vars)
|
return client.NewCosClient(vars)
|
||||||
}
|
case constant.Kodo:
|
||||||
if vars["type"] == constant.Kodo {
|
|
||||||
return client.NewKodoClient(vars)
|
return client.NewKodoClient(vars)
|
||||||
|
case constant.OneDrive:
|
||||||
|
return client.NewOneDriveClient(vars)
|
||||||
|
default:
|
||||||
|
return nil, constant.ErrNotSupportType
|
||||||
}
|
}
|
||||||
return nil, constant.ErrNotSupportType
|
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||||
@@ -117,6 +118,43 @@ func Execf(cmdStr string, a ...interface{}) (string, error) {
|
|||||||
return stdout.String(), nil
|
return stdout.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExecWithCheck(name string, a ...string) (string, error) {
|
||||||
|
cmd := exec.Command(name, a...)
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
errMsg := ""
|
||||||
|
if len(stderr.String()) != 0 {
|
||||||
|
errMsg = fmt.Sprintf("stderr: %s", stderr.String())
|
||||||
|
}
|
||||||
|
if len(stdout.String()) != 0 {
|
||||||
|
if len(errMsg) != 0 {
|
||||||
|
errMsg = fmt.Sprintf("%s; stdout: %s", errMsg, stdout.String())
|
||||||
|
} else {
|
||||||
|
errMsg = fmt.Sprintf("stdout: %s", stdout.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errMsg, err
|
||||||
|
}
|
||||||
|
return stdout.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckIllegal(args ...string) bool {
|
||||||
|
if args == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, arg := range args {
|
||||||
|
if strings.Contains(arg, "&") || strings.Contains(arg, "|") || strings.Contains(arg, ";") ||
|
||||||
|
strings.Contains(arg, "$") || strings.Contains(arg, "'") || strings.Contains(arg, "`") ||
|
||||||
|
strings.Contains(arg, "(") || strings.Contains(arg, ")") || strings.Contains(arg, "\"") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func HasNoPasswordSudo() bool {
|
func HasNoPasswordSudo() bool {
|
||||||
cmd2 := exec.Command("sudo", "-n", "ls")
|
cmd2 := exec.Command("sudo", "-n", "ls")
|
||||||
err2 := cmd2.Run()
|
err2 := cmd2.Run()
|
||||||
|
@@ -4,32 +4,37 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func Pull(filePath string) (string, error) {
|
||||||
|
stdout, err := cmd.Execf("docker-compose -f %s pull", filePath)
|
||||||
|
return stdout, err
|
||||||
|
}
|
||||||
|
|
||||||
func Up(filePath string) (string, error) {
|
func Up(filePath string) (string, error) {
|
||||||
stdout, err := cmd.Execf("docker compose -f %s up -d", filePath)
|
stdout, err := cmd.Execf("docker-compose -f %s up -d", filePath)
|
||||||
return stdout, err
|
return stdout, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Down(filePath string) (string, error) {
|
func Down(filePath string) (string, error) {
|
||||||
stdout, err := cmd.Execf("docker compose -f %s down --remove-orphans", filePath)
|
stdout, err := cmd.Execf("docker-compose -f %s down --remove-orphans", filePath)
|
||||||
return stdout, err
|
return stdout, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start(filePath string) (string, error) {
|
func Start(filePath string) (string, error) {
|
||||||
stdout, err := cmd.Execf("docker compose -f %s start", filePath)
|
stdout, err := cmd.Execf("docker-compose -f %s start", filePath)
|
||||||
return stdout, err
|
return stdout, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Stop(filePath string) (string, error) {
|
func Stop(filePath string) (string, error) {
|
||||||
stdout, err := cmd.Execf("docker compose -f %s stop", filePath)
|
stdout, err := cmd.Execf("docker-compose -f %s stop", filePath)
|
||||||
return stdout, err
|
return stdout, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Restart(filePath string) (string, error) {
|
func Restart(filePath string) (string, error) {
|
||||||
stdout, err := cmd.Execf("docker compose -f %s restart", filePath)
|
stdout, err := cmd.Execf("docker-compose -f %s restart", filePath)
|
||||||
return stdout, err
|
return stdout, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Operate(filePath, operation string) (string, error) {
|
func Operate(filePath, operation string) (string, error) {
|
||||||
stdout, err := cmd.Execf("docker compose -f %s %s", filePath, operation)
|
stdout, err := cmd.Execf("docker-compose -f %s %s", filePath, operation)
|
||||||
return stdout, err
|
return stdout, err
|
||||||
}
|
}
|
||||||
|
@@ -89,6 +89,10 @@ func (s *ComposeService) ComposeBuild() error {
|
|||||||
return s.Build(context.Background(), s.project, api.BuildOptions{})
|
return s.Build(context.Background(), s.project, api.BuildOptions{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ComposeService) ComposePull() error {
|
||||||
|
return s.Pull(context.Background(), s.project, api.PullOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
func GetComposeProject(projectName, workDir string, yml []byte, env []byte, skipNormalization bool) (*types.Project, error) {
|
func GetComposeProject(projectName, workDir string, yml []byte, env []byte, skipNormalization bool) (*types.Project, error) {
|
||||||
var configFiles []types.ConfigFile
|
var configFiles []types.ConfigFile
|
||||||
configFiles = append(configFiles, types.ConfigFile{
|
configFiles = append(configFiles, types.ConfigFile{
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user