mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 16:00:29 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8bbfada8c | ||
|
|
47dda4ac9f | ||
|
|
b1d40960c4 | ||
|
|
5222caecf9 | ||
|
|
bc8d4cbb0f | ||
|
|
0bb31f6caf | ||
|
|
03c8e4d3cb | ||
|
|
68ad653545 | ||
|
|
fd5b34d644 | ||
|
|
000096bc26 | ||
|
|
02023102d3 | ||
|
|
95bc3d9855 | ||
|
|
f9fb16198b | ||
|
|
11ae05163c | ||
|
|
0275716ff5 | ||
|
|
e9bdca5279 | ||
|
|
cd84116a03 | ||
|
|
457effae85 | ||
|
|
9361c358f1 | ||
|
|
39f952e460 | ||
|
|
7c600a357c | ||
|
|
8f036a0c3d | ||
|
|
1184ae2b52 | ||
|
|
de7ef19034 | ||
|
|
e91b7caf4f | ||
|
|
033b3c10c0 | ||
|
|
97774c88d5 | ||
|
|
95e1c73ced | ||
|
|
d2a067db77 | ||
|
|
586dee9e70 | ||
|
|
6a717b2517 | ||
|
|
203af988fa | ||
|
|
911166152a | ||
|
|
b3ecddb20f | ||
|
|
a41d4e55aa | ||
|
|
c7a3ef7d72 | ||
|
|
b7495a63e9 | ||
|
|
6b3093aa09 | ||
|
|
8f8369c989 | ||
|
|
f79293be69 | ||
|
|
4631da2212 | ||
|
|
e2a7c51de0 | ||
|
|
633e26b1de | ||
|
|
08992f83b5 | ||
|
|
921e886e71 | ||
|
|
9fc4cad80e | ||
|
|
4fdb81642a | ||
|
|
056c771d2e | ||
|
|
84183bdfeb | ||
|
|
4e786fee31 | ||
|
|
9f4e5050dd | ||
|
|
bb49a610a2 | ||
|
|
5e3e580f51 | ||
|
|
75fa498a7c | ||
|
|
05e5f06cf1 | ||
|
|
3a17f4f29f | ||
|
|
5e7524e4f8 | ||
|
|
b2e17d4c42 | ||
|
|
b88303d36a | ||
|
|
5a5b0e3a1b | ||
|
|
e504e55a34 | ||
|
|
b6758ff92d | ||
|
|
957499e4d7 | ||
|
|
3773d64aa7 | ||
|
|
d89f823bef | ||
|
|
a8b7c3d8c5 | ||
|
|
9a45ce3110 | ||
|
|
3ad3b180af | ||
|
|
6919ce7b5c | ||
|
|
e7a9c3814b | ||
|
|
488eb319a1 | ||
|
|
4f650cad9d | ||
|
|
ce9c2e6d3c | ||
|
|
90a5e741fd | ||
|
|
bfedd02d1d | ||
|
|
0187d2f5c1 | ||
|
|
7d968348f5 | ||
|
|
317017a2b4 | ||
|
|
35ca52620c | ||
|
|
01ed60fcb7 | ||
|
|
80599a3576 | ||
|
|
d40b2734a9 | ||
|
|
6abd313bd2 | ||
|
|
88f573e2a6 | ||
|
|
eb55e16465 | ||
|
|
bb48964cca | ||
|
|
cbdf1ad7b7 | ||
|
|
df77ac4318 | ||
|
|
b3caa343d2 | ||
|
|
49f4f316f8 | ||
|
|
2207711400 | ||
|
|
763f450f43 | ||
|
|
0fa11213bb | ||
|
|
c743775a1e | ||
|
|
b854aa3bfe | ||
|
|
8dfba82a70 | ||
|
|
824051e89e | ||
|
|
ab45cc27d5 | ||
|
|
627e467fdf | ||
|
|
ca3b4b02e0 | ||
|
|
34ac685e65 | ||
|
|
7a66e71215 | ||
|
|
1e96a1ae84 | ||
|
|
ce0342e235 | ||
|
|
a48ea497b0 | ||
|
|
4b3c9419f3 | ||
|
|
f51b09dc40 | ||
|
|
800f9e2d38 | ||
|
|
b5093e4d93 | ||
|
|
10684f9aac | ||
|
|
7c0327c1b8 | ||
|
|
9f5a03419e | ||
|
|
ec843f2396 | ||
|
|
2d6925ac4f | ||
|
|
7cbaa4f63d | ||
|
|
ebb9e7a2a0 | ||
|
|
84e44127b3 | ||
|
|
0b66bb5eff | ||
|
|
e70ec0e978 | ||
|
|
d62f566bb3 | ||
|
|
4e1c3be03a | ||
|
|
792e96e95f | ||
|
|
c1acd8f5f0 | ||
|
|
d64e1713fb | ||
|
|
3e6fefe1b7 | ||
|
|
48e3ef3e73 | ||
|
|
c76c24e102 | ||
|
|
4b25dafb92 | ||
|
|
c8237d59be | ||
|
|
c2629e3945 | ||
|
|
b5864ca3a3 | ||
|
|
b84c983727 | ||
|
|
aa3e8783ae | ||
|
|
f65f0d86aa | ||
|
|
93b03c7212 | ||
|
|
c96b999b2c | ||
|
|
da155fadf2 | ||
|
|
c69a1c8ec2 | ||
|
|
83ac2f1ff7 | ||
|
|
f77972fa38 | ||
|
|
d7c08295f8 | ||
|
|
be6b7157f4 | ||
|
|
b979c2574c | ||
|
|
f7c76c012f | ||
|
|
13abe8cb66 | ||
|
|
7596099aa1 | ||
|
|
626782102a | ||
|
|
3e068a0020 | ||
|
|
09af1e9cdf | ||
|
|
5ac9298db0 | ||
|
|
0d084861e0 | ||
|
|
c052887d58 | ||
|
|
f5cd45438b | ||
|
|
a0a1cc410f | ||
|
|
74cfc11a37 | ||
|
|
53600900f2 | ||
|
|
ce19107c95 | ||
|
|
7c56ed7b16 | ||
|
|
152cc76e3f | ||
|
|
8fd4060562 | ||
|
|
c2f5908a9d | ||
|
|
57aa2aba74 | ||
|
|
d851aeed45 | ||
|
|
03d338d6c9 | ||
|
|
a74cdcc061 | ||
|
|
4fca9aeb48 | ||
|
|
205e32dde8 | ||
|
|
d065558865 | ||
|
|
950c6b9d08 | ||
|
|
d20d5946f2 | ||
|
|
72bc99bddc | ||
|
|
8c4016792b | ||
|
|
2975cf6d5a | ||
|
|
73d93d6104 | ||
|
|
7452cc19e0 | ||
|
|
efd545882f | ||
|
|
b19cdd9339 | ||
|
|
da54794aca | ||
|
|
a8df6d0668 | ||
|
|
1f65d2243e | ||
|
|
36db30471c | ||
|
|
80e22ffc82 | ||
|
|
872581fa4b | ||
|
|
aeabed70db | ||
|
|
d443103e2c |
@@ -0,0 +1,36 @@
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- dev
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
name: Build Test
|
||||
|
||||
jobs:
|
||||
build-linux-binary:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install cross-compilers
|
||||
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
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18.14'
|
||||
- name: Build Web
|
||||
id: build_frontend
|
||||
run: |
|
||||
cd frontend && npm install && npm run build:dev
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.20.x'
|
||||
- name: Build Server
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
with:
|
||||
args: release --snapshot --clean
|
||||
@@ -0,0 +1,52 @@
|
||||
on:
|
||||
push:
|
||||
# Sequence of patterns matched against refs/tags
|
||||
tags:
|
||||
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||
|
||||
name: Create Release And Upload assets
|
||||
|
||||
jobs:
|
||||
create-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install cross-compilers
|
||||
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
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18.14'
|
||||
- name: Build Web
|
||||
run: |
|
||||
cd frontend && npm install && npm run build:dev
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.20.x'
|
||||
- name: Build Release
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
with:
|
||||
args: release --skip-publish --clean
|
||||
- name: Upload Assets
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
draft: true
|
||||
files: |
|
||||
dist/*.tar.gz
|
||||
dist/checksums.txt
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Setup OSSUTIL
|
||||
uses: yizhoumo/setup-ossutil@v1
|
||||
with:
|
||||
endpoint: ${{ secrets.OSS_ENDPOINT }}
|
||||
access-key-id: ${{ secrets.OSS_ACCESS_KEY_ID }}
|
||||
access-key-secret: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
|
||||
ossutil-version: '1.7.14'
|
||||
- name: Upload Assets to OSS
|
||||
run: ossutil cp -r dist/ oss://resource-fit2cloud-com/1panel/package/stable/${{ github.ref_name }}/release/ --include "*.tar.gz" --include "checksums.txt" --only-current-dir
|
||||
@@ -23,3 +23,12 @@ cmd/server/web/assets
|
||||
cmd/server/web/monacoeditorwork
|
||||
cmd/server/web/index.html
|
||||
frontend/auto-imports.d.ts
|
||||
frontend/components.d.ts
|
||||
|
||||
.history/
|
||||
dist/
|
||||
1pctl
|
||||
1panel.service
|
||||
install.sh
|
||||
cmd/server/web/.DS_Store
|
||||
cmd/server/.DS_Store
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# This is an example .goreleaser.yml file with some sensible defaults.
|
||||
# Make sure to check the documentation at https://goreleaser.com
|
||||
before:
|
||||
hooks:
|
||||
# - export NODE_OPTIONS="--max-old-space-size=8192"
|
||||
# - make build_web
|
||||
- chmod +x ./script.sh
|
||||
- ./script.sh
|
||||
- sed -i 's@ORIGINAL_VERSION=.*@ORIGINAL_VERSION=v{{ .Version }}@g' 1pctl
|
||||
- go mod tidy
|
||||
|
||||
builds:
|
||||
- main: ./cmd/server/main.go
|
||||
binary: 1panel
|
||||
flags:
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -w -s
|
||||
- --extldflags "-static -fpic"
|
||||
tags:
|
||||
- osusergo
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- >-
|
||||
{{- 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:
|
||||
- linux
|
||||
goarm:
|
||||
- 7
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
- arm
|
||||
- ppc64le
|
||||
- s390x
|
||||
|
||||
archives:
|
||||
- format: tar.gz
|
||||
name_template: "1panel-v{{ .Version }}-{{ .Os }}-{{ .Arch }}{{- if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
wrap_in_directory: true
|
||||
files:
|
||||
- 1pctl
|
||||
- 1panel.service
|
||||
- install.sh
|
||||
- README.md
|
||||
- LICENSE
|
||||
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
snapshot:
|
||||
name_template: "{{ incpatch .Version }}-next"
|
||||
release:
|
||||
draft: true
|
||||
mode: append
|
||||
extra_files:
|
||||
- glob: dist/*.tar.gz
|
||||
- glob: dist/checksums.txt
|
||||
name_template: "Release {{.Tag}}"
|
||||
|
||||
# The lines beneath this are called `modelines`. See `:help modeline`
|
||||
# Feel free to remove those if you don't want/use them.
|
||||
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
||||
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ansible.python.interpreterPath": "/opt/homebrew/bin/python3"
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
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,9 +10,16 @@ WEB_PATH=$(BASE_PAH)/frontend
|
||||
SERVER_PATH=$(BASE_PAH)/backend
|
||||
MAIN= $(BASE_PAH)/cmd/server/main.go
|
||||
APP_NAME=1panel
|
||||
ASSERT_PATH= $(BASE_PAH)/cmd/server/web/assets
|
||||
|
||||
clean_assets:
|
||||
rm -rf $(ASSERT_PATH)
|
||||
|
||||
upx_bin:
|
||||
upx $(BUILD_PATH)/$(APP_NAME)
|
||||
|
||||
build_frontend:
|
||||
cd $(WEB_PATH) && npm install && npm run build:dev
|
||||
cd $(WEB_PATH) && npm install && npm run build:pro
|
||||
|
||||
build_backend_on_linux:
|
||||
cd $(SERVER_PATH) \
|
||||
@@ -27,3 +34,5 @@ build_backend_on_archlinux:
|
||||
&& CGO_ENABLED=1 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-fpic"' -tags osusergo -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
|
||||
build_all: build_frontend build_backend_on_linux
|
||||
|
||||
build_on_local: clean_assets build_frontend build_backend_on_darwin upx_bin
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -39,6 +40,15 @@ func (b *BaseApi) SearchApp(c *gin.Context) {
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"应用商店同步","formatEN":"App store synchronization"}
|
||||
func (b *BaseApi) SyncApp(c *gin.Context) {
|
||||
go appService.SyncAppListFromLocal()
|
||||
res, err := appService.GetAppUpdate()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if !res.CanUpdate {
|
||||
helper.SuccessWithMsg(c, i18n.GetMsgByKey("AppStoreIsUpToDate"))
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
global.LOG.Infof("sync app list start ...")
|
||||
if err := appService.SyncAppListFromRemote(); err != nil {
|
||||
@@ -128,7 +138,7 @@ func (b *BaseApi) GetAppDetailByID(c *gin.Context) {
|
||||
// @Success 200 {object} model.AppInstall
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/install [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"name","input_value":"name","isList":false,"db":"app_installs","output_colume":"app_id","output_value":"appId"},{"info":"appId","isList":false,"db":"apps","output_colume":"key","output_value":"appKey"}],"formatZH":"安装应用 [appKey]-[name]","formatEN":"Install app [appKey]-[name]"}
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[{"input_column":"name","input_value":"name","isList":false,"db":"app_installs","output_column":"app_id","output_value":"appId"},{"info":"appId","isList":false,"db":"apps","output_column":"key","output_value":"appKey"}],"formatZH":"安装应用 [appKey]-[name]","formatEN":"Install app [appKey]-[name]"}
|
||||
func (b *BaseApi) InstallApp(c *gin.Context) {
|
||||
var req request.AppInstallCreate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
||||
@@ -159,7 +159,7 @@ func (b *BaseApi) SyncInstalled(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/installed/op [post]
|
||||
// @x-panel-log {"bodyKeys":["installId","operate"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"installId","isList":false,"db":"app_installs","output_colume":"app_id","output_value":"appId"},{"input_colume":"id","input_value":"installId","isList":false,"db":"app_installs","output_colume":"name","output_value":"appName"},{"input_colume":"id","input_value":"appId","isList":false,"db":"apps","output_colume":"key","output_value":"appKey"}],"formatZH":"[appKey] 应用 [appName] [operate]","formatEN":"[appKey] App [appName] [operate]"}
|
||||
// @x-panel-log {"bodyKeys":["installId","operate"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"installId","isList":false,"db":"app_installs","output_column":"app_id","output_value":"appId"},{"input_column":"id","input_value":"installId","isList":false,"db":"app_installs","output_column":"name","output_value":"appName"},{"input_column":"id","input_value":"appId","isList":false,"db":"apps","output_column":"key","output_value":"appKey"}],"formatZH":"[operate] 应用 [appKey][appName]","formatEN":"[operate] App [appKey][appName]"}
|
||||
func (b *BaseApi) OperateInstalled(c *gin.Context) {
|
||||
var req request.AppInstalledOperate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
||||
@@ -103,7 +103,12 @@ func (b *BaseApi) Captcha(c *gin.Context) {
|
||||
// @Router /auth/issafety [get]
|
||||
func (b *BaseApi) CheckIsSafety(c *gin.Context) {
|
||||
code := c.DefaultQuery("code", "")
|
||||
helper.SuccessWithData(c, authService.CheckIsSafety(code))
|
||||
status, err := authService.CheckIsSafety(code)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, status)
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
|
||||
@@ -106,7 +106,7 @@ func (b *BaseApi) ListBuckets(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":true,"db":"backup_accounts","output_colume":"type","output_value":"types"}],"formatZH":"删除备份账号 [types]","formatEN":"delete backup account [types]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":true,"db":"backup_accounts","output_column":"type","output_value":"types"}],"formatZH":"删除备份账号 [types]","formatEN":"delete backup account [types]"}
|
||||
func (b *BaseApi) DeleteBackup(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -188,7 +188,7 @@ func (b *BaseApi) DownloadRecord(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/record/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"backup_records","output_colume":"file_name","output_value":"files"}],"formatZH":"删除备份记录 [files]","formatEN":"delete backup records [files]"}
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"backup_records","output_column":"file_name","output_value":"files"}],"formatZH":"删除备份记录 [files]","formatEN":"delete backup records [files]"}
|
||||
func (b *BaseApi) DeleteBackupRecord(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
||||
@@ -85,7 +85,7 @@ func (b *BaseApi) ListCommand(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/command/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"commands","output_colume":"name","output_value":"names"}],"formatZH":"删除快捷命令 [names]","formatEN":"delete quick command [names]"}
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"commands","output_column":"name","output_value":"names"}],"formatZH":"删除快捷命令 [names]","formatEN":"delete quick command [names]"}
|
||||
func (b *BaseApi) DeleteCommand(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
||||
@@ -87,7 +87,7 @@ func (b *BaseApi) ListComposeTemplate(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/template/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"compose_templates","output_colume":"name","output_value":"names"}],"formatZH":"删除 compose 模版 [names]","formatEN":"delete compose template [names]"}
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"compose_templates","output_column":"name","output_value":"names"}],"formatZH":"删除 compose 模版 [names]","formatEN":"delete compose template [names]"}
|
||||
func (b *BaseApi) DeleteComposeTemplate(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -114,7 +114,7 @@ func (b *BaseApi) DeleteComposeTemplate(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/template/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"compose_templates","output_colume":"name","output_value":"name"}],"formatZH":"更新 compose 模版 [name]","formatEN":"update compose template information [name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"compose_templates","output_column":"name","output_value":"name"}],"formatZH":"更新 compose 模版 [name]","formatEN":"update compose template information [name]"}
|
||||
func (b *BaseApi) UpdateComposeTemplate(c *gin.Context) {
|
||||
var req dto.ComposeTemplateUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
||||
@@ -179,6 +179,59 @@ func (b *BaseApi) ContainerCreate(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Clean container
|
||||
// @Description 容器清理
|
||||
// @Accept json
|
||||
// @Param request body dto.ContainerPrune true "request"
|
||||
// @Success 200 {object} dto.ContainerPruneReport
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/prune [post]
|
||||
// @x-panel-log {"bodyKeys":["pruneType"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"清理容器 [pruneType]","formatEN":"clean container [pruneType]"}
|
||||
func (b *BaseApi) ContainerPrune(c *gin.Context) {
|
||||
var req dto.ContainerPrune
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
report, err := containerService.Prune(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, report)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Clean container log
|
||||
// @Description 清理容器日志
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithName true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/clean/log [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"清理容器 [name] 日志","formatEN":"clean container [name] logs"}
|
||||
func (b *BaseApi) CleanContainerLog(c *gin.Context) {
|
||||
var req dto.OperationWithName
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := containerService.ContainerLogClean(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Operate Container
|
||||
// @Description 容器操作
|
||||
@@ -342,13 +395,13 @@ func (b *BaseApi) DeleteNetwork(c *gin.Context) {
|
||||
// @Summary Create network
|
||||
// @Description 创建容器网络
|
||||
// @Accept json
|
||||
// @Param request body dto.NetworkCreat true "request"
|
||||
// @Param request body dto.NetworkCreate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/network [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建容器网络 name","formatEN":"create container network [name]"}
|
||||
func (b *BaseApi) CreateNetwork(c *gin.Context) {
|
||||
var req dto.NetworkCreat
|
||||
var req dto.NetworkCreate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
@@ -445,13 +498,13 @@ func (b *BaseApi) DeleteVolume(c *gin.Context) {
|
||||
// @Summary Create volume
|
||||
// @Description 创建容器存储卷
|
||||
// @Accept json
|
||||
// @Param request body dto.VolumeCreat true "request"
|
||||
// @Param request body dto.VolumeCreate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/volume [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建容器存储卷 [name]","formatEN":"create container volume [name]"}
|
||||
func (b *BaseApi) CreateVolume(c *gin.Context) {
|
||||
var req dto.VolumeCreat
|
||||
var req dto.VolumeCreate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
|
||||
@@ -102,7 +102,7 @@ func (b *BaseApi) SearchJobRecords(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /cronjobs/records/clean [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"cronjobs","output_colume":"name","output_value":"name"}],"formatZH":"清空计划任务记录 [name]","formatEN":"clean cronjob [name] records"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"cronjobs","output_column":"name","output_value":"name"}],"formatZH":"清空计划任务记录 [name]","formatEN":"clean cronjob [name] records"}
|
||||
func (b *BaseApi) CleanRecord(c *gin.Context) {
|
||||
var req dto.CronjobClean
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -126,7 +126,7 @@ func (b *BaseApi) CleanRecord(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /cronjobs/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"cronjobs","output_colume":"name","output_value":"names"}],"formatZH":"删除计划任务 [names]","formatEN":"delete cronjob [names]"}
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"cronjobs","output_column":"name","output_value":"names"}],"formatZH":"删除计划任务 [names]","formatEN":"delete cronjob [names]"}
|
||||
func (b *BaseApi) DeleteCronjob(c *gin.Context) {
|
||||
var req dto.CronjobBatchDelete
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -153,7 +153,7 @@ func (b *BaseApi) DeleteCronjob(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /cronjobs/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"cronjobs","output_colume":"name","output_value":"name"}],"formatZH":"更新计划任务 [name]","formatEN":"update cronjob [name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"cronjobs","output_column":"name","output_value":"name"}],"formatZH":"更新计划任务 [name]","formatEN":"update cronjob [name]"}
|
||||
func (b *BaseApi) UpdateCronjob(c *gin.Context) {
|
||||
var req dto.CronjobUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -180,7 +180,7 @@ func (b *BaseApi) UpdateCronjob(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /cronjobs/status [post]
|
||||
// @x-panel-log {"bodyKeys":["id","status"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"cronjobs","output_colume":"name","output_value":"name"}],"formatZH":"修改计划任务 [name] 状态为 [status]","formatEN":"change the status of cronjob [name] to [status]."}
|
||||
// @x-panel-log {"bodyKeys":["id","status"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"cronjobs","output_column":"name","output_value":"name"}],"formatZH":"修改计划任务 [name] 状态为 [status]","formatEN":"change the status of cronjob [name] to [status]."}
|
||||
func (b *BaseApi) UpdateCronjobStatus(c *gin.Context) {
|
||||
var req dto.CronjobUpdateStatus
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -207,7 +207,7 @@ func (b *BaseApi) UpdateCronjobStatus(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /cronjobs/download [post]
|
||||
// @x-panel-log {"bodyKeys":["recordID"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"recordID","isList":false,"db":"job_records","output_colume":"file","output_value":"file"}],"formatZH":"下载计划任务记录 [file]","formatEN":"download the cronjob record [file]"}
|
||||
// @x-panel-log {"bodyKeys":["recordID"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"recordID","isList":false,"db":"job_records","output_column":"file","output_value":"file"}],"formatZH":"下载计划任务记录 [file]","formatEN":"download the cronjob record [file]"}
|
||||
func (b *BaseApi) TargetDownload(c *gin.Context) {
|
||||
var req dto.CronjobDownload
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -235,7 +235,7 @@ func (b *BaseApi) TargetDownload(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /cronjobs/handle [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"cronjobs","output_colume":"name","output_value":"name"}],"formatZH":"手动执行计划任务 [name]","formatEN":"manually execute the cronjob [name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"cronjobs","output_column":"name","output_value":"name"}],"formatZH":"手动执行计划任务 [name]","formatEN":"manually execute the cronjob [name]"}
|
||||
func (b *BaseApi) HandleOnce(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
||||
@@ -54,7 +54,7 @@ func (b *BaseApi) CreateMysql(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/description/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"database_mysqls","output_colume":"name","output_value":"name"}],"formatZH":"mysql 数据库 [name] 描述信息修改 [description]","formatEN":"The description of the mysql database [name] is modified => [description]"}
|
||||
// @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_mysqls","output_column":"name","output_value":"name"}],"formatZH":"mysql 数据库 [name] 描述信息修改 [description]","formatEN":"The description of the mysql database [name] is modified => [description]"}
|
||||
func (b *BaseApi) UpdateMysqlDescription(c *gin.Context) {
|
||||
var req dto.UpdateDescription
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -80,7 +80,7 @@ func (b *BaseApi) UpdateMysqlDescription(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/change/password [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"database_mysqls","output_colume":"name","output_value":"name"}],"formatZH":"更新数据库 [name] 密码","formatEN":"Update database [name] password"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_mysqls","output_column":"name","output_value":"name"}],"formatZH":"更新数据库 [name] 密码","formatEN":"Update database [name] password"}
|
||||
func (b *BaseApi) ChangeMysqlPassword(c *gin.Context) {
|
||||
var req dto.ChangeDBInfo
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -115,7 +115,7 @@ func (b *BaseApi) ChangeMysqlPassword(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/change/access [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"database_mysqls","output_colume":"name","output_value":"name"}],"formatZH":"更新数据库 [name] 访问权限","formatEN":"Update database [name] access"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_mysqls","output_column":"name","output_value":"name"}],"formatZH":"更新数据库 [name] 访问权限","formatEN":"Update database [name] access"}
|
||||
func (b *BaseApi) ChangeMysqlAccess(c *gin.Context) {
|
||||
var req dto.ChangeDBInfo
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -264,7 +264,7 @@ func (b *BaseApi) DeleteCheckMysql(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"database_mysqls","output_colume":"name","output_value":"name"}],"formatZH":"删除 mysql 数据库 [name]","formatEN":"delete mysql database [name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_mysqls","output_column":"name","output_value":"name"}],"formatZH":"删除 mysql 数据库 [name]","formatEN":"delete mysql database [name]"}
|
||||
func (b *BaseApi) DeleteMysql(c *gin.Context) {
|
||||
var req dto.MysqlDBDelete
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
||||
@@ -58,13 +58,13 @@ func (b *BaseApi) LoadDaemonJson(c *gin.Context) {
|
||||
// @Summary Update docker daemon.json
|
||||
// @Description 修改 docker 配置信息
|
||||
// @Accept json
|
||||
// @Param request body dto.DaemonJsonConf true "request"
|
||||
// @Param request body dto.SettingUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/daemonjson/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新 docker daemon.json 配置","formatEN":"Updated the docker daemon.json configuration"}
|
||||
// @x-panel-log {"bodyKeys":["key", "value"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新 docker daemon.json 配置 [key]=>[value]","formatEN":"Updated the docker daemon.json configuration [key]=>[value]"}
|
||||
func (b *BaseApi) UpdateDaemonJson(c *gin.Context) {
|
||||
var req dto.DaemonJsonConf
|
||||
var req dto.SettingUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
@@ -78,6 +78,30 @@ func (b *BaseApi) UpdateDaemonJson(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Container Docker
|
||||
// @Summary Update docker daemon.json log option
|
||||
// @Description 修改 docker 日志配置
|
||||
// @Accept json
|
||||
// @Param request body dto.LogOption true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/daemonjson/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新 docker daemon.json 日志配置","formatEN":"Updated the docker daemon.json log option"}
|
||||
func (b *BaseApi) UpdateLogOption(c *gin.Context) {
|
||||
var req dto.LogOption
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := dockerService.UpdateLogOption(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Container Docker
|
||||
// @Summary Update docker daemon.json by upload file
|
||||
// @Description 上传替换 docker 配置文件
|
||||
|
||||
@@ -26,9 +26,10 @@ var (
|
||||
|
||||
cronjobService = service.NewICronjobService()
|
||||
|
||||
hostService = service.NewIHostService()
|
||||
groupService = service.NewIGroupService()
|
||||
fileService = service.NewIFileService()
|
||||
hostService = service.NewIHostService()
|
||||
groupService = service.NewIGroupService()
|
||||
fileService = service.NewIFileService()
|
||||
sshService = service.NewISSHService()
|
||||
firewallService = service.NewIFirewallService()
|
||||
|
||||
settingService = service.NewISettingService()
|
||||
|
||||
+96
-13
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -454,17 +455,93 @@ func (b *BaseApi) MoveFile(c *gin.Context) {
|
||||
// @Router /files/download [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"下载文件 [name]","formatEN":"Download file [name]"}
|
||||
func (b *BaseApi) Download(c *gin.Context) {
|
||||
var req request.FileDownload
|
||||
filePath := c.Query("path")
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
}
|
||||
info, _ := file.Stat()
|
||||
c.Header("Content-Length", strconv.FormatInt(info.Size(), 10))
|
||||
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(info.Name()))
|
||||
http.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), file)
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
// @Summary Chunk Download file
|
||||
// @Description 分片下载下载文件
|
||||
// @Accept json
|
||||
// @Param request body request.FileDownload true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /files/chunkdownload [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"下载文件 [name]","formatEN":"Download file [name]"}
|
||||
func (b *BaseApi) DownloadChunkFiles(c *gin.Context) {
|
||||
var req request.FileChunkDownload
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
filePath, err := fileService.FileDownload(req)
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(req.Path) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrPathNotFound, nil)
|
||||
return
|
||||
}
|
||||
filePath := req.Path
|
||||
fstFile, err := fileOp.OpenFile(filePath)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
c.File(filePath)
|
||||
info, err := fstFile.Stat()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if info.IsDir() {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrFileDownloadDir, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.Writer.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", req.Name))
|
||||
c.Writer.Header().Set("Content-Type", "application/octet-stream")
|
||||
c.Writer.Header().Set("Content-Length", strconv.FormatInt(info.Size(), 10))
|
||||
c.Writer.Header().Set("Accept-Ranges", "bytes")
|
||||
|
||||
if c.Request.Header.Get("Range") != "" {
|
||||
rangeHeader := c.Request.Header.Get("Range")
|
||||
rangeArr := strings.Split(rangeHeader, "=")[1]
|
||||
rangeParts := strings.Split(rangeArr, "-")
|
||||
|
||||
startPos, _ := strconv.ParseInt(rangeParts[0], 10, 64)
|
||||
|
||||
var endPos int64
|
||||
if rangeParts[1] == "" {
|
||||
endPos = info.Size() - 1
|
||||
} else {
|
||||
endPos, _ = strconv.ParseInt(rangeParts[1], 10, 64)
|
||||
}
|
||||
|
||||
c.Writer.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", startPos, endPos, info.Size()))
|
||||
c.Writer.WriteHeader(http.StatusPartialContent)
|
||||
|
||||
buffer := make([]byte, 1024*1024)
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
file.Seek(startPos, 0)
|
||||
reader := io.LimitReader(file, endPos-startPos+1)
|
||||
_, err = io.CopyBuffer(c.Writer, reader, buffer)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
c.File(filePath)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
@@ -575,6 +652,7 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int)
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /files/chunkupload [post]
|
||||
func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
|
||||
var err error
|
||||
fileForm, err := c.FormFile("chunk")
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
@@ -585,19 +663,16 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
chunkIndex, err := strconv.Atoi(c.PostForm("chunkIndex"))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
chunkCount, err := strconv.Atoi(c.PostForm("chunkCount"))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
tmpDir := path.Join(global.CONF.System.TmpDir, "upload")
|
||||
if !fileOp.Stat(tmpDir) {
|
||||
@@ -606,37 +681,45 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
filename := c.PostForm("filename")
|
||||
fileDir := filepath.Join(tmpDir, filename)
|
||||
|
||||
_ = os.MkdirAll(fileDir, 0755)
|
||||
filePath := filepath.Join(fileDir, filename)
|
||||
|
||||
emptyFile, err := os.Create(filePath)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
_ = os.Remove(fileDir)
|
||||
}
|
||||
}()
|
||||
var (
|
||||
emptyFile *os.File
|
||||
chunkData []byte
|
||||
)
|
||||
|
||||
emptyFile, err = os.Create(filePath)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
defer emptyFile.Close()
|
||||
|
||||
chunkData, err := io.ReadAll(uploadFile)
|
||||
chunkData, err = io.ReadAll(uploadFile)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrFileUpload, err)
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, buserr.WithMap(constant.ErrFileUpload, map[string]interface{}{"name": filename, "detail": err.Error()}, err))
|
||||
return
|
||||
}
|
||||
|
||||
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", filename, chunkIndex))
|
||||
err = os.WriteFile(chunkPath, chunkData, 0644)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrFileUpload, err)
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, buserr.WithMap(constant.ErrFileUpload, map[string]interface{}{"name": filename, "detail": err.Error()}, err))
|
||||
return
|
||||
}
|
||||
|
||||
if chunkIndex+1 == chunkCount {
|
||||
err = mergeChunks(filename, fileDir, c.PostForm("path"), chunkCount)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrFileUpload, err)
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, buserr.WithMap(constant.ErrFileUpload, map[string]interface{}{"name": filename, "detail": err.Error()}, err))
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, true)
|
||||
|
||||
@@ -149,7 +149,7 @@ func (b *BaseApi) BatchOperateRule(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := firewallService.BacthOperateRule(req); err != nil {
|
||||
if err := firewallService.BatchOperateRule(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func (b *BaseApi) CreateGroup(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /groups/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"groups","output_colume":"name","output_value":"name"},{"input_colume":"id","input_value":"id","isList":false,"db":"groups","output_colume":"type","output_value":"type"}],"formatZH":"删除组 [type][name]","formatEN":"delete group [type][name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"groups","output_column":"name","output_value":"name"},{"input_column":"id","input_value":"id","isList":false,"db":"groups","output_column":"type","output_value":"type"}],"formatZH":"删除组 [type][name]","formatEN":"delete group [type][name]"}
|
||||
func (b *BaseApi) DeleteGroup(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -92,7 +92,7 @@ func (b *BaseApi) UpdateGroup(c *gin.Context) {
|
||||
// @Description 查询系统组
|
||||
// @Accept json
|
||||
// @Param request body dto.GroupSearch true "request"
|
||||
// @Success 200 {anrry} dto.GroupInfo
|
||||
// @Success 200 {array} dto.GroupInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /groups/search [post]
|
||||
func (b *BaseApi) ListGroup(c *gin.Context) {
|
||||
|
||||
@@ -183,7 +183,7 @@ func (b *BaseApi) GetHostInfo(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"hosts","output_colume":"addr","output_value":"addrs"}],"formatZH":"删除主机 [addrs]","formatEN":"delete host [addrs]"}
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"hosts","output_column":"addr","output_value":"addrs"}],"formatZH":"删除主机 [addrs]","formatEN":"delete host [addrs]"}
|
||||
func (b *BaseApi) DeleteHost(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -269,7 +269,7 @@ func (b *BaseApi) UpdateHost(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/update/group [post]
|
||||
// @x-panel-log {"bodyKeys":["id","group"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"hosts","output_colume":"addr","output_value":"addr"}],"formatZH":"切换主机[addr]分组 => [group]","formatEN":"change host [addr] group => [group]"}
|
||||
// @x-panel-log {"bodyKeys":["id","group"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"hosts","output_column":"addr","output_value":"addr"}],"formatZH":"切换主机[addr]分组 => [group]","formatEN":"change host [addr] group => [group]"}
|
||||
func (b *BaseApi) UpdateHostGroup(c *gin.Context) {
|
||||
var req dto.ChangeHostGroup
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
||||
@@ -93,7 +93,7 @@ func (b *BaseApi) ImageBuild(c *gin.Context) {
|
||||
// @Success 200 {string} log
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/image/pull [post]
|
||||
// @x-panel-log {"bodyKeys":["repoID","imageName"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"repoID","isList":false,"db":"image_repos","output_colume":"name","output_value":"reponame"}],"formatZH":"镜像拉取 [reponame][imageName]","formatEN":"image pull [reponame][imageName]"}
|
||||
// @x-panel-log {"bodyKeys":["repoID","imageName"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"repoID","isList":false,"db":"image_repos","output_column":"name","output_value":"reponame"}],"formatZH":"镜像拉取 [reponame][imageName]","formatEN":"image pull [reponame][imageName]"}
|
||||
func (b *BaseApi) ImagePull(c *gin.Context) {
|
||||
var req dto.ImagePull
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -122,7 +122,7 @@ func (b *BaseApi) ImagePull(c *gin.Context) {
|
||||
// @Success 200 {string} log
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/image/push [post]
|
||||
// @x-panel-log {"bodyKeys":["repoID","tagName","name"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"repoID","isList":false,"db":"image_repos","output_colume":"name","output_value":"reponame"}],"formatZH":"[tagName] 推送到 [reponame][name]","formatEN":"push [tagName] to [reponame][name]"}
|
||||
// @x-panel-log {"bodyKeys":["repoID","tagName","name"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"repoID","isList":false,"db":"image_repos","output_column":"name","output_value":"reponame"}],"formatZH":"[tagName] 推送到 [reponame][name]","formatEN":"push [tagName] to [reponame][name]"}
|
||||
func (b *BaseApi) ImagePush(c *gin.Context) {
|
||||
var req dto.ImagePush
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -207,7 +207,7 @@ func (b *BaseApi) ImageSave(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/image/tag [post]
|
||||
// @x-panel-log {"bodyKeys":["repoID","targetName"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"repoID","isList":false,"db":"image_repos","output_colume":"name","output_value":"reponame"}],"formatZH":"tag 镜像 [reponame][targetName]","formatEN":"tag image [reponame][targetName]"}
|
||||
// @x-panel-log {"bodyKeys":["repoID","targetName"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"repoID","isList":false,"db":"image_repos","output_column":"name","output_value":"reponame"}],"formatZH":"tag 镜像 [reponame][targetName]","formatEN":"tag image [reponame][targetName]"}
|
||||
func (b *BaseApi) ImageTag(c *gin.Context) {
|
||||
var req dto.ImageTag
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
||||
@@ -119,7 +119,7 @@ func (b *BaseApi) CreateRepo(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/repo/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"image_repos","output_colume":"name","output_value":"names"}],"formatZH":"删除镜像仓库 [names]","formatEN":"delete image repo [names]"}
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"image_repos","output_column":"name","output_value":"names"}],"formatZH":"删除镜像仓库 [names]","formatEN":"delete image repo [names]"}
|
||||
func (b *BaseApi) DeleteRepo(c *gin.Context) {
|
||||
var req dto.ImageRepoDelete
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -147,7 +147,7 @@ func (b *BaseApi) DeleteRepo(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/repo/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"image_repos","output_colume":"name","output_value":"name"}],"formatZH":"更新镜像仓库 [name]","formatEN":"update image repo information [name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"image_repos","output_column":"name","output_value":"name"}],"formatZH":"更新镜像仓库 [name]","formatEN":"update image repo information [name]"}
|
||||
func (b *BaseApi) UpdateRepo(c *gin.Context) {
|
||||
var req dto.ImageRepoUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
||||
@@ -53,7 +53,7 @@ func (b *BaseApi) GetNginxConfigByScope(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /openResty/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteId","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新 nginx 配置 [domain]","formatEN":"Update nginx conf [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新 nginx 配置 [domain]","formatEN":"Update nginx conf [domain]"}
|
||||
func (b *BaseApi) UpdateNginxConfigByScope(c *gin.Context) {
|
||||
var req request.NginxConfigUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
||||
@@ -2,14 +2,12 @@ package v1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/mfa"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -189,26 +187,40 @@ func (b *BaseApi) HandlePasswordExpired(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Sync system time
|
||||
// @Description 系统时间同步
|
||||
// @Success 200 {string} ntime
|
||||
// @Summary Load time zone options
|
||||
// @Description 加载系统可用时区
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/time/sync [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"系统时间同步","formatEN":"sync system time"}
|
||||
func (b *BaseApi) SyncTime(c *gin.Context) {
|
||||
ntime, err := ntp.GetRemoteTime()
|
||||
// @Router /settings/time/option [get]
|
||||
func (b *BaseApi) LoadTimeZone(c *gin.Context) {
|
||||
zones, err := settingService.LoadTimeZone()
|
||||
if err != nil {
|
||||
helper.SuccessWithData(c, time.Now().Format("2006-01-02 15:04:05 MST -0700"))
|
||||
return
|
||||
}
|
||||
|
||||
ts := ntime.Format("2006-01-02 15:04:05")
|
||||
if err := ntp.UpdateSystemDate(ts); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, zones)
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, ntime.Format("2006-01-02 15:04:05 MST -0700"))
|
||||
// @Tags System Setting
|
||||
// @Summary Sync system time
|
||||
// @Description 系统时间同步
|
||||
// @Accept json
|
||||
// @Param request body dto.SyncTime true "request"
|
||||
// @Success 200 {string} ntime
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/time/sync [post]
|
||||
// @x-panel-log {"bodyKeys":["ntpSite"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"系统时间同步[ntpSite]","formatEN":"sync system time [ntpSite]"}
|
||||
func (b *BaseApi) SyncTime(c *gin.Context) {
|
||||
var req dto.SyncTime
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := settingService.SyncTime(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
|
||||
@@ -68,7 +68,7 @@ func (b *BaseApi) ImportSnapshot(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/snapshot/description/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"snapshots","output_colume":"name","output_value":"name"}],"formatZH":"快照 [name] 描述信息修改 [description]","formatEN":"The description of the snapshot [name] is modified => [description]"}
|
||||
// @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"snapshots","output_column":"name","output_value":"name"}],"formatZH":"快照 [name] 描述信息修改 [description]","formatEN":"The description of the snapshot [name] is modified => [description]"}
|
||||
func (b *BaseApi) UpdateSnapDescription(c *gin.Context) {
|
||||
var req dto.UpdateDescription
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -119,7 +119,7 @@ func (b *BaseApi) SearchSnapshot(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/snapshot/recover [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"snapshots","output_colume":"name","output_value":"name"}],"formatZH":"从系统快照 [name] 恢复","formatEN":"Recover from system backup [name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"snapshots","output_column":"name","output_value":"name"}],"formatZH":"从系统快照 [name] 恢复","formatEN":"Recover from system backup [name]"}
|
||||
func (b *BaseApi) RecoverSnapshot(c *gin.Context) {
|
||||
var req dto.SnapshotRecover
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -146,7 +146,7 @@ func (b *BaseApi) RecoverSnapshot(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/snapshot/rollback [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"snapshots","output_colume":"name","output_value":"name"}],"formatZH":"从系统快照 [name] 回滚","formatEN":"Rollback from system backup [name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"snapshots","output_column":"name","output_value":"name"}],"formatZH":"从系统快照 [name] 回滚","formatEN":"Rollback from system backup [name]"}
|
||||
func (b *BaseApi) RollbackSnapshot(c *gin.Context) {
|
||||
var req dto.SnapshotRecover
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -173,7 +173,7 @@ func (b *BaseApi) RollbackSnapshot(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/snapshot/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"snapshots","output_colume":"name","output_value":"name"}],"formatZH":"删除系统快照 [name]","formatEN":"Delete system backup [name]"}
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"snapshots","output_column":"name","output_value":"name"}],"formatZH":"删除系统快照 [name]","formatEN":"Delete system backup [name]"}
|
||||
func (b *BaseApi) DeleteSnapshot(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Load host ssh setting info
|
||||
// @Description 加载 SSH 配置信息
|
||||
// @Success 200 {object} dto.SSHInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/ssh/search [post]
|
||||
func (b *BaseApi) GetSSHInfo(c *gin.Context) {
|
||||
info, err := sshService.GetSSHInfo()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, info)
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Operate ssh
|
||||
// @Description 修改 SSH 服务状态
|
||||
// @Accept json
|
||||
// @Param request body dto.Operate true "request"
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/ssh/operate [post]
|
||||
// @x-panel-log {"bodyKeys":["operation"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"[operation] SSH ","formatEN":"[operation] SSH"}
|
||||
func (b *BaseApi) OperateSSH(c *gin.Context) {
|
||||
var req dto.Operate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := sshService.OperateSSH(req.Operation); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Update host ssh setting
|
||||
// @Description 更新 SSH 配置
|
||||
// @Accept json
|
||||
// @Param request body dto.SettingUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/ssh/update [post]
|
||||
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"修改 SSH 配置 [key] => [value]","formatEN":"update SSH setting [key] => [value]"}
|
||||
func (b *BaseApi) UpdateSSH(c *gin.Context) {
|
||||
var req dto.SettingUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := sshService.Update(req.Key, req.Value); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Update host ssh setting by file
|
||||
// @Description 上传文件更新 SSH 配置
|
||||
// @Accept json
|
||||
// @Param request body dto.SSHConf true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/conffile/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"修改 SSH 配置文件","formatEN":"update SSH conf"}
|
||||
func (b *BaseApi) UpdateSSHByfile(c *gin.Context) {
|
||||
var req dto.SSHConf
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := sshService.UpdateByFile(req.File); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Generate host ssh secret
|
||||
// @Description 生成 ssh 密钥
|
||||
// @Accept json
|
||||
// @Param request body dto.GenerateSSH true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/ssh/generate [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"生成 SSH 密钥 ","formatEN":"generate SSH secret"}
|
||||
func (b *BaseApi) GenerateSSH(c *gin.Context) {
|
||||
var req dto.GenerateSSH
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := sshService.GenerateSSH(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Load host ssh secret
|
||||
// @Description 获取 ssh 密钥
|
||||
// @Accept json
|
||||
// @Param request body dto.GenerateLoad true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/ssh/secret [post]
|
||||
func (b *BaseApi) LoadSSHSecret(c *gin.Context) {
|
||||
var req dto.GenerateLoad
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := sshService.LoadSSHSecret(req.EncryptionMode)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Load host ssh logs
|
||||
// @Description 获取 ssh 登录日志
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchSSHLog true "request"
|
||||
// @Success 200 {object} dto.SSHLog
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/ssh/logs [post]
|
||||
func (b *BaseApi) LoadSSHLogs(c *gin.Context) {
|
||||
var req dto.SearchSSHLog
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := sshService.LoadLog(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
@@ -112,11 +112,12 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
||||
if len(redisConf.Requirepass) != 0 {
|
||||
commands = fmt.Sprintf("redis-cli -a %s --no-auth-warning", redisConf.Requirepass)
|
||||
}
|
||||
pidMap := loadMapFromDockerTop(redisConf.ContainerName)
|
||||
slave, err := terminal.NewCommand(fmt.Sprintf("docker exec -it %s %s", redisConf.ContainerName, commands))
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(redisConf.ContainerName, commands)
|
||||
defer killBash(redisConf.ContainerName, commands, pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave)
|
||||
@@ -175,11 +176,12 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
|
||||
if len(user) != 0 {
|
||||
commands = fmt.Sprintf("docker exec -it -u %s %s %s", user, containerID, command)
|
||||
}
|
||||
pidMap := loadMapFromDockerTop(containerID)
|
||||
slave, err := terminal.NewCommand(commands)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(containerID, command)
|
||||
defer killBash(containerID, command, pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave)
|
||||
@@ -219,13 +221,40 @@ func wshandleError(ws *websocket.Conn, err error) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func killBash(containerID, comm string) {
|
||||
sudo := ""
|
||||
if cmd.HasNoPasswordSudo() {
|
||||
sudo = "sudo"
|
||||
func loadMapFromDockerTop(containerID string) map[string]string {
|
||||
pidMap := make(map[string]string)
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
|
||||
stdout, err := cmd.Execf("%s docker top %s -eo pid,command ", sudo, containerID)
|
||||
if err != nil {
|
||||
return pidMap
|
||||
}
|
||||
lines := strings.Split(stdout, "\n")
|
||||
for _, line := range lines {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
pidMap[parts[0]] = parts[1]
|
||||
}
|
||||
return pidMap
|
||||
}
|
||||
|
||||
func killBash(containerID, comm string, pidMap map[string]string) {
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
newPidMap := loadMapFromDockerTop(containerID)
|
||||
for pid, command := range newPidMap {
|
||||
isOld := false
|
||||
for pid2 := range pidMap {
|
||||
if pid == pid2 {
|
||||
isOld = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isOld && command == comm {
|
||||
_, _ = cmd.Execf("%s kill -9 %s", sudo, pid)
|
||||
}
|
||||
}
|
||||
command := exec.Command("sh", "-c", fmt.Sprintf("%s kill -9 $(docker top %s -eo pid,command | grep '%s' | awk '{print $1}')", sudo, containerID, comm))
|
||||
_, _ = command.CombinedOutput()
|
||||
}
|
||||
|
||||
var upGrader = websocket.Upgrader{
|
||||
|
||||
@@ -95,7 +95,7 @@ func (b *BaseApi) CreateWebsite(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/operate [post]
|
||||
// @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"[operate] 网站 [domain]","formatEN":"[operate] website [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"[operate] 网站 [domain]","formatEN":"[operate] website [domain]"}
|
||||
func (b *BaseApi) OpWebsite(c *gin.Context) {
|
||||
var req request.WebsiteOp
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -118,7 +118,7 @@ func (b *BaseApi) OpWebsite(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"删除网站 [domain]","formatEN":"Delete website [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"删除网站 [domain]","formatEN":"Delete website [domain]"}
|
||||
func (b *BaseApi) DeleteWebsite(c *gin.Context) {
|
||||
var req request.WebsiteDelete
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -232,7 +232,7 @@ func (b *BaseApi) GetWebDomains(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/domains/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_domains","output_colume":"domain","output_value":"domain"}],"formatZH":"删除域名 [domain]","formatEN":"Delete domain [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_domains","output_column":"domain","output_value":"domain"}],"formatZH":"删除域名 [domain]","formatEN":"Delete domain [domain]"}
|
||||
func (b *BaseApi) DeleteWebDomain(c *gin.Context) {
|
||||
var req request.WebsiteDomainDelete
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -300,7 +300,7 @@ func (b *BaseApi) GetNginxConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/config/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteId","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"nginx 配置修改 [domain]","formatEN":"Nginx conf update [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"nginx 配置修改 [domain]","formatEN":"Nginx conf update [domain]"}
|
||||
func (b *BaseApi) UpdateNginxConfig(c *gin.Context) {
|
||||
var req request.NginxConfigUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -344,7 +344,7 @@ func (b *BaseApi) GetHTTPSConfig(c *gin.Context) {
|
||||
// @Success 200 {object} response.WebsiteHTTPS
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/:id/https [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteId","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] https 配置","formatEN":"Update website https [domain] conf"}
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] https 配置","formatEN":"Update website https [domain] conf"}
|
||||
func (b *BaseApi) UpdateHTTPSConfig(c *gin.Context) {
|
||||
var req request.WebsiteHTTPSOp
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -414,7 +414,7 @@ func (b *BaseApi) GetWebsiteWafConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/waf/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteId","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"WAF 配置修改 [domain]","formatEN":"WAF conf update [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"WAF 配置修改 [domain]","formatEN":"WAF conf update [domain]"}
|
||||
func (b *BaseApi) UpdateWebsiteWafConfig(c *gin.Context) {
|
||||
var req request.WebsiteWafUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -436,7 +436,7 @@ func (b *BaseApi) UpdateWebsiteWafConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/nginx/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"[domain] Nginx 配置修改","formatEN":"[domain] Nginx conf update"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"[domain] Nginx 配置修改","formatEN":"[domain] Nginx conf update"}
|
||||
func (b *BaseApi) UpdateWebsiteNginxConfig(c *gin.Context) {
|
||||
var req request.WebsiteNginxUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -458,7 +458,7 @@ func (b *BaseApi) UpdateWebsiteNginxConfig(c *gin.Context) {
|
||||
// @Success 200 {object} response.WebsiteLog
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/log [post]
|
||||
// @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"[domain][operate] 日志","formatEN":"[domain][operate] logs"}
|
||||
// @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"[domain][operate] 日志","formatEN":"[domain][operate] logs"}
|
||||
func (b *BaseApi) OpWebsiteLog(c *gin.Context) {
|
||||
var req request.WebsiteLogReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -481,7 +481,7 @@ func (b *BaseApi) OpWebsiteLog(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/default/server [post]
|
||||
// @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"修改默认 server => [domain]","formatEN":"Change default server => [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"修改默认 server => [domain]","formatEN":"Change default server => [domain]"}
|
||||
func (b *BaseApi) ChangeDefaultServer(c *gin.Context) {
|
||||
var req request.WebsiteDefaultUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -525,7 +525,7 @@ func (b *BaseApi) GetWebsitePHPConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/php/config [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"[domain] PHP 配置修改","formatEN":"[domain] PHP conf update"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"[domain] PHP 配置修改","formatEN":"[domain] PHP conf update"}
|
||||
func (b *BaseApi) UpdateWebsitePHPConfig(c *gin.Context) {
|
||||
var req request.WebsitePHPConfigUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -547,7 +547,7 @@ func (b *BaseApi) UpdateWebsitePHPConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/php/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteId","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"php 配置修改 [domain]","formatEN":"Nginx conf update [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"php 配置修改 [domain]","formatEN":"Nginx conf update [domain]"}
|
||||
func (b *BaseApi) UpdatePHPFile(c *gin.Context) {
|
||||
var req request.WebsitePHPFileUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -591,7 +591,7 @@ func (b *BaseApi) GetRewriteConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/rewrite/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteID","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"伪静态配置修改 [domain]","formatEN":"Nginx conf rewrite update [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteID","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"伪静态配置修改 [domain]","formatEN":"Nginx conf rewrite update [domain]"}
|
||||
func (b *BaseApi) UpdateRewriteConfig(c *gin.Context) {
|
||||
var req request.NginxRewriteUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -613,7 +613,7 @@ func (b *BaseApi) UpdateRewriteConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/dir/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] 目录","formatEN":"Update domain [domain] dir"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] 目录","formatEN":"Update domain [domain] dir"}
|
||||
func (b *BaseApi) UpdateSiteDir(c *gin.Context) {
|
||||
var req request.WebsiteUpdateDir
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -635,7 +635,7 @@ func (b *BaseApi) UpdateSiteDir(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/dir/permission [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] 目录权限","formatEN":"Update domain [domain] dir permission"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] 目录权限","formatEN":"Update domain [domain] dir permission"}
|
||||
func (b *BaseApi) UpdateSiteDirPermission(c *gin.Context) {
|
||||
var req request.WebsiteUpdateDirPermission
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -679,7 +679,7 @@ func (b *BaseApi) GetProxyConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/proxies/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"修改网站 [domain] 反向代理配置 ","formatEN":"Update domain [domain] proxy config"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"修改网站 [domain] 反向代理配置 ","formatEN":"Update domain [domain] proxy config"}
|
||||
func (b *BaseApi) UpdateProxyConfig(c *gin.Context) {
|
||||
var req request.WebsiteProxyConfig
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -702,7 +702,7 @@ func (b *BaseApi) UpdateProxyConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/proxy/file [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteID","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新反向代理文件 [domain]","formatEN":"Nginx conf proxy file update [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteID","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新反向代理文件 [domain]","formatEN":"Nginx conf proxy file update [domain]"}
|
||||
func (b *BaseApi) UpdateProxyConfigFile(c *gin.Context) {
|
||||
var req request.NginxProxyUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -758,3 +758,46 @@ func (b *BaseApi) UpdateAuthConfig(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Get AntiLeech conf
|
||||
// @Description 获取防盗链配置
|
||||
// @Accept json
|
||||
// @Param request body request.NginxCommonReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/leech [post]
|
||||
func (b *BaseApi) GetAntiLeech(c *gin.Context) {
|
||||
var req request.NginxCommonReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
res, err := websiteService.GetAntiLeech(req.WebsiteID)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update AntiLeech
|
||||
// @Description 更新防盗链配置
|
||||
// @Accept json
|
||||
// @Param request body request.NginxAntiLeechUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/leech/update [post]
|
||||
func (b *BaseApi) UpdateAntiLeech(c *gin.Context) {
|
||||
var req request.NginxAntiLeechUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateAntiLeech(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func (b *BaseApi) CreateWebsiteAcmeAccount(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/acme/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_acme_accounts","output_colume":"email","output_value":"email"}],"formatZH":"删除网站 acme [email]","formatEN":"Delete website acme [email]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_acme_accounts","output_column":"email","output_value":"email"}],"formatZH":"删除网站 acme [email]","formatEN":"Delete website acme [email]"}
|
||||
func (b *BaseApi) DeleteWebsiteAcmeAccount(c *gin.Context) {
|
||||
var req request.WebsiteResourceReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
||||
@@ -85,7 +85,7 @@ func (b *BaseApi) UpdateWebsiteDnsAccount(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/dns/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_dns_accounts","output_colume":"name","output_value":"name"}],"formatZH":"删除网站 dns [name]","formatEN":"Delete website dns [name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_dns_accounts","output_column":"name","output_value":"name"}],"formatZH":"删除网站 dns [name]","formatEN":"Delete website dns [name]"}
|
||||
func (b *BaseApi) DeleteWebsiteDnsAccount(c *gin.Context) {
|
||||
var req request.WebsiteResourceReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
||||
@@ -75,7 +75,7 @@ func (b *BaseApi) CreateWebsiteSSL(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/ssl/renew [post]
|
||||
// @x-panel-log {"bodyKeys":["SSLId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"SSLId","isList":false,"db":"website_ssls","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"重置 ssl [domain]","formatEN":"Renew ssl [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["SSLId"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"SSLId","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"重置 ssl [domain]","formatEN":"Renew ssl [domain]"}
|
||||
func (b *BaseApi) RenewWebsiteSSL(c *gin.Context) {
|
||||
var req request.WebsiteSSLRenew
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -119,7 +119,7 @@ func (b *BaseApi) GetDNSResolve(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/ssl/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_ssls","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"删除 ssl [domain]","formatEN":"Delete ssl [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"删除 ssl [domain]","formatEN":"Delete ssl [domain]"}
|
||||
func (b *BaseApi) DeleteWebsiteSSL(c *gin.Context) {
|
||||
var req request.WebsiteResourceReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -185,7 +185,7 @@ func (b *BaseApi) GetWebsiteSSLById(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/ssl/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_ssls","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新证书设置 [domain]","formatEN":"Update ssl config [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新证书设置 [domain]","formatEN":"Update ssl config [domain]"}
|
||||
func (b *BaseApi) UpdateWebsiteSSL(c *gin.Context) {
|
||||
var req request.WebsiteSSLUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
||||
+42
-10
@@ -1,7 +1,7 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
)
|
||||
|
||||
type AppDatabase struct {
|
||||
@@ -32,19 +32,47 @@ type AppVersion struct {
|
||||
}
|
||||
|
||||
type AppList struct {
|
||||
Version string `json:"version"`
|
||||
Tags []Tag `json:"tags"`
|
||||
Items []AppDefine `json:"items"`
|
||||
Valid bool `json:"valid"`
|
||||
Violations []string `json:"violations"`
|
||||
LastModified int `json:"lastModified"`
|
||||
|
||||
Apps []AppDefine `json:"apps"`
|
||||
Extra ExtraProperties `json:"additionalProperties"`
|
||||
}
|
||||
|
||||
type AppDefine struct {
|
||||
Key string `json:"key"`
|
||||
Icon string `json:"icon"`
|
||||
Name string `json:"name"`
|
||||
ReadMe string `json:"readMe"`
|
||||
LastModified int `json:"lastModified"`
|
||||
|
||||
AppProperty AppProperty `json:"additionalProperties"`
|
||||
Versions []AppConfigVersion `json:"versions"`
|
||||
}
|
||||
|
||||
type LocalAppAppDefine struct {
|
||||
AppProperty model.App `json:"additionalProperties" yaml:"additionalProperties"`
|
||||
}
|
||||
|
||||
type LocalAppParam struct {
|
||||
AppParams LocalAppInstallDefine `json:"additionalProperties" yaml:"additionalProperties"`
|
||||
}
|
||||
|
||||
type LocalAppInstallDefine struct {
|
||||
FormFields interface{} `json:"formFields" yaml:"formFields"`
|
||||
}
|
||||
|
||||
type ExtraProperties struct {
|
||||
Tags []Tag `json:"tags"`
|
||||
}
|
||||
|
||||
type AppProperty struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Tags []string `json:"tags"`
|
||||
Versions []string `json:"versions"`
|
||||
ShortDescZh string `json:"shortDescZh"`
|
||||
ShortDescEn string `json:"shortDescEn"`
|
||||
Type string `json:"type"`
|
||||
Key string `json:"key"`
|
||||
Required []string `json:"Required"`
|
||||
CrossVersionUpdate bool `json:"crossVersionUpdate"`
|
||||
Limit int `json:"limit"`
|
||||
@@ -54,9 +82,12 @@ type AppDefine struct {
|
||||
Document string `json:"document"`
|
||||
}
|
||||
|
||||
func (define AppDefine) GetRequired() string {
|
||||
by, _ := json.Marshal(define.Required)
|
||||
return string(by)
|
||||
type AppConfigVersion struct {
|
||||
Name string `json:"name"`
|
||||
LastModified int `json:"lastModified"`
|
||||
DownloadUrl string `json:"downloadUrl"`
|
||||
DownloadCallBackUrl string `json:"downloadCallBackUrl"`
|
||||
AppForm interface{} `json:"additionalProperties"`
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
@@ -79,6 +110,7 @@ type AppFormFields struct {
|
||||
Edit bool `json:"edit"`
|
||||
Rule string `json:"rule"`
|
||||
Multiple bool `json:"multiple"`
|
||||
Child interface{} `json:"child"`
|
||||
Values []AppFormValue `json:"values"`
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,10 @@ type OperateByID struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
type Operate struct {
|
||||
Operation string `json:"operation" validate:"required"`
|
||||
}
|
||||
|
||||
type BatchDeleteReq struct {
|
||||
Ids []uint `json:"ids" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -80,6 +80,16 @@ type ContainerOperation struct {
|
||||
NewName string `json:"newName"`
|
||||
}
|
||||
|
||||
type ContainerPrune struct {
|
||||
PruneType string `json:"pruneType" validate:"required,oneof=container image volume network"`
|
||||
WithTagAll bool `josn:"withTagAll"`
|
||||
}
|
||||
|
||||
type ContainerPruneReport struct {
|
||||
DeletedNumber int `json:"deletedNumber"`
|
||||
SpaceReclaimed int `json:"spaceReclaimed"`
|
||||
}
|
||||
|
||||
type Network struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
@@ -91,7 +101,7 @@ type Network struct {
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Attachable bool `json:"attachable"`
|
||||
}
|
||||
type NetworkCreat struct {
|
||||
type NetworkCreate struct {
|
||||
Name string `json:"name"`
|
||||
Driver string `json:"driver"`
|
||||
Options []string `json:"options"`
|
||||
@@ -108,7 +118,7 @@ type Volume struct {
|
||||
Mountpoint string `json:"mountpoint"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
type VolumeCreat struct {
|
||||
type VolumeCreate struct {
|
||||
Name string `json:"name"`
|
||||
Driver string `json:"driver"`
|
||||
Options []string `json:"options"`
|
||||
|
||||
@@ -6,10 +6,11 @@ type CronjobCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
SpecType string `json:"specType" validate:"required"`
|
||||
Week int `json:"week" validate:"number,max=7,min=1"`
|
||||
Week int `json:"week" validate:"number,max=6,min=0"`
|
||||
Day int `json:"day" validate:"number"`
|
||||
Hour int `json:"hour" validate:"number"`
|
||||
Minute int `json:"minute" validate:"number"`
|
||||
Second int `json:"second" validate:"number"`
|
||||
|
||||
Script string `json:"script"`
|
||||
Website string `json:"website"`
|
||||
@@ -26,10 +27,11 @@ type CronjobUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
SpecType string `json:"specType" validate:"required"`
|
||||
Week int `json:"week" validate:"number,max=7,min=1"`
|
||||
Week int `json:"week" validate:"number,max=6,min=0"`
|
||||
Day int `json:"day" validate:"number"`
|
||||
Hour int `json:"hour" validate:"number"`
|
||||
Minute int `json:"minute" validate:"number"`
|
||||
Second int `json:"second" validate:"number"`
|
||||
|
||||
Script string `json:"script"`
|
||||
Website string `json:"website"`
|
||||
@@ -71,6 +73,7 @@ type CronjobInfo struct {
|
||||
Day int `json:"day"`
|
||||
Hour int `json:"hour"`
|
||||
Minute int `json:"minute"`
|
||||
Second int `json:"second"`
|
||||
|
||||
Script string `json:"script"`
|
||||
Website string `json:"website"`
|
||||
@@ -83,7 +86,7 @@ type CronjobInfo struct {
|
||||
TargetDirID int `json:"targetDirID"`
|
||||
RetainCopies int `json:"retainCopies"`
|
||||
|
||||
LastRecrodTime string `json:"lastRecrodTime"`
|
||||
LastRecordTime string `json:"lastRecordTime"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
|
||||
@@ -13,10 +13,16 @@ type DaemonJsonConf struct {
|
||||
LiveRestore bool `json:"liveRestore"`
|
||||
IPTables bool `json:"iptables"`
|
||||
CgroupDriver string `json:"cgroupDriver"`
|
||||
|
||||
LogMaxSize string `json:"logMaxSize"`
|
||||
LogMaxFile string `json:"logMaxFile"`
|
||||
}
|
||||
|
||||
type LogOption struct {
|
||||
LogMaxSize string `json:"logMaxSize"`
|
||||
LogMaxFile string `json:"logMaxFile"`
|
||||
}
|
||||
|
||||
type DockerOperation struct {
|
||||
StopSocket bool `json:"stopSocket"`
|
||||
StopService bool `json:"stopService"`
|
||||
Operation string `json:"operation" validate:"required,oneof=start restart stop"`
|
||||
Operation string `json:"operation" validate:"required,oneof=start restart stop"`
|
||||
}
|
||||
|
||||
@@ -18,6 +18,18 @@ type AppInstallCreate struct {
|
||||
Params map[string]interface{} `json:"params"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Services map[string]string `json:"services"`
|
||||
AppContainerConfig
|
||||
}
|
||||
|
||||
type AppContainerConfig struct {
|
||||
Advanced bool `json:"advanced"`
|
||||
CpuQuota float64 `json:"cpuQuota"`
|
||||
MemoryLimit float64 `json:"memoryLimit"`
|
||||
MemoryUnit string `json:"memoryUnit"`
|
||||
ContainerName string `json:"containerName"`
|
||||
AllowPort bool `json:"allowPort"`
|
||||
EditCompose bool `json:"editCompose"`
|
||||
DockerCompose string `json:"dockerCompose"`
|
||||
}
|
||||
|
||||
type AppInstalledSearch struct {
|
||||
@@ -51,6 +63,7 @@ type AppInstalledOperate struct {
|
||||
type AppInstalledUpdate struct {
|
||||
InstallId uint `json:"installId" validate:"required"`
|
||||
Params map[string]interface{} `json:"params" validate:"required"`
|
||||
AppContainerConfig
|
||||
}
|
||||
|
||||
type PortUpdate struct {
|
||||
|
||||
@@ -82,6 +82,11 @@ type FileDownload struct {
|
||||
Compress bool `json:"compress" validate:"required"`
|
||||
}
|
||||
|
||||
type FileChunkDownload struct {
|
||||
Path string `json:"path" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
|
||||
type DirSizeReq struct {
|
||||
Path string `json:"path" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -48,3 +48,21 @@ type NginxAuthUpdate struct {
|
||||
type NginxAuthReq struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
}
|
||||
|
||||
type NginxCommonReq struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
}
|
||||
|
||||
type NginxAntiLeechUpdate struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
Extends string `json:"extends" validate:"required"`
|
||||
Return string `json:"return" validate:"required"`
|
||||
Enable bool `json:"enable" validate:"required"`
|
||||
ServerNames []string `json:"serverNames"`
|
||||
Cache bool `json:"cache"`
|
||||
CacheTime int `json:"cacheTime"`
|
||||
CacheUint string `json:"cacheUint"`
|
||||
NoneRef bool `json:"noneRef"`
|
||||
LogEnable bool `json:"logEnable"`
|
||||
Blocked bool `json:"blocked"`
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ type WebsiteCreate struct {
|
||||
OtherDomains string `json:"otherDomains"`
|
||||
Proxy string `json:"proxy"`
|
||||
WebsiteGroupID uint `json:"webSiteGroupID" validate:"required"`
|
||||
IPV6 bool `json:"IPV6"`
|
||||
|
||||
AppType string `json:"appType" validate:"oneof=new installed"`
|
||||
AppInstall NewAppInstall `json:"appInstall"`
|
||||
@@ -37,6 +38,8 @@ type NewAppInstall struct {
|
||||
Name string `json:"name"`
|
||||
AppDetailId uint `json:"appDetailID"`
|
||||
Params map[string]interface{} `json:"params"`
|
||||
|
||||
AppContainerConfig
|
||||
}
|
||||
|
||||
type WebsiteInstallCheckReq struct {
|
||||
@@ -49,6 +52,7 @@ type WebsiteUpdate struct {
|
||||
Remark string `json:"remark"`
|
||||
WebsiteGroupID uint `json:"webSiteGroupID" validate:"required"`
|
||||
ExpireDate string `json:"expireDate"`
|
||||
IPV6 bool `json:"IPV6"`
|
||||
}
|
||||
|
||||
type WebsiteDelete struct {
|
||||
@@ -136,8 +140,11 @@ type WebsiteDefaultUpdate struct {
|
||||
}
|
||||
|
||||
type WebsitePHPConfigUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Params map[string]string `json:"params" validate:"required"`
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Params map[string]string `json:"params"`
|
||||
Scope string `json:"scope" validate:"required"`
|
||||
DisableFunctions []string `json:"disableFunctions"`
|
||||
UploadMaxSize string `json:"uploadMaxSize"`
|
||||
}
|
||||
|
||||
type WebsitePHPFileUpdate struct {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
@@ -12,15 +13,15 @@ type AppRes struct {
|
||||
}
|
||||
|
||||
type AppUpdateRes struct {
|
||||
Version string `json:"version"`
|
||||
CanUpdate bool `json:"canUpdate"`
|
||||
DownloadPath string `json:"downloadPath"`
|
||||
CanUpdate bool `json:"canUpdate"`
|
||||
AppStoreLastModified int `json:"appStoreLastModified"`
|
||||
}
|
||||
|
||||
type AppDTO struct {
|
||||
model.App
|
||||
Versions []string `json:"versions"`
|
||||
Tags []model.Tag `json:"tags"`
|
||||
Installed bool `json:"installed"`
|
||||
Versions []string `json:"versions"`
|
||||
Tags []model.Tag `json:"tags"`
|
||||
}
|
||||
|
||||
type TagDTO struct {
|
||||
@@ -54,6 +55,7 @@ type AppInstalledDTO struct {
|
||||
AppName string `json:"appName"`
|
||||
Icon string `json:"icon"`
|
||||
CanUpdate bool `json:"canUpdate"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
type DatabaseConn struct {
|
||||
@@ -81,3 +83,8 @@ type AppParam struct {
|
||||
Required bool `json:"required"`
|
||||
Multiple bool `json:"multiple"`
|
||||
}
|
||||
|
||||
type AppConfig struct {
|
||||
Params []AppParam `json:"params"`
|
||||
request.AppContainerConfig
|
||||
}
|
||||
|
||||
@@ -21,3 +21,16 @@ type NginxAuthRes struct {
|
||||
Enable bool `json:"enable"`
|
||||
Items []dto.NginxAuth `json:"items"`
|
||||
}
|
||||
|
||||
type NginxAntiLeechRes struct {
|
||||
Enable bool `json:"enable"`
|
||||
Extends string `json:"extends"`
|
||||
Return string `json:"return"`
|
||||
ServerNames []string `json:"serverNames"`
|
||||
Cache bool `json:"cache"`
|
||||
CacheTime int `json:"cacheTime"`
|
||||
CacheUint string `json:"cacheUint"`
|
||||
NoneRef bool `json:"noneRef"`
|
||||
LogEnable bool `json:"logEnable"`
|
||||
Blocked bool `json:"blocked"`
|
||||
}
|
||||
|
||||
@@ -45,7 +45,9 @@ type WebsiteLog struct {
|
||||
}
|
||||
|
||||
type PHPConfig struct {
|
||||
Params map[string]string `json:"params"`
|
||||
Params map[string]string `json:"params"`
|
||||
DisableFunctions []string `json:"disableFunctions"`
|
||||
UploadMaxSize string `json:"uploadMaxSize"`
|
||||
}
|
||||
|
||||
type NginxRewriteRes struct {
|
||||
|
||||
@@ -9,6 +9,8 @@ type SettingInfo struct {
|
||||
|
||||
SessionTimeout string `json:"sessionTimeout"`
|
||||
LocalTime string `json:"localTime"`
|
||||
TimeZone string `json:"timeZone"`
|
||||
NtpSite string `json:"ntpSite"`
|
||||
|
||||
Port string `json:"port"`
|
||||
PanelName string `json:"panelName"`
|
||||
@@ -18,6 +20,8 @@ type SettingInfo struct {
|
||||
ServerPort string `json:"serverPort"`
|
||||
SSL string `json:"ssl"`
|
||||
SSLType string `json:"sslType"`
|
||||
BindDomain string `json:"bindDomain"`
|
||||
AllowIPs string `json:"allowIPs"`
|
||||
SecurityEntrance string `json:"securityEntrance"`
|
||||
ExpirationDays string `json:"expirationDays"`
|
||||
ExpirationTime string `json:"expirationTime"`
|
||||
@@ -26,6 +30,7 @@ type SettingInfo struct {
|
||||
MFASecret string `json:"mfaSecret"`
|
||||
|
||||
MonitorStatus string `json:"monitorStatus"`
|
||||
MonitorInterval string `json:"monitorInterval"`
|
||||
MonitorStoreDays string `json:"monitorStoreDays"`
|
||||
|
||||
MessageType string `json:"messageType"`
|
||||
@@ -33,7 +38,8 @@ type SettingInfo struct {
|
||||
WeChatVars string `json:"weChatVars"`
|
||||
DingVars string `json:"dingVars"`
|
||||
|
||||
AppStoreVersion string `json:"appStoreVersion"`
|
||||
AppStoreVersion string `json:"appStoreVersion"`
|
||||
AppStoreLastModified string `json:"appStoreLastModified"`
|
||||
}
|
||||
|
||||
type SettingUpdate struct {
|
||||
@@ -106,6 +112,10 @@ type UpgradeInfo struct {
|
||||
ReleaseNote string `json:"releaseNote"`
|
||||
}
|
||||
|
||||
type SyncTime struct {
|
||||
NtpSite string `json:"ntpSite"`
|
||||
}
|
||||
|
||||
type Upgrade struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package dto
|
||||
|
||||
import "time"
|
||||
|
||||
type SSHInfo struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Port string `json:"port"`
|
||||
ListenAddress string `json:"listenAddress"`
|
||||
PasswordAuthentication string `json:"passwordAuthentication"`
|
||||
PubkeyAuthentication string `json:"pubkeyAuthentication"`
|
||||
PermitRootLogin string `json:"permitRootLogin"`
|
||||
UseDNS string `json:"useDNS"`
|
||||
}
|
||||
|
||||
type GenerateSSH struct {
|
||||
EncryptionMode string `json:"encryptionMode" validate:"required,oneof=rsa ed25519 ecdsa dsa"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type GenerateLoad struct {
|
||||
EncryptionMode string `json:"encryptionMode" validate:"required,oneof=rsa ed25519 ecdsa dsa"`
|
||||
}
|
||||
|
||||
type SSHConf struct {
|
||||
File string `json:"file"`
|
||||
}
|
||||
type SearchSSHLog struct {
|
||||
PageInfo
|
||||
Info string `json:"info"`
|
||||
Status string `json:"Status" validate:"required,oneof=Success Failed All"`
|
||||
}
|
||||
type SSHLog struct {
|
||||
Logs []SSHHistory `json:"logs"`
|
||||
TotalCount int `json:"totalCount"`
|
||||
SuccessfulCount int `json:"successfulCount"`
|
||||
FailedCount int `json:"failedCount"`
|
||||
}
|
||||
type SSHHistory struct {
|
||||
Date time.Time `json:"date"`
|
||||
DateStr string `json:"dateStr"`
|
||||
Area string `json:"area"`
|
||||
User string `json:"user"`
|
||||
AuthMode string `json:"authMode"`
|
||||
Address string `json:"address"`
|
||||
Port string `json:"port"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
+21
-18
@@ -2,22 +2,25 @@ package model
|
||||
|
||||
type App struct {
|
||||
BaseModel
|
||||
Name string `json:"name" gorm:"type:varchar(64);not null"`
|
||||
Key string `json:"key" gorm:"type:varchar(64);not null;uniqueIndex"`
|
||||
ShortDescZh string `json:"shortDescZh" gorm:"type:longtext;"`
|
||||
ShortDescEn string `json:"shortDescEn" gorm:"type:longtext;"`
|
||||
Icon string `json:"icon" gorm:"type:longtext;"`
|
||||
Type string `json:"type" gorm:"type:varchar(64);not null"`
|
||||
Status string `json:"status" gorm:"type:varchar(64);not null"`
|
||||
Required string `json:"required" gorm:"type:varchar(64);not null"`
|
||||
CrossVersionUpdate bool `json:"crossVersionUpdate"`
|
||||
Limit int `json:"limit" gorm:"type:Integer;not null"`
|
||||
Website string `json:"website" gorm:"type:varchar(64);not null"`
|
||||
Github string `json:"github" gorm:"type:varchar(64);not null"`
|
||||
Document string `json:"document" gorm:"type:varchar(64);not null"`
|
||||
Recommend int `json:"recommend" gorm:"type:Integer;not null"`
|
||||
Resource string `json:"resource" gorm:"type:varchar;not null;default:remote"`
|
||||
Details []AppDetail `json:"-" gorm:"-:migration"`
|
||||
TagsKey []string `json:"-" gorm:"-"`
|
||||
AppTags []AppTag `json:"-" gorm:"-:migration"`
|
||||
Name string `json:"name" gorm:"type:varchar(64);not null"`
|
||||
Key string `json:"key" gorm:"type:varchar(64);not null;"`
|
||||
ShortDescZh string `json:"shortDescZh" yaml:"shortDescZh" gorm:"type:longtext;"`
|
||||
ShortDescEn string `json:"shortDescEn" yaml:"shortDescEn" gorm:"type:longtext;"`
|
||||
Icon string `json:"icon" gorm:"type:longtext;"`
|
||||
Type string `json:"type" gorm:"type:varchar(64);not null"`
|
||||
Status string `json:"status" gorm:"type:varchar(64);not null"`
|
||||
Required string `json:"required" gorm:"type:varchar(64);"`
|
||||
CrossVersionUpdate bool `json:"crossVersionUpdate"`
|
||||
Limit int `json:"limit" gorm:"type:Integer;not null"`
|
||||
Website string `json:"website" gorm:"type:varchar(64);not null"`
|
||||
Github string `json:"github" gorm:"type:varchar(64);not null"`
|
||||
Document string `json:"document" gorm:"type:varchar(64);not null"`
|
||||
Recommend int `json:"recommend" gorm:"type:Integer;not null"`
|
||||
Resource string `json:"resource" gorm:"type:varchar;not null;default:remote"`
|
||||
ReadMe string `json:"readMe" gorm:"type:varchar;"`
|
||||
LastModified int `json:"lastModified" gorm:"type:Integer;"`
|
||||
|
||||
Details []AppDetail `json:"-" gorm:"-:migration"`
|
||||
TagsKey []string `json:"tags" yaml:"tags" gorm:"-"`
|
||||
AppTags []AppTag `json:"-" gorm:"-:migration"`
|
||||
}
|
||||
|
||||
@@ -2,11 +2,14 @@ package model
|
||||
|
||||
type AppDetail struct {
|
||||
BaseModel
|
||||
AppId uint `json:"appId" gorm:"type:integer;not null"`
|
||||
Version string `json:"version" gorm:"type:varchar(64);not null"`
|
||||
Params string `json:"-" gorm:"type:longtext;"`
|
||||
DockerCompose string `json:"-" gorm:"type:longtext;not null"`
|
||||
Readme string `json:"readme" gorm:"type:longtext;"`
|
||||
Status string `json:"status" gorm:"type:varchar(64);not null"`
|
||||
LastVersion string `json:"lastVersion" gorm:"type:varchar(64);"`
|
||||
AppId uint `json:"appId" gorm:"type:integer;not null"`
|
||||
Version string `json:"version" gorm:"type:varchar(64);not null"`
|
||||
Params string `json:"-" gorm:"type:longtext;"`
|
||||
DockerCompose string `json:"dockerCompose" gorm:"type:longtext;"`
|
||||
Status string `json:"status" gorm:"type:varchar(64);not null"`
|
||||
LastVersion string `json:"lastVersion" gorm:"type:varchar(64);"`
|
||||
LastModified int `json:"lastModified" gorm:"type:integer;"`
|
||||
DownloadUrl string `json:"downloadUrl" gorm:"type:varchar;"`
|
||||
DownloadCallBackUrl string `json:"downloadCallBackUrl" gorm:"type:longtext;"`
|
||||
Update bool `json:"update"`
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ type Cronjob struct {
|
||||
Day uint64 `gorm:"type:decimal" json:"day"`
|
||||
Hour uint64 `gorm:"type:decimal" json:"hour"`
|
||||
Minute uint64 `gorm:"type:decimal" json:"minute"`
|
||||
Second uint64 `gorm:"type:decimal" json:"second"`
|
||||
|
||||
Script string `gorm:"longtext" json:"script"`
|
||||
Website string `gorm:"type:varchar(64)" json:"website"`
|
||||
|
||||
@@ -19,6 +19,7 @@ type Website struct {
|
||||
ErrorLog bool `json:"errorLog"`
|
||||
AccessLog bool `json:"accessLog"`
|
||||
DefaultServer bool `json:"defaultServer"`
|
||||
IPV6 bool `json:"IPV6"`
|
||||
Rewrite string `gorm:"type:varchar" json:"rewrite"`
|
||||
|
||||
WebsiteGroupID uint `gorm:"type:integer" json:"webSiteGroupId"`
|
||||
|
||||
@@ -24,6 +24,7 @@ type IAppRepo interface {
|
||||
GetByKey(ctx context.Context, key string) (model.App, error)
|
||||
Create(ctx context.Context, app *model.App) error
|
||||
Save(ctx context.Context, app *model.App) error
|
||||
BatchDelete(ctx context.Context, apps []model.App) error
|
||||
}
|
||||
|
||||
func NewIAppRepo() IAppRepo {
|
||||
@@ -106,3 +107,7 @@ func (a AppRepo) Create(ctx context.Context, app *model.App) error {
|
||||
func (a AppRepo) Save(ctx context.Context, app *model.App) error {
|
||||
return getTx(ctx).Omit(clause.Associations).Save(app).Error
|
||||
}
|
||||
|
||||
func (a AppRepo) BatchDelete(ctx context.Context, apps []model.App) error {
|
||||
return getTx(ctx).Omit(clause.Associations).Delete(&apps).Error
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type AppDetailRepo struct {
|
||||
@@ -18,6 +19,7 @@ type IAppDetailRepo interface {
|
||||
DeleteByAppIds(ctx context.Context, appIds []uint) error
|
||||
GetBy(opts ...DBOption) ([]model.AppDetail, error)
|
||||
BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error
|
||||
BatchDelete(ctx context.Context, appDetails []model.AppDetail) error
|
||||
}
|
||||
|
||||
func NewIAppDetailRepo() IAppDetailRepo {
|
||||
@@ -66,3 +68,7 @@ func (a AppDetailRepo) BatchUpdateBy(maps map[string]interface{}, opts ...DBOpti
|
||||
}
|
||||
return db.Updates(&maps).Error
|
||||
}
|
||||
|
||||
func (a AppDetailRepo) BatchDelete(ctx context.Context, appDetails []model.AppDetail) error {
|
||||
return getTx(ctx).Omit(clause.Associations).Delete(&appDetails).Error
|
||||
}
|
||||
|
||||
@@ -19,8 +19,10 @@ type IAppInstallRepo interface {
|
||||
WithAppIdsIn(appIds []uint) DBOption
|
||||
WithStatus(status string) DBOption
|
||||
WithServiceName(serviceName string) DBOption
|
||||
WithContainerName(containerName string) DBOption
|
||||
WithPort(port int) DBOption
|
||||
WithIdNotInWebsite() DBOption
|
||||
WithIDNotIs(id uint) DBOption
|
||||
ListBy(opts ...DBOption) ([]model.AppInstall, error)
|
||||
GetFirst(opts ...DBOption) (model.AppInstall, error)
|
||||
Create(ctx context.Context, install *model.AppInstall) error
|
||||
@@ -55,6 +57,12 @@ func (a *AppInstallRepo) WithAppId(appId uint) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AppInstallRepo) WithIDNotIs(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("id != ?", id)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AppInstallRepo) WithAppIdsIn(appIds []uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("app_id in (?)", appIds)
|
||||
@@ -73,6 +81,12 @@ func (a *AppInstallRepo) WithServiceName(serviceName string) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AppInstallRepo) WithContainerName(containerName string) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("container_name = ?", containerName)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AppInstallRepo) WithPort(port int) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("https_port = ? or http_port = ?", port, port)
|
||||
|
||||
@@ -19,6 +19,7 @@ type IAppInstallResourceRpo interface {
|
||||
GetFirst(opts ...DBOption) (model.AppInstallResource, error)
|
||||
Create(ctx context.Context, resource *model.AppInstallResource) error
|
||||
DeleteBy(ctx context.Context, opts ...DBOption) error
|
||||
BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error
|
||||
}
|
||||
|
||||
func NewIAppInstallResourceRpo() IAppInstallResourceRpo {
|
||||
@@ -71,3 +72,11 @@ func (a AppInstallResourceRpo) Create(ctx context.Context, resource *model.AppIn
|
||||
func (a AppInstallResourceRpo) DeleteBy(ctx context.Context, opts ...DBOption) error {
|
||||
return getTx(ctx, opts...).Delete(&model.AppInstallResource{}).Error
|
||||
}
|
||||
|
||||
func (a *AppInstallResourceRpo) BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error {
|
||||
db := getDb(opts...).Model(&model.AppInstallResource{})
|
||||
if len(opts) == 0 {
|
||||
db = db.Where("1=1")
|
||||
}
|
||||
return db.Updates(&maps).Error
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ type IRuntimeRepo interface {
|
||||
WithImage(image string) DBOption
|
||||
WithNotId(id uint) DBOption
|
||||
WithStatus(status string) DBOption
|
||||
WithDetailId(id uint) DBOption
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error)
|
||||
Create(ctx context.Context, runtime *model.Runtime) error
|
||||
Save(runtime *model.Runtime) error
|
||||
@@ -43,6 +44,12 @@ func (r *RuntimeRepo) WithImage(image string) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) WithDetailId(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("app_detail_id = ?", id)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) WithNotId(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("id != ?", id)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"gorm.io/gorm"
|
||||
@@ -14,6 +16,13 @@ type ISettingRepo interface {
|
||||
Create(key, value string) error
|
||||
Update(key, value string) error
|
||||
WithByKey(key string) DBOption
|
||||
|
||||
CreateMonitorBase(model model.MonitorBase) error
|
||||
BatchCreateMonitorIO(ioList []model.MonitorIO) error
|
||||
BatchCreateMonitorNet(ioList []model.MonitorNetwork) error
|
||||
DelMonitorBase(timeForDelete time.Time) error
|
||||
DelMonitorIO(timeForDelete time.Time) error
|
||||
DelMonitorNet(timeForDelete time.Time) error
|
||||
}
|
||||
|
||||
func NewISettingRepo() ISettingRepo {
|
||||
@@ -57,3 +66,22 @@ func (c *SettingRepo) WithByKey(key string) DBOption {
|
||||
func (u *SettingRepo) Update(key, value string) error {
|
||||
return global.DB.Model(&model.Setting{}).Where("key = ?", key).Updates(map[string]interface{}{"value": value}).Error
|
||||
}
|
||||
|
||||
func (u *SettingRepo) CreateMonitorBase(model model.MonitorBase) error {
|
||||
return global.DB.Create(&model).Error
|
||||
}
|
||||
func (u *SettingRepo) BatchCreateMonitorIO(ioList []model.MonitorIO) error {
|
||||
return global.DB.CreateInBatches(ioList, len(ioList)).Error
|
||||
}
|
||||
func (u *SettingRepo) BatchCreateMonitorNet(ioList []model.MonitorNetwork) error {
|
||||
return global.DB.CreateInBatches(ioList, len(ioList)).Error
|
||||
}
|
||||
func (u *SettingRepo) DelMonitorBase(timeForDelete time.Time) error {
|
||||
return global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorBase{}).Error
|
||||
}
|
||||
func (u *SettingRepo) DelMonitorIO(timeForDelete time.Time) error {
|
||||
return global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorIO{}).Error
|
||||
}
|
||||
func (u *SettingRepo) DelMonitorNet(timeForDelete time.Time) error {
|
||||
return global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error
|
||||
}
|
||||
|
||||
+421
-252
@@ -5,25 +5,25 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
http2 "github.com/1Panel-dev/1Panel/backend/utils/http"
|
||||
"gopkg.in/yaml.v3"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type AppService struct {
|
||||
@@ -100,6 +100,8 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
|
||||
continue
|
||||
}
|
||||
appDTO.Tags = tags
|
||||
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(a.ID))
|
||||
appDTO.Installed = len(installs) > 0
|
||||
}
|
||||
res.Items = appDTOs
|
||||
res.Total = total
|
||||
@@ -160,7 +162,13 @@ func (a AppService) GetAppDetail(appId uint, version, appType string) (response.
|
||||
return appDetailDTO, err
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
buildPath := path.Join(constant.AppResourceDir, app.Key, "versions", detail.Version, "build")
|
||||
versionPath := path.Join(constant.AppResourceDir, app.Resource, app.Key, detail.Version)
|
||||
if !fileOp.Stat(versionPath) {
|
||||
if err = downloadApp(app, detail, nil); err != nil {
|
||||
return appDetailDTO, err
|
||||
}
|
||||
}
|
||||
buildPath := path.Join(versionPath, "build")
|
||||
paramsPath := path.Join(buildPath, "config.json")
|
||||
if !fileOp.Stat(paramsPath) {
|
||||
return appDetailDTO, buserr.New(constant.ErrFileNotExist)
|
||||
@@ -253,7 +261,7 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
||||
}
|
||||
app, err = appRepo.GetFirst(commonRepo.WithByID(appDetail.AppId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
if err = checkRequiredAndLimit(app); err != nil {
|
||||
return
|
||||
@@ -270,35 +278,55 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
||||
App: app,
|
||||
}
|
||||
composeMap := make(map[string]interface{})
|
||||
if err = yaml.Unmarshal([]byte(appDetail.DockerCompose), &composeMap); err != nil {
|
||||
return
|
||||
if req.EditCompose {
|
||||
if err = yaml.Unmarshal([]byte(req.DockerCompose), &composeMap); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err = yaml.Unmarshal([]byte(appDetail.DockerCompose), &composeMap); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
value, ok := composeMap["services"]
|
||||
if !ok {
|
||||
err = buserr.New("")
|
||||
err = buserr.New(constant.ErrFileParse)
|
||||
return
|
||||
}
|
||||
servicesMap := value.(map[string]interface{})
|
||||
changeKeys := make(map[string]string, len(servicesMap))
|
||||
containerName := constant.ContainerPrefix + app.Key + "-" + common.RandStr(4)
|
||||
if req.Advanced && req.ContainerName != "" {
|
||||
containerName = req.ContainerName
|
||||
appInstalls, _ := appInstallRepo.ListBy(appInstallRepo.WithContainerName(containerName))
|
||||
if len(appInstalls) > 0 {
|
||||
err = buserr.New(constant.ErrContainerName)
|
||||
return
|
||||
}
|
||||
containerExist := false
|
||||
containerExist, err = checkContainerNameIsExist(req.ContainerName, appInstall.GetPath())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if containerExist {
|
||||
err = buserr.New(constant.ErrContainerName)
|
||||
return
|
||||
}
|
||||
}
|
||||
req.Params[constant.ContainerName] = containerName
|
||||
appInstall.ContainerName = containerName
|
||||
|
||||
index := 0
|
||||
for k := range servicesMap {
|
||||
serviceName := k + "-" + common.RandStr(4)
|
||||
changeKeys[k] = serviceName
|
||||
containerName := constant.ContainerPrefix + k + "-" + common.RandStr(4)
|
||||
appInstall.ServiceName = k
|
||||
if index > 0 {
|
||||
continue
|
||||
}
|
||||
req.Params["CONTAINER_NAME"] = containerName
|
||||
appInstall.ServiceName = serviceName
|
||||
appInstall.ContainerName = containerName
|
||||
index++
|
||||
}
|
||||
for k, v := range changeKeys {
|
||||
servicesMap[v] = servicesMap[k]
|
||||
delete(servicesMap, k)
|
||||
}
|
||||
|
||||
if err = addDockerComposeCommonParam(composeMap, appInstall.ServiceName, req.AppContainerConfig, req.Params); err != nil {
|
||||
return
|
||||
}
|
||||
var (
|
||||
composeByte []byte
|
||||
paramByte []byte
|
||||
@@ -318,34 +346,258 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err = copyAppData(app.Key, appDetail.Version, req.Name, req.Params, app.Resource == constant.AppResourceLocal); err != nil {
|
||||
return
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
if err = fileOp.WriteFile(appInstall.GetComposePath(), strings.NewReader(string(composeByte)), 0775); err != nil {
|
||||
return
|
||||
}
|
||||
paramByte, err = json.Marshal(req.Params)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
appInstall.Env = string(paramByte)
|
||||
|
||||
if err = appInstallRepo.Create(ctx, appInstall); err != nil {
|
||||
return
|
||||
}
|
||||
if err = createLink(ctx, app, appInstall, req.Params); err != nil {
|
||||
return
|
||||
}
|
||||
if err = upAppPre(app, appInstall); err != nil {
|
||||
return
|
||||
}
|
||||
go upApp(appInstall)
|
||||
go func() {
|
||||
if err = copyData(app, appDetail, appInstall, req); err != nil {
|
||||
if appInstall.Status == constant.Installing {
|
||||
appInstall.Status = constant.Error
|
||||
appInstall.Message = err.Error()
|
||||
}
|
||||
_ = appInstallRepo.Save(context.Background(), appInstall)
|
||||
return
|
||||
}
|
||||
if err = upAppPre(app, appInstall); err != nil {
|
||||
return
|
||||
}
|
||||
upApp(appInstall)
|
||||
}()
|
||||
go updateToolApp(appInstall)
|
||||
return
|
||||
}
|
||||
|
||||
func (a AppService) SyncAppListFromLocal() {
|
||||
fileOp := files.NewFileOp()
|
||||
localAppDir := constant.LocalAppResourceDir
|
||||
if !fileOp.Stat(localAppDir) {
|
||||
return
|
||||
}
|
||||
var (
|
||||
err error
|
||||
dirEntries []os.DirEntry
|
||||
localApps []model.App
|
||||
)
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
global.LOG.Errorf("sync app failed %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
global.LOG.Infof("start sync local apps...")
|
||||
dirEntries, err = os.ReadDir(localAppDir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, dirEntry := range dirEntries {
|
||||
if dirEntry.IsDir() {
|
||||
appDir := path.Join(localAppDir, dirEntry.Name())
|
||||
appDirEntries, err := os.ReadDir(appDir)
|
||||
if err != nil {
|
||||
global.LOG.Errorf(i18n.GetMsgWithMap("ErrAppDirNull", map[string]interface{}{"name": dirEntry.Name(), "err": err.Error()}))
|
||||
continue
|
||||
}
|
||||
app, err := handleLocalApp(appDir)
|
||||
if err != nil {
|
||||
global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppErr", map[string]interface{}{"name": dirEntry.Name(), "err": err.Error()}))
|
||||
continue
|
||||
}
|
||||
var appDetails []model.AppDetail
|
||||
for _, appDirEntry := range appDirEntries {
|
||||
if appDirEntry.IsDir() {
|
||||
appDetail := model.AppDetail{
|
||||
Version: appDirEntry.Name(),
|
||||
Status: constant.AppNormal,
|
||||
}
|
||||
versionDir := path.Join(appDir, appDirEntry.Name())
|
||||
if err = handleLocalAppDetail(versionDir, &appDetail); err != nil {
|
||||
global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppVersionErr", map[string]interface{}{"name": app.Name, "version": appDetail.Version, "err": err.Error()}))
|
||||
continue
|
||||
}
|
||||
appDetails = append(appDetails, appDetail)
|
||||
}
|
||||
}
|
||||
if len(appDetails) > 0 {
|
||||
app.Details = appDetails
|
||||
localApps = append(localApps, *app)
|
||||
} else {
|
||||
global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppVersionNull", map[string]interface{}{"name": app.Name}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
newApps []model.App
|
||||
deleteApps []model.App
|
||||
updateApps []model.App
|
||||
oldAppIds []uint
|
||||
|
||||
deleteAppIds []uint
|
||||
deleteAppDetails []model.AppDetail
|
||||
newAppDetails []model.AppDetail
|
||||
updateDetails []model.AppDetail
|
||||
|
||||
appTags []*model.AppTag
|
||||
)
|
||||
|
||||
oldApps, _ := appRepo.GetBy(appRepo.WithResource(constant.AppResourceLocal))
|
||||
apps := make(map[string]model.App, len(oldApps))
|
||||
for _, old := range oldApps {
|
||||
old.Status = constant.AppTakeDown
|
||||
apps[old.Key] = old
|
||||
}
|
||||
for _, app := range localApps {
|
||||
if oldApp, ok := apps[app.Key]; ok {
|
||||
app.ID = oldApp.ID
|
||||
appDetails := make(map[string]model.AppDetail, len(oldApp.Details))
|
||||
for _, old := range oldApp.Details {
|
||||
old.Status = constant.AppTakeDown
|
||||
appDetails[old.Version] = old
|
||||
}
|
||||
for i, newDetail := range app.Details {
|
||||
version := newDetail.Version
|
||||
newDetail.Status = constant.AppNormal
|
||||
newDetail.AppId = app.ID
|
||||
oldDetail, exist := appDetails[version]
|
||||
if exist {
|
||||
newDetail.ID = oldDetail.ID
|
||||
}
|
||||
app.Details[i] = newDetail
|
||||
}
|
||||
}
|
||||
app.TagsKey = append(app.TagsKey, constant.AppResourceLocal)
|
||||
apps[app.Key] = app
|
||||
}
|
||||
|
||||
for _, app := range apps {
|
||||
if app.ID == 0 {
|
||||
newApps = append(newApps, app)
|
||||
} else {
|
||||
oldAppIds = append(oldAppIds, app.ID)
|
||||
if app.Status == constant.AppTakeDown {
|
||||
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(app.ID))
|
||||
if len(installs) > 0 {
|
||||
updateApps = append(updateApps, app)
|
||||
continue
|
||||
}
|
||||
deleteAppIds = append(deleteAppIds, app.ID)
|
||||
deleteApps = append(deleteApps, app)
|
||||
deleteAppDetails = append(deleteAppDetails, app.Details...)
|
||||
} else {
|
||||
updateApps = append(updateApps, app)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
tags, _ := tagRepo.All()
|
||||
tagMap := make(map[string]uint, len(tags))
|
||||
for _, tag := range tags {
|
||||
tagMap[tag.Key] = tag.ID
|
||||
}
|
||||
|
||||
tx, ctx := getTxAndContext()
|
||||
defer tx.Rollback()
|
||||
if len(newApps) > 0 {
|
||||
if err = appRepo.BatchCreate(ctx, newApps); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, update := range updateApps {
|
||||
if err = appRepo.Save(ctx, &update); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(deleteApps) > 0 {
|
||||
if err = appRepo.BatchDelete(ctx, deleteApps); err != nil {
|
||||
return
|
||||
}
|
||||
if err = appDetailRepo.DeleteByAppIds(ctx, deleteAppIds); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {
|
||||
return
|
||||
}
|
||||
for _, newApp := range newApps {
|
||||
if newApp.ID > 0 {
|
||||
for _, detail := range newApp.Details {
|
||||
detail.AppId = newApp.ID
|
||||
newAppDetails = append(newAppDetails, detail)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, update := range updateApps {
|
||||
for _, detail := range update.Details {
|
||||
if detail.ID == 0 {
|
||||
detail.AppId = update.ID
|
||||
newAppDetails = append(newAppDetails, detail)
|
||||
} else {
|
||||
if detail.Status == constant.AppNormal {
|
||||
updateDetails = append(updateDetails, detail)
|
||||
} else {
|
||||
deleteAppDetails = append(deleteAppDetails, detail)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allApps := append(newApps, updateApps...)
|
||||
for _, app := range allApps {
|
||||
for _, t := range app.TagsKey {
|
||||
tagId, ok := tagMap[t]
|
||||
if ok {
|
||||
appTags = append(appTags, &model.AppTag{
|
||||
AppId: app.ID,
|
||||
TagId: tagId,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(newAppDetails) > 0 {
|
||||
if err = appDetailRepo.BatchCreate(ctx, newAppDetails); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, updateAppDetail := range updateDetails {
|
||||
if err = appDetailRepo.Update(ctx, updateAppDetail); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(deleteAppDetails) > 0 {
|
||||
if err = appDetailRepo.BatchDelete(ctx, deleteAppDetails); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(oldAppIds) > 0 {
|
||||
if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(appTags) > 0 {
|
||||
if err = appTagRepo.BatchCreate(ctx, appTags); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
global.LOG.Infof("sync local apps success")
|
||||
}
|
||||
|
||||
func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
|
||||
res := &response.AppUpdateRes{
|
||||
CanUpdate: false,
|
||||
@@ -354,8 +606,8 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
versionUrl := fmt.Sprintf("%s/%s/%s/appstore/apps.json", global.CONF.System.RepoUrl, global.CONF.System.Mode, setting.SystemVersion)
|
||||
versionRes, err := http.Get(versionUrl)
|
||||
versionUrl := fmt.Sprintf("%s/%s/1panel.json.version.txt", global.CONF.System.AppRepo, global.CONF.System.Mode)
|
||||
versionRes, err := http2.GetHttpRes(versionUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -364,182 +616,49 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list := &dto.AppList{}
|
||||
if err = json.Unmarshal(body, list); err != nil {
|
||||
lastModifiedStr := string(body)
|
||||
|
||||
lastModified, err := strconv.Atoi(lastModifiedStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.Version = list.Version
|
||||
if setting.AppStoreVersion == "" || common.CompareVersion(list.Version, setting.AppStoreVersion) {
|
||||
appStoreLastModified, _ := strconv.Atoi(setting.AppStoreLastModified)
|
||||
if setting.AppStoreLastModified == "" || lastModified != appStoreLastModified {
|
||||
res.CanUpdate = true
|
||||
res.DownloadPath = fmt.Sprintf("%s/%s/%s/appstore/apps-%s.tar.gz", global.CONF.System.RepoUrl, global.CONF.System.Mode, setting.SystemVersion, list.Version)
|
||||
return res, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (a AppService) SyncAppListFromLocal() {
|
||||
func getAppFromRepo(downloadPath string) error {
|
||||
downloadUrl := downloadPath
|
||||
global.LOG.Infof("download file from %s", downloadUrl)
|
||||
fileOp := files.NewFileOp()
|
||||
appDir := constant.LocalAppResourceDir
|
||||
listFile := path.Join(appDir, "list.json")
|
||||
if !fileOp.Stat(listFile) {
|
||||
return
|
||||
packagePath := path.Join(constant.ResourceDir, path.Base(downloadUrl))
|
||||
if err := fileOp.DownloadFile(downloadUrl, packagePath); err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Infof("start sync local apps...")
|
||||
content, err := fileOp.GetContent(listFile)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get list.json content failed %s", err.Error())
|
||||
return
|
||||
if err := fileOp.Decompress(packagePath, constant.ResourceDir, files.Zip); err != nil {
|
||||
return err
|
||||
}
|
||||
list := &dto.AppList{}
|
||||
if err := json.Unmarshal(content, list); err != nil {
|
||||
global.LOG.Errorf("unmarshal list.json failed %s", err.Error())
|
||||
return
|
||||
}
|
||||
oldApps, _ := appRepo.GetBy(appRepo.WithResource(constant.AppResourceLocal))
|
||||
appsMap := getApps(oldApps, list.Items, true)
|
||||
for _, l := range list.Items {
|
||||
localKey := "local" + l.Key
|
||||
app := appsMap[localKey]
|
||||
icon, err := os.ReadFile(path.Join(appDir, l.Key, "metadata", "logo.png"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] icon error: %s", l.Name, err.Error())
|
||||
continue
|
||||
}
|
||||
iconStr := base64.StdEncoding.EncodeToString(icon)
|
||||
app.Icon = iconStr
|
||||
app.TagsKey = append(l.Tags, "Local")
|
||||
app.Recommend = 9999
|
||||
versions := l.Versions
|
||||
detailsMap := getAppDetails(app.Details, versions)
|
||||
|
||||
for _, v := range versions {
|
||||
detail := detailsMap[v]
|
||||
detailPath := path.Join(appDir, l.Key, "versions", v)
|
||||
if _, err := os.Stat(detailPath); err != nil {
|
||||
global.LOG.Errorf("get [%s] folder error: %s", detailPath, err.Error())
|
||||
continue
|
||||
}
|
||||
readmeStr, err := os.ReadFile(path.Join(detailPath, "README.md"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] README error: %s", detailPath, err.Error())
|
||||
}
|
||||
detail.Readme = string(readmeStr)
|
||||
dockerComposeStr, err := os.ReadFile(path.Join(detailPath, "docker-compose.yml"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] docker-compose.yml error: %s", detailPath, err.Error())
|
||||
continue
|
||||
}
|
||||
detail.DockerCompose = string(dockerComposeStr)
|
||||
paramStr, err := os.ReadFile(path.Join(detailPath, "config.json"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] form.json error: %s", detailPath, err.Error())
|
||||
}
|
||||
detail.Params = string(paramStr)
|
||||
detailsMap[v] = detail
|
||||
}
|
||||
var newDetails []model.AppDetail
|
||||
for _, v := range detailsMap {
|
||||
newDetails = append(newDetails, v)
|
||||
}
|
||||
app.Details = newDetails
|
||||
appsMap[localKey] = app
|
||||
}
|
||||
var (
|
||||
addAppArray []model.App
|
||||
updateArray []model.App
|
||||
appIds []uint
|
||||
)
|
||||
for _, v := range appsMap {
|
||||
if v.ID == 0 {
|
||||
addAppArray = append(addAppArray, v)
|
||||
} else {
|
||||
updateArray = append(updateArray, v)
|
||||
appIds = append(appIds, v.ID)
|
||||
}
|
||||
}
|
||||
tx, ctx := getTxAndContext()
|
||||
if len(addAppArray) > 0 {
|
||||
if err := appRepo.BatchCreate(ctx, addAppArray); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, update := range updateArray {
|
||||
if err := appRepo.Save(ctx, &update); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := appTagRepo.DeleteByAppIds(ctx, appIds); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
apps := append(addAppArray, updateArray...)
|
||||
var (
|
||||
addDetails []model.AppDetail
|
||||
updateDetails []model.AppDetail
|
||||
appTags []*model.AppTag
|
||||
)
|
||||
tags, _ := tagRepo.All()
|
||||
tagMap := make(map[string]uint, len(tags))
|
||||
for _, app := range tags {
|
||||
tagMap[app.Key] = app.ID
|
||||
}
|
||||
for _, a := range apps {
|
||||
for _, t := range a.TagsKey {
|
||||
tagId, ok := tagMap[t]
|
||||
if ok {
|
||||
appTags = append(appTags, &model.AppTag{
|
||||
AppId: a.ID,
|
||||
TagId: tagId,
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, d := range a.Details {
|
||||
d.AppId = a.ID
|
||||
if d.ID == 0 {
|
||||
addDetails = append(addDetails, d)
|
||||
} else {
|
||||
updateDetails = append(updateDetails, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(addDetails) > 0 {
|
||||
if err := appDetailRepo.BatchCreate(ctx, addDetails); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, u := range updateDetails {
|
||||
if err := appDetailRepo.Update(ctx, u); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(appTags) > 0 {
|
||||
if err := appTagRepo.BatchCreate(ctx, appTags); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
global.LOG.Infof("sync local apps success")
|
||||
defer func() {
|
||||
_ = fileOp.DeleteFile(packagePath)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AppService) SyncAppListFromRemote() error {
|
||||
updateRes, err := a.GetAppUpdate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !updateRes.CanUpdate {
|
||||
global.LOG.Infof("The latest version is [%s] The app store is already up to date", updateRes.Version)
|
||||
return nil
|
||||
}
|
||||
if err := getAppFromRepo(updateRes.DownloadPath, updateRes.Version); err != nil {
|
||||
global.LOG.Errorf("get app from oss error: %s", err.Error())
|
||||
if err = getAppFromRepo(fmt.Sprintf("%s/%s/1panel.json.zip", global.CONF.System.AppRepo, global.CONF.System.Mode)); err != nil {
|
||||
return err
|
||||
}
|
||||
appDir := constant.AppResourceDir
|
||||
listFile := path.Join(appDir, "list.json")
|
||||
listFile := path.Join(constant.ResourceDir, "1panel.json")
|
||||
content, err := os.ReadFile(listFile)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -548,11 +667,13 @@ func (a AppService) SyncAppListFromRemote() error {
|
||||
if err := json.Unmarshal(content, list); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
tags []*model.Tag
|
||||
appTags []*model.AppTag
|
||||
tags []*model.Tag
|
||||
appTags []*model.AppTag
|
||||
oldAppIds []uint
|
||||
)
|
||||
for _, t := range list.Tags {
|
||||
for _, t := range list.Extra.Tags {
|
||||
tags = append(tags, &model.Tag{
|
||||
Key: t.Key,
|
||||
Name: t.Name,
|
||||
@@ -562,145 +683,193 @@ func (a AppService) SyncAppListFromRemote() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appsMap := getApps(oldApps, list.Items, false)
|
||||
for _, l := range list.Items {
|
||||
app := appsMap[l.Key]
|
||||
icon, err := os.ReadFile(path.Join(appDir, l.Key, "metadata", "logo.png"))
|
||||
for _, old := range oldApps {
|
||||
oldAppIds = append(oldAppIds, old.ID)
|
||||
}
|
||||
|
||||
baseRemoteUrl := fmt.Sprintf("%s/%s/1panel", global.CONF.System.AppRepo, global.CONF.System.Mode)
|
||||
appsMap := getApps(oldApps, list.Apps)
|
||||
for _, l := range list.Apps {
|
||||
app := appsMap[l.AppProperty.Key]
|
||||
iconRes, err := http.Get(l.Icon)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] icon error: %s", l.Name, err.Error())
|
||||
continue
|
||||
return err
|
||||
}
|
||||
iconStr := base64.StdEncoding.EncodeToString(icon)
|
||||
body, err := io.ReadAll(iconRes.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iconStr := base64.StdEncoding.EncodeToString(body)
|
||||
app.Icon = iconStr
|
||||
app.TagsKey = l.Tags
|
||||
if l.Recommend > 0 {
|
||||
app.Recommend = l.Recommend
|
||||
app.TagsKey = l.AppProperty.Tags
|
||||
if l.AppProperty.Recommend > 0 {
|
||||
app.Recommend = l.AppProperty.Recommend
|
||||
} else {
|
||||
app.Recommend = 9999
|
||||
}
|
||||
|
||||
app.ReadMe = l.ReadMe
|
||||
app.LastModified = l.LastModified
|
||||
versions := l.Versions
|
||||
detailsMap := getAppDetails(app.Details, versions)
|
||||
|
||||
for _, v := range versions {
|
||||
detail := detailsMap[v]
|
||||
detailPath := path.Join(appDir, l.Key, "versions", v)
|
||||
if _, err := os.Stat(detailPath); err != nil {
|
||||
global.LOG.Errorf("get [%s] folder error: %s", detailPath, err.Error())
|
||||
continue
|
||||
}
|
||||
readmeStr, err := os.ReadFile(path.Join(detailPath, "README.md"))
|
||||
version := v.Name
|
||||
detail := detailsMap[version]
|
||||
|
||||
dockerComposeUrl := fmt.Sprintf("%s/%s/%s/%s", baseRemoteUrl, app.Key, version, "docker-compose.yml")
|
||||
composeRes, err := http.Get(dockerComposeUrl)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] README error: %s", detailPath, err.Error())
|
||||
return err
|
||||
}
|
||||
detail.Readme = string(readmeStr)
|
||||
dockerComposeStr, err := os.ReadFile(path.Join(detailPath, "docker-compose.yml"))
|
||||
bodyContent, err := io.ReadAll(composeRes.Body)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] docker-compose.yml error: %s", detailPath, err.Error())
|
||||
continue
|
||||
return err
|
||||
}
|
||||
detail.DockerCompose = string(dockerComposeStr)
|
||||
paramStr, err := os.ReadFile(path.Join(detailPath, "config.json"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] form.json error: %s", detailPath, err.Error())
|
||||
detail.DockerCompose = string(bodyContent)
|
||||
|
||||
paramByte, _ := json.Marshal(v.AppForm)
|
||||
detail.Params = string(paramByte)
|
||||
detail.DownloadUrl = v.DownloadUrl
|
||||
detail.DownloadCallBackUrl = v.DownloadCallBackUrl
|
||||
if v.LastModified > detail.LastModified {
|
||||
detail.Update = true
|
||||
detail.LastModified = v.LastModified
|
||||
}
|
||||
detail.Params = string(paramStr)
|
||||
detailsMap[v] = detail
|
||||
detailsMap[version] = detail
|
||||
}
|
||||
var newDetails []model.AppDetail
|
||||
for _, v := range detailsMap {
|
||||
newDetails = append(newDetails, v)
|
||||
for _, detail := range detailsMap {
|
||||
newDetails = append(newDetails, detail)
|
||||
}
|
||||
app.Details = newDetails
|
||||
appsMap[l.Key] = app
|
||||
appsMap[l.AppProperty.Key] = app
|
||||
}
|
||||
|
||||
var (
|
||||
addAppArray []model.App
|
||||
updateArray []model.App
|
||||
tagMap = make(map[string]uint, len(tags))
|
||||
addAppArray []model.App
|
||||
updateAppArray []model.App
|
||||
deleteAppArray []model.App
|
||||
deleteIds []uint
|
||||
tagMap = make(map[string]uint, len(tags))
|
||||
)
|
||||
|
||||
for _, v := range appsMap {
|
||||
if v.ID == 0 {
|
||||
addAppArray = append(addAppArray, v)
|
||||
} else {
|
||||
updateArray = append(updateArray, v)
|
||||
if v.Status == constant.AppTakeDown {
|
||||
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(v.ID))
|
||||
if len(installs) > 0 {
|
||||
updateAppArray = append(updateAppArray, v)
|
||||
continue
|
||||
}
|
||||
deleteAppArray = append(deleteAppArray, v)
|
||||
deleteIds = append(deleteIds, v.ID)
|
||||
} else {
|
||||
updateAppArray = append(updateAppArray, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
tx, ctx := getTxAndContext()
|
||||
defer tx.Rollback()
|
||||
if len(addAppArray) > 0 {
|
||||
if err := appRepo.BatchCreate(ctx, addAppArray); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(deleteAppArray) > 0 {
|
||||
if err := appRepo.BatchDelete(ctx, deleteAppArray); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := appDetailRepo.DeleteByAppIds(ctx, deleteIds); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := tagRepo.DeleteAll(ctx); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if len(tags) > 0 {
|
||||
if err := tagRepo.BatchCreate(ctx, tags); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
for _, t := range tags {
|
||||
tagMap[t.Key] = t.ID
|
||||
}
|
||||
}
|
||||
for _, update := range updateArray {
|
||||
for _, update := range updateAppArray {
|
||||
if err := appRepo.Save(ctx, &update); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
apps := append(addAppArray, updateArray...)
|
||||
apps := append(addAppArray, updateAppArray...)
|
||||
|
||||
var (
|
||||
addDetails []model.AppDetail
|
||||
updateDetails []model.AppDetail
|
||||
deleteDetails []model.AppDetail
|
||||
)
|
||||
for _, a := range apps {
|
||||
for _, t := range a.TagsKey {
|
||||
for _, app := range apps {
|
||||
for _, t := range app.TagsKey {
|
||||
tagId, ok := tagMap[t]
|
||||
if ok {
|
||||
appTags = append(appTags, &model.AppTag{
|
||||
AppId: a.ID,
|
||||
AppId: app.ID,
|
||||
TagId: tagId,
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, d := range a.Details {
|
||||
d.AppId = a.ID
|
||||
for _, d := range app.Details {
|
||||
d.AppId = app.ID
|
||||
if d.ID == 0 {
|
||||
addDetails = append(addDetails, d)
|
||||
} else {
|
||||
updateDetails = append(updateDetails, d)
|
||||
if d.Status == constant.AppTakeDown {
|
||||
runtime, _ := runtimeRepo.GetFirst(runtimeRepo.WithDetailId(d.ID))
|
||||
if runtime != nil {
|
||||
updateDetails = append(updateDetails, d)
|
||||
continue
|
||||
}
|
||||
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithDetailIdsIn([]uint{d.ID}))
|
||||
if len(installs) > 0 {
|
||||
updateDetails = append(updateDetails, d)
|
||||
continue
|
||||
}
|
||||
deleteDetails = append(deleteDetails, d)
|
||||
} else {
|
||||
updateDetails = append(updateDetails, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(addDetails) > 0 {
|
||||
if err := appDetailRepo.BatchCreate(ctx, addDetails); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(deleteDetails) > 0 {
|
||||
if err := appDetailRepo.BatchDelete(ctx, deleteDetails); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, u := range updateDetails {
|
||||
if err := appDetailRepo.Update(ctx, u); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := appTagRepo.DeleteAll(ctx); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
|
||||
if len(oldAppIds) > 0 {
|
||||
if err := appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(appTags) > 0 {
|
||||
if err := appTagRepo.BatchCreate(ctx, appTags); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
if err := NewISettingService().Update("AppStoreLastModified", strconv.Itoa(list.LastModified)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/env"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx"
|
||||
"github.com/joho/godotenv"
|
||||
@@ -45,7 +48,7 @@ type IAppInstallService interface {
|
||||
SyncAll(systemInit bool) error
|
||||
GetServices(key string) ([]response.AppService, error)
|
||||
GetUpdateVersions(installId uint) ([]dto.AppVersion, error)
|
||||
GetParams(id uint) ([]response.AppParam, error)
|
||||
GetParams(id uint) (*response.AppConfig, error)
|
||||
ChangeAppPort(req request.PortUpdate) error
|
||||
GetDefaultConfigByKey(key string) (string, error)
|
||||
DeleteCheck(installId uint) ([]dto.AppResource, error)
|
||||
@@ -183,6 +186,9 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !req.ForceDelete && !files.NewFileOp().Stat(install.GetPath()) {
|
||||
return buserr.New(constant.ErrInstallDirNotFound)
|
||||
}
|
||||
dockerComposePath := install.GetComposePath()
|
||||
switch req.Operate {
|
||||
case constant.Rebuild:
|
||||
@@ -225,35 +231,68 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
return err
|
||||
}
|
||||
changePort := false
|
||||
var (
|
||||
oldPorts []int
|
||||
newPorts []int
|
||||
)
|
||||
port, ok := req.Params["PANEL_APP_PORT_HTTP"]
|
||||
if ok {
|
||||
portN := int(math.Ceil(port.(float64)))
|
||||
if portN != installed.HttpPort {
|
||||
oldPorts = append(oldPorts, installed.HttpPort)
|
||||
changePort = true
|
||||
httpPort, err := checkPort("PANEL_APP_PORT_HTTP", req.Params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
installed.HttpPort = httpPort
|
||||
newPorts = append(newPorts, httpPort)
|
||||
}
|
||||
}
|
||||
ports, ok := req.Params["PANEL_APP_PORT_HTTPS"]
|
||||
if ok {
|
||||
portN := int(math.Ceil(ports.(float64)))
|
||||
if portN != installed.HttpsPort {
|
||||
oldPorts = append(oldPorts, installed.HttpsPort)
|
||||
httpsPort, err := checkPort("PANEL_APP_PORT_HTTPS", req.Params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
installed.HttpsPort = httpsPort
|
||||
newPorts = append(newPorts, httpsPort)
|
||||
}
|
||||
}
|
||||
|
||||
backupDockerCompose := installed.DockerCompose
|
||||
if req.Advanced {
|
||||
composeMap := make(map[string]interface{})
|
||||
if req.EditCompose {
|
||||
if err = yaml.Unmarshal([]byte(req.DockerCompose), &composeMap); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err = yaml.Unmarshal([]byte(installed.DockerCompose), &composeMap); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = addDockerComposeCommonParam(composeMap, installed.ServiceName, req.AppContainerConfig, req.Params); err != nil {
|
||||
return err
|
||||
}
|
||||
composeByte, err := yaml.Marshal(composeMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
installed.DockerCompose = string(composeByte)
|
||||
if req.ContainerName == "" {
|
||||
req.Params[constant.ContainerName] = installed.ContainerName
|
||||
} else {
|
||||
req.Params[constant.ContainerName] = req.ContainerName
|
||||
if installed.ContainerName != req.ContainerName {
|
||||
exist, _ := appInstallRepo.GetFirst(appInstallRepo.WithContainerName(req.ContainerName), appInstallRepo.WithIDNotIs(installed.ID))
|
||||
if exist.ID > 0 {
|
||||
return buserr.New(constant.ErrContainerName)
|
||||
}
|
||||
containerExist, err := checkContainerNameIsExist(req.ContainerName, installed.GetPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if containerExist {
|
||||
return buserr.New(constant.ErrContainerName)
|
||||
}
|
||||
installed.ContainerName = req.ContainerName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,6 +301,7 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
backupEnvMaps := oldEnvMaps
|
||||
handleMap(req.Params, oldEnvMaps)
|
||||
paramByte, err := json.Marshal(oldEnvMaps)
|
||||
if err != nil {
|
||||
@@ -271,36 +311,42 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
if err := env.Write(oldEnvMaps, envPath); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = appInstallRepo.Save(context.Background(), &installed)
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
_ = fileOp.WriteFile(installed.GetComposePath(), strings.NewReader(installed.DockerCompose), 0755)
|
||||
if err := rebuildApp(installed); err != nil {
|
||||
_ = env.Write(backupEnvMaps, envPath)
|
||||
_ = fileOp.WriteFile(installed.GetComposePath(), strings.NewReader(backupDockerCompose), 0755)
|
||||
return err
|
||||
}
|
||||
installed.Status = constant.Running
|
||||
_ = appInstallRepo.Save(context.Background(), &installed)
|
||||
|
||||
website, _ := websiteRepo.GetFirst(websiteRepo.WithAppInstallId(installed.ID))
|
||||
if changePort && website.ID != 0 && website.Status == constant.Running {
|
||||
nginxInstall, err := getNginxFull(&website)
|
||||
if err != nil {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
||||
}
|
||||
config := nginxInstall.SiteConfig.Config
|
||||
servers := config.FindServers()
|
||||
if len(servers) == 0 {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, errors.New("nginx config is not valid"))
|
||||
}
|
||||
server := servers[0]
|
||||
proxy := fmt.Sprintf("http://127.0.0.1:%d", installed.HttpPort)
|
||||
server.UpdateRootProxy([]string{proxy})
|
||||
|
||||
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
||||
}
|
||||
if err := nginxCheckAndReload(nginxInstall.SiteConfig.OldContent, config.FilePath, nginxInstall.Install.ContainerName); err != nil {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
||||
}
|
||||
}
|
||||
if changePort {
|
||||
go func() {
|
||||
_ = OperateFirewallPort(oldPorts, newPorts)
|
||||
nginxInstall, err := getNginxFull(&website)
|
||||
if err != nil {
|
||||
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, err).Error())
|
||||
return
|
||||
}
|
||||
config := nginxInstall.SiteConfig.Config
|
||||
servers := config.FindServers()
|
||||
if len(servers) == 0 {
|
||||
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, errors.New("nginx config is not valid")).Error())
|
||||
return
|
||||
}
|
||||
server := servers[0]
|
||||
proxy := fmt.Sprintf("http://127.0.0.1:%d", installed.HttpPort)
|
||||
server.UpdateRootProxy([]string{proxy})
|
||||
|
||||
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, err).Error())
|
||||
return
|
||||
}
|
||||
if err := nginxCheckAndReload(nginxInstall.SiteConfig.OldContent, config.FilePath, nginxInstall.Install.ContainerName); err != nil {
|
||||
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, err).Error())
|
||||
return
|
||||
}
|
||||
}()
|
||||
}
|
||||
return nil
|
||||
@@ -312,10 +358,10 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
|
||||
return err
|
||||
}
|
||||
for _, i := range allList {
|
||||
if i.Status == constant.Installing {
|
||||
if i.Status == constant.Installing || i.Status == constant.Upgrading {
|
||||
if systemInit {
|
||||
i.Status = constant.Error
|
||||
i.Message = "System restart causes application exception"
|
||||
i.Message = "1Panel restart causes the task to terminate"
|
||||
_ = appInstallRepo.Save(context.Background(), &i)
|
||||
}
|
||||
continue
|
||||
@@ -366,6 +412,9 @@ func (a *AppInstallService) GetUpdateVersions(installId uint) ([]dto.AppVersion,
|
||||
return versions, err
|
||||
}
|
||||
for _, detail := range details {
|
||||
if common.IsCrossVersion(install.Version, detail.Version) && !app.CrossVersionUpdate {
|
||||
continue
|
||||
}
|
||||
if common.CompareVersion(detail.Version, install.Version) {
|
||||
versions = append(versions, dto.AppVersion{
|
||||
Version: detail.Version,
|
||||
@@ -401,10 +450,6 @@ func (a *AppInstallService) ChangeAppPort(req request.PortUpdate) error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := OperateFirewallPort([]int{int(appInstall.Port)}, []int{int(req.Port)}); err != nil {
|
||||
global.LOG.Errorf("allow firewall failed, err: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -452,7 +497,16 @@ func (a *AppInstallService) GetDefaultConfigByKey(key string) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
filePath := path.Join(constant.AppResourceDir, appInstall.App.Key, "versions", appInstall.Version, "conf")
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
filePath := path.Join(constant.AppResourceDir, "remote", appInstall.App.Key, appInstall.Version, "conf")
|
||||
if !fileOp.Stat(filePath) {
|
||||
filePath = path.Join(constant.AppResourceDir, appInstall.App.Key, "versions", appInstall.Version, "conf")
|
||||
}
|
||||
if !fileOp.Stat(filePath) {
|
||||
return "", buserr.New(constant.ErrPathNotFound)
|
||||
}
|
||||
|
||||
if key == constant.AppMysql {
|
||||
filePath = path.Join(filePath, "my.cnf")
|
||||
}
|
||||
@@ -469,11 +523,12 @@ func (a *AppInstallService) GetDefaultConfigByKey(key string) (string, error) {
|
||||
return string(contentByte), nil
|
||||
}
|
||||
|
||||
func (a *AppInstallService) GetParams(id uint) ([]response.AppParam, error) {
|
||||
func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
||||
var (
|
||||
res []response.AppParam
|
||||
params []response.AppParam
|
||||
appForm dto.AppForm
|
||||
envs = make(map[string]interface{})
|
||||
res response.AppConfig
|
||||
)
|
||||
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(id))
|
||||
if err != nil {
|
||||
@@ -515,10 +570,15 @@ func (a *AppInstallService) GetParams(id uint) ([]response.AppParam, error) {
|
||||
}
|
||||
appParam.Values = form.Values
|
||||
}
|
||||
res = append(res, appParam)
|
||||
params = append(params, appParam)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
|
||||
config := getAppCommonConfig(envs)
|
||||
config.DockerCompose = install.DockerCompose
|
||||
res.Params = params
|
||||
res.AppContainerConfig = config
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func syncById(installId uint) error {
|
||||
|
||||
+407
-117
@@ -2,16 +2,22 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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/i18n"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/subosito/gotenv"
|
||||
"gopkg.in/yaml.v3"
|
||||
"math"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -29,6 +35,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
composeV2 "github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -43,7 +50,6 @@ func checkPort(key string, params map[string]interface{}) (int, error) {
|
||||
port, ok := params[key]
|
||||
if ok {
|
||||
portN := int(math.Ceil(port.(float64)))
|
||||
|
||||
oldInstalled, _ := appInstallRepo.ListBy(appInstallRepo.WithPort(portN))
|
||||
if len(oldInstalled) > 0 {
|
||||
var apps []string
|
||||
@@ -225,40 +231,121 @@ func upgradeInstall(installId uint, detailId uint) error {
|
||||
return err
|
||||
}
|
||||
|
||||
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Key, "versions", detail.Version)
|
||||
if install.App.Resource == constant.AppResourceLocal {
|
||||
detailDir = path.Join(constant.ResourceDir, "localApps", strings.TrimPrefix(install.App.Key, "local"), "versions", detail.Version)
|
||||
}
|
||||
install.Status = constant.Upgrading
|
||||
|
||||
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rf %s/* %s", detailDir, install.GetPath()))
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if stdout != nil {
|
||||
return errors.New(string(stdout))
|
||||
}
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
var upErr error
|
||||
defer func() {
|
||||
if upErr != nil {
|
||||
install.Status = constant.UpgradeErr
|
||||
install.Message = upErr.Error()
|
||||
_ = appInstallRepo.Save(context.Background(), &install)
|
||||
}
|
||||
}()
|
||||
|
||||
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
return errors.New(out)
|
||||
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Resource, install.App.Key, detail.Version)
|
||||
if install.App.Resource == constant.AppResourceRemote {
|
||||
if upErr = downloadApp(install.App, detail, &install); upErr != nil {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
_, _ = http.Get(detail.DownloadCallBackUrl)
|
||||
}()
|
||||
}
|
||||
if install.App.Resource == constant.AppResourceLocal {
|
||||
detailDir = path.Join(constant.ResourceDir, "apps", "local", strings.TrimPrefix(install.App.Key, "local"), detail.Version)
|
||||
}
|
||||
return err
|
||||
}
|
||||
install.DockerCompose = detail.DockerCompose
|
||||
install.Version = detail.Version
|
||||
install.AppDetailId = detailId
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
if err := fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); err != nil {
|
||||
return err
|
||||
}
|
||||
if out, err := compose.Up(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
return errors.New(out)
|
||||
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rf %s/* %s", detailDir, install.GetPath()))
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if stdout != nil {
|
||||
upErr = errors.New(string(stdout))
|
||||
return
|
||||
}
|
||||
upErr = err
|
||||
return
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
composeMap := make(map[string]interface{})
|
||||
if upErr = yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); upErr != nil {
|
||||
return
|
||||
}
|
||||
value, ok := composeMap["services"]
|
||||
if !ok {
|
||||
upErr = buserr.New(constant.ErrFileParse)
|
||||
return
|
||||
}
|
||||
servicesMap := value.(map[string]interface{})
|
||||
index := 0
|
||||
oldServiceName := ""
|
||||
for k := range servicesMap {
|
||||
oldServiceName = k
|
||||
index++
|
||||
if index > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
servicesMap[install.ServiceName] = servicesMap[oldServiceName]
|
||||
if install.ServiceName != oldServiceName {
|
||||
delete(servicesMap, oldServiceName)
|
||||
}
|
||||
|
||||
envs := make(map[string]interface{})
|
||||
if upErr = json.Unmarshal([]byte(install.Env), &envs); upErr != nil {
|
||||
return
|
||||
}
|
||||
config := getAppCommonConfig(envs)
|
||||
if config.ContainerName == "" {
|
||||
config.ContainerName = install.ContainerName
|
||||
envs[constant.ContainerName] = install.ContainerName
|
||||
}
|
||||
config.Advanced = true
|
||||
if upErr = addDockerComposeCommonParam(composeMap, install.ServiceName, config, envs); upErr != nil {
|
||||
return
|
||||
}
|
||||
paramByte, upErr := json.Marshal(envs)
|
||||
if upErr != nil {
|
||||
return
|
||||
}
|
||||
install.Env = string(paramByte)
|
||||
composeByte, upErr := yaml.Marshal(composeMap)
|
||||
if upErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
install.DockerCompose = string(composeByte)
|
||||
install.Version = detail.Version
|
||||
install.AppDetailId = detailId
|
||||
|
||||
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
upErr = errors.New(out)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
envParams := make(map[string]string, len(envs))
|
||||
handleMap(envs, envParams)
|
||||
if err = env.Write(envParams, install.GetEnvPath()); err != nil {
|
||||
return
|
||||
}
|
||||
if upErr = fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); upErr != nil {
|
||||
return
|
||||
}
|
||||
if out, err := compose.Up(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
upErr = errors.New(out)
|
||||
return
|
||||
}
|
||||
upErr = err
|
||||
return
|
||||
}
|
||||
install.Status = constant.Running
|
||||
_ = appInstallRepo.Save(context.Background(), &install)
|
||||
}()
|
||||
|
||||
return appInstallRepo.Save(context.Background(), &install)
|
||||
}
|
||||
|
||||
@@ -315,34 +402,6 @@ func checkRequiredAndLimit(app model.App) error {
|
||||
if err := checkLimit(app); err != nil {
|
||||
return err
|
||||
}
|
||||
if app.Required != "" {
|
||||
var requiredArray []string
|
||||
if err := json.Unmarshal([]byte(app.Required), &requiredArray); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, key := range requiredArray {
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
requireApp, err := appRepo.GetFirst(appRepo.WithKey(key))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
details, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(requireApp.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var detailIds []uint
|
||||
for _, d := range details {
|
||||
detailIds = append(detailIds, d.ID)
|
||||
}
|
||||
|
||||
_, err = appInstallRepo.GetFirst(appInstallRepo.WithDetailIdsIn(detailIds))
|
||||
if err != nil {
|
||||
return buserr.WithDetail(constant.ErrAppRequired, requireApp.Name, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -359,24 +418,74 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
|
||||
}
|
||||
}
|
||||
|
||||
func copyAppData(key, version, installName string, params map[string]interface{}, isLocal bool) (err error) {
|
||||
func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall) (err error) {
|
||||
appResourceDir := path.Join(constant.AppResourceDir, app.Resource)
|
||||
appDownloadDir := path.Join(appResourceDir, app.Key)
|
||||
appVersionDir := path.Join(appDownloadDir, appDetail.Version)
|
||||
fileOp := files.NewFileOp()
|
||||
appResourceDir := constant.AppResourceDir
|
||||
installAppDir := path.Join(constant.AppInstallDir, key)
|
||||
appKey := key
|
||||
if isLocal {
|
||||
if !appDetail.Update && fileOp.Stat(appVersionDir) {
|
||||
return
|
||||
}
|
||||
if !fileOp.Stat(appDownloadDir) {
|
||||
_ = fileOp.CreateDir(appDownloadDir, 0755)
|
||||
}
|
||||
if !fileOp.Stat(appVersionDir) {
|
||||
_ = fileOp.CreateDir(appVersionDir, 0755)
|
||||
}
|
||||
global.LOG.Infof("download app[%s] from %s", app.Name, appDetail.DownloadUrl)
|
||||
filePath := path.Join(appVersionDir, appDetail.Version+".tar.gz")
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if appInstall != nil {
|
||||
appInstall.Status = constant.DownloadErr
|
||||
appInstall.Message = err.Error()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err = fileOp.DownloadFile(appDetail.DownloadUrl, filePath); err != nil {
|
||||
global.LOG.Errorf("download app[%s] error %v", app.Name, err)
|
||||
return
|
||||
}
|
||||
if err = fileOp.Decompress(filePath, appVersionDir, files.TarGz); err != nil {
|
||||
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err)
|
||||
return
|
||||
}
|
||||
_ = fileOp.DeleteFile(filePath)
|
||||
appDetail.Update = false
|
||||
_ = appDetailRepo.Update(context.Background(), appDetail)
|
||||
return
|
||||
}
|
||||
|
||||
func copyData(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall, req request.AppInstallCreate) (err error) {
|
||||
fileOp := files.NewFileOp()
|
||||
appResourceDir := path.Join(constant.AppResourceDir, app.Resource)
|
||||
|
||||
if app.Resource == constant.AppResourceRemote {
|
||||
err = downloadApp(app, appDetail, appInstall)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
_, _ = http.Get(appDetail.DownloadCallBackUrl)
|
||||
}()
|
||||
}
|
||||
appKey := app.Key
|
||||
installAppDir := path.Join(constant.AppInstallDir, app.Key)
|
||||
if app.Resource == constant.AppResourceLocal {
|
||||
appResourceDir = constant.LocalAppResourceDir
|
||||
appKey = strings.TrimPrefix(key, "local")
|
||||
appKey = strings.TrimPrefix(app.Key, "local")
|
||||
installAppDir = path.Join(constant.LocalAppInstallDir, appKey)
|
||||
}
|
||||
resourceDir := path.Join(appResourceDir, appKey, "versions", version)
|
||||
resourceDir := path.Join(appResourceDir, appKey, appDetail.Version)
|
||||
|
||||
if !fileOp.Stat(installAppDir) {
|
||||
if err = fileOp.CreateDir(installAppDir, 0755); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
appDir := path.Join(installAppDir, installName)
|
||||
appDir := path.Join(installAppDir, req.Name)
|
||||
if fileOp.Stat(appDir) {
|
||||
if err = fileOp.DeleteDir(appDir); err != nil {
|
||||
return
|
||||
@@ -385,17 +494,20 @@ func copyAppData(key, version, installName string, params map[string]interface{}
|
||||
if err = fileOp.Copy(resourceDir, installAppDir); err != nil {
|
||||
return
|
||||
}
|
||||
versionDir := path.Join(installAppDir, version)
|
||||
versionDir := path.Join(installAppDir, appDetail.Version)
|
||||
if err = fileOp.Rename(versionDir, appDir); err != nil {
|
||||
return
|
||||
}
|
||||
envPath := path.Join(appDir, ".env")
|
||||
|
||||
envParams := make(map[string]string, len(params))
|
||||
handleMap(params, envParams)
|
||||
envParams := make(map[string]string, len(req.Params))
|
||||
handleMap(req.Params, envParams)
|
||||
if err = env.Write(envParams, envPath); err != nil {
|
||||
return
|
||||
}
|
||||
if err := fileOp.WriteFile(appInstall.GetComposePath(), strings.NewReader(appInstall.DockerCompose), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -431,6 +543,31 @@ func getServiceFromInstall(appInstall *model.AppInstall) (service *composeV2.Com
|
||||
return
|
||||
}
|
||||
|
||||
func checkContainerNameIsExist(containerName, appDir string) (bool, error) {
|
||||
client, err := composeV2.NewDockerClient()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
var options dockerTypes.ContainerListOptions
|
||||
list, err := client.ContainerList(context.Background(), options)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, container := range list {
|
||||
if containerName == container.Names[0][1:] {
|
||||
if workDir, ok := container.Labels[composeWorkdirLabel]; ok {
|
||||
if workDir != appDir {
|
||||
return true, nil
|
||||
}
|
||||
} else {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func upApp(appInstall *model.AppInstall) {
|
||||
upProject := func(appInstall *model.AppInstall) (err error) {
|
||||
if err == nil {
|
||||
@@ -473,21 +610,21 @@ func rebuildApp(appInstall model.AppInstall) error {
|
||||
return syncById(appInstall.ID)
|
||||
}
|
||||
|
||||
func getAppDetails(details []model.AppDetail, versions []string) map[string]model.AppDetail {
|
||||
func getAppDetails(details []model.AppDetail, versions []dto.AppConfigVersion) map[string]model.AppDetail {
|
||||
appDetails := make(map[string]model.AppDetail, len(details))
|
||||
for _, old := range details {
|
||||
old.Status = constant.AppTakeDown
|
||||
appDetails[old.Version] = old
|
||||
}
|
||||
|
||||
for _, v := range versions {
|
||||
detail, ok := appDetails[v]
|
||||
version := v.Name
|
||||
detail, ok := appDetails[version]
|
||||
if ok {
|
||||
detail.Status = constant.AppNormal
|
||||
appDetails[v] = detail
|
||||
appDetails[version] = detail
|
||||
} else {
|
||||
appDetails[v] = model.AppDetail{
|
||||
Version: v,
|
||||
appDetails[version] = model.AppDetail{
|
||||
Version: version,
|
||||
Status: constant.AppNormal,
|
||||
}
|
||||
}
|
||||
@@ -495,43 +632,110 @@ func getAppDetails(details []model.AppDetail, versions []string) map[string]mode
|
||||
return appDetails
|
||||
}
|
||||
|
||||
func getApps(oldApps []model.App, items []dto.AppDefine, isLocal bool) map[string]model.App {
|
||||
func getApps(oldApps []model.App, items []dto.AppDefine) map[string]model.App {
|
||||
apps := make(map[string]model.App, len(oldApps))
|
||||
for _, old := range oldApps {
|
||||
old.Status = constant.AppTakeDown
|
||||
apps[old.Key] = old
|
||||
}
|
||||
for _, item := range items {
|
||||
key := item.Key
|
||||
if isLocal {
|
||||
key = "local" + key
|
||||
}
|
||||
config := item.AppProperty
|
||||
key := config.Key
|
||||
app, ok := apps[key]
|
||||
if !ok {
|
||||
app = model.App{}
|
||||
}
|
||||
if isLocal {
|
||||
app.Resource = constant.AppResourceLocal
|
||||
} else {
|
||||
app.Resource = constant.AppResourceRemote
|
||||
}
|
||||
app.Resource = constant.AppResourceRemote
|
||||
app.Name = item.Name
|
||||
app.Limit = item.Limit
|
||||
app.Limit = config.Limit
|
||||
app.Key = key
|
||||
app.ShortDescZh = item.ShortDescZh
|
||||
app.ShortDescEn = item.ShortDescEn
|
||||
app.Website = item.Website
|
||||
app.Document = item.Document
|
||||
app.Github = item.Github
|
||||
app.Type = item.Type
|
||||
app.CrossVersionUpdate = item.CrossVersionUpdate
|
||||
app.Required = item.GetRequired()
|
||||
app.ShortDescZh = config.ShortDescZh
|
||||
app.ShortDescEn = config.ShortDescEn
|
||||
app.Website = config.Website
|
||||
app.Document = config.Document
|
||||
app.Github = config.Github
|
||||
app.Type = config.Type
|
||||
app.CrossVersionUpdate = config.CrossVersionUpdate
|
||||
app.Status = constant.AppNormal
|
||||
app.LastModified = item.LastModified
|
||||
app.ReadMe = item.ReadMe
|
||||
apps[key] = app
|
||||
}
|
||||
return apps
|
||||
}
|
||||
|
||||
func handleLocalAppDetail(versionDir string, appDetail *model.AppDetail) error {
|
||||
fileOp := files.NewFileOp()
|
||||
dockerComposePath := path.Join(versionDir, "docker-compose.yml")
|
||||
if !fileOp.Stat(dockerComposePath) {
|
||||
return errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "docker-compose.yml"}))
|
||||
}
|
||||
dockerComposeByte, _ := fileOp.GetContent(dockerComposePath)
|
||||
if dockerComposeByte == nil {
|
||||
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "docker-compose.yml"}))
|
||||
}
|
||||
appDetail.DockerCompose = string(dockerComposeByte)
|
||||
paramPath := path.Join(versionDir, "data.yml")
|
||||
if !fileOp.Stat(paramPath) {
|
||||
return errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "data.yml"}))
|
||||
}
|
||||
paramByte, _ := fileOp.GetContent(paramPath)
|
||||
if paramByte == nil {
|
||||
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml"}))
|
||||
}
|
||||
appParamConfig := dto.LocalAppParam{}
|
||||
if err := yaml.Unmarshal(paramByte, &appParamConfig); err != nil {
|
||||
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml"}))
|
||||
}
|
||||
dataJson, err := json.Marshal(appParamConfig.AppParams)
|
||||
if err != nil {
|
||||
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
|
||||
}
|
||||
appDetail.Params = string(dataJson)
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleLocalApp(appDir string) (app *model.App, err error) {
|
||||
fileOp := files.NewFileOp()
|
||||
configYamlPath := path.Join(appDir, "data.yml")
|
||||
if !fileOp.Stat(configYamlPath) {
|
||||
err = errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "data.yml"}))
|
||||
return
|
||||
}
|
||||
iconPath := path.Join(appDir, "logo.png")
|
||||
if !fileOp.Stat(iconPath) {
|
||||
err = errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "logo.png"}))
|
||||
return
|
||||
}
|
||||
configYamlByte, err := fileOp.GetContent(configYamlPath)
|
||||
if err != nil {
|
||||
err = errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
|
||||
return
|
||||
}
|
||||
localAppDefine := dto.LocalAppAppDefine{}
|
||||
if err = yaml.Unmarshal(configYamlByte, &localAppDefine); err != nil {
|
||||
err = errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
|
||||
return
|
||||
}
|
||||
app = &localAppDefine.AppProperty
|
||||
app.Resource = constant.AppResourceLocal
|
||||
app.Status = constant.AppNormal
|
||||
app.Recommend = 9999
|
||||
app.TagsKey = append(app.TagsKey, "Local")
|
||||
app.Key = "local" + app.Key
|
||||
readMePath := path.Join(appDir, "README.md")
|
||||
readMeByte, err := fileOp.GetContent(readMePath)
|
||||
if err == nil {
|
||||
app.ReadMe = string(readMeByte)
|
||||
}
|
||||
iconByte, _ := fileOp.GetContent(iconPath)
|
||||
if iconByte != nil {
|
||||
iconStr := base64.StdEncoding.EncodeToString(iconByte)
|
||||
app.Icon = iconStr
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func handleErr(install model.AppInstall, err error, out string) error {
|
||||
reErr := err
|
||||
install.Message = err.Error()
|
||||
@@ -544,29 +748,6 @@ func handleErr(install model.AppInstall, err error, out string) error {
|
||||
return reErr
|
||||
}
|
||||
|
||||
func getAppFromRepo(downloadPath, version string) error {
|
||||
downloadUrl := downloadPath
|
||||
appDir := constant.AppResourceDir
|
||||
|
||||
global.LOG.Infof("download file from %s", downloadUrl)
|
||||
fileOp := files.NewFileOp()
|
||||
if _, err := fileOp.CopyAndBackup(appDir); err != nil {
|
||||
return err
|
||||
}
|
||||
packagePath := path.Join(constant.ResourceDir, path.Base(downloadUrl))
|
||||
if err := fileOp.DownloadFile(downloadUrl, packagePath); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.Decompress(packagePath, constant.ResourceDir, files.TarGz); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = NewISettingService().Update("AppStoreVersion", version)
|
||||
defer func() {
|
||||
_ = fileOp.DeleteFile(packagePath)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]response.AppInstalledDTO, error) {
|
||||
var res []response.AppInstalledDTO
|
||||
for _, installed := range appInstallList {
|
||||
@@ -575,6 +756,7 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]respons
|
||||
}
|
||||
installDTO := response.AppInstalledDTO{
|
||||
AppInstall: installed,
|
||||
Path: installed.GetPath(),
|
||||
}
|
||||
app, err := appRepo.GetFirst(commonRepo.WithByID(installed.AppId))
|
||||
if err != nil {
|
||||
@@ -586,6 +768,9 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]respons
|
||||
}
|
||||
var versions []string
|
||||
for _, detail := range details {
|
||||
if common.IsCrossVersion(installed.Version, detail.Version) && !app.CrossVersionUpdate {
|
||||
continue
|
||||
}
|
||||
versions = append(versions, detail.Version)
|
||||
}
|
||||
versions = common.GetSortedVersions(versions)
|
||||
@@ -667,3 +852,108 @@ func updateToolApp(installed *model.AppInstall) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName string, req request.AppContainerConfig, params map[string]interface{}) error {
|
||||
services, serviceValid := composeMap["services"].(map[string]interface{})
|
||||
if !serviceValid {
|
||||
return buserr.New(constant.ErrFileParse)
|
||||
}
|
||||
service, serviceExist := services[serviceName]
|
||||
if !serviceExist {
|
||||
return buserr.New(constant.ErrFileParse)
|
||||
}
|
||||
serviceValue := service.(map[string]interface{})
|
||||
deploy := map[string]interface{}{
|
||||
"resources": map[string]interface{}{
|
||||
"limits": map[string]interface{}{
|
||||
"cpus": "${CPUS}",
|
||||
"memory": "${MEMORY_LIMIT}",
|
||||
},
|
||||
},
|
||||
}
|
||||
serviceValue["deploy"] = deploy
|
||||
|
||||
ports, ok := serviceValue["ports"].([]interface{})
|
||||
if ok {
|
||||
for i, port := range ports {
|
||||
portStr, portOK := port.(string)
|
||||
if !portOK {
|
||||
continue
|
||||
}
|
||||
portArray := strings.Split(portStr, ":")
|
||||
if len(portArray) == 2 {
|
||||
portArray = append([]string{"${HOST_IP}"}, portArray...)
|
||||
}
|
||||
ports[i] = strings.Join(portArray, ":")
|
||||
}
|
||||
serviceValue["ports"] = ports
|
||||
}
|
||||
|
||||
params[constant.CPUS] = "0"
|
||||
params[constant.MemoryLimit] = "0"
|
||||
if req.Advanced {
|
||||
if req.CpuQuota > 0 {
|
||||
params[constant.CPUS] = req.CpuQuota
|
||||
}
|
||||
if req.MemoryLimit > 0 {
|
||||
params[constant.MemoryLimit] = strconv.FormatFloat(req.MemoryLimit, 'f', -1, 32) + req.MemoryUnit
|
||||
}
|
||||
}
|
||||
_, portExist := serviceValue["ports"].([]interface{})
|
||||
if portExist {
|
||||
allowHost := "127.0.0.1"
|
||||
if req.Advanced && req.AllowPort {
|
||||
allowHost = "0.0.0.0"
|
||||
}
|
||||
params[constant.HostIP] = allowHost
|
||||
}
|
||||
services[serviceName] = serviceValue
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAppCommonConfig(envs map[string]interface{}) request.AppContainerConfig {
|
||||
config := request.AppContainerConfig{}
|
||||
|
||||
if hostIp, ok := envs[constant.HostIP]; ok {
|
||||
config.AllowPort = hostIp.(string) == "0.0.0.0"
|
||||
} else {
|
||||
config.AllowPort = true
|
||||
}
|
||||
if cpuCore, ok := envs[constant.CPUS]; ok {
|
||||
numStr, ok := cpuCore.(string)
|
||||
if ok {
|
||||
num, err := strconv.ParseFloat(numStr, 64)
|
||||
if err == nil {
|
||||
config.CpuQuota = num
|
||||
}
|
||||
} else {
|
||||
num64, flOk := cpuCore.(float64)
|
||||
if flOk {
|
||||
config.CpuQuota = num64
|
||||
}
|
||||
}
|
||||
} else {
|
||||
config.CpuQuota = 0
|
||||
}
|
||||
if memLimit, ok := envs[constant.MemoryLimit]; ok {
|
||||
re := regexp.MustCompile(`(\d+)([A-Za-z]+)`)
|
||||
matches := re.FindStringSubmatch(memLimit.(string))
|
||||
if len(matches) == 3 {
|
||||
num, err := strconv.ParseFloat(matches[1], 64)
|
||||
if err == nil {
|
||||
unit := matches[2]
|
||||
config.MemoryLimit = num
|
||||
config.MemoryUnit = unit
|
||||
}
|
||||
}
|
||||
} else {
|
||||
config.MemoryLimit = 0
|
||||
config.MemoryUnit = "M"
|
||||
}
|
||||
|
||||
if containerName, ok := envs[constant.ContainerName]; ok {
|
||||
config.ContainerName = containerName.(string)
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
+13
-10
@@ -17,7 +17,7 @@ import (
|
||||
type AuthService struct{}
|
||||
|
||||
type IAuthService interface {
|
||||
CheckIsSafety(code string) bool
|
||||
CheckIsSafety(code string) (string, error)
|
||||
VerifyCode(code string) (bool, error)
|
||||
Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error)
|
||||
LogOut(c *gin.Context) error
|
||||
@@ -33,11 +33,11 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo,
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
|
||||
}
|
||||
passwrodSetting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
|
||||
passwordSetting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
|
||||
}
|
||||
pass, err := encrypt.StringDecrypt(passwrodSetting.Value)
|
||||
pass, err := encrypt.StringDecrypt(passwordSetting.Value)
|
||||
if err != nil {
|
||||
return nil, constant.ErrAuth
|
||||
}
|
||||
@@ -60,11 +60,11 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin) (*dto.UserLogi
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
|
||||
}
|
||||
passwrodSetting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
|
||||
passwordSetting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
|
||||
}
|
||||
pass, err := encrypt.StringDecrypt(passwrodSetting.Value)
|
||||
pass, err := encrypt.StringDecrypt(passwordSetting.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -109,7 +109,7 @@ func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) (
|
||||
sessionUser, err := global.SESSION.Get(sID)
|
||||
if err != nil {
|
||||
sID = uuid.New().String()
|
||||
c.SetCookie(constant.SessionName, sID, 604800, "", "", false, false)
|
||||
c.SetCookie(constant.SessionName, sID, 0, "", "", false, false)
|
||||
err := global.SESSION.Set(sID, sessionUser, lifeTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -143,13 +143,16 @@ func (u *AuthService) VerifyCode(code string) (bool, error) {
|
||||
return setting.Value == code, nil
|
||||
}
|
||||
|
||||
func (u *AuthService) CheckIsSafety(code string) bool {
|
||||
func (u *AuthService) CheckIsSafety(code string) (string, error) {
|
||||
status, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
|
||||
if err != nil {
|
||||
return false
|
||||
return "", err
|
||||
}
|
||||
if len(status.Value) == 0 {
|
||||
return true
|
||||
return "disable", nil
|
||||
}
|
||||
return status.Value == code
|
||||
if status.Value == code {
|
||||
return "pass", nil
|
||||
}
|
||||
return "unpass", nil
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
|
||||
return err
|
||||
}
|
||||
|
||||
appPath := fmt.Sprintf("%s/%s", install.GetPath(), install.Name)
|
||||
appPath := install.GetPath()
|
||||
if err := handleTar(appPath, tmpDir, "app.tar.gz", ""); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -148,7 +148,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
if err := json.Unmarshal(appjson, &oldInstall); err != nil {
|
||||
return fmt.Errorf("unmarshal app.json failed, err: %v", err)
|
||||
}
|
||||
if oldInstall.App.Key != install.App.Key || oldInstall.Name != install.Name || oldInstall.Version != install.Version || oldInstall.ID != install.ID {
|
||||
if oldInstall.App.Key != install.App.Key || oldInstall.Name != install.Name {
|
||||
return errors.New("the current backup file does not match the application")
|
||||
}
|
||||
|
||||
@@ -172,6 +172,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
}()
|
||||
}
|
||||
|
||||
newEnvFile := ""
|
||||
resource, _ := appInstallResourceRepo.GetFirst(appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||
if resource.ID != 0 && install.App.Key != "mysql" {
|
||||
mysqlInfo, err := appInstallRepo.LoadBaseInfo(resource.Key, "")
|
||||
@@ -182,7 +183,22 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handleMysqlRecover(mysqlInfo, tmpPath, db.Name, fmt.Sprintf("%s.sql.gz", install.Name), true); err != nil {
|
||||
|
||||
newDB, envMap, err := reCreateDB(db.ID, oldInstall.Env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldHost := fmt.Sprintf("\"PANEL_DB_HOST\":\"%v\"", envMap["PANEL_DB_HOST"].(string))
|
||||
newHost := fmt.Sprintf("\"PANEL_DB_HOST\":\"%v\"", mysqlInfo.ServiceName)
|
||||
oldInstall.Env = strings.ReplaceAll(oldInstall.Env, oldHost, newHost)
|
||||
envMap["PANEL_DB_HOST"] = mysqlInfo.ServiceName
|
||||
newEnvFile, err = coverEnvJsonToStr(oldInstall.Env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = appInstallResourceRepo.BatchUpdateBy(map[string]interface{}{"resource_id": newDB.ID}, commonRepo.WithByID(resource.ID))
|
||||
|
||||
if err := handleMysqlRecover(mysqlInfo, tmpPath, newDB.Name, fmt.Sprintf("%s.sql.gz", install.Name), true); err != nil {
|
||||
global.LOG.Errorf("handle recover from sql.gz failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
@@ -193,11 +209,50 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
return err
|
||||
}
|
||||
|
||||
oldInstall.Status = constant.Running
|
||||
if err := appInstallRepo.Save(context.Background(), install); err != nil {
|
||||
if len(newEnvFile) != 0 {
|
||||
envPath := fmt.Sprintf("%s/%s/%s/.env", constant.AppInstallDir, install.App.Key, install.Name)
|
||||
file, err := os.OpenFile(envPath, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
_, _ = file.WriteString(newEnvFile)
|
||||
}
|
||||
|
||||
oldInstall.ID = install.ID
|
||||
oldInstall.Status = constant.StatusRunning
|
||||
oldInstall.AppId = install.AppId
|
||||
oldInstall.AppDetailId = install.AppDetailId
|
||||
if err := appInstallRepo.Save(context.Background(), &oldInstall); err != nil {
|
||||
global.LOG.Errorf("save db app install failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
isOk = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func reCreateDB(dbID uint, oldEnv string) (*model.DatabaseMysql, map[string]interface{}, error) {
|
||||
mysqlService := NewIMysqlService()
|
||||
ctx := context.Background()
|
||||
_ = mysqlService.Delete(ctx, dto.MysqlDBDelete{ID: dbID, DeleteBackup: true, ForceDelete: true})
|
||||
|
||||
envMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(oldEnv), &envMap); err != nil {
|
||||
return nil, envMap, err
|
||||
}
|
||||
oldName, _ := envMap["PANEL_DB_NAME"].(string)
|
||||
oldUser, _ := envMap["PANEL_DB_USER"].(string)
|
||||
oldPassword, _ := envMap["PANEL_DB_USER_PASSWORD"].(string)
|
||||
createDB, err := mysqlService.Create(context.Background(), dto.MysqlDBCreate{
|
||||
Name: oldName,
|
||||
Format: "utf8mb4",
|
||||
Username: oldUser,
|
||||
Password: oldPassword,
|
||||
Permission: "%",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, envMap, err
|
||||
}
|
||||
|
||||
return createDB, envMap, nil
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ func (u *BackupService) RedisBackup() error {
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
fileName := fmt.Sprintf("%s.rdb", timeNow)
|
||||
if appendonly == "yes" {
|
||||
if redisInfo.Version == "6.0.16" {
|
||||
if strings.HasPrefix(redisInfo.Version, "6.") {
|
||||
fileName = fmt.Sprintf("%s.aof", timeNow)
|
||||
} else {
|
||||
fileName = fmt.Sprintf("%s.tar.gz", timeNow)
|
||||
@@ -120,10 +120,10 @@ func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback
|
||||
}
|
||||
|
||||
if appendonly == "yes" {
|
||||
if redisInfo.Version == "6.0.16" && !strings.HasSuffix(recoverFile, ".aof") {
|
||||
if strings.HasPrefix(redisInfo.Version, "6.") && !strings.HasSuffix(recoverFile, ".aof") {
|
||||
return buserr.New(constant.ErrTypeOfRedis)
|
||||
}
|
||||
if redisInfo.Version == "7.0.5" && !strings.HasSuffix(recoverFile, ".tar.gz") {
|
||||
if strings.HasPrefix(redisInfo.Version, "7.") && !strings.HasSuffix(recoverFile, ".tar.gz") {
|
||||
return buserr.New(constant.ErrTypeOfRedis)
|
||||
}
|
||||
} else {
|
||||
@@ -137,7 +137,7 @@ func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback
|
||||
if !isRollback {
|
||||
suffix := "rdb"
|
||||
if appendonly == "yes" {
|
||||
if redisInfo.Version == "6.0.16" {
|
||||
if strings.HasPrefix(redisInfo.Version, "6.") {
|
||||
suffix = "aof"
|
||||
} else {
|
||||
suffix = "tar.gz"
|
||||
@@ -165,14 +165,14 @@ func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback
|
||||
if _, err := compose.Down(composeDir + "/docker-compose.yml"); err != nil {
|
||||
return err
|
||||
}
|
||||
if appendonly == "yes" && redisInfo.Version == "7.0.5" {
|
||||
if appendonly == "yes" && strings.HasPrefix(redisInfo.Version, "7.") {
|
||||
redisDataDir := fmt.Sprintf("%s/%s/%s/data", constant.AppInstallDir, "redis", redisInfo.Name)
|
||||
if err := handleUnTar(recoverFile, redisDataDir); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
itemName := "dump.rdb"
|
||||
if appendonly == "yes" && redisInfo.Version == "6.0.16" {
|
||||
if appendonly == "yes" && strings.HasPrefix(redisInfo.Version, "6.") {
|
||||
itemName = "appendonly.aof"
|
||||
}
|
||||
input, err := os.ReadFile(recoverFile)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -39,16 +40,18 @@ type IContainerService interface {
|
||||
CreateCompose(req dto.ComposeCreate) (string, error)
|
||||
ComposeOperation(req dto.ComposeOperation) error
|
||||
ContainerCreate(req dto.ContainerCreate) error
|
||||
ContainerLogClean(req dto.OperationWithName) error
|
||||
ContainerOperation(req dto.ContainerOperation) error
|
||||
ContainerLogs(param dto.ContainerLog) (string, error)
|
||||
ContainerStats(id string) (*dto.ContainterStats, error)
|
||||
Inspect(req dto.InspectReq) (string, error)
|
||||
DeleteNetwork(req dto.BatchDelete) error
|
||||
CreateNetwork(req dto.NetworkCreat) error
|
||||
CreateNetwork(req dto.NetworkCreate) error
|
||||
DeleteVolume(req dto.BatchDelete) error
|
||||
CreateVolume(req dto.VolumeCreat) error
|
||||
CreateVolume(req dto.VolumeCreate) error
|
||||
TestCompose(req dto.ComposeCreate) (bool, error)
|
||||
ComposeUpdate(req dto.ComposeUpdate) error
|
||||
Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error)
|
||||
}
|
||||
|
||||
func NewIContainerService() IContainerService {
|
||||
@@ -75,11 +78,11 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
return 0, nil, err
|
||||
}
|
||||
if len(req.Name) != 0 {
|
||||
lenth, count := len(list), 0
|
||||
for count < lenth {
|
||||
length, count := len(list), 0
|
||||
for count < length {
|
||||
if !strings.Contains(list[count].Names[0][1:], req.Name) {
|
||||
list = append(list[:count], list[(count+1):]...)
|
||||
lenth--
|
||||
length--
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
@@ -165,6 +168,51 @@ func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error) {
|
||||
report := dto.ContainerPruneReport{}
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
pruneFilters := filters.NewArgs()
|
||||
if req.WithTagAll {
|
||||
pruneFilters.Add("dangling", "false")
|
||||
if req.PruneType != "image" {
|
||||
pruneFilters.Add("until", "24h")
|
||||
}
|
||||
}
|
||||
switch req.PruneType {
|
||||
case "container":
|
||||
rep, err := client.ContainersPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
report.DeletedNumber = len(rep.ContainersDeleted)
|
||||
report.SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||
case "image":
|
||||
rep, err := client.ImagesPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
report.DeletedNumber = len(rep.ImagesDeleted)
|
||||
report.SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||
case "network":
|
||||
rep, err := client.NetworksPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
report.DeletedNumber = len(rep.NetworksDeleted)
|
||||
case "volume":
|
||||
rep, err := client.VolumesPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
report.DeletedNumber = len(rep.VolumesDeleted)
|
||||
report.SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||
}
|
||||
return report, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
|
||||
portMap, err := checkPortStats(req.ExposedPorts)
|
||||
if err != nil {
|
||||
@@ -263,6 +311,27 @@ func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error
|
||||
return err
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
container, err := client.ContainerInspect(context.Background(), req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file, err := os.OpenFile(container.LogPath, os.O_RDWR|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
if err = file.Truncate(0); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = file.Seek(0, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerLogs(req dto.ContainerLog) (string, error) {
|
||||
cmd := exec.Command("docker", "logs", req.ContainerID)
|
||||
if req.Mode != "all" {
|
||||
|
||||
@@ -101,11 +101,11 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
||||
records = append(records, value)
|
||||
}
|
||||
if len(req.Info) != 0 {
|
||||
lenth, count := len(records), 0
|
||||
for count < lenth {
|
||||
length, count := len(records), 0
|
||||
for count < length {
|
||||
if !strings.Contains(records[count].Name, req.Info) {
|
||||
records = append(records[:count], records[(count+1):]...)
|
||||
lenth--
|
||||
length--
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@ func (u *ContainerService) PageNetwork(req dto.SearchWithPage) (int64, interface
|
||||
return 0, nil, err
|
||||
}
|
||||
if len(req.Info) != 0 {
|
||||
lenth, count := len(list), 0
|
||||
for count < lenth {
|
||||
length, count := len(list), 0
|
||||
for count < length {
|
||||
if !strings.Contains(list[count].Name, req.Info) {
|
||||
list = append(list[:count], list[(count+1):]...)
|
||||
lenth--
|
||||
length--
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
@@ -90,7 +90,7 @@ func (u *ContainerService) DeleteNetwork(req dto.BatchDelete) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (u *ContainerService) CreateNetwork(req dto.NetworkCreat) error {
|
||||
func (u *ContainerService) CreateNetwork(req dto.NetworkCreate) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -24,11 +24,11 @@ func (u *ContainerService) PageVolume(req dto.SearchWithPage) (int64, interface{
|
||||
return 0, nil, err
|
||||
}
|
||||
if len(req.Info) != 0 {
|
||||
lenth, count := len(list.Volumes), 0
|
||||
for count < lenth {
|
||||
length, count := len(list.Volumes), 0
|
||||
for count < length {
|
||||
if !strings.Contains(list.Volumes[count].Name, req.Info) {
|
||||
list.Volumes = append(list.Volumes[:count], list.Volumes[(count+1):]...)
|
||||
lenth--
|
||||
length--
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
@@ -103,7 +103,7 @@ func (u *ContainerService) DeleteVolume(req dto.BatchDelete) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (u *ContainerService) CreateVolume(req dto.VolumeCreat) error {
|
||||
func (u *ContainerService) CreateVolume(req dto.VolumeCreate) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -53,9 +53,9 @@ func (u *CronjobService) SearchWithPage(search dto.SearchWithPage) (int64, inter
|
||||
}
|
||||
record, _ := cronjobRepo.RecordFirst(cronjob.ID)
|
||||
if record.ID != 0 {
|
||||
item.LastRecrodTime = record.StartTime.Format("2006-01-02 15:04:05")
|
||||
item.LastRecordTime = record.StartTime.Format("2006-01-02 15:04:05")
|
||||
} else {
|
||||
item.LastRecrodTime = "-"
|
||||
item.LastRecordTime = "-"
|
||||
}
|
||||
dtoCronjobs = append(dtoCronjobs, item)
|
||||
}
|
||||
@@ -85,7 +85,7 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req.CleanData && cronjob.Type != "shell" && cronjob.Type != "curl" {
|
||||
if req.CleanData && (cronjob.Type == "database" || cronjob.Type == "website" || cronjob.Type == "directory") {
|
||||
cronjob.RetainCopies = 0
|
||||
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
|
||||
if err != nil {
|
||||
@@ -190,7 +190,6 @@ func (u *CronjobService) StartJob(cronjob *model.Cronjob) (int, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
global.LOG.Debug(global.Cron.Entries())
|
||||
return entryID, nil
|
||||
}
|
||||
|
||||
@@ -222,16 +221,20 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
|
||||
if err != nil {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
upMap := make(map[string]interface{})
|
||||
cronjob.EntryID = cronModel.EntryID
|
||||
cronjob.Type = cronModel.Type
|
||||
cronjob.Spec = loadSpec(cronjob)
|
||||
newEntryID, err := u.StartJob(&cronjob)
|
||||
if err != nil {
|
||||
return err
|
||||
if cronModel.Status == constant.StatusEnable {
|
||||
newEntryID, err := u.StartJob(&cronjob)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
upMap["entry_id"] = newEntryID
|
||||
} else {
|
||||
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
|
||||
}
|
||||
|
||||
upMap := make(map[string]interface{})
|
||||
upMap["entry_id"] = newEntryID
|
||||
upMap["name"] = req.Name
|
||||
upMap["spec"] = cronjob.Spec
|
||||
upMap["script"] = req.Script
|
||||
@@ -240,6 +243,7 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
|
||||
upMap["day"] = req.Day
|
||||
upMap["hour"] = req.Hour
|
||||
upMap["minute"] = req.Minute
|
||||
upMap["second"] = req.Second
|
||||
upMap["website"] = req.Website
|
||||
upMap["exclusion_rules"] = req.ExclusionRules
|
||||
upMap["db_name"] = req.DBName
|
||||
@@ -322,6 +326,8 @@ func loadSpec(cronjob model.Cronjob) string {
|
||||
return fmt.Sprintf("%v * * * *", cronjob.Minute)
|
||||
case "perNMinute":
|
||||
return fmt.Sprintf("@every %vm", cronjob.Minute)
|
||||
case "perNSecond":
|
||||
return fmt.Sprintf("@every %vs", cronjob.Second)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
@@ -14,6 +15,8 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -29,31 +32,31 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
if len(cronjob.Script) == 0 {
|
||||
return
|
||||
}
|
||||
stdout, errExec := cmd.ExecCronjobWithTimeOut(cronjob.Script, 5*time.Minute)
|
||||
if errExec != nil {
|
||||
err = errExec
|
||||
}
|
||||
message = []byte(stdout)
|
||||
message, err = u.handleShell(cronjob.Type, cronjob.Name, cronjob.Script)
|
||||
u.HandleRmExpired("LOCAL", "", cronjob, nil)
|
||||
case "website":
|
||||
record.File, err = u.HandleBackup(cronjob, record.StartTime)
|
||||
case "database":
|
||||
record.File, err = u.HandleBackup(cronjob, record.StartTime)
|
||||
case "directory":
|
||||
if len(cronjob.SourceDir) == 0 {
|
||||
return
|
||||
}
|
||||
record.File, err = u.HandleBackup(cronjob, record.StartTime)
|
||||
case "curl":
|
||||
if len(cronjob.URL) == 0 {
|
||||
return
|
||||
}
|
||||
stdout, errCurl := cmd.ExecWithTimeOut("curl "+cronjob.URL, 5*time.Minute)
|
||||
if err != nil {
|
||||
err = errCurl
|
||||
}
|
||||
message = []byte(stdout)
|
||||
message, err = u.handleShell(cronjob.Type, cronjob.Name, fmt.Sprintf("curl '%s'", cronjob.URL))
|
||||
u.HandleRmExpired("LOCAL", "", cronjob, nil)
|
||||
case "ntp":
|
||||
err = u.handleNtpSync()
|
||||
u.HandleRmExpired("LOCAL", "", cronjob, nil)
|
||||
case "website":
|
||||
record.File, err = u.handleBackup(cronjob, record.StartTime)
|
||||
case "database":
|
||||
record.File, err = u.handleBackup(cronjob, record.StartTime)
|
||||
case "directory":
|
||||
if len(cronjob.SourceDir) == 0 {
|
||||
return
|
||||
}
|
||||
record.File, err = u.handleBackup(cronjob, record.StartTime)
|
||||
case "cutWebsiteLog":
|
||||
record.File, err = u.handleCutWebsiteLog(cronjob, record.StartTime)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("cut website log file failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), string(message))
|
||||
@@ -69,7 +72,36 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
}()
|
||||
}
|
||||
|
||||
func (u *CronjobService) HandleBackup(cronjob *model.Cronjob, startTime time.Time) (string, error) {
|
||||
func (u *CronjobService) handleShell(cronType, cornName, script string) ([]byte, error) {
|
||||
handleDir := fmt.Sprintf("%s/task/%s/%s", constant.DataDir, cronType, cornName)
|
||||
if _, err := os.Stat(handleDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(handleDir, os.ModePerm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
stdout, err := cmd.ExecCronjobWithTimeOut(script, handleDir, 24*time.Hour)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(stdout), nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleNtpSync() error {
|
||||
ntpServer, err := settingRepo.Get(settingRepo.WithByKey("NtpSite"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ntime, err := ntp.GetRemoteTime(ntpServer.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ntp.UpdateSystemTime(ntime.Format("2006-01-02 15:04:05")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleBackup(cronjob *model.Cronjob, startTime time.Time) (string, error) {
|
||||
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -127,15 +159,17 @@ func (u *CronjobService) HandleRmExpired(backType, localDir string, cronjob *mod
|
||||
records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), commonRepo.WithOrderBy("created_at desc"))
|
||||
if len(records) > int(cronjob.RetainCopies) {
|
||||
for i := int(cronjob.RetainCopies); i < len(records); i++ {
|
||||
files := strings.Split(records[i].File, ",")
|
||||
for _, file := range files {
|
||||
if backType != "LOCAL" {
|
||||
_, _ = backClient.Delete(strings.ReplaceAll(file, localDir+"/", ""))
|
||||
_ = os.Remove(file)
|
||||
} else {
|
||||
_ = os.Remove(file)
|
||||
if len(records[i].File) != 0 {
|
||||
files := strings.Split(records[i].File, ",")
|
||||
for _, file := range files {
|
||||
if backType != "LOCAL" {
|
||||
_, _ = backClient.Delete(strings.ReplaceAll(file, localDir+"/", ""))
|
||||
_ = os.Remove(file)
|
||||
} else {
|
||||
_ = os.Remove(file)
|
||||
}
|
||||
_ = backupRepo.DeleteRecord(context.TODO(), backupRepo.WithByFileName(path.Base(file)))
|
||||
}
|
||||
_ = backupRepo.DeleteRecord(context.TODO(), backupRepo.WithByFileName(path.Base(file)))
|
||||
}
|
||||
|
||||
_ = cronjobRepo.DeleteRecord(commonRepo.WithByID(uint(records[i].ID)))
|
||||
@@ -151,18 +185,18 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
}
|
||||
}
|
||||
|
||||
excludes := strings.Split(exclusionRules, ";")
|
||||
excludes := strings.Split(exclusionRules, ",")
|
||||
excludeRules := ""
|
||||
for _, exclude := range excludes {
|
||||
if len(exclude) == 0 {
|
||||
continue
|
||||
}
|
||||
excludeRules += (" --exclude " + exclude)
|
||||
excludeRules += " --exclude " + exclude
|
||||
}
|
||||
path := ""
|
||||
if strings.Contains(sourceDir, "/") {
|
||||
itemDir := strings.ReplaceAll(sourceDir[strings.LastIndex(sourceDir, "/"):], "/", "")
|
||||
aheadDir := strings.ReplaceAll(sourceDir, itemDir, "")
|
||||
aheadDir := sourceDir[:strings.LastIndex(sourceDir, "/")]
|
||||
path += fmt.Sprintf("-C %s %s", aheadDir, itemDir)
|
||||
} else {
|
||||
path = sourceDir
|
||||
@@ -262,6 +296,72 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, app *repo.RootInf
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime time.Time) (string, error) {
|
||||
var (
|
||||
websites []string
|
||||
err error
|
||||
filePaths []string
|
||||
)
|
||||
if cronjob.Website == "all" {
|
||||
websites, _ = NewIWebsiteService().GetWebsiteOptions()
|
||||
if len(websites) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
} else {
|
||||
websites = append(websites, cronjob.Website)
|
||||
}
|
||||
|
||||
nginx, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
baseDir := path.Join(nginx.GetPath(), "www", "sites")
|
||||
fileOp := files.NewFileOp()
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(websites))
|
||||
for _, websiteName := range websites {
|
||||
name := websiteName
|
||||
go func() {
|
||||
website, _ := websiteRepo.GetFirst(websiteRepo.WithDomain(name))
|
||||
if website.ID == 0 {
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
websiteLogDir := path.Join(baseDir, website.PrimaryDomain, "log")
|
||||
srcAccessLogPath := path.Join(websiteLogDir, "access.log")
|
||||
srcErrorLogPath := path.Join(websiteLogDir, "error.log")
|
||||
dstLogDir := path.Join(global.CONF.System.Backup, "log", "website", website.PrimaryDomain)
|
||||
if !fileOp.Stat(dstLogDir) {
|
||||
_ = os.MkdirAll(dstLogDir, 0755)
|
||||
}
|
||||
|
||||
dstName := fmt.Sprintf("%s_log_%s.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
|
||||
filePaths = append(filePaths, path.Join(dstLogDir, dstName))
|
||||
if err = fileOp.Compress([]string{srcAccessLogPath, srcErrorLogPath}, dstLogDir, dstName, files.Gz); err != nil {
|
||||
global.LOG.Errorf("There was an error in compressing the website[%s] access.log", website.PrimaryDomain)
|
||||
} else {
|
||||
_ = fileOp.WriteFile(srcAccessLogPath, strings.NewReader(""), 0755)
|
||||
_ = fileOp.WriteFile(srcErrorLogPath, strings.NewReader(""), 0755)
|
||||
}
|
||||
global.LOG.Infof("The website[%s] log file was successfully rotated in the directory [%s]", website.PrimaryDomain, dstLogDir)
|
||||
var record model.BackupRecord
|
||||
record.Type = "cutWebsiteLog"
|
||||
record.Name = cronjob.Website
|
||||
record.Source = "LOCAL"
|
||||
record.BackupType = "LOCAL"
|
||||
record.FileDir = dstLogDir
|
||||
record.FileName = dstName
|
||||
if err = backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
u.HandleRmExpired("LOCAL", "", cronjob, nil)
|
||||
return strings.Join(filePaths, ","), nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
|
||||
var paths []string
|
||||
localDir, err := loadLocalDir()
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
@@ -33,7 +32,7 @@ type IMysqlService interface {
|
||||
Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error)
|
||||
ChangeAccess(info dto.ChangeDBInfo) error
|
||||
ChangePassword(info dto.ChangeDBInfo) error
|
||||
UpdateVariables(updatas []dto.MysqlVariablesUpdate) error
|
||||
UpdateVariables(updates []dto.MysqlVariablesUpdate) error
|
||||
UpdateConfByFile(info dto.MysqlConfUpdateByFile) error
|
||||
UpdateDescription(req dto.UpdateDescription) error
|
||||
DeleteCheck(id uint) ([]string, error)
|
||||
@@ -100,7 +99,7 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if err := u.createUser(app, req); err != nil {
|
||||
if err := u.createUser(app.ContainerName, app.Password, app.Version, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -195,7 +194,7 @@ func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error {
|
||||
}
|
||||
|
||||
passwordChangeCMD := fmt.Sprintf("set password for '%s'@'%s' = password('%s')", mysql.Username, mysql.Permission, info.Value)
|
||||
if app.Version != "5.7.39" {
|
||||
if !strings.HasPrefix(app.Version, "5.7") {
|
||||
passwordChangeCMD = fmt.Sprintf("ALTER USER '%s'@'%s' IDENTIFIED WITH mysql_native_password BY '%s';", mysql.Username, mysql.Permission, info.Value)
|
||||
}
|
||||
if info.ID != 0 {
|
||||
@@ -230,7 +229,7 @@ func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error {
|
||||
for _, host := range hosts {
|
||||
if host == "%" || host == "localhost" {
|
||||
passwordRootChangeCMD := fmt.Sprintf("set password for 'root'@'%s' = password('%s')", host, info.Value)
|
||||
if app.Version != "5.7.39" {
|
||||
if !strings.HasPrefix(app.Version, "5.7") {
|
||||
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 {
|
||||
@@ -291,7 +290,7 @@ func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := u.createUser(app, dto.MysqlDBCreate{
|
||||
if err := u.createUser(app.ContainerName, app.Password, app.Version, dto.MysqlDBCreate{
|
||||
Username: mysql.Username,
|
||||
Name: mysql.Name,
|
||||
Permission: info.Value,
|
||||
@@ -331,7 +330,7 @@ func (u *MysqlService) UpdateConfByFile(info dto.MysqlConfUpdateByFile) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) UpdateVariables(updatas []dto.MysqlVariablesUpdate) error {
|
||||
func (u *MysqlService) UpdateVariables(updates []dto.MysqlVariablesUpdate) error {
|
||||
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -346,8 +345,8 @@ func (u *MysqlService) UpdateVariables(updatas []dto.MysqlVariablesUpdate) error
|
||||
files = strings.Split(string(lineBytes), "\n")
|
||||
|
||||
group := "[mysqld]"
|
||||
for _, info := range updatas {
|
||||
if app.Version != "5.7.39" {
|
||||
for _, info := range updates {
|
||||
if !strings.HasPrefix(app.Version, "5.7") {
|
||||
if info.Param == "query_cache_size" {
|
||||
continue
|
||||
}
|
||||
@@ -470,7 +469,7 @@ func (u *MysqlService) LoadStatus() (*dto.MysqlStatus, error) {
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) createUser(app *repo.RootInfo, req dto.MysqlDBCreate) error {
|
||||
func (u *MysqlService) createUser(container, password, version string, req dto.MysqlDBCreate) error {
|
||||
var userlist []string
|
||||
if strings.Contains(req.Permission, ",") {
|
||||
ips := strings.Split(req.Permission, ",")
|
||||
@@ -484,32 +483,35 @@ func (u *MysqlService) createUser(app *repo.RootInfo, req dto.MysqlDBCreate) err
|
||||
}
|
||||
|
||||
for _, user := range userlist {
|
||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("create user %s identified by '%s';", user, req.Password)); err != nil {
|
||||
handleCreateError(req.Name, userlist, app)
|
||||
if err := excSQL(container, password, fmt.Sprintf("create user %s identified by '%s';", user, req.Password)); err != nil {
|
||||
if strings.Contains(err.Error(), "ERROR 1396") {
|
||||
handleCreateError(container, password, req.Name, userlist, false)
|
||||
return buserr.New(constant.ErrUserIsExist)
|
||||
}
|
||||
handleCreateError(container, password, req.Name, userlist, true)
|
||||
return err
|
||||
}
|
||||
grantStr := fmt.Sprintf("grant all privileges on `%s`.* to %s", req.Name, user)
|
||||
if req.Name == "*" {
|
||||
grantStr = fmt.Sprintf("grant all privileges on *.* to %s", user)
|
||||
}
|
||||
if app.Version == "5.7.39" {
|
||||
if strings.HasPrefix(version, "5.7") {
|
||||
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, req.Password)
|
||||
}
|
||||
if err := excSQL(app.ContainerName, app.Password, grantStr); err != nil {
|
||||
handleCreateError(req.Name, userlist, app)
|
||||
if err := excSQL(container, password, grantStr); err != nil {
|
||||
handleCreateError(container, password, req.Name, userlist, true)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func handleCreateError(dbName string, userlist []string, app *repo.RootInfo) {
|
||||
_ = excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop database `%s`", dbName))
|
||||
for _, user := range userlist {
|
||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists %s", user)); err != nil {
|
||||
global.LOG.Errorf("drop user failed, err: %v", err)
|
||||
func handleCreateError(contaienr, password, dbName string, userlist []string, dropUser bool) {
|
||||
_ = excSQL(contaienr, password, fmt.Sprintf("drop database `%s`", dbName))
|
||||
if dropUser {
|
||||
for _, user := range userlist {
|
||||
if err := excSQL(contaienr, password, fmt.Sprintf("drop user if exists %s", user)); err != nil {
|
||||
global.LOG.Errorf("drop user failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+172
-67
@@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
@@ -18,7 +19,8 @@ import (
|
||||
type DockerService struct{}
|
||||
|
||||
type IDockerService interface {
|
||||
UpdateConf(req dto.DaemonJsonConf) error
|
||||
UpdateConf(req dto.SettingUpdate) error
|
||||
UpdateLogOption(req dto.LogOption) error
|
||||
UpdateConfByFile(info dto.DaemonJsonUpdateByFile) error
|
||||
LoadDockerStatus() string
|
||||
LoadDockerConf() *dto.DaemonJsonConf
|
||||
@@ -30,46 +32,54 @@ func NewIDockerService() IDockerService {
|
||||
}
|
||||
|
||||
type daemonJsonItem struct {
|
||||
Status string `json:"status"`
|
||||
Mirrors []string `json:"registry-mirrors"`
|
||||
Registries []string `json:"insecure-registries"`
|
||||
LiveRestore bool `json:"live-restore"`
|
||||
IPTables bool `json:"iptables"`
|
||||
ExecOpts []string `json:"exec-opts"`
|
||||
Status string `json:"status"`
|
||||
Mirrors []string `json:"registry-mirrors"`
|
||||
Registries []string `json:"insecure-registries"`
|
||||
LiveRestore bool `json:"live-restore"`
|
||||
IPTables bool `json:"iptables"`
|
||||
ExecOpts []string `json:"exec-opts"`
|
||||
LogOption logOption `json:"log-opts"`
|
||||
}
|
||||
type logOption struct {
|
||||
LogMaxSize string `json:"max-size"`
|
||||
LogMaxFile string `json:"max-file"`
|
||||
}
|
||||
|
||||
func (u *DockerService) LoadDockerStatus() string {
|
||||
status := constant.StatusRunning
|
||||
stdout, err := cmd.Exec("systemctl is-active docker")
|
||||
if string(stdout) != "active\n" || err != nil {
|
||||
status = constant.Stopped
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return constant.Stopped
|
||||
}
|
||||
if _, err := client.Ping(context.Background()); err != nil {
|
||||
return constant.Stopped
|
||||
}
|
||||
|
||||
return status
|
||||
return constant.StatusRunning
|
||||
}
|
||||
|
||||
func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
|
||||
ctx := context.Background()
|
||||
var data dto.DaemonJsonConf
|
||||
data.IPTables = true
|
||||
data.Status = constant.StatusRunning
|
||||
stdout, err := cmd.Exec("systemctl is-active docker")
|
||||
if string(stdout) != "active\n" || err != nil {
|
||||
data.Version = "-"
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
data.Status = constant.Stopped
|
||||
} else {
|
||||
if _, err := client.Ping(ctx); err != nil {
|
||||
data.Status = constant.Stopped
|
||||
}
|
||||
itemVersion, err := client.ServerVersion(ctx)
|
||||
if err == nil {
|
||||
data.Version = itemVersion.Version
|
||||
}
|
||||
}
|
||||
data.IsSwarm = false
|
||||
stdout2, _ := cmd.Exec("docker info | grep Swarm")
|
||||
if string(stdout2) == " Swarm: active\n" {
|
||||
data.IsSwarm = true
|
||||
}
|
||||
data.Version = "-"
|
||||
client, err := docker.NewDockerClient()
|
||||
if err == nil {
|
||||
ctx := context.Background()
|
||||
itemVersion, err := client.ServerVersion(ctx)
|
||||
if err == nil {
|
||||
data.Version = itemVersion.Version
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(constant.DaemonJsonPath); err != nil {
|
||||
return &data
|
||||
}
|
||||
@@ -78,18 +88,19 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
|
||||
return &data
|
||||
}
|
||||
var conf daemonJsonItem
|
||||
deamonMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal(file, &deamonMap); err != nil {
|
||||
daemonMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal(file, &daemonMap); err != nil {
|
||||
return &data
|
||||
}
|
||||
arr, err := json.Marshal(deamonMap)
|
||||
arr, err := json.Marshal(daemonMap)
|
||||
if err != nil {
|
||||
return &data
|
||||
}
|
||||
if err := json.Unmarshal(arr, &conf); err != nil {
|
||||
fmt.Println(err)
|
||||
return &data
|
||||
}
|
||||
if _, ok := deamonMap["iptables"]; !ok {
|
||||
if _, ok := daemonMap["iptables"]; !ok {
|
||||
conf.IPTables = true
|
||||
}
|
||||
data.CgroupDriver = "cgroupfs"
|
||||
@@ -99,6 +110,8 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
|
||||
break
|
||||
}
|
||||
}
|
||||
data.LogMaxSize = conf.LogOption.LogMaxSize
|
||||
data.LogMaxFile = conf.LogOption.LogMaxFile
|
||||
data.Mirrors = conf.Mirrors
|
||||
data.Registries = conf.Registries
|
||||
data.IPTables = conf.IPTables
|
||||
@@ -106,7 +119,7 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
|
||||
return &data
|
||||
}
|
||||
|
||||
func (u *DockerService) UpdateConf(req dto.DaemonJsonConf) error {
|
||||
func (u *DockerService) UpdateConf(req dto.SettingUpdate) error {
|
||||
if _, err := os.Stat(constant.DaemonJsonPath); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(path.Dir(constant.DaemonJsonPath), os.ModePerm); err != nil {
|
||||
return err
|
||||
@@ -118,50 +131,96 @@ func (u *DockerService) UpdateConf(req dto.DaemonJsonConf) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deamonMap := make(map[string]interface{})
|
||||
_ = json.Unmarshal(file, &deamonMap)
|
||||
daemonMap := make(map[string]interface{})
|
||||
_ = json.Unmarshal(file, &daemonMap)
|
||||
|
||||
if len(req.Registries) == 0 {
|
||||
delete(deamonMap, "insecure-registries")
|
||||
} else {
|
||||
deamonMap["insecure-registries"] = req.Registries
|
||||
}
|
||||
if len(req.Mirrors) == 0 {
|
||||
delete(deamonMap, "registry-mirrors")
|
||||
} else {
|
||||
deamonMap["registry-mirrors"] = req.Mirrors
|
||||
}
|
||||
if !req.LiveRestore {
|
||||
delete(deamonMap, "live-restore")
|
||||
} else {
|
||||
deamonMap["live-restore"] = req.LiveRestore
|
||||
}
|
||||
if req.IPTables {
|
||||
delete(deamonMap, "iptables")
|
||||
} else {
|
||||
deamonMap["iptables"] = false
|
||||
}
|
||||
if opts, ok := deamonMap["exec-opts"]; ok {
|
||||
if optsValue, isArray := opts.([]interface{}); isArray {
|
||||
for i := 0; i < len(optsValue); i++ {
|
||||
if opt, isStr := optsValue[i].(string); isStr {
|
||||
if strings.HasPrefix(opt, "native.cgroupdriver=") {
|
||||
optsValue[i] = "native.cgroupdriver=" + req.CgroupDriver
|
||||
break
|
||||
switch req.Key {
|
||||
case "Registries":
|
||||
if len(req.Value) == 0 {
|
||||
delete(daemonMap, "insecure-registries")
|
||||
} else {
|
||||
daemonMap["insecure-registries"] = strings.Split(req.Value, ",")
|
||||
}
|
||||
case "Mirrors":
|
||||
if len(req.Value) == 0 {
|
||||
delete(daemonMap, "registry-mirrors")
|
||||
} else {
|
||||
daemonMap["registry-mirrors"] = strings.Split(req.Value, ",")
|
||||
}
|
||||
case "LogOption":
|
||||
if req.Value == "disable" {
|
||||
delete(daemonMap, "log-opts")
|
||||
}
|
||||
case "LiveRestore":
|
||||
if req.Value == "disable" {
|
||||
delete(daemonMap, "live-restore")
|
||||
} else {
|
||||
daemonMap["live-restore"] = true
|
||||
}
|
||||
case "IPtables":
|
||||
if req.Value == "enable" {
|
||||
delete(daemonMap, "iptables")
|
||||
} else {
|
||||
daemonMap["iptables"] = false
|
||||
}
|
||||
case "Dirver":
|
||||
if opts, ok := daemonMap["exec-opts"]; ok {
|
||||
if optsValue, isArray := opts.([]interface{}); isArray {
|
||||
for i := 0; i < len(optsValue); i++ {
|
||||
if opt, isStr := optsValue[i].(string); isStr {
|
||||
if strings.HasPrefix(opt, "native.cgroupdriver=") {
|
||||
optsValue[i] = "native.cgroupdriver=" + req.Value
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if req.CgroupDriver == "systemd" {
|
||||
deamonMap["exec-opts"] = []string{"native.cgroupdriver=systemd"}
|
||||
} else {
|
||||
if req.Value == "systemd" {
|
||||
daemonMap["exec-opts"] = []string{"native.cgroupdriver=systemd"}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(deamonMap) == 0 {
|
||||
if len(daemonMap) == 0 {
|
||||
_ = os.Remove(constant.DaemonJsonPath)
|
||||
return nil
|
||||
}
|
||||
newJson, err := json.MarshalIndent(deamonMap, "", "\t")
|
||||
newJson, err := json.MarshalIndent(daemonMap, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(constant.DaemonJsonPath, newJson, 0640); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stdout, err := cmd.Exec("systemctl restart docker")
|
||||
if err != nil {
|
||||
return errors.New(string(stdout))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *DockerService) UpdateLogOption(req dto.LogOption) error {
|
||||
if _, err := os.Stat(constant.DaemonJsonPath); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(path.Dir(constant.DaemonJsonPath), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = os.Create(constant.DaemonJsonPath)
|
||||
}
|
||||
|
||||
file, err := os.ReadFile(constant.DaemonJsonPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
daemonMap := make(map[string]interface{})
|
||||
_ = json.Unmarshal(file, &daemonMap)
|
||||
|
||||
changeLogOption(daemonMap, req.LogMaxFile, req.LogMaxSize)
|
||||
if len(daemonMap) == 0 {
|
||||
_ = os.Remove(constant.DaemonJsonPath)
|
||||
return nil
|
||||
}
|
||||
newJson, err := json.MarshalIndent(daemonMap, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -206,10 +265,7 @@ func (u *DockerService) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error {
|
||||
func (u *DockerService) OperateDocker(req dto.DockerOperation) error {
|
||||
service := "docker"
|
||||
if req.Operation == "stop" {
|
||||
service = "docker.service"
|
||||
if req.StopSocket {
|
||||
service = "docker.socket"
|
||||
}
|
||||
service = "docker.socket"
|
||||
}
|
||||
stdout, err := cmd.Execf("systemctl %s %s ", req.Operation, service)
|
||||
if err != nil {
|
||||
@@ -217,3 +273,52 @@ func (u *DockerService) OperateDocker(req dto.DockerOperation) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func changeLogOption(daemonMap map[string]interface{}, logMaxFile, logMaxSize string) {
|
||||
if opts, ok := daemonMap["log-opts"]; ok {
|
||||
if len(logMaxFile) != 0 || len(logMaxSize) != 0 {
|
||||
daemonMap["log-driver"] = "json-file"
|
||||
}
|
||||
optsMap, isMap := opts.(map[string]interface{})
|
||||
if isMap {
|
||||
if len(logMaxFile) != 0 {
|
||||
optsMap["max-file"] = logMaxFile
|
||||
} else {
|
||||
delete(optsMap, "max-file")
|
||||
}
|
||||
if len(logMaxSize) != 0 {
|
||||
optsMap["max-size"] = logMaxSize
|
||||
} else {
|
||||
delete(optsMap, "max-size")
|
||||
}
|
||||
if len(optsMap) == 0 {
|
||||
delete(daemonMap, "log-opts")
|
||||
}
|
||||
} else {
|
||||
optsMap := make(map[string]interface{})
|
||||
if len(logMaxFile) != 0 {
|
||||
optsMap["max-file"] = logMaxFile
|
||||
}
|
||||
if len(logMaxSize) != 0 {
|
||||
optsMap["max-size"] = logMaxSize
|
||||
}
|
||||
if len(optsMap) != 0 {
|
||||
daemonMap["log-opts"] = optsMap
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if len(logMaxFile) != 0 || len(logMaxSize) != 0 {
|
||||
daemonMap["log-driver"] = "json-file"
|
||||
}
|
||||
optsMap := make(map[string]interface{})
|
||||
if len(logMaxFile) != 0 {
|
||||
optsMap["max-file"] = logMaxFile
|
||||
}
|
||||
if len(logMaxSize) != 0 {
|
||||
optsMap["max-size"] = logMaxSize
|
||||
}
|
||||
if len(optsMap) != 0 {
|
||||
daemonMap["log-opts"] = optsMap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ type IFirewallService interface {
|
||||
OperateAddressRule(req dto.AddrRuleOperate, reload bool) error
|
||||
UpdatePortRule(req dto.PortRuleUpdate) error
|
||||
UpdateAddrRule(req dto.AddrRuleUpdate) error
|
||||
BacthOperateRule(req dto.BatchRuleOperate) error
|
||||
BatchOperateRule(req dto.BatchRuleOperate) error
|
||||
}
|
||||
|
||||
func NewIFirewallService() IFirewallService {
|
||||
@@ -276,7 +276,7 @@ func (u *FirewallService) UpdateAddrRule(req dto.AddrRuleUpdate) error {
|
||||
return client.Reload()
|
||||
}
|
||||
|
||||
func (u *FirewallService) BacthOperateRule(req dto.BatchRuleOperate) error {
|
||||
func (u *FirewallService) BatchOperateRule(req dto.BatchRuleOperate) error {
|
||||
client, err := firewall.NewFirewallClient()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -368,18 +368,16 @@ func (u *FirewallService) pingStatus() string {
|
||||
if _, err := os.Stat("/etc/sysctl.conf"); err != nil {
|
||||
return constant.StatusNone
|
||||
}
|
||||
commond := "cat /etc/sysctl.conf | grep net/ipv4/icmp_echo_ignore_all= "
|
||||
if cmd.HasNoPasswordSudo() {
|
||||
commond = "sudo cat /etc/sysctl.conf | grep net/ipv4/icmp_echo_ignore_all= "
|
||||
}
|
||||
stdout, _ := cmd.Exec(commond)
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
command := fmt.Sprintf("%s cat /etc/sysctl.conf | grep net/ipv4/icmp_echo_ignore_all= ", sudo)
|
||||
stdout, _ := cmd.Exec(command)
|
||||
if stdout == "net/ipv4/icmp_echo_ignore_all=1\n" {
|
||||
return constant.StatusEnable
|
||||
}
|
||||
return constant.StatusDisable
|
||||
}
|
||||
|
||||
func (u *FirewallService) updatePingStatus(enabel string) error {
|
||||
func (u *FirewallService) updatePingStatus(enable string) error {
|
||||
lineBytes, err := os.ReadFile(confPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -389,14 +387,14 @@ func (u *FirewallService) updatePingStatus(enabel string) error {
|
||||
hasLine := false
|
||||
for _, line := range files {
|
||||
if strings.Contains(line, "net/ipv4/icmp_echo_ignore_all") || strings.HasPrefix(line, "net/ipv4/icmp_echo_ignore_all") {
|
||||
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enabel)
|
||||
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enable)
|
||||
hasLine = true
|
||||
} else {
|
||||
newFiles = append(newFiles, line)
|
||||
}
|
||||
}
|
||||
if !hasLine {
|
||||
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enabel)
|
||||
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enable)
|
||||
}
|
||||
file, err := os.OpenFile(confPath, os.O_WRONLY|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
@@ -408,11 +406,9 @@ func (u *FirewallService) updatePingStatus(enabel string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
commond := "sysctl -p"
|
||||
if cmd.HasNoPasswordSudo() {
|
||||
commond = "sudo sysctl -p"
|
||||
}
|
||||
stdout, err := cmd.Exec(commond)
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
command := fmt.Sprintf("%s sysctl -p", sudo)
|
||||
stdout, err := cmd.Exec(command)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update ping status failed, err: %v", stdout)
|
||||
}
|
||||
|
||||
@@ -50,9 +50,11 @@ func (u *ImageRepoService) Login(req dto.OperateByID) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := u.CheckConn(repo.DownloadUrl, repo.Username, repo.Password); err != nil {
|
||||
_ = imageRepoRepo.Update(repo.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error})
|
||||
return err
|
||||
if repo.Auth {
|
||||
if err := u.CheckConn(repo.DownloadUrl, repo.Username, repo.Password); err != nil {
|
||||
_ = imageRepoRepo.Update(repo.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||
return err
|
||||
}
|
||||
}
|
||||
_ = imageRepoRepo.Update(repo.ID, map[string]interface{}{"status": constant.StatusSuccess})
|
||||
return nil
|
||||
@@ -85,12 +87,12 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
|
||||
return errors.New(string(stdout))
|
||||
}
|
||||
ticker := time.NewTicker(3 * time.Second)
|
||||
ctx, cancle := context.WithTimeout(context.Background(), time.Second*20)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
if err := func() error {
|
||||
for range ticker.C {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
cancle()
|
||||
cancel()
|
||||
return errors.New("the docker service cannot be restarted")
|
||||
default:
|
||||
stdout, err := cmd.Exec("systemctl is-active docker")
|
||||
@@ -111,9 +113,11 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
|
||||
}
|
||||
|
||||
imageRepo.Status = constant.StatusSuccess
|
||||
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
|
||||
imageRepo.Status = constant.StatusFailed
|
||||
imageRepo.Message = err.Error()
|
||||
if req.Auth {
|
||||
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
|
||||
imageRepo.Status = constant.StatusFailed
|
||||
imageRepo.Message = err.Error()
|
||||
}
|
||||
}
|
||||
if err := imageRepoRepo.Create(&imageRepo); err != nil {
|
||||
return err
|
||||
@@ -142,7 +146,7 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if repo.DownloadUrl != req.DownloadUrl {
|
||||
if repo.DownloadUrl != req.DownloadUrl || (!repo.Auth && req.Auth) {
|
||||
_ = u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update")
|
||||
if repo.Auth {
|
||||
_, _ = cmd.Execf("docker logout %s", repo.DownloadUrl)
|
||||
@@ -162,9 +166,11 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
|
||||
|
||||
upMap["status"] = constant.StatusSuccess
|
||||
upMap["message"] = ""
|
||||
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
|
||||
upMap["status"] = constant.StatusFailed
|
||||
upMap["message"] = err.Error()
|
||||
if req.Auth {
|
||||
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
|
||||
upMap["status"] = constant.StatusFailed
|
||||
upMap["message"] = err.Error()
|
||||
}
|
||||
}
|
||||
return imageRepoRepo.Update(req.ID, upMap)
|
||||
}
|
||||
@@ -188,16 +194,16 @@ func (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) err
|
||||
_, _ = os.Create(constant.DaemonJsonPath)
|
||||
}
|
||||
|
||||
deamonMap := make(map[string]interface{})
|
||||
daemonMap := make(map[string]interface{})
|
||||
file, err := os.ReadFile(constant.DaemonJsonPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(file, &deamonMap); err != nil {
|
||||
if err := json.Unmarshal(file, &daemonMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iRegistries := deamonMap["insecure-registries"]
|
||||
iRegistries := daemonMap["insecure-registries"]
|
||||
registries, _ := iRegistries.([]interface{})
|
||||
switch handle {
|
||||
case "create":
|
||||
@@ -217,11 +223,11 @@ func (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) err
|
||||
}
|
||||
}
|
||||
if len(registries) == 0 {
|
||||
delete(deamonMap, "insecure-registries")
|
||||
delete(daemonMap, "insecure-registries")
|
||||
} else {
|
||||
deamonMap["insecure-registries"] = registries
|
||||
daemonMap["insecure-registries"] = registries
|
||||
}
|
||||
newJson, err := json.MarshalIndent(deamonMap, "", "\t")
|
||||
newJson, err := json.MarshalIndent(daemonMap, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package job
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/robfig/cron/v3"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"github.com/shirou/gopsutil/v3/load"
|
||||
@@ -14,14 +15,17 @@ import (
|
||||
"github.com/shirou/gopsutil/v3/net"
|
||||
)
|
||||
|
||||
type monitor struct{}
|
||||
type MonitorService struct{}
|
||||
|
||||
func NewMonitorJob() *monitor {
|
||||
return &monitor{}
|
||||
type IMonitorService interface {
|
||||
Run()
|
||||
}
|
||||
|
||||
func (m *monitor) Run() {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
func NewIMonitorService() IMonitorService {
|
||||
return &MonitorService{}
|
||||
}
|
||||
|
||||
func (m *MonitorService) Run() {
|
||||
monitorStatus, _ := settingRepo.Get(settingRepo.WithByKey("MonitorStatus"))
|
||||
if monitorStatus.Value == "disable" {
|
||||
return
|
||||
@@ -42,7 +46,7 @@ func (m *monitor) Run() {
|
||||
memoryInfo, _ := mem.VirtualMemory()
|
||||
itemModel.Memory = memoryInfo.UsedPercent
|
||||
|
||||
if err := global.DB.Create(&itemModel).Error; err != nil {
|
||||
if err := settingRepo.CreateMonitorBase(itemModel); err != nil {
|
||||
global.LOG.Errorf("Insert basic monitoring data failed, err: %v", err)
|
||||
}
|
||||
|
||||
@@ -55,9 +59,9 @@ func (m *monitor) Run() {
|
||||
}
|
||||
storeDays, _ := strconv.Atoi(MonitorStoreDays.Value)
|
||||
timeForDelete := time.Now().AddDate(0, 0, -storeDays)
|
||||
_ = global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorBase{}).Error
|
||||
_ = global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorIO{}).Error
|
||||
_ = global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error
|
||||
_ = settingRepo.DelMonitorBase(timeForDelete)
|
||||
_ = settingRepo.DelMonitorIO(timeForDelete)
|
||||
_ = settingRepo.DelMonitorNet(timeForDelete)
|
||||
}
|
||||
|
||||
func loadDiskIO() {
|
||||
@@ -91,7 +95,7 @@ func loadDiskIO() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := global.DB.CreateInBatches(ioList, len(ioList)).Error; err != nil {
|
||||
if err := settingRepo.BatchCreateMonitorIO(ioList); err != nil {
|
||||
global.LOG.Errorf("Insert io monitoring data failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -133,7 +137,19 @@ func loadNetIO() {
|
||||
}
|
||||
}
|
||||
|
||||
if err := global.DB.CreateInBatches(netList, len(netList)).Error; err != nil {
|
||||
if err := settingRepo.BatchCreateMonitorNet(netList); err != nil {
|
||||
global.LOG.Errorf("Insert network monitoring data failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func StartMonitor(removeBefore bool, interval string) error {
|
||||
if removeBefore {
|
||||
global.Cron.Remove(cron.EntryID(global.MonitorCronID))
|
||||
}
|
||||
monitorID, err := global.Cron.AddJob(fmt.Sprintf("@every %sm", interval), NewIMonitorService())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
global.MonitorCronID = int(monitorID)
|
||||
return nil
|
||||
}
|
||||
@@ -64,7 +64,13 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
|
||||
return err
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
buildDir := path.Join(constant.AppResourceDir, app.Key, "versions", appDetail.Version, "build")
|
||||
appVersionDir := path.Join(constant.AppResourceDir, app.Resource, app.Key, appDetail.Version)
|
||||
if !fileOp.Stat(appVersionDir) {
|
||||
if err := downloadApp(app, appDetail, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
buildDir := path.Join(appVersionDir, "build")
|
||||
if !fileOp.Stat(buildDir) {
|
||||
return buserr.New(constant.ErrDirNotFound)
|
||||
}
|
||||
|
||||
@@ -19,20 +19,24 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ssl"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
type SettingService struct{}
|
||||
|
||||
type ISettingService interface {
|
||||
GetSettingInfo() (*dto.SettingInfo, error)
|
||||
LoadTimeZone() ([]string, error)
|
||||
Update(key, value string) error
|
||||
UpdatePassword(c *gin.Context, old, new string) error
|
||||
UpdatePort(port uint) error
|
||||
UpdateSSL(c *gin.Context, req dto.SSLUpdate) error
|
||||
LoadFromCert() (*dto.SSLInfo, error)
|
||||
HandlePasswordExpired(c *gin.Context, old, new string) error
|
||||
SyncTime(req dto.SyncTime) error
|
||||
}
|
||||
|
||||
func NewISettingService() ISettingService {
|
||||
@@ -60,18 +64,85 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
|
||||
return &info, err
|
||||
}
|
||||
|
||||
func (u *SettingService) LoadTimeZone() ([]string, error) {
|
||||
std, err := cmd.Exec("timedatectl list-timezones")
|
||||
if err != nil {
|
||||
return []string{}, nil
|
||||
}
|
||||
return strings.Split(std, "\n"), err
|
||||
}
|
||||
|
||||
func (u *SettingService) Update(key, value string) error {
|
||||
if key == "ExpirationDays" {
|
||||
switch key {
|
||||
case "MonitorStatus":
|
||||
if value == "enable" && global.MonitorCronID == 0 {
|
||||
interval, err := settingRepo.Get(settingRepo.WithByKey("MonitorInterval"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := StartMonitor(false, interval.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if value == "disable" && global.MonitorCronID != 0 {
|
||||
global.Cron.Remove(cron.EntryID(global.MonitorCronID))
|
||||
global.MonitorCronID = 0
|
||||
}
|
||||
case "MonitorInterval":
|
||||
status, err := settingRepo.Get(settingRepo.WithByKey("MonitorStatus"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status.Value == "enable" && global.MonitorCronID != 0 {
|
||||
if err := StartMonitor(true, value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case "TimeZone":
|
||||
if err := ntp.UpdateSystemTimeZone(value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := settingRepo.Update(key, value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch key {
|
||||
case "ExpirationDays":
|
||||
timeout, _ := strconv.Atoi(value)
|
||||
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006-01-02 15:04:05")); err != nil {
|
||||
return err
|
||||
}
|
||||
case "TimeZone":
|
||||
go func() {
|
||||
_, err := cmd.Exec("systemctl restart 1panel.service")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("restart system for new time zone failed, err: %v", err)
|
||||
}
|
||||
}()
|
||||
case "BindDomain":
|
||||
if len(value) != 0 {
|
||||
_ = global.SESSION.Clean()
|
||||
}
|
||||
case "UserName", "Password":
|
||||
_ = global.SESSION.Clean()
|
||||
}
|
||||
if err := settingRepo.Update(key, value); err != nil {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SettingService) SyncTime(req dto.SyncTime) error {
|
||||
if err := settingRepo.Update("NtpSite", req.NtpSite); err != nil {
|
||||
return err
|
||||
}
|
||||
if key == "UserName" {
|
||||
_ = global.SESSION.Clean()
|
||||
ntime, err := ntp.GetRemoteTime(req.NtpSite)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ts := ntime.Format("2006-01-02 15:04:05")
|
||||
if err := ntp.UpdateSystemTime(ts); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ func (u *SnapshotService) UpdateDescription(req dto.UpdateDescription) error {
|
||||
type SnapshotJson struct {
|
||||
OldBaseDir string `json:"oldBaseDir"`
|
||||
OldDockerDataDir string `json:"oldDockerDataDir"`
|
||||
OldBackupDataDir string `json:"oldDackupDataDir"`
|
||||
OldBackupDataDir string `json:"oldBackupDataDir"`
|
||||
OldPanelDataDir string `json:"oldPanelDataDir"`
|
||||
|
||||
BaseDir string `json:"baseDir"`
|
||||
@@ -113,7 +113,7 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
backupAccont, err := NewIBackupService().NewClient(&backup)
|
||||
backupAccount, err := NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -202,7 +202,7 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
|
||||
global.LOG.Infof("start to upload snapshot to %s, please wait", backup.Type)
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusUploading})
|
||||
localPath := fmt.Sprintf("%s/system/1panel_%s_%s.tar.gz", localDir, versionItem.Value, timeNow)
|
||||
if ok, err := backupAccont.Upload(localPath, fmt.Sprintf("system_snapshot/1panel_%s_%s.tar.gz", versionItem.Value, timeNow)); err != nil || !ok {
|
||||
if ok, err := backupAccount.Upload(localPath, fmt.Sprintf("system_snapshot/1panel_%s_%s.tar.gz", versionItem.Value, timeNow)); err != nil || !ok {
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||
global.LOG.Errorf("upload snapshot to %s failed, err: %v", backup.Type, err)
|
||||
return
|
||||
@@ -796,15 +796,15 @@ func (u *SnapshotService) updateLiveRestore(enabled bool) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deamonMap := make(map[string]interface{})
|
||||
_ = json.Unmarshal(file, &deamonMap)
|
||||
daemonMap := make(map[string]interface{})
|
||||
_ = json.Unmarshal(file, &daemonMap)
|
||||
|
||||
if !enabled {
|
||||
delete(deamonMap, "live-restore")
|
||||
delete(daemonMap, "live-restore")
|
||||
} else {
|
||||
deamonMap["live-restore"] = enabled
|
||||
daemonMap["live-restore"] = enabled
|
||||
}
|
||||
newJson, err := json.MarshalIndent(deamonMap, "", "\t")
|
||||
newJson, err := json.MarshalIndent(daemonMap, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -818,8 +818,8 @@ func (u *SnapshotService) updateLiveRestore(enabled bool) error {
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(3 * time.Second)
|
||||
ctx, cancle := context.WithTimeout(context.Background(), time.Second*30)
|
||||
defer cancle()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||
defer cancel()
|
||||
for range ticker.C {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
||||
@@ -0,0 +1,425 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/qqwry"
|
||||
)
|
||||
|
||||
const sshPath = "/etc/ssh/sshd_config"
|
||||
|
||||
type SSHService struct{}
|
||||
|
||||
type ISSHService interface {
|
||||
GetSSHInfo() (*dto.SSHInfo, error)
|
||||
OperateSSH(operation string) error
|
||||
UpdateByFile(value string) error
|
||||
Update(key, value string) error
|
||||
GenerateSSH(req dto.GenerateSSH) error
|
||||
LoadSSHSecret(mode string) (string, error)
|
||||
LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error)
|
||||
}
|
||||
|
||||
func NewISSHService() ISSHService {
|
||||
return &SSHService{}
|
||||
}
|
||||
|
||||
func (u *SSHService) GetSSHInfo() (*dto.SSHInfo, error) {
|
||||
data := dto.SSHInfo{
|
||||
Status: constant.StatusEnable,
|
||||
Message: "",
|
||||
Port: "22",
|
||||
ListenAddress: "0.0.0.0",
|
||||
PasswordAuthentication: "yes",
|
||||
PubkeyAuthentication: "yes",
|
||||
PermitRootLogin: "yes",
|
||||
UseDNS: "yes",
|
||||
}
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
stdout, err := cmd.Execf("%s systemctl status sshd", sudo)
|
||||
if err != nil {
|
||||
data.Message = stdout
|
||||
data.Status = constant.StatusDisable
|
||||
}
|
||||
stdLines := strings.Split(stdout, "\n")
|
||||
for _, stdline := range stdLines {
|
||||
if strings.Contains(stdline, "active (running)") {
|
||||
data.Status = constant.StatusEnable
|
||||
break
|
||||
}
|
||||
}
|
||||
sshConf, err := os.ReadFile(sshPath)
|
||||
if err != nil {
|
||||
data.Message = err.Error()
|
||||
data.Status = constant.StatusDisable
|
||||
}
|
||||
lines := strings.Split(string(sshConf), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "Port ") {
|
||||
data.Port = strings.ReplaceAll(line, "Port ", "")
|
||||
}
|
||||
if strings.HasPrefix(line, "ListenAddress ") {
|
||||
data.ListenAddress = strings.ReplaceAll(line, "ListenAddress ", "")
|
||||
}
|
||||
if strings.HasPrefix(line, "PasswordAuthentication ") {
|
||||
data.PasswordAuthentication = strings.ReplaceAll(line, "PasswordAuthentication ", "")
|
||||
}
|
||||
if strings.HasPrefix(line, "PubkeyAuthentication ") {
|
||||
data.PubkeyAuthentication = strings.ReplaceAll(line, "PubkeyAuthentication ", "")
|
||||
}
|
||||
if strings.HasPrefix(line, "PermitRootLogin ") {
|
||||
data.PermitRootLogin = strings.ReplaceAll(line, "PermitRootLogin ", "")
|
||||
}
|
||||
if strings.HasPrefix(line, "UseDNS ") {
|
||||
data.UseDNS = strings.ReplaceAll(line, "UseDNS ", "")
|
||||
}
|
||||
}
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func (u *SSHService) OperateSSH(operation string) error {
|
||||
if operation == "start" || operation == "stop" || operation == "restart" {
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
stdout, err := cmd.Execf("%s systemctl %s sshd", sudo, operation)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s sshd failed, stdout: %s, err: %v", operation, stdout, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("not support such operation: %s", operation)
|
||||
}
|
||||
|
||||
func (u *SSHService) Update(key, value string) error {
|
||||
sshConf, err := os.ReadFile(sshPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lines := strings.Split(string(sshConf), "\n")
|
||||
newFiles := updateSSHConf(lines, key, value)
|
||||
if err := settingRepo.Update(key, value); err != nil {
|
||||
return err
|
||||
}
|
||||
file, err := os.OpenFile(sshPath, os.O_WRONLY|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
if _, err = file.WriteString(strings.Join(newFiles, "\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
if key == "Port" {
|
||||
stdout, _ := cmd.Execf("%s getenforce", sudo)
|
||||
if stdout == "Enforcing\n" {
|
||||
_, _ = cmd.Execf("%s semanage port -a -t ssh_port_t -p tcp %s", sudo, value)
|
||||
}
|
||||
}
|
||||
_, _ = cmd.Execf("%s systemctl restart sshd", sudo)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SSHService) UpdateByFile(value string) error {
|
||||
file, err := os.OpenFile(sshPath, os.O_WRONLY|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
if _, err = file.WriteString(value); err != nil {
|
||||
return err
|
||||
}
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
_, _ = cmd.Execf("%s systemctl restart sshd", sudo)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SSHService) GenerateSSH(req dto.GenerateSSH) error {
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
return fmt.Errorf("load current user failed, err: %v", err)
|
||||
}
|
||||
secretFile := fmt.Sprintf("%s/.ssh/id_item_%s", currentUser.HomeDir, req.EncryptionMode)
|
||||
secretPubFile := fmt.Sprintf("%s/.ssh/id_item_%s.pub", currentUser.HomeDir, req.EncryptionMode)
|
||||
authFile := currentUser.HomeDir + "/.ssh/authorized_keys"
|
||||
|
||||
command := fmt.Sprintf("ssh-keygen -t %s -f %s/.ssh/id_item_%s | echo y", req.EncryptionMode, currentUser.HomeDir, req.EncryptionMode)
|
||||
if len(req.Password) != 0 {
|
||||
command = fmt.Sprintf("ssh-keygen -t %s -P %s -f %s/.ssh/id_item_%s | echo y", req.EncryptionMode, req.Password, currentUser.HomeDir, req.EncryptionMode)
|
||||
}
|
||||
stdout, err := cmd.Exec(command)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generate failed, err: %v, message: %s", err, stdout)
|
||||
}
|
||||
defer func() {
|
||||
_ = os.Remove(secretFile)
|
||||
}()
|
||||
defer func() {
|
||||
_ = os.Remove(secretPubFile)
|
||||
}()
|
||||
|
||||
if _, err := os.Stat(authFile); err != nil {
|
||||
_, _ = os.Create(authFile)
|
||||
}
|
||||
stdout1, err := cmd.Execf("cat %s >> %s/.ssh/authorized_keys", secretPubFile, currentUser.HomeDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generate failed, err: %v, message: %s", err, stdout1)
|
||||
}
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
if err := fileOp.Rename(secretFile, fmt.Sprintf("%s/.ssh/id_%s", currentUser.HomeDir, req.EncryptionMode)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.Rename(secretPubFile, fmt.Sprintf("%s/.ssh/id_%s.pub", currentUser.HomeDir, req.EncryptionMode)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SSHService) LoadSSHSecret(mode string) (string, error) {
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("load current user failed, err: %v", err)
|
||||
}
|
||||
|
||||
homeDir := currentUser.HomeDir
|
||||
if _, err := os.Stat(fmt.Sprintf("%s/.ssh/id_%s", homeDir, mode)); err != nil {
|
||||
return "", nil
|
||||
}
|
||||
file, err := os.ReadFile(fmt.Sprintf("%s/.ssh/id_%s", homeDir, mode))
|
||||
return string(file), err
|
||||
}
|
||||
|
||||
func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
|
||||
var fileList []string
|
||||
var data dto.SSHLog
|
||||
baseDir := "/var/log"
|
||||
if err := filepath.Walk(baseDir, func(pathItem string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() && strings.HasPrefix(info.Name(), "secure") || strings.HasPrefix(info.Name(), "auth") {
|
||||
if strings.HasSuffix(info.Name(), ".gz") {
|
||||
if err := handleGunzip(pathItem); err == nil {
|
||||
fileList = append(fileList, strings.ReplaceAll(pathItem, ".gz", ""))
|
||||
}
|
||||
} else {
|
||||
fileList = append(fileList, pathItem)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fileList = sortFileList(fileList)
|
||||
|
||||
command := ""
|
||||
if len(req.Info) != 0 {
|
||||
command = fmt.Sprintf(" | grep '%s'", req.Info)
|
||||
}
|
||||
for i := 0; i < len(fileList); i++ {
|
||||
if strings.HasPrefix(path.Base(fileList[i]), "secure") {
|
||||
commandItem := fmt.Sprintf("cat %s | grep -a 'Failed password for' | grep -v 'invalid' %s", fileList[i], command)
|
||||
dataItem := loadFailedSecureDatas(commandItem)
|
||||
data.FailedCount += len(dataItem)
|
||||
data.TotalCount += len(dataItem)
|
||||
if req.Status != constant.StatusSuccess {
|
||||
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 -a Accepted %s", fileList[i], command)
|
||||
dataItem := loadSuccessDatas(commandItem)
|
||||
data.TotalCount += len(dataItem)
|
||||
if req.Status != constant.StatusFailed {
|
||||
data.Logs = append(data.Logs, dataItem...)
|
||||
}
|
||||
}
|
||||
data.SuccessfulCount = data.TotalCount - data.FailedCount
|
||||
if len(data.Logs) < 1 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var itemDatas []dto.SSHHistory
|
||||
total, start, end := len(data.Logs), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||
if start > total {
|
||||
itemDatas = make([]dto.SSHHistory, 0)
|
||||
} else {
|
||||
if end >= total {
|
||||
end = total
|
||||
}
|
||||
itemDatas = data.Logs[start:end]
|
||||
}
|
||||
data.Logs = itemDatas
|
||||
|
||||
timeNow := time.Now()
|
||||
nyc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||
qqWry, err := qqwry.NewQQwry()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load qqwry datas failed: %s", err)
|
||||
}
|
||||
var itemLogs []dto.SSHHistory
|
||||
for i := len(data.Logs) - 1; i >= 0; i-- {
|
||||
data.Logs[i].Area = qqWry.Find(data.Logs[i].Address).Area
|
||||
data.Logs[i].Date, _ = time.ParseInLocation("2006 Jan 2 15:04:05", fmt.Sprintf("%d %s", timeNow.Year(), data.Logs[i].DateStr), nyc)
|
||||
itemLogs = append(itemLogs, data.Logs[i])
|
||||
}
|
||||
data.Logs = itemLogs
|
||||
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func sortFileList(fileNames []string) []string {
|
||||
if len(fileNames) < 2 {
|
||||
return fileNames
|
||||
}
|
||||
var itemFile []string
|
||||
if strings.Contains(fileNames[0], "secure") {
|
||||
sort.Slice(fileNames, func(i, j int) bool {
|
||||
return fileNames[i] < fileNames[j]
|
||||
})
|
||||
itemFile = append(itemFile, fileNames[1:]...)
|
||||
itemFile = append(itemFile, fileNames[0])
|
||||
return itemFile
|
||||
}
|
||||
|
||||
sort.Slice(fileNames, func(i, j int) bool {
|
||||
return fileNames[i] > fileNames[j]
|
||||
})
|
||||
return fileNames
|
||||
}
|
||||
|
||||
func updateSSHConf(oldFiles []string, param string, value interface{}) []string {
|
||||
hasKey := false
|
||||
var newFiles []string
|
||||
for _, line := range oldFiles {
|
||||
if strings.HasPrefix(line, param+" ") {
|
||||
newFiles = append(newFiles, fmt.Sprintf("%s %v", param, value))
|
||||
hasKey = true
|
||||
continue
|
||||
}
|
||||
newFiles = append(newFiles, line)
|
||||
}
|
||||
if !hasKey {
|
||||
newFiles = []string{}
|
||||
for _, line := range oldFiles {
|
||||
if strings.HasPrefix(line, fmt.Sprintf("#%s ", param)) && !hasKey {
|
||||
newFiles = append(newFiles, fmt.Sprintf("%s %v", param, value))
|
||||
hasKey = true
|
||||
continue
|
||||
}
|
||||
newFiles = append(newFiles, line)
|
||||
}
|
||||
}
|
||||
if !hasKey {
|
||||
newFiles = []string{}
|
||||
newFiles = append(newFiles, oldFiles...)
|
||||
newFiles = append(newFiles, fmt.Sprintf("%s %v", param, value))
|
||||
}
|
||||
return newFiles
|
||||
}
|
||||
|
||||
func loadSuccessDatas(command string) []dto.SSHHistory {
|
||||
var datas []dto.SSHHistory
|
||||
stdout2, err := cmd.Exec(command)
|
||||
if err == nil {
|
||||
lines := strings.Split(string(stdout2), "\n")
|
||||
for _, line := range lines {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) < 14 {
|
||||
continue
|
||||
}
|
||||
historyItem := dto.SSHHistory{
|
||||
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
||||
AuthMode: parts[6],
|
||||
User: parts[8],
|
||||
Address: parts[10],
|
||||
Port: parts[12],
|
||||
Status: constant.StatusSuccess,
|
||||
}
|
||||
datas = append(datas, historyItem)
|
||||
}
|
||||
}
|
||||
return datas
|
||||
}
|
||||
|
||||
func loadFailedAuthDatas(command string) []dto.SSHHistory {
|
||||
var datas []dto.SSHHistory
|
||||
stdout2, err := cmd.Exec(command)
|
||||
if err == nil {
|
||||
lines := strings.Split(string(stdout2), "\n")
|
||||
for _, line := range lines {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) < 14 {
|
||||
continue
|
||||
}
|
||||
historyItem := dto.SSHHistory{
|
||||
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
||||
AuthMode: parts[8],
|
||||
User: parts[10],
|
||||
Address: parts[11],
|
||||
Port: parts[13],
|
||||
Status: constant.StatusFailed,
|
||||
}
|
||||
if strings.Contains(line, ": ") {
|
||||
historyItem.Message = strings.Split(line, ": ")[1]
|
||||
}
|
||||
datas = append(datas, historyItem)
|
||||
}
|
||||
}
|
||||
return datas
|
||||
}
|
||||
|
||||
func loadFailedSecureDatas(command string) []dto.SSHHistory {
|
||||
var datas []dto.SSHHistory
|
||||
stdout2, err := cmd.Exec(command)
|
||||
if err == nil {
|
||||
lines := strings.Split(string(stdout2), "\n")
|
||||
for _, line := range lines {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) < 14 {
|
||||
continue
|
||||
}
|
||||
historyItem := dto.SSHHistory{
|
||||
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
||||
AuthMode: parts[6],
|
||||
User: parts[8],
|
||||
Address: parts[10],
|
||||
Port: parts[12],
|
||||
Status: constant.StatusFailed,
|
||||
}
|
||||
if strings.Contains(line, ": ") {
|
||||
historyItem.Message = strings.Split(line, ": ")[1]
|
||||
}
|
||||
datas = append(datas, historyItem)
|
||||
}
|
||||
}
|
||||
return datas
|
||||
}
|
||||
|
||||
func handleGunzip(path string) error {
|
||||
if _, err := cmd.Execf("gunzip %s", path); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -12,6 +11,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
@@ -67,7 +68,7 @@ func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) {
|
||||
notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, global.CONF.System.Mode, itemVersion, itemVersion))
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load relase-notes of version %s failed, err: %v", latestVersion, err)
|
||||
return nil, fmt.Errorf("load releases-notes of version %s failed, err: %v", latestVersion, err)
|
||||
}
|
||||
upgrade.ReleaseNote = notes
|
||||
return &upgrade, nil
|
||||
@@ -76,7 +77,7 @@ func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) {
|
||||
func (u *UpgradeService) LoadNotes(req dto.Upgrade) (string, error) {
|
||||
notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, global.CONF.System.Mode, req.Version, req.Version))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("load relase-notes of version %s failed, err: %v", req.Version, err)
|
||||
return "", fmt.Errorf("load releases-notes of version %s failed, err: %v", req.Version, err)
|
||||
}
|
||||
return notes, nil
|
||||
}
|
||||
@@ -93,9 +94,13 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
|
||||
if err := os.MkdirAll(originalDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
itemArch, err := loadArch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
downloadPath := fmt.Sprintf("%s/%s/%s/release", global.CONF.System.RepoUrl, global.CONF.System.Mode, req.Version)
|
||||
fileName := fmt.Sprintf("1panel-%s-%s-%s.tar.gz", req.Version, "linux", runtime.GOARCH)
|
||||
fileName := fmt.Sprintf("1panel-%s-%s-%s.tar.gz", req.Version, "linux", itemArch)
|
||||
_ = settingRepo.Update("SystemStatus", "Upgrading")
|
||||
go func() {
|
||||
if err := fileOp.DownloadFile(downloadPath+"/"+fileName, rootDir+"/"+fileName); err != nil {
|
||||
@@ -200,12 +205,12 @@ func (u *UpgradeService) loadVersion(isLatest bool, currentVersion string) (stri
|
||||
}
|
||||
latestVersionRes, err := http.Get(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", buserr.New(constant.ErrOSSConn)
|
||||
}
|
||||
defer latestVersionRes.Body.Close()
|
||||
version, err := io.ReadAll(latestVersionRes.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", buserr.New(constant.ErrOSSConn)
|
||||
}
|
||||
if isLatest {
|
||||
return string(version), nil
|
||||
@@ -213,7 +218,7 @@ func (u *UpgradeService) loadVersion(isLatest bool, currentVersion string) (stri
|
||||
|
||||
versionMap := make(map[string]string)
|
||||
if err := json.Unmarshal(version, &versionMap); err != nil {
|
||||
return "", fmt.Errorf("load version map failed, err: %v", err)
|
||||
return "", buserr.New(constant.ErrOSSConn)
|
||||
}
|
||||
|
||||
if len(currentVersion) < 4 {
|
||||
@@ -222,7 +227,7 @@ func (u *UpgradeService) loadVersion(isLatest bool, currentVersion string) (stri
|
||||
if version, ok := versionMap[currentVersion[0:4]]; ok {
|
||||
return version, nil
|
||||
}
|
||||
return "", errors.New("load version failed in latest.current")
|
||||
return "", buserr.New(constant.ErrOSSConn)
|
||||
}
|
||||
|
||||
func (u *UpgradeService) loadReleaseNotes(path string) (string, error) {
|
||||
@@ -237,3 +242,21 @@ func (u *UpgradeService) loadReleaseNotes(path string) (string, error) {
|
||||
}
|
||||
return string(release), nil
|
||||
}
|
||||
|
||||
func loadArch() (string, error) {
|
||||
switch runtime.GOARCH {
|
||||
case "amd64", "ppc64le", "s390x", "arm64":
|
||||
return runtime.GOARCH, nil
|
||||
case "arm":
|
||||
std, err := cmd.Exec("uname -m")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("std: %s, err: %s", std, err.Error())
|
||||
}
|
||||
if std == "armv7l\n" {
|
||||
return "armv7", nil
|
||||
}
|
||||
return "", fmt.Errorf("unsupport such arch: arm-%s", std)
|
||||
default:
|
||||
return "", fmt.Errorf("unsupport such arch: %s", runtime.GOARCH)
|
||||
}
|
||||
}
|
||||
|
||||
+257
-31
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser"
|
||||
"github.com/1Panel-dev/1Panel/cmd/server/nginx_conf"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gopkg.in/ini.v1"
|
||||
"gorm.io/gorm"
|
||||
"os"
|
||||
"path"
|
||||
@@ -75,6 +76,8 @@ type IWebsiteService interface {
|
||||
UpdateProxyFile(req request.NginxProxyUpdate) (err error)
|
||||
GetAuthBasics(req request.NginxAuthReq) (res response.NginxAuthRes, err error)
|
||||
UpdateAuthBasic(req request.NginxAuthUpdate) (err error)
|
||||
GetAntiLeech(id uint) (*response.NginxAntiLeechRes, error)
|
||||
UpdateAntiLeech(req request.NginxAntiLeechUpdate) (err error)
|
||||
}
|
||||
|
||||
func NewIWebsiteService() IWebsiteService {
|
||||
@@ -173,6 +176,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
||||
SiteDir: "/",
|
||||
AccessLog: true,
|
||||
ErrorLog: true,
|
||||
IPV6: create.IPV6,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -206,6 +210,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
||||
req.Name = create.AppInstall.Name
|
||||
req.AppDetailId = create.AppInstall.AppDetailId
|
||||
req.Params = create.AppInstall.Params
|
||||
req.AppContainerConfig = create.AppInstall.AppContainerConfig
|
||||
tx, installCtx := getTxAndContext()
|
||||
install, err = NewIAppService().Install(installCtx, req)
|
||||
if err != nil {
|
||||
@@ -243,6 +248,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
||||
req.AppDetailId = create.AppInstall.AppDetailId
|
||||
req.Params = create.AppInstall.Params
|
||||
req.Params["IMAGE_NAME"] = runtime.Image
|
||||
req.AppContainerConfig = create.AppInstall.AppContainerConfig
|
||||
nginxInstall, err = getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -335,6 +341,8 @@ func (w WebsiteService) UpdateWebsite(req request.WebsiteUpdate) error {
|
||||
website.PrimaryDomain = req.PrimaryDomain
|
||||
website.WebsiteGroupID = req.WebsiteGroupID
|
||||
website.Remark = req.Remark
|
||||
website.IPV6 = req.IPV6
|
||||
|
||||
if req.ExpireDate != "" {
|
||||
expireDate, err := time.Parse(constant.DateLayout, req.ExpireDate)
|
||||
if err != nil {
|
||||
@@ -673,21 +681,16 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
|
||||
websiteSSL.ExpireDate = cert.NotAfter
|
||||
websiteSSL.StartDate = cert.NotBefore
|
||||
websiteSSL.Type = cert.Issuer.CommonName
|
||||
websiteSSL.Organization = cert.Issuer.Organization[0]
|
||||
websiteSSL.PrimaryDomain = cert.Subject.CommonName
|
||||
if len(cert.Subject.Names) > 0 {
|
||||
var domains []string
|
||||
for _, name := range cert.Subject.Names {
|
||||
if v, ok := name.Value.(string); ok {
|
||||
if v != cert.Subject.CommonName {
|
||||
domains = append(domains, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(domains) > 0 {
|
||||
websiteSSL.Domains = strings.Join(domains, "")
|
||||
}
|
||||
if len(cert.Issuer.Organization) > 0 {
|
||||
websiteSSL.Organization = cert.Issuer.Organization[0]
|
||||
} else {
|
||||
websiteSSL.Organization = cert.Issuer.CommonName
|
||||
}
|
||||
if len(cert.DNSNames) > 0 {
|
||||
websiteSSL.PrimaryDomain = cert.DNSNames[0]
|
||||
websiteSSL.Domains = strings.Join(cert.DNSNames, ",")
|
||||
}
|
||||
|
||||
websiteSSL.Provider = constant.Manual
|
||||
websiteSSL.PrivateKey = req.PrivateKey
|
||||
websiteSSL.Pem = req.Certificate
|
||||
@@ -859,7 +862,7 @@ func (w WebsiteService) OpWebsiteLog(req request.WebsiteLogReq) (*response.Websi
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fileInfo.Size() > 10<<20 {
|
||||
if fileInfo.Size() > 20<<20 {
|
||||
return nil, buserr.New(constant.ErrFileToLarge)
|
||||
}
|
||||
fileInfo.Size()
|
||||
@@ -905,6 +908,11 @@ func (w WebsiteService) OpWebsiteLog(req request.WebsiteLogReq) (*response.Websi
|
||||
if err := websiteRepo.Save(context.Background(), &website); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case constant.DeleteLog:
|
||||
logPath := path.Join(nginx.Install.GetPath(), "www", "sites", website.Alias, "log", req.LogType)
|
||||
if err := files.NewFileOp().WriteFile(logPath, strings.NewReader(""), 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
@@ -1002,7 +1010,27 @@ func (w WebsiteService) GetPHPConfig(id uint) (*response.PHPConfig, error) {
|
||||
params[matches[1]] = matches[2]
|
||||
}
|
||||
}
|
||||
return &response.PHPConfig{Params: params}, nil
|
||||
cfg, err := ini.Load(phpConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
phpConfig, err := cfg.GetSection("PHP")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disableFunctionStr := phpConfig.Key("disable_functions").Value()
|
||||
res := &response.PHPConfig{Params: params}
|
||||
if disableFunctionStr != "" {
|
||||
disableFunctions := strings.Split(disableFunctionStr, ",")
|
||||
if len(disableFunctions) > 0 {
|
||||
res.DisableFunctions = disableFunctions
|
||||
}
|
||||
}
|
||||
uploadMaxSize := phpConfig.Key("upload_max_filesize").Value()
|
||||
if uploadMaxSize != "" {
|
||||
res.UploadMaxSize = uploadMaxSize
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (w WebsiteService) UpdatePHPConfig(req request.WebsitePHPConfigUpdate) (err error) {
|
||||
@@ -1026,23 +1054,55 @@ func (w WebsiteService) UpdatePHPConfig(req request.WebsitePHPConfigUpdate) (err
|
||||
defer configFile.Close()
|
||||
|
||||
contentBytes, err := fileOp.GetContent(phpConfigPath)
|
||||
content := string(contentBytes)
|
||||
lines := strings.Split(content, "\n")
|
||||
for i, line := range lines {
|
||||
if strings.HasPrefix(line, ";") {
|
||||
continue
|
||||
}
|
||||
for key, value := range req.Params {
|
||||
pattern := "^" + regexp.QuoteMeta(key) + "\\s*=\\s*.*$"
|
||||
if matched, _ := regexp.MatchString(pattern, line); matched {
|
||||
lines[i] = key + " = " + value
|
||||
}
|
||||
}
|
||||
}
|
||||
updatedContent := strings.Join(lines, "\n")
|
||||
if err := fileOp.WriteFile(phpConfigPath, strings.NewReader(updatedContent), 0755); err != nil {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if req.Scope == "params" {
|
||||
content := string(contentBytes)
|
||||
lines := strings.Split(content, "\n")
|
||||
for i, line := range lines {
|
||||
if strings.HasPrefix(line, ";") {
|
||||
continue
|
||||
}
|
||||
for key, value := range req.Params {
|
||||
pattern := "^" + regexp.QuoteMeta(key) + "\\s*=\\s*.*$"
|
||||
if matched, _ := regexp.MatchString(pattern, line); matched {
|
||||
lines[i] = key + " = " + value
|
||||
}
|
||||
}
|
||||
}
|
||||
updatedContent := strings.Join(lines, "\n")
|
||||
if err := fileOp.WriteFile(phpConfigPath, strings.NewReader(updatedContent), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
cfg, err := ini.Load(phpConfigPath)
|
||||
if err != nil {
|
||||
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{
|
||||
InstallId: appInstall.ID,
|
||||
Operate: constant.Restart,
|
||||
@@ -1051,6 +1111,7 @@ func (w WebsiteService) UpdatePHPConfig(req request.WebsitePHPConfigUpdate) (err
|
||||
_ = fileOp.WriteFile(phpConfigPath, strings.NewReader(string(contentBytes)), 0755)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1584,3 +1645,168 @@ func (w WebsiteService) GetAuthBasics(req request.NginxAuthReq) (res response.Ng
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w WebsiteService) UpdateAntiLeech(req request.NginxAntiLeechUpdate) (err error) {
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nginxFull, err := getNginxFull(&website)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
backpContent, err := fileOp.GetContent(nginxFull.SiteConfig.Config.FilePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
block := nginxFull.SiteConfig.Config.FindServers()[0]
|
||||
locations := block.FindDirectives("location")
|
||||
for _, location := range locations {
|
||||
loParams := location.GetParameters()
|
||||
if len(loParams) > 1 || loParams[0] == "~" {
|
||||
extendStr := loParams[1]
|
||||
if strings.HasPrefix(extendStr, `.*\.(`) && strings.HasSuffix(extendStr, `)$`) {
|
||||
block.RemoveDirective("location", loParams)
|
||||
}
|
||||
}
|
||||
}
|
||||
if req.Enable {
|
||||
exts := strings.Split(req.Extends, ",")
|
||||
newDirective := components.Directive{
|
||||
Name: "location",
|
||||
Parameters: []string{"~", fmt.Sprintf(`.*\.(%s)$`, strings.Join(exts, "|"))},
|
||||
}
|
||||
|
||||
newBlock := &components.Block{}
|
||||
newBlock.Directives = make([]components.IDirective, 0)
|
||||
if req.Cache {
|
||||
newBlock.Directives = append(newBlock.Directives, &components.Directive{
|
||||
Name: "expires",
|
||||
Parameters: []string{strconv.Itoa(req.CacheTime) + req.CacheUint},
|
||||
})
|
||||
}
|
||||
newBlock.Directives = append(newBlock.Directives, &components.Directive{
|
||||
Name: "log_not_found",
|
||||
Parameters: []string{"off"},
|
||||
})
|
||||
validDir := &components.Directive{
|
||||
Name: "valid_referers",
|
||||
Parameters: []string{},
|
||||
}
|
||||
if req.NoneRef {
|
||||
validDir.Parameters = append(validDir.Parameters, "none")
|
||||
}
|
||||
if len(req.ServerNames) > 0 {
|
||||
validDir.Parameters = append(validDir.Parameters, strings.Join(req.ServerNames, " "))
|
||||
}
|
||||
newBlock.Directives = append(newBlock.Directives, validDir)
|
||||
|
||||
ifDir := &components.Directive{
|
||||
Name: "if",
|
||||
Parameters: []string{"($invalid_referer)"},
|
||||
}
|
||||
ifDir.Block = &components.Block{
|
||||
Directives: []components.IDirective{
|
||||
&components.Directive{
|
||||
Name: "return",
|
||||
Parameters: []string{req.Return},
|
||||
},
|
||||
&components.Directive{
|
||||
Name: "access_log",
|
||||
Parameters: []string{"off"},
|
||||
},
|
||||
},
|
||||
}
|
||||
newBlock.Directives = append(newBlock.Directives, ifDir)
|
||||
newDirective.Block = newBlock
|
||||
block.Directives = append(block.Directives, &newDirective)
|
||||
}
|
||||
|
||||
if err = nginx.WriteConfig(nginxFull.SiteConfig.Config, nginx.IndentedStyle); err != nil {
|
||||
return
|
||||
}
|
||||
if err = updateNginxConfig(constant.NginxScopeServer, nil, &website); err != nil {
|
||||
_ = fileOp.WriteFile(nginxFull.SiteConfig.Config.FilePath, bytes.NewReader(backpContent), 0755)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w WebsiteService) GetAntiLeech(id uint) (*response.NginxAntiLeechRes, error) {
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nginxFull, err := getNginxFull(&website)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := &response.NginxAntiLeechRes{
|
||||
LogEnable: true,
|
||||
ServerNames: []string{},
|
||||
}
|
||||
block := nginxFull.SiteConfig.Config.FindServers()[0]
|
||||
locations := block.FindDirectives("location")
|
||||
for _, location := range locations {
|
||||
loParams := location.GetParameters()
|
||||
if len(loParams) > 1 || loParams[0] == "~" {
|
||||
extendStr := loParams[1]
|
||||
if strings.HasPrefix(extendStr, `.*\.(`) && strings.HasSuffix(extendStr, `)$`) {
|
||||
str1 := strings.TrimPrefix(extendStr, `.*\.(`)
|
||||
str2 := strings.TrimSuffix(str1, ")$")
|
||||
res.Extends = strings.Join(strings.Split(str2, "|"), ",")
|
||||
}
|
||||
}
|
||||
lDirectives := location.GetBlock().GetDirectives()
|
||||
for _, lDir := range lDirectives {
|
||||
if lDir.GetName() == "valid_referers" {
|
||||
res.Enable = true
|
||||
params := lDir.GetParameters()
|
||||
for _, param := range params {
|
||||
if param == "none" {
|
||||
res.NoneRef = true
|
||||
continue
|
||||
}
|
||||
if param == "blocked" {
|
||||
res.Blocked = true
|
||||
continue
|
||||
}
|
||||
if param == "server_names" {
|
||||
continue
|
||||
}
|
||||
res.ServerNames = append(res.ServerNames, param)
|
||||
}
|
||||
}
|
||||
if lDir.GetName() == "if" && lDir.GetParameters()[0] == "($invalid_referer)" {
|
||||
directives := lDir.GetBlock().GetDirectives()
|
||||
for _, dir := range directives {
|
||||
if dir.GetName() == "return" {
|
||||
res.Return = strings.Join(dir.GetParameters(), " ")
|
||||
}
|
||||
if dir.GetName() == "access_log" {
|
||||
if strings.Join(dir.GetParameters(), "") == "off" {
|
||||
res.LogEnable = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if lDir.GetName() == "expires" {
|
||||
res.Cache = true
|
||||
re := regexp.MustCompile(`^(\d+)(\w+)$`)
|
||||
matches := re.FindStringSubmatch(lDir.GetParameters()[0])
|
||||
if matches == nil {
|
||||
continue
|
||||
}
|
||||
cacheTime, err := strconv.Atoi(matches[1])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
unit := matches[2]
|
||||
res.CacheUint = unit
|
||||
res.CacheTime = cacheTime
|
||||
}
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -190,6 +190,9 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
|
||||
for _, domain := range domains {
|
||||
serverNames = append(serverNames, domain.Domain)
|
||||
server.UpdateListen(strconv.Itoa(domain.Port), false)
|
||||
if website.IPV6 {
|
||||
server.UpdateListen("[::]:"+strconv.Itoa(domain.Port), false)
|
||||
}
|
||||
}
|
||||
server.UpdateServerName(serverNames)
|
||||
|
||||
@@ -291,6 +294,9 @@ func addListenAndServerName(website model.Website, ports []int, domains []string
|
||||
server := config.FindServers()[0]
|
||||
for _, port := range ports {
|
||||
server.AddListen(strconv.Itoa(port), false)
|
||||
if website.IPV6 {
|
||||
server.UpdateListen("[::]:"+strconv.Itoa(port), false)
|
||||
}
|
||||
}
|
||||
for _, domain := range domains {
|
||||
server.AddServerName(domain)
|
||||
@@ -311,6 +317,7 @@ func deleteListenAndServerName(website model.Website, binds []string, domains []
|
||||
server := config.FindServers()[0]
|
||||
for _, bind := range binds {
|
||||
server.DeleteListen(bind)
|
||||
server.DeleteListen("[::]:" + bind)
|
||||
}
|
||||
for _, domain := range domains {
|
||||
server.DeleteServerName(domain)
|
||||
@@ -372,6 +379,9 @@ func applySSL(website model.Website, websiteSSL model.WebsiteSSL, req request.We
|
||||
config := nginxFull.SiteConfig.Config
|
||||
server := config.FindServers()[0]
|
||||
server.UpdateListen("443", website.DefaultServer, "ssl")
|
||||
if website.IPV6 {
|
||||
server.UpdateListen("[::]:443", website.DefaultServer, "ssl")
|
||||
}
|
||||
|
||||
switch req.HttpConfig {
|
||||
case constant.HTTPSOnly:
|
||||
@@ -380,10 +390,16 @@ func applySSL(website model.Website, websiteSSL model.WebsiteSSL, req request.We
|
||||
server.RemoveDirective("if", []string{"($scheme"})
|
||||
case constant.HTTPToHTTPS:
|
||||
server.UpdateListen("80", website.DefaultServer)
|
||||
if website.IPV6 {
|
||||
server.UpdateListen("[::]:80", website.DefaultServer)
|
||||
}
|
||||
server.AddHTTP2HTTPS()
|
||||
case constant.HTTPAlso:
|
||||
server.UpdateListen("80", website.DefaultServer)
|
||||
server.RemoveDirective("if", []string{"($scheme"})
|
||||
if website.IPV6 {
|
||||
server.UpdateListen("[::]:80", website.DefaultServer)
|
||||
}
|
||||
}
|
||||
|
||||
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
|
||||
@@ -19,5 +19,6 @@ type System struct {
|
||||
Password string `mapstructure:"password"`
|
||||
Entrance string `mapstructure:"entrance"`
|
||||
IsDemo bool `mapstructure:"is_demo"`
|
||||
AppRepo string `mapstructure:"app_repo"`
|
||||
ChangeUserInfo bool `mapstructure:"change_user_info"`
|
||||
}
|
||||
|
||||
+15
-6
@@ -1,12 +1,16 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
Running = "Running"
|
||||
UnHealthy = "UnHealthy"
|
||||
Error = "Error"
|
||||
Stopped = "Stopped"
|
||||
Installing = "Installing"
|
||||
Syncing = "Syncing"
|
||||
Running = "Running"
|
||||
UnHealthy = "UnHealthy"
|
||||
Error = "Error"
|
||||
Stopped = "Stopped"
|
||||
Installing = "Installing"
|
||||
Syncing = "Syncing"
|
||||
DownloadErr = "DownloadErr"
|
||||
DirNotFound = "DirNotFound"
|
||||
Upgrading = "Upgrading"
|
||||
UpgradeErr = "UpgradeErr"
|
||||
|
||||
ContainerPrefix = "1Panel-"
|
||||
|
||||
@@ -19,6 +23,11 @@ const (
|
||||
|
||||
AppResourceLocal = "local"
|
||||
AppResourceRemote = "remote"
|
||||
|
||||
CPUS = "CPUS"
|
||||
MemoryLimit = "MEMORY_LIMIT"
|
||||
HostIP = "HOST_IP"
|
||||
ContainerName = "CONTAINER_NAME"
|
||||
)
|
||||
|
||||
type AppOperate string
|
||||
|
||||
@@ -7,11 +7,12 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
DataDir = global.CONF.System.DataDir
|
||||
ResourceDir = path.Join(DataDir, "resource")
|
||||
AppResourceDir = path.Join(ResourceDir, "apps")
|
||||
AppInstallDir = path.Join(DataDir, "apps")
|
||||
LocalAppResourceDir = path.Join(ResourceDir, "localApps")
|
||||
LocalAppInstallDir = path.Join(DataDir, "localApps")
|
||||
RuntimeDir = path.Join(DataDir, "runtime")
|
||||
DataDir = global.CONF.System.DataDir
|
||||
ResourceDir = path.Join(DataDir, "resource")
|
||||
AppResourceDir = path.Join(ResourceDir, "apps")
|
||||
AppInstallDir = path.Join(DataDir, "apps")
|
||||
LocalAppResourceDir = path.Join(AppResourceDir, "local")
|
||||
LocalAppInstallDir = path.Join(AppInstallDir, "local")
|
||||
RemoteAppResourceDir = path.Join(AppResourceDir, "remote")
|
||||
RuntimeDir = path.Join(DataDir, "runtime")
|
||||
)
|
||||
|
||||
@@ -14,6 +14,8 @@ const (
|
||||
CodePasswordExpired = 405
|
||||
CodeAuth = 406
|
||||
CodeGlobalLoading = 407
|
||||
CodeErrIP = 408
|
||||
CodeErrDomain = 409
|
||||
CodeErrInternalServer = 500
|
||||
CodeErrHeader = 406
|
||||
)
|
||||
@@ -30,45 +32,39 @@ var (
|
||||
ErrInvalidParams = errors.New("ErrInvalidParams")
|
||||
|
||||
ErrTokenParse = errors.New("ErrTokenParse")
|
||||
|
||||
ErrPageGenerate = errors.New("generate page info failed")
|
||||
ErrRepoNotValid = "ErrRepoNotValid"
|
||||
)
|
||||
|
||||
// api
|
||||
var (
|
||||
ErrTypeInternalServer = "ErrInternalServer"
|
||||
ErrTypeInvalidParams = "ErrInvalidParams"
|
||||
ErrTypeToken = "ErrToken"
|
||||
ErrTypeTokenTimeOut = "ErrTokenTimeOut"
|
||||
ErrTypeNotLogin = "ErrNotLogin"
|
||||
ErrTypePasswordExpired = "ErrPasswordExpired"
|
||||
ErrTypeNotSafety = "ErrNotSafety"
|
||||
ErrNameIsExist = "ErrNameIsExist"
|
||||
ErrDemoEnvironment = "ErrDemoEnvironment"
|
||||
ErrInitUser = "ErrInitUser"
|
||||
)
|
||||
|
||||
// app
|
||||
var (
|
||||
ErrPortInUsed = "ErrPortInUsed"
|
||||
ErrAppLimit = "ErrAppLimit"
|
||||
ErrAppRequired = "ErrAppRequired"
|
||||
ErrFileCanNotRead = "ErrFileCanNotRead"
|
||||
ErrFileToLarge = "ErrFileToLarge"
|
||||
ErrFileCanNotRead = "ErrFileCanNotRead"
|
||||
ErrNotInstall = "ErrNotInstall"
|
||||
ErrPortInOtherApp = "ErrPortInOtherApp"
|
||||
ErrDbUserNotValid = "ErrDbUserNotValid"
|
||||
ErrUpdateBuWebsite = "ErrUpdateBuWebsite"
|
||||
Err1PanelNetworkFailed = "Err1PanelNetworkFailed"
|
||||
ErrCmdTimeout = "ErrCmdTimeout"
|
||||
ErrFileParse = "ErrFileParse"
|
||||
ErrInstallDirNotFound = "ErrInstallDirNotFound"
|
||||
ErrContainerName = "ErrContainerName"
|
||||
)
|
||||
|
||||
// website
|
||||
var (
|
||||
ErrDomainIsExist = "ErrDomainIsExist"
|
||||
ErrAliasIsExist = "ErrAliasIsExist"
|
||||
ErrAppDelete = "ErrAppDelete"
|
||||
ErrGroupIsUsed = "ErrGroupIsUsed"
|
||||
ErrUsernameIsExist = "ErrUsernameIsExist"
|
||||
ErrUsernameIsNotExist = "ErrUsernameIsNotExist"
|
||||
@@ -89,6 +85,7 @@ var (
|
||||
ErrLinkPathNotFound = "ErrLinkPathNotFound"
|
||||
ErrFileIsExit = "ErrFileIsExit"
|
||||
ErrFileUpload = "ErrFileUpload"
|
||||
ErrFileDownloadDir = "ErrFileDownloadDir"
|
||||
)
|
||||
|
||||
// mysql
|
||||
@@ -121,4 +118,5 @@ var (
|
||||
|
||||
var (
|
||||
ErrBackupInUsed = "ErrBackupInUsed"
|
||||
ErrOSSConn = "ErrOSSConn"
|
||||
)
|
||||
|
||||
@@ -38,6 +38,7 @@ const (
|
||||
GetLog = "get"
|
||||
DisableLog = "disable"
|
||||
EnableLog = "enable"
|
||||
DeleteLog = "delete"
|
||||
|
||||
AccessLog = "access.log"
|
||||
ErrorLog = "error.log"
|
||||
|
||||
+23
-8
@@ -15,19 +15,34 @@ import (
|
||||
|
||||
func Run() {
|
||||
nyc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||
Cron := cron.New(cron.WithLocation(nyc), cron.WithChain(cron.Recover(cron.DefaultLogger)), cron.WithChain(cron.DelayIfStillRunning(cron.DefaultLogger)))
|
||||
if _, err := Cron.AddJob("@every 5m", job.NewMonitorJob()); err != nil {
|
||||
global.LOG.Errorf("can not add monitor corn job: %s", err.Error())
|
||||
global.Cron = cron.New(cron.WithLocation(nyc), cron.WithChain(cron.Recover(cron.DefaultLogger)), cron.WithChain(cron.DelayIfStillRunning(cron.DefaultLogger)))
|
||||
|
||||
var (
|
||||
interval model.Setting
|
||||
status model.Setting
|
||||
)
|
||||
if err := global.DB.Where("key = ?", "MonitorStatus").Find(&status).Error; err != nil {
|
||||
global.LOG.Errorf("load monitor status from db failed, err: %v", err)
|
||||
}
|
||||
if _, err := Cron.AddJob("@daily", job.NewWebsiteJob()); err != nil {
|
||||
if status.Value == "enable" {
|
||||
if err := global.DB.Where("key = ?", "MonitorInterval").Find(&interval).Error; err != nil {
|
||||
global.LOG.Errorf("load monitor interval from db failed, err: %v", err)
|
||||
}
|
||||
if err := service.StartMonitor(false, interval.Value); err != nil {
|
||||
global.LOG.Errorf("can not add monitor corn job: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := global.Cron.AddJob("@daily", job.NewWebsiteJob()); err != nil {
|
||||
global.LOG.Errorf("can not add website corn job: %s", err.Error())
|
||||
}
|
||||
if _, err := Cron.AddJob("@daily", job.NewSSLJob()); err != nil {
|
||||
if _, err := global.Cron.AddJob("@daily", job.NewSSLJob()); err != nil {
|
||||
global.LOG.Errorf("can not add ssl corn job: %s", err.Error())
|
||||
}
|
||||
Cron.Start()
|
||||
|
||||
global.Cron = Cron
|
||||
if _, err := global.Cron.AddJob("@daily", job.NewAppStoreJob()); err != nil {
|
||||
global.LOG.Errorf("can not add appstore corn job: %s", err.Error())
|
||||
}
|
||||
global.Cron.Start()
|
||||
|
||||
var cronJobs []model.Cronjob
|
||||
if err := global.DB.Where("status = ?", constant.StatusEnable).Find(&cronJobs).Error; err != nil {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package job
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/service"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
)
|
||||
|
||||
type app struct{}
|
||||
|
||||
func NewAppStoreJob() *app {
|
||||
return &app{}
|
||||
}
|
||||
|
||||
func (a *app) Run() {
|
||||
global.LOG.Info("AppStore scheduled task in progress ...")
|
||||
if err := service.NewIAppService().SyncAppListFromRemote(); err != nil {
|
||||
global.LOG.Errorf("AppStore sync failed %s", err.Error())
|
||||
}
|
||||
global.LOG.Info("AppStore scheduled task has completed")
|
||||
}
|
||||
@@ -20,5 +20,6 @@ var (
|
||||
CACHE *badger_db.Cache
|
||||
Viper *viper.Viper
|
||||
|
||||
Cron *cron.Cron
|
||||
Cron *cron.Cron
|
||||
MonitorCronID int
|
||||
)
|
||||
|
||||
@@ -46,6 +46,13 @@ func GetErrMsg(key string, maps map[string]interface{}) string {
|
||||
return content
|
||||
}
|
||||
|
||||
func GetMsgByKey(key string) string {
|
||||
content := ginI18n.MustGetMessage(&i18n.LocalizeConfig{
|
||||
MessageID: key,
|
||||
})
|
||||
return content
|
||||
}
|
||||
|
||||
//go:embed lang/*
|
||||
var fs embed.FS
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ ErrRepoNotValid: "Remote repository verification failed!"
|
||||
ErrNameIsExist: "Name is already exist"
|
||||
ErrDemoEnvironment: "Demo server, prohibit this operation!"
|
||||
ErrCmdTimeout: "Command execution timed out!"
|
||||
ErrInitUser: "The user initialization has been completed by the system. Please do not repeat the operation!"
|
||||
|
||||
#app
|
||||
ErrPortInUsed: "{{ .detail }} port already in use"
|
||||
@@ -29,6 +28,22 @@ ErrDbUserNotValid: "Stock database, username and password do not match!"
|
||||
ErrDockerComposeNotValid: "docker-compose file format error!"
|
||||
ErrUpdateBuWebsite: 'The application was updated successfully, but the modification of the website configuration file failed, please check the configuration!'
|
||||
Err1PanelNetworkFailed: 'Default container network creation failed! {{ .detail }}'
|
||||
ErrFileParse: 'Application docker-compose file parsing failed!'
|
||||
ErrInstallDirNotFound: 'installation directory does not exist'
|
||||
AppStoreIsUpToDate: 'The app store is already up to date!'
|
||||
LocalAppVersionNull: 'The {{.name}} app is not synced to version! Could not add to application list'
|
||||
LocalAppVersionErr: '{{.name}} failed to sync version {{.version}}! {{.err}}'
|
||||
ErrFileNotFound: '{{.name}} file does not exist'
|
||||
ErrFileParseApp: 'Failed to parse {{.name}} file {{.err}}'
|
||||
ErrAppDirNull: 'version folder does not exist'
|
||||
LocalAppErr: "App {{.name}} sync failed! {{.err}}"
|
||||
ErrContainerName: "ContainerName is already exist"
|
||||
ErrAppSystemRestart: "1Panel restart causes the task to terminate"
|
||||
ErrCreateHttpClient: "Failed to create HTTP request {{.err}}"
|
||||
ErrHttpReqTimeOut: "Request timed out {{.err}}"
|
||||
ErrHttpReqFailed: "Request failed {{.err}}"
|
||||
ErrHttpReqNotFound: "The file does not exist"
|
||||
ErrNoSuchHost: "Network connection failed"
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: "File can not read"
|
||||
@@ -37,15 +52,14 @@ ErrPathNotFound: "Path is not found"
|
||||
ErrMovePathFailed: "The target path cannot contain the original path!"
|
||||
ErrLinkPathNotFound: "Target path does not exist!"
|
||||
ErrFileIsExit: "File already exists!"
|
||||
ErrFileUpload: "Failed to upload file"
|
||||
ErrFileUpload: "Failed to upload file {{.name}} {{.detail}}"
|
||||
ErrFileDownloadDir: "Download folder not supported"
|
||||
|
||||
#website
|
||||
ErrDomainIsExist: "Domain is already exist"
|
||||
ErrAliasIsExist: "Alias is already exist"
|
||||
ErrAppDelete: 'Other Website use this App'
|
||||
ErrGroupIsUsed: 'The group is in use and cannot be deleted'
|
||||
ErrUsernameIsExist: 'Username is already exist'
|
||||
ErrUsernameIsNotExist: 'Username is not exist'
|
||||
|
||||
#ssl
|
||||
ErrSSLCannotDelete: "The certificate is being used by the website and cannot be removed"
|
||||
@@ -64,7 +78,6 @@ ErrTypeOfRedis: "The recovery file type does not match the current persistence m
|
||||
#container
|
||||
ErrInUsed: "{{ .detail }} is in use and cannot be deleted"
|
||||
ErrObjectInUsed: "This object is in use and cannot be deleted"
|
||||
ErrPortRules: "Invalid port specification"
|
||||
|
||||
#runtime
|
||||
ErrDirNotFound: "The build folder does not exist! Please check file integrity!"
|
||||
@@ -74,4 +87,6 @@ ErrImageExist: "Image is already exist!"
|
||||
ErrDelWithWebsite: "The operating environment has been associated with a website and cannot be deleted"
|
||||
|
||||
#setting
|
||||
ErrBackupInUsed: "The backup account is already being used in a cronjob and cannot be deleted."
|
||||
ErrBackupInUsed: "The backup account is already being used in a cronjob and cannot be deleted."
|
||||
|
||||
ErrOSSConn: "Unable to successfully request the latest version. Please check if the server can connect to the external network environment."
|
||||
@@ -17,7 +17,6 @@ ErrRepoNotValid: "远程仓库校验失败!"
|
||||
ErrNameIsExist: "名称已存在"
|
||||
ErrDemoEnvironment: "演示服务器,禁止此操作!"
|
||||
ErrCmdTimeout: "命令执行超时!"
|
||||
ErrInitUser: "系统已完成用户初始化,请勿重复操作!"
|
||||
|
||||
#app
|
||||
ErrPortInUsed: "{{ .detail }} 端口已被占用!"
|
||||
@@ -29,6 +28,22 @@ 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: "网络连接失败"
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: "此文件不支持预览"
|
||||
@@ -37,15 +52,14 @@ ErrPathNotFound: "目录不存在"
|
||||
ErrMovePathFailed: "目标路径不能包含原路径!"
|
||||
ErrLinkPathNotFound: "目标路径不存在!"
|
||||
ErrFileIsExit: "文件已存在!"
|
||||
ErrFileUpload: "上传文件失败"
|
||||
ErrFileUpload: "{{ .name }} 上传文件失败 {{ .detail}}"
|
||||
ErrFileDownloadDir: "不支持下载文件夹"
|
||||
|
||||
#website
|
||||
ErrDomainIsExist: "域名已存在"
|
||||
ErrAliasIsExist: "代号已存在"
|
||||
ErrAppDelete: '其他网站使用此应用,无法删除'
|
||||
ErrGroupIsUsed: '分组正在使用中,无法删除'
|
||||
ErrUsernameIsExist: '用户名已存在'
|
||||
ErrUsernameIsNotExist: '用户不存在'
|
||||
|
||||
#ssl
|
||||
ErrSSLCannotDelete: "证书正在被网站使用,无法删除"
|
||||
@@ -64,7 +78,6 @@ ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后
|
||||
#container
|
||||
ErrInUsed: "{{ .detail }} 正被使用,无法删除"
|
||||
ErrObjectInUsed: "该对象正被使用,无法删除"
|
||||
ErrPortRules: "无效的端口规格"
|
||||
|
||||
#runtime
|
||||
ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!"
|
||||
@@ -75,3 +88,5 @@ ErrDelWithWebsite: "运行环境已经关联网站,无法删除"
|
||||
|
||||
#setting
|
||||
ErrBackupInUsed: "该备份账号已在计划任务中使用,无法删除"
|
||||
|
||||
ErrOSSConn: "无法成功请求最新版本,请检查服务器是否能够连接到外部网络环境。"
|
||||
|
||||
@@ -15,10 +15,13 @@ func Init() {
|
||||
constant.AppResourceDir = path.Join(constant.ResourceDir, "apps")
|
||||
constant.AppInstallDir = path.Join(constant.DataDir, "apps")
|
||||
constant.RuntimeDir = path.Join(constant.DataDir, "runtime")
|
||||
constant.LocalAppResourceDir = path.Join(constant.ResourceDir, "localApps")
|
||||
constant.LocalAppInstallDir = path.Join(constant.DataDir, "localApps")
|
||||
|
||||
dirs := []string{constant.DataDir, constant.ResourceDir, constant.AppResourceDir, constant.AppInstallDir, global.CONF.System.Backup, constant.RuntimeDir, constant.LocalAppResourceDir}
|
||||
constant.LocalAppResourceDir = path.Join(constant.AppResourceDir, "local")
|
||||
constant.LocalAppInstallDir = path.Join(constant.AppInstallDir, "local")
|
||||
constant.RemoteAppResourceDir = path.Join(constant.AppResourceDir, "remote")
|
||||
|
||||
dirs := []string{constant.DataDir, constant.ResourceDir, constant.AppResourceDir, constant.AppInstallDir,
|
||||
global.CONF.System.Backup, constant.RuntimeDir, constant.LocalAppResourceDir, constant.RemoteAppResourceDir}
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
for _, dir := range dirs {
|
||||
|
||||
@@ -16,13 +16,13 @@ func syncApp() {
|
||||
global.LOG.Errorf("sync app error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
if setting.AppStoreVersion != "" {
|
||||
if setting.AppStoreLastModified != "0" {
|
||||
global.LOG.Info("no need to sync")
|
||||
return
|
||||
}
|
||||
global.LOG.Info("sync app start...")
|
||||
if err := service.NewIAppService().SyncAppListFromRemote(); err != nil {
|
||||
global.LOG.Errorf("sync app error: %s", err.Error())
|
||||
global.LOG.Errorf("sync app error")
|
||||
return
|
||||
}
|
||||
global.LOG.Info("sync app successful")
|
||||
|
||||
Vendored
+8
-8
@@ -14,29 +14,29 @@ func Init() {
|
||||
options := badger.Options{
|
||||
Dir: c,
|
||||
ValueDir: c,
|
||||
ValueLogFileSize: 102400000,
|
||||
ValueLogMaxEntries: 100000,
|
||||
ValueLogFileSize: 64 << 20,
|
||||
ValueLogMaxEntries: 10 << 20,
|
||||
VLogPercentile: 0.1,
|
||||
|
||||
MemTableSize: 64 << 20,
|
||||
MemTableSize: 32 << 20,
|
||||
BaseTableSize: 2 << 20,
|
||||
BaseLevelSize: 10 << 20,
|
||||
TableSizeMultiplier: 2,
|
||||
LevelSizeMultiplier: 10,
|
||||
MaxLevels: 7,
|
||||
NumGoroutines: 8,
|
||||
NumGoroutines: 4,
|
||||
MetricsEnabled: true,
|
||||
NumCompactors: 4,
|
||||
NumCompactors: 2,
|
||||
NumLevelZeroTables: 5,
|
||||
NumLevelZeroTablesStall: 15,
|
||||
NumMemtables: 5,
|
||||
NumMemtables: 1,
|
||||
BloomFalsePositive: 0.01,
|
||||
BlockSize: 4 * 1024,
|
||||
BlockSize: 2 * 1024,
|
||||
SyncWrites: false,
|
||||
NumVersionsToKeep: 1,
|
||||
CompactL0OnClose: false,
|
||||
VerifyValueChecksum: false,
|
||||
BlockCacheSize: 256 << 20,
|
||||
BlockCacheSize: 32 << 20,
|
||||
IndexCacheSize: 0,
|
||||
ZSTDCompressionLevel: 1,
|
||||
EncryptionKey: []byte{},
|
||||
|
||||
@@ -45,10 +45,7 @@ func Init() {
|
||||
global.LOG.Fatalf("init entrance before start failed, err: %v", err)
|
||||
}
|
||||
|
||||
if cmd.HasNoPasswordSudo() {
|
||||
_, _ = cmd.Exec("sudo sed -i '/CHANGE_USER_INFO=true/d' /usr/local/bin/1pctl")
|
||||
} else {
|
||||
_, _ = cmd.Exec("sed -i '/CHANGE_USER_INFO=true/d' /usr/local/bin/1pctl")
|
||||
}
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
_, _ = cmd.Execf("%s sed -i '/CHANGE_USER_INFO=true/d' /usr/local/bin/1pctl", sudo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,11 @@ func Init() {
|
||||
migrations.UpdateTableHost,
|
||||
migrations.UpdateTableWebsite,
|
||||
migrations.AddEntranceAndSSL,
|
||||
migrations.UpdateTableSetting,
|
||||
migrations.UpdateTableAppDetail,
|
||||
migrations.AddBindAndAllowIPs,
|
||||
migrations.UpdateCronjobWithSecond,
|
||||
migrations.UpdateWebsite,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
||||
@@ -2,6 +2,7 @@ package migrations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
@@ -312,3 +313,80 @@ var AddEntranceAndSSL = &gormigrate.Migration{
|
||||
return tx.AutoMigrate(&model.Website{})
|
||||
},
|
||||
}
|
||||
|
||||
var UpdateTableSetting = &gormigrate.Migration{
|
||||
ID: "20200516-update-table-setting",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.Create(&model.Setting{Key: "AppStoreLastModified", Value: "0"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var UpdateTableAppDetail = &gormigrate.Migration{
|
||||
ID: "20200517-update-table-app-detail",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.App{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.AutoMigrate(&model.AppDetail{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var AddBindAndAllowIPs = &gormigrate.Migration{
|
||||
ID: "20230517-add-bind-and-allow",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.Create(&model.Setting{Key: "BindDomain", Value: ""}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "AllowIPs", Value: ""}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "TimeZone", Value: common.LoadTimeZoneByCmd()}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "NtpSite", Value: "pool.ntp.org"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "MonitorInterval", Value: "1"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var UpdateCronjobWithSecond = &gormigrate.Migration{
|
||||
ID: "20200524-update-table-cronjob",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.Cronjob{}); err != nil {
|
||||
return err
|
||||
}
|
||||
var jobs []model.Cronjob
|
||||
if err := tx.Where("exclusion_rules != ?", "").Find(&jobs).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
for _, job := range jobs {
|
||||
if strings.Contains(job.ExclusionRules, ";") {
|
||||
newRules := strings.ReplaceAll(job.ExclusionRules, ";", ",")
|
||||
if err := tx.Model(&model.Cronjob{}).Where("id = ?", job.ID).Update("exclusion_rules", newRules).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var UpdateWebsite = &gormigrate.Migration{
|
||||
ID: "20200530-update-table-website",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.Website{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/gin-contrib/gzip"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-contrib/gzip"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||
"github.com/1Panel-dev/1Panel/backend/middleware"
|
||||
@@ -18,23 +17,16 @@ import (
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
)
|
||||
|
||||
func setWebStatic(rootRouter *gin.Engine) {
|
||||
rootRouter.StaticFS("/fav", http.FS(web.Favicon))
|
||||
func setWebStatic(rootRouter *gin.RouterGroup) {
|
||||
rootRouter.StaticFS("/public", http.FS(web.Favicon))
|
||||
rootRouter.GET("/assets/*filepath", func(c *gin.Context) {
|
||||
staticServer := http.FileServer(http.FS(web.Assets))
|
||||
staticServer.ServeHTTP(c.Writer, c.Request)
|
||||
})
|
||||
|
||||
rootRouter.GET("/", func(c *gin.Context) {
|
||||
staticServer := http.FileServer(http.FS(web.IndexHtml))
|
||||
staticServer.ServeHTTP(c.Writer, c.Request)
|
||||
})
|
||||
rootRouter.NoRoute(func(c *gin.Context) {
|
||||
c.Writer.WriteHeader(http.StatusOK)
|
||||
_, _ = c.Writer.Write(web.IndexByte)
|
||||
c.Writer.Header().Add("Accept", "text/html")
|
||||
c.Writer.Flush()
|
||||
})
|
||||
}
|
||||
|
||||
func Routers() *gin.Engine {
|
||||
@@ -45,8 +37,14 @@ func Routers() *gin.Engine {
|
||||
if global.CONF.System.IsDemo {
|
||||
Router.Use(middleware.DemoHandle())
|
||||
}
|
||||
Router.Use(gzip.Gzip(gzip.DefaultCompression))
|
||||
setWebStatic(Router)
|
||||
|
||||
Router.NoRoute(func(c *gin.Context) {
|
||||
c.Writer.WriteHeader(http.StatusOK)
|
||||
_, _ = c.Writer.Write(web.IndexByte)
|
||||
c.Writer.Header().Add("Accept", "text/html")
|
||||
c.Writer.Flush()
|
||||
})
|
||||
|
||||
Router.Use(i18n.GinI18nLocalize())
|
||||
Router.SetFuncMap(template.FuncMap{
|
||||
"Localize": ginI18n.GetMessage,
|
||||
@@ -61,8 +59,12 @@ func Routers() *gin.Engine {
|
||||
PublicGroup.GET("/health", func(c *gin.Context) {
|
||||
c.JSON(200, "ok")
|
||||
})
|
||||
PublicGroup.Use(gzip.Gzip(gzip.DefaultCompression))
|
||||
setWebStatic(PublicGroup)
|
||||
}
|
||||
PrivateGroup := Router.Group("/api/v1")
|
||||
PrivateGroup.Use(middleware.WhiteAllow())
|
||||
PrivateGroup.Use(middleware.BindDomain())
|
||||
PrivateGroup.Use(middleware.GlobalLoading())
|
||||
{
|
||||
systemRouter.InitBaseRouter(PrivateGroup)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var stdErrFileHandler *os.File
|
||||
|
||||
func dupWrite(file *os.File) error {
|
||||
stdErrFileHandler = file
|
||||
if err := syscall.Dup2(int(file.Fd()), int(os.Stderr.Fd())); err != nil {
|
||||
if err := unix.Dup2(int(file.Fd()), int(os.Stderr.Fd())); err != nil {
|
||||
return err
|
||||
}
|
||||
runtime.SetFinalizer(stdErrFileHandler, func(fd *os.File) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user