mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 16:00:29 +00:00
Compare commits
227
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3350b54b3 | ||
|
|
cd4e0374d0 | ||
|
|
5d2084fda4 | ||
|
|
8c32fa3e6c | ||
|
|
fef13211a0 | ||
|
|
5ff5b0a5ad | ||
|
|
8ac9d022b6 | ||
|
|
2c02dc07e9 | ||
|
|
ba999c516a | ||
|
|
28303ffa82 | ||
|
|
1f9a3aa51a | ||
|
|
656dda234c | ||
|
|
63b62f4074 | ||
|
|
158180a052 | ||
|
|
dbe479e920 | ||
|
|
7aefa34871 | ||
|
|
7d92436b92 | ||
|
|
66a070c239 | ||
|
|
bb9d9042f0 | ||
|
|
a5d86d4c00 | ||
|
|
e867deb547 | ||
|
|
af15659d99 | ||
|
|
f63658db9e | ||
|
|
d4edf2f874 | ||
|
|
94f7d78cc9 | ||
|
|
5cede39e5c | ||
|
|
508ac2ae84 | ||
|
|
997b820a42 | ||
|
|
5de83985e3 | ||
|
|
c0220dbed0 | ||
|
|
2e9305b9b1 | ||
|
|
b18bdea1c9 | ||
|
|
25fb431163 | ||
|
|
ac43f00273 | ||
|
|
8ccf1fcbf3 | ||
|
|
b2803f574b | ||
|
|
bbc14bb723 | ||
|
|
e89311d5d4 | ||
|
|
b5e56c6b65 | ||
|
|
3d2023858c | ||
|
|
74b48a18be | ||
|
|
42f61f60fb | ||
|
|
e32a11910e | ||
|
|
dc9fdf897b | ||
|
|
35c946a064 | ||
|
|
747a0c45ee | ||
|
|
8e03b24b0c | ||
|
|
18c65c3096 | ||
|
|
3b306f990a | ||
|
|
92fa6552b1 | ||
|
|
a39dc33770 | ||
|
|
0a42d4942c | ||
|
|
8086d5cecb | ||
|
|
a6875614f6 | ||
|
|
6b061fa327 | ||
|
|
b416e6b6f1 | ||
|
|
ac1f49f8dd | ||
|
|
6ddee2649d | ||
|
|
fd14f53cb9 | ||
|
|
029e2210f5 | ||
|
|
69769e6203 | ||
|
|
2a21f194d3 | ||
|
|
e46d8c5812 | ||
|
|
938605a169 | ||
|
|
c3cc26a136 | ||
|
|
dda83b6307 | ||
|
|
48e2e01a57 | ||
|
|
d6b00967ca | ||
|
|
6d4c1f8e79 | ||
|
|
e88fe0ac2f | ||
|
|
496c0b50b4 | ||
|
|
9d73d74c63 | ||
|
|
7e4e12107a | ||
|
|
5b15de91e6 | ||
|
|
98b3dc9d7d | ||
|
|
c05e610b13 | ||
|
|
63f9368e26 | ||
|
|
3f47a6e701 | ||
|
|
d7c9b3b192 | ||
|
|
063ed23acb | ||
|
|
7c33dd9026 | ||
|
|
a853d8c869 | ||
|
|
4ddeb81972 | ||
|
|
4dd9f00637 | ||
|
|
49f3779fd2 | ||
|
|
7389aad122 | ||
|
|
bf095c677c | ||
|
|
fb929687d8 | ||
|
|
bddcf76fc3 | ||
|
|
9370bdb5ac | ||
|
|
ef6d8bb17b | ||
|
|
99c2eb04c9 | ||
|
|
d83b673c90 | ||
|
|
7498f3f66b | ||
|
|
147eda529c | ||
|
|
c1f0da4248 | ||
|
|
8656dada06 | ||
|
|
217d780ed1 | ||
|
|
fe62dc18fa | ||
|
|
d0e95903c9 | ||
|
|
1ec7f519c6 | ||
|
|
450e8b18cf | ||
|
|
bb882286aa | ||
|
|
d9bbde9e50 | ||
|
|
29746d1b32 | ||
|
|
5f211961ae | ||
|
|
91d364b18b | ||
|
|
f1677f5a67 | ||
|
|
fd270263f4 | ||
|
|
81c1e90e54 | ||
|
|
3b737184ea | ||
|
|
4779bffceb | ||
|
|
4209ee4486 | ||
|
|
6ca352bf85 | ||
|
|
a4d77a1cc2 | ||
|
|
b9367abfb9 | ||
|
|
a6062897d5 | ||
|
|
1afe84ed02 | ||
|
|
388458f759 | ||
|
|
512ff23f19 | ||
|
|
45a32ff255 | ||
|
|
e4e2d76f75 | ||
|
|
282e196534 | ||
|
|
e4fbd3cf86 | ||
|
|
402931862f | ||
|
|
a70e2635ab | ||
|
|
8c9ae38017 | ||
|
|
3920076bd9 | ||
|
|
55cbf572e0 | ||
|
|
c4bd4734a5 | ||
|
|
e7810febd6 | ||
|
|
0bc9e85734 | ||
|
|
4d85ae5fa6 | ||
|
|
a7342d6ef2 | ||
|
|
5b0fccd19a | ||
|
|
ebbfed711c | ||
|
|
d631888d09 | ||
|
|
afd62e8539 | ||
|
|
4aca66c417 | ||
|
|
944463e9a8 | ||
|
|
cf4274afdc | ||
|
|
102f7c316d | ||
|
|
ffc81ea85a | ||
|
|
02d22ba63f | ||
|
|
51e98623c9 | ||
|
|
8e94170fed | ||
|
|
42cee513e0 | ||
|
|
2c18382f46 | ||
|
|
3773412139 | ||
|
|
e17bbdd6a7 | ||
|
|
82a0f3a770 | ||
|
|
11e16abe64 | ||
|
|
6db79f4b7b | ||
|
|
977a4be3c1 | ||
|
|
51ae668154 | ||
|
|
c6fe487e46 | ||
|
|
ec4533e7de | ||
|
|
3b73de32ad | ||
|
|
da5668ac27 | ||
|
|
c1039fb7f2 | ||
|
|
c82d6838c5 | ||
|
|
c40c7b57cb | ||
|
|
009237a35a | ||
|
|
f4f6f65b4c | ||
|
|
313a8a3380 | ||
|
|
f686e7cbc4 | ||
|
|
7fa39687b5 | ||
|
|
9d84120181 | ||
|
|
3be0dab8a3 | ||
|
|
3a9275d5fa | ||
|
|
6296aa04c6 | ||
|
|
cfe9e94e16 | ||
|
|
902d329e78 | ||
|
|
62f59eb1c3 | ||
|
|
a7d4c4ebec | ||
|
|
0ca96621d4 | ||
|
|
7ea179c2da | ||
|
|
6982396572 | ||
|
|
36c4fbfeee | ||
|
|
868c6f47a0 | ||
|
|
74695ab276 | ||
|
|
cb74d8d51b | ||
|
|
eaa620b081 | ||
|
|
18ab07ec4f | ||
|
|
e86bc9a8db | ||
|
|
75837c87b4 | ||
|
|
9a73095a8f | ||
|
|
9242bf6482 | ||
|
|
f552a0ac36 | ||
|
|
141e2f61eb | ||
|
|
b6d7b319f2 | ||
|
|
4cdc6c80ec | ||
|
|
cee9d7ffb0 | ||
|
|
3f48bdb216 | ||
|
|
2cef8dcea4 | ||
|
|
c327936db1 | ||
|
|
1b53cd4ca8 | ||
|
|
08f9f6f8e5 | ||
|
|
fb6b2729c6 | ||
|
|
aa55a18ed8 | ||
|
|
6c74c64b29 | ||
|
|
fddbf34684 | ||
|
|
ea878bba73 | ||
|
|
db002ec915 | ||
|
|
606d3c1717 | ||
|
|
b0fbb39cd3 | ||
|
|
2478d26c59 | ||
|
|
e072620f9b | ||
|
|
dd3419bf52 | ||
|
|
f12e70ef5d | ||
|
|
2836207850 | ||
|
|
4253b48bba | ||
|
|
ff36b65af4 | ||
|
|
1ef4803954 | ||
|
|
08ebbee328 | ||
|
|
d8e4144d46 | ||
|
|
0b1819576d | ||
|
|
6ce33754a8 | ||
|
|
21e0808a86 | ||
|
|
3480182473 | ||
|
|
e516158294 | ||
|
|
c8ee0af152 | ||
|
|
3b82d2a1ee | ||
|
|
274fd526b7 | ||
|
|
86b0e01d9d | ||
|
|
c06ba18023 | ||
|
|
f462591e9c |
@@ -6,6 +6,7 @@ permissions:
|
||||
jobs:
|
||||
generic_handler:
|
||||
name: Add Labels to PR
|
||||
if: github.repository == '1Panel-dev/1Panel'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.23'
|
||||
go-version: '1.24'
|
||||
- name: Build Release
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.23'
|
||||
go-version: '1.24'
|
||||
- name: Build Release
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
|
||||
@@ -8,6 +8,7 @@ on:
|
||||
jobs:
|
||||
sonarcloud:
|
||||
name: SonarCloud
|
||||
if: github.repository == '1Panel-dev/1Panel'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -2,6 +2,7 @@ name: Synchronize to Gitee
|
||||
on: [push]
|
||||
jobs:
|
||||
repo-sync:
|
||||
if: github.repository == '1Panel-dev/1Panel'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Mirror the Github organization repos to Gitee.
|
||||
|
||||
+3
-8
@@ -33,9 +33,6 @@ builds:
|
||||
- ppc64le
|
||||
- s390x
|
||||
- riscv64
|
||||
hooks:
|
||||
post:
|
||||
- ./ci/script.sh compress_binary {{ .Path }} {{ .Arch }}
|
||||
|
||||
- id: core
|
||||
dir: core
|
||||
@@ -59,9 +56,6 @@ builds:
|
||||
- ppc64le
|
||||
- s390x
|
||||
- riscv64
|
||||
hooks:
|
||||
post:
|
||||
- ./ci/script.sh compress_binary {{ .Path }} {{ .Arch }}
|
||||
|
||||
archives:
|
||||
- formats: [ 'tar.gz' ]
|
||||
@@ -70,11 +64,12 @@ archives:
|
||||
wrap_in_directory: true
|
||||
files:
|
||||
- 1pctl
|
||||
- install.sh
|
||||
- 1panel-core.service
|
||||
- 1panel-agent.service
|
||||
- install.sh
|
||||
- GeoIP.mmdb
|
||||
- initscript/*
|
||||
- lang/*
|
||||
- GeoIP.mmdb
|
||||
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
|
||||
@@ -30,20 +30,20 @@ build_frontend:
|
||||
|
||||
build_core_on_linux:
|
||||
cd $(CORE_PATH) \
|
||||
&& GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(CORE_NAME) $(CORE_MAIN)
|
||||
&& CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(CORE_NAME) $(CORE_MAIN)
|
||||
|
||||
build_agent_on_linux:
|
||||
cd $(AGENT_PATH) \
|
||||
&& GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(AGENT_NAME) $(AGENT_MAIN)
|
||||
&& CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(AGENT_NAME) $(AGENT_MAIN)
|
||||
|
||||
build_core_on_darwin:
|
||||
cd $(CORE_PATH) \
|
||||
&& GOOS=linux GOARCH=amd64 $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(CORE_NAME) $(CORE_MAIN)
|
||||
&& CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(CORE_NAME) $(CORE_MAIN)
|
||||
|
||||
build_agent_on_darwin:
|
||||
cd $(AGENT_PATH) \
|
||||
&& GOOS=linux GOARCH=amd64 $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(AGENT_NAME) $(AGENT_MAIN)
|
||||
&& CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(AGENT_NAME) $(AGENT_MAIN)
|
||||
|
||||
build_all: build_frontend build_core_on_linux build_agent_on_linux
|
||||
|
||||
build_on_local: clean_assets build_frontend build_core_on_darwin build_agent_on_darwin upx_bin
|
||||
build_on_local: clean_assets build_frontend build_core_on_darwin build_agent_on_darwin
|
||||
|
||||
+29
-1
@@ -1,6 +1,9 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
@@ -26,7 +29,7 @@ func (b *BaseApi) SearchApp(c *gin.Context) {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
helper.SuccessWithDataGzipped(c, list)
|
||||
}
|
||||
|
||||
// @Tags App
|
||||
@@ -192,3 +195,28 @@ func (b *BaseApi) GetAppListUpdate(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags App
|
||||
// @Summary Get app icon by app_id
|
||||
// @Accept json
|
||||
// @Param appId path integer true "app id"
|
||||
// @Success 200 {file} file "app icon"
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /apps/icon/:appId [get]
|
||||
func (b *BaseApi) GetAppIcon(c *gin.Context) {
|
||||
appID, err := helper.GetIntParamByKey(c, "appID")
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
iconBytes, err := appService.GetAppIcon(appID)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
c.Header("Content-Type", "image/png")
|
||||
c.Header("Cache-Control", "public, max-age=31536000, immutable")
|
||||
c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
|
||||
c.Data(http.StatusOK, "image/png", iconBytes)
|
||||
}
|
||||
|
||||
@@ -28,6 +28,28 @@ func (b *BaseApi) CreateComposeTemplate(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Container Compose-template
|
||||
// @Summary Bacth compose template
|
||||
// @Accept json
|
||||
// @Param request body dto.ComposeTemplateBatch true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/template/batch [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"批量导入编排模版","formatEN":"batch import compose templates"}
|
||||
func (b *BaseApi) BatchComposeTemplate(c *gin.Context) {
|
||||
var req dto.ComposeTemplateBatch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := composeTemplateService.Batch(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Container Compose-template
|
||||
// @Summary Page compose templates
|
||||
// @Accept json
|
||||
|
||||
@@ -263,6 +263,27 @@ func (b *BaseApi) ContainerListStats(c *gin.Context) {
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Summary Load container stats size
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithName true "request"
|
||||
// @Success 200 {object} dto.ContainerItemStats
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/item/stats [post]
|
||||
func (b *BaseApi) ContainerItemStats(c *gin.Context) {
|
||||
var req dto.OperationWithName
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := containerService.ContainerItemStats(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Create container
|
||||
// @Accept json
|
||||
@@ -314,7 +335,7 @@ func (b *BaseApi) ContainerCreateByCommand(c *gin.Context) {
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/upgrade [post]
|
||||
// @x-panel-log {"bodyKeys":["name","image"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新容器镜像 [name][image]","formatEN":"upgrade container image [name][image]"}
|
||||
// @x-panel-log {"bodyKeys":["names","image"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新容器镜像 [names][image]","formatEN":"upgrade container image [names][image]"}
|
||||
func (b *BaseApi) ContainerUpgrade(c *gin.Context) {
|
||||
var req dto.ContainerUpgrade
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
|
||||
@@ -211,7 +211,7 @@ func (b *BaseApi) LoadRecordLog(c *gin.Context) {
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /cronjobs/records/clean [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"cronjobs","output_column":"name","output_value":"name"}],"formatZH":"清空计划任务记录 [name]","formatEN":"clean cronjob [name] records"}
|
||||
// @x-panel-log {"bodyKeys":["cronjobID"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"cronjobID","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 := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
|
||||
@@ -37,7 +37,7 @@ func (b *BaseApi) LoadAppLauncher(c *gin.Context) {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
helper.SuccessWithDataGzipped(c, data)
|
||||
}
|
||||
|
||||
// @Tags Dashboard
|
||||
@@ -179,6 +179,28 @@ func (b *BaseApi) LoadDashboardCurrentInfo(c *gin.Context) {
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Dashboard
|
||||
// @Summary Load top cpu processes
|
||||
// @Success 200 {Array} dto.Process
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /dashboard/current/top/cpu [get]
|
||||
func (b *BaseApi) LoadDashboardTopCPU(c *gin.Context) {
|
||||
data := dashboardService.LoadTopCPU()
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Dashboard
|
||||
// @Summary Load top memory processes
|
||||
// @Success 200 {Array} dto.Process
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /dashboard/current/top/mem [get]
|
||||
func (b *BaseApi) LoadDashboardTopMem(c *gin.Context) {
|
||||
data := dashboardService.LoadTopMem()
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Dashboard
|
||||
// @Summary System restart
|
||||
// @Accept json
|
||||
|
||||
@@ -195,21 +195,19 @@ func (b *BaseApi) SearchMysql(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags Database Mysql
|
||||
// @Summary List mysql database names
|
||||
// @Summary List mysql database format collation options
|
||||
// @Accept json
|
||||
// @Param request body dto.PageInfo true "request"
|
||||
// @Success 200 {array} dto.MysqlOption
|
||||
// @Param request body dto.OperationWithName true "request"
|
||||
// @Success 200 {array} dto.MysqlFormatCollationOption
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /databases/options [get]
|
||||
func (b *BaseApi) ListDBName(c *gin.Context) {
|
||||
list, err := mysqlService.ListDBOption()
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
// @Router /databases/format/options [post]
|
||||
func (b *BaseApi) ListDBFormatCollationOptions(c *gin.Context) {
|
||||
var req dto.OperationWithName
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, list)
|
||||
helper.SuccessWithData(c, mysqlService.LoadFormatOption(req))
|
||||
}
|
||||
|
||||
// @Tags Database Mysql
|
||||
|
||||
@@ -38,6 +38,7 @@ var (
|
||||
fileService = service.NewIFileService()
|
||||
sshService = service.NewISSHService()
|
||||
firewallService = service.NewIFirewallService()
|
||||
iptablesService = service.NewIIptablesService()
|
||||
monitorService = service.NewIMonitorService()
|
||||
systemService = service.NewISystemService()
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ func (b *BaseApi) GetFileTree(c *gin.Context) {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, tree)
|
||||
helper.SuccessWithDataGzipped(c, tree)
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
@@ -265,7 +265,11 @@ func (b *BaseApi) GetContent(c *gin.Context) {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, info)
|
||||
if info.Size > 2*1024 && info.Size < 5*1024*1024 {
|
||||
helper.SuccessWithDataGzipped(c, info)
|
||||
} else {
|
||||
helper.SuccessWithData(c, info)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
@@ -521,9 +525,14 @@ func (b *BaseApi) Download(c *gin.Context) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
info, _ := file.Stat()
|
||||
info, err := file.Stat()
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
c.Header("Content-Length", strconv.FormatInt(info.Size(), 10))
|
||||
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(info.Name()))
|
||||
http.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), file)
|
||||
@@ -841,7 +850,11 @@ func (b *BaseApi) ReadFileByLine(c *gin.Context) {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
if res.TotalLines > 100 {
|
||||
helper.SuccessWithDataGzipped(c, res)
|
||||
} else {
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
|
||||
@@ -8,12 +8,19 @@ import (
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary Load firewall base info
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithName true "request"
|
||||
// @Success 200 {object} dto.FirewallBaseInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/firewall/base [get]
|
||||
// @Router /hosts/firewall/base [post]
|
||||
func (b *BaseApi) LoadFirewallBaseInfo(c *gin.Context) {
|
||||
data, err := firewallService.LoadBaseInfo()
|
||||
var req dto.OperationWithName
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := firewallService.LoadBaseInfo(req.Name)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
@@ -95,14 +102,14 @@ func (b *BaseApi) OperatePortRule(c *gin.Context) {
|
||||
|
||||
// OperateForwardRule
|
||||
// @Tags Firewall
|
||||
// @Summary Create group
|
||||
// @Summary Operate forward rule
|
||||
// @Accept json
|
||||
// @Param request body dto.ForwardRuleOperate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/firewall/forward [post]
|
||||
// @x-panel-log {"bodyKeys":["source_port"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新端口转发规则 [source_port]","formatEN":"update port forward rules [source_port]"}
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新端口转发规则","formatEN":"update port forward rules"}
|
||||
func (b *BaseApi) OperateForwardRule(c *gin.Context) {
|
||||
var req dto.ForwardRuleOperate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -117,7 +124,7 @@ func (b *BaseApi) OperateForwardRule(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary Create group
|
||||
// @Summary Operate Ip rule
|
||||
// @Accept json
|
||||
// @Param request body dto.AddrRuleOperate true "request"
|
||||
// @Success 200
|
||||
@@ -139,7 +146,7 @@ func (b *BaseApi) OperateIPRule(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary Create group
|
||||
// @Summary Batch operate rule
|
||||
// @Accept json
|
||||
// @Param request body dto.BatchRuleOperate true "request"
|
||||
// @Success 200
|
||||
@@ -181,7 +188,7 @@ func (b *BaseApi) UpdateFirewallDescription(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary Create group
|
||||
// @Summary Update port rule
|
||||
// @Accept json
|
||||
// @Param request body dto.PortRuleUpdate true "request"
|
||||
// @Success 200
|
||||
@@ -202,7 +209,7 @@ func (b *BaseApi) UpdatePortRule(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary Create group
|
||||
// @Summary Update Ip rule
|
||||
// @Accept json
|
||||
// @Param request body dto.AddrRuleUpdate true "request"
|
||||
// @Success 200
|
||||
@@ -221,3 +228,112 @@ func (b *BaseApi) UpdateAddrRule(c *gin.Context) {
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary search iptables filter rules
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchPageWithType true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/firewall/filter/search [post]
|
||||
func (b *BaseApi) SearchFilterRules(c *gin.Context) {
|
||||
var req dto.SearchPageWithType
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
total, list, err := iptablesService.Search(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary Operate iptables filter rule
|
||||
// @Accept json
|
||||
// @Param request body dto.IptablesRuleOp true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/firewall/filter/rule/operate [post]
|
||||
// @x-panel-log {"bodyKeys":["operation","chain"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operation] filter规则到 [chain]","formatEN":"[operation] filter rule to [chain]"}
|
||||
func (b *BaseApi) OperateFilterRule(c *gin.Context) {
|
||||
var req dto.IptablesRuleOp
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := iptablesService.OperateRule(req, true); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary Batch operate iptables filter rules
|
||||
// @Accept json
|
||||
// @Param request body dto.IptablesBatchOperate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/firewall/filter/rule/batch [post]
|
||||
func (b *BaseApi) BatchOperateFilterRule(c *gin.Context) {
|
||||
var req dto.IptablesBatchOperate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := iptablesService.BatchOperate(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary Apply/Unload/Init iptables filter
|
||||
// @Accept json
|
||||
// @Param request body dto.IptablesOp true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/firewall/filter/operate [post]
|
||||
// @x-panel-log {"bodyKeys":["operate"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] iptables filter 防火墙","formatEN":"[operate] iptables filter firewall"}
|
||||
func (b *BaseApi) OperateFilterChain(c *gin.Context) {
|
||||
var req dto.IptablesOp
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := iptablesService.Operate(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary load chain status with name
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithName true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/firewall/filter/chain/status [post]
|
||||
func (b *BaseApi) LoadChainStatus(c *gin.Context) {
|
||||
var req dto.OperationWithName
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, iptablesService.LoadChainStatus(req))
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"gorm.io/gorm"
|
||||
@@ -46,6 +50,42 @@ func SuccessWithData(ctx *gin.Context, data interface{}) {
|
||||
ctx.Abort()
|
||||
}
|
||||
|
||||
var gzipWriterPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return gzip.NewWriter(nil)
|
||||
},
|
||||
}
|
||||
|
||||
func SuccessWithDataGzipped(ctx *gin.Context, data interface{}) {
|
||||
if !strings.Contains(ctx.GetHeader("Accept-Encoding"), "gzip") {
|
||||
SuccessWithData(ctx, data)
|
||||
return
|
||||
}
|
||||
if data == nil {
|
||||
data = gin.H{}
|
||||
}
|
||||
res := dto.Response{
|
||||
Code: http.StatusOK,
|
||||
Data: data,
|
||||
}
|
||||
jsonBytes, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
ErrorWithDetail(ctx, http.StatusInternalServerError, "ErrInternalServer", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Header("Content-Encoding", "gzip")
|
||||
ctx.Header("Content-Type", "application/json; charset=utf-8")
|
||||
ctx.Status(http.StatusOK)
|
||||
|
||||
gz := gzipWriterPool.Get().(*gzip.Writer)
|
||||
gz.Reset(ctx.Writer)
|
||||
_, _ = gz.Write(jsonBytes)
|
||||
_ = gz.Close()
|
||||
gzipWriterPool.Put(gz)
|
||||
ctx.Abort()
|
||||
}
|
||||
|
||||
func Success(ctx *gin.Context) {
|
||||
res := dto.Response{
|
||||
Code: http.StatusOK,
|
||||
|
||||
@@ -28,6 +28,27 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithDataGzipped(c, data)
|
||||
}
|
||||
|
||||
// @Tags Monitor
|
||||
// @Summary Load monitor data
|
||||
// @Param request body dto.MonitorGPUSearch true "request"
|
||||
// @Success 200 {object} dto.dto.MonitorGPUData
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/monitor/gpu/search [post]
|
||||
func (b *BaseApi) LoadGPUMonitor(c *gin.Context) {
|
||||
var req dto.MonitorGPUSearch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := monitorService.LoadGPUMonitorData(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
@@ -105,3 +126,7 @@ func (b *BaseApi) GetIOOptions(c *gin.Context) {
|
||||
sort.Strings(options)
|
||||
helper.SuccessWithData(c, options)
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetCPUOptions(c *gin.Context) {
|
||||
helper.SuccessWithData(c, monitorService.LoadGPUOptions())
|
||||
}
|
||||
|
||||
@@ -161,13 +161,13 @@ func (b *BaseApi) GetNginxModules(c *gin.Context) {
|
||||
// @Tags OpenResty
|
||||
// @Summary Operate default HTTPs
|
||||
// @Accept json
|
||||
// @Param request body request.NginxOperateReq true "request"
|
||||
// @Param request body request.NginxDefaultHTTPSUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /openresty/https [post]
|
||||
func (b *BaseApi) OperateDefaultHTTPs(c *gin.Context) {
|
||||
var req request.NginxOperateReq
|
||||
var req request.NginxDefaultHTTPSUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func (b *BaseApi) StopProcess(c *gin.Context) {
|
||||
// @Tags Process
|
||||
// @Summary Get Process Info By PID
|
||||
// @Param pid path int true "PID"
|
||||
// @Success 200 {object} websocket.PsProcessData
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /process/{pid} [get]
|
||||
|
||||
@@ -155,3 +155,24 @@ func (b *BaseApi) GetSettingByKey(c *gin.Context) {
|
||||
value := settingService.GetSettingByKey(key)
|
||||
helper.SuccessWithData(c, value)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Save common description
|
||||
// @Accept json
|
||||
// @Param request body dto.CommonDescription true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /settings/description/save [post]
|
||||
func (b *BaseApi) SaveDescription(c *gin.Context) {
|
||||
var req dto.CommonDescription
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := settingService.SaveDescription(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func (b *BaseApi) OperateSSH(c *gin.Context) {
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/ssh/update [post]
|
||||
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 SSH 配置 [key] => [value]","formatEN":"update SSH setting [key] => [value]"}
|
||||
// @x-panel-log {"bodyKeys":["key","newValue"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 SSH 配置 [key] => [newValue]","formatEN":"update SSH setting [key] => [newValue]"}
|
||||
func (b *BaseApi) UpdateSSH(c *gin.Context) {
|
||||
var req dto.SSHUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
|
||||
@@ -228,8 +228,8 @@ func wshandleError(ws *websocket.Conn, err error) bool {
|
||||
}
|
||||
|
||||
var upGrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024 * 1024 * 10,
|
||||
ReadBufferSize: 4096,
|
||||
WriteBufferSize: 16384,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
|
||||
@@ -672,7 +672,7 @@ func (b *BaseApi) UpdateAntiLeech(c *gin.Context) {
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/redirect/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"websiteID","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"修改网站 [domain] 重定向理配置 ","formatEN":"Update domain [domain] redirect config"}
|
||||
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"websiteID","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"修改网站 [domain] 重定向配置 ","formatEN":"Update domain [domain] redirect config"}
|
||||
func (b *BaseApi) UpdateRedirectConfig(c *gin.Context) {
|
||||
var req request.NginxRedirectReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -1145,7 +1145,7 @@ func (b *BaseApi) ExecComposer(c *gin.Context) {
|
||||
// @Tags Website
|
||||
// @Summary Batch operate websites
|
||||
// @Accept json
|
||||
// @Param request body request.BatchOpWebsite true "request"
|
||||
// @Param request body request.BatchWebsiteOp true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
@@ -1162,6 +1162,26 @@ func (b *BaseApi) BatchOpWebsites(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Batch set website group
|
||||
// @Accept json
|
||||
// @Param request body request.BatchWebsiteGroup true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/batch/group [post]
|
||||
func (b *BaseApi) BatchSetWebsiteGroup(c *gin.Context) {
|
||||
var req request.BatchWebsiteGroup
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := websiteService.BatchSetGroup(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Get CORS Config
|
||||
// @Accept json
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
@@ -9,6 +8,8 @@ import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
@@ -34,7 +35,7 @@ func (b *BaseApi) PageWebsiteSSL(c *gin.Context) {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
helper.SuccessWithDataGzipped(c, dto.PageResult{
|
||||
Total: total,
|
||||
Items: accounts,
|
||||
})
|
||||
@@ -44,7 +45,7 @@ func (b *BaseApi) PageWebsiteSSL(c *gin.Context) {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
helper.SuccessWithDataGzipped(c, list)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +236,6 @@ func (b *BaseApi) UploadWebsiteSSL(c *gin.Context) {
|
||||
func (b *BaseApi) UploadSSLFile(c *gin.Context) {
|
||||
var req request.WebsiteSSLFileUpload
|
||||
|
||||
req.Type = c.PostForm("type")
|
||||
req.Description = c.PostForm("description")
|
||||
sslID := c.PostForm("sslID")
|
||||
if sslID != "" {
|
||||
@@ -279,7 +279,7 @@ func (b *BaseApi) UploadSSLFile(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
func readUploadedFile(fileHeader *multipart.FileHeader) ([]byte, error) {
|
||||
|
||||
@@ -317,7 +317,7 @@ type AlertCommonConfig struct {
|
||||
|
||||
type AlertSmsConfig struct {
|
||||
Phone string `json:"phone"`
|
||||
AlertDailyNum uint `json:"alertDailyNum"`
|
||||
AlertDailyNum string `json:"alertDailyNum"`
|
||||
}
|
||||
|
||||
type AlertEmailConfig struct {
|
||||
|
||||
@@ -12,6 +12,8 @@ type AppDatabase struct {
|
||||
DbUser string `json:"PANEL_DB_USER"`
|
||||
Password string `json:"PANEL_DB_USER_PASSWORD"`
|
||||
DatabaseName string `json:"DATABASE_NAME"`
|
||||
Format string `json:"format"`
|
||||
Collation string `json:"collation"`
|
||||
}
|
||||
|
||||
type AuthParam struct {
|
||||
@@ -91,13 +93,13 @@ type AppProperty struct {
|
||||
Key string `json:"key"`
|
||||
Required []string `json:"Required"`
|
||||
CrossVersionUpdate bool `json:"crossVersionUpdate"`
|
||||
Limit int `json:"limit"`
|
||||
Recommend int `json:"recommend"`
|
||||
Limit int `json:"limit" yaml:"limit"`
|
||||
Recommend int `json:"recommend" yaml:"recommend"`
|
||||
Website string `json:"website"`
|
||||
Github string `json:"github"`
|
||||
Document string `json:"document"`
|
||||
Architectures []string `json:"architectures"`
|
||||
MemoryRequired int `json:"memoryRequired"`
|
||||
MemoryRequired int `json:"memoryRequired" yaml:"memoryRequired"`
|
||||
GpuSupport bool `json:"gpuSupport"`
|
||||
Version float64 `json:"version"`
|
||||
Deprecated float64 `json:"deprecated"`
|
||||
|
||||
@@ -8,6 +8,10 @@ type ComposeTemplateCreate struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type ComposeTemplateBatch struct {
|
||||
Templates []ComposeTemplateCreate `json:"templates" validate:"required"`
|
||||
}
|
||||
|
||||
type ComposeTemplateUpdate struct {
|
||||
ID uint `json:"id"`
|
||||
Description string `json:"description"`
|
||||
|
||||
+40
-21
@@ -37,6 +37,9 @@ type ContainerInfo struct {
|
||||
AppName string `json:"appName"`
|
||||
AppInstallName string `json:"appInstallName"`
|
||||
Websites []string `json:"websites"`
|
||||
|
||||
IsPinned bool `json:"isPinned"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type ContainerOptions struct {
|
||||
@@ -45,14 +48,13 @@ type ContainerOptions struct {
|
||||
}
|
||||
|
||||
type ContainerStatus struct {
|
||||
All uint `json:"all"`
|
||||
Created uint `json:"created"`
|
||||
Running uint `json:"running"`
|
||||
Paused uint `json:"paused"`
|
||||
Restarting uint `json:"restarting"`
|
||||
Removing uint `json:"removing"`
|
||||
Exited uint `json:"exited"`
|
||||
Dead uint `json:"dead"`
|
||||
Created int `json:"created"`
|
||||
Running int `json:"running"`
|
||||
Paused int `json:"paused"`
|
||||
Restarting int `json:"restarting"`
|
||||
Removing int `json:"removing"`
|
||||
Exited int `json:"exited"`
|
||||
Dead int `json:"dead"`
|
||||
|
||||
ContainerCount int `json:"containerCount"`
|
||||
ComposeCount int `json:"composeCount"`
|
||||
@@ -61,8 +63,6 @@ type ContainerStatus struct {
|
||||
NetworkCount int `json:"networkCount"`
|
||||
VolumeCount int `json:"volumeCount"`
|
||||
RepoCount int `json:"repoCount"`
|
||||
|
||||
ImageSize uint64 `json:"imageSize"`
|
||||
}
|
||||
type ResourceLimit struct {
|
||||
CPU int `json:"cpu"`
|
||||
@@ -70,17 +70,16 @@ type ResourceLimit struct {
|
||||
}
|
||||
|
||||
type ContainerOperate struct {
|
||||
TaskID string `json:"taskID"`
|
||||
ForcePull bool `json:"forcePull"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Image string `json:"image" validate:"required"`
|
||||
Network string `json:"network"`
|
||||
Hostname string `json:"hostname"`
|
||||
DomainName string `json:"domainName"`
|
||||
MacAddr string `json:"macAddr"`
|
||||
DNS []string `json:"dns"`
|
||||
Ipv4 string `json:"ipv4"`
|
||||
Ipv6 string `json:"ipv6"`
|
||||
TaskID string `json:"taskID"`
|
||||
ForcePull bool `json:"forcePull"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Image string `json:"image" validate:"required"`
|
||||
|
||||
Hostname string `json:"hostname"`
|
||||
DomainName string `json:"domainName"`
|
||||
DNS []string `json:"dns"`
|
||||
Networks []ContainerNetwork `json:"networks"`
|
||||
|
||||
PublishAllPorts bool `json:"publishAllPorts"`
|
||||
ExposedPorts []PortHelper `json:"exposedPorts"`
|
||||
Tty bool `json:"tty"`
|
||||
@@ -99,6 +98,12 @@ type ContainerOperate struct {
|
||||
Env []string `json:"env"`
|
||||
RestartPolicy string `json:"restartPolicy"`
|
||||
}
|
||||
type ContainerNetwork struct {
|
||||
Network string `json:"network"`
|
||||
Ipv4 string `json:"ipv4"`
|
||||
Ipv6 string `json:"ipv6"`
|
||||
MacAddr string `json:"macAddr"`
|
||||
}
|
||||
|
||||
type ContainerCreateByCommand struct {
|
||||
TaskID string `json:"taskID"`
|
||||
@@ -112,6 +117,19 @@ type ContainerUpgrade struct {
|
||||
ForcePull bool `json:"forcePull"`
|
||||
}
|
||||
|
||||
type ContainerItemStats struct {
|
||||
SizeRw int64 `json:"sizeRw"`
|
||||
SizeRootFs int64 `json:"sizeRootFs"`
|
||||
|
||||
ContainerUsage int64 `json:"containerUsage"`
|
||||
ContainerReclaimable int64 `json:"containerReclaimable"`
|
||||
ImageUsage int64 `json:"imageUsage"`
|
||||
ImageReclaimable int64 `json:"imageReclaimable"`
|
||||
VolumeUsage int64 `json:"volumeUsage"`
|
||||
VolumeReclaimable int64 `json:"volumeReclaimable"`
|
||||
BuildCacheUsage int64 `json:"buildCacheUsage"`
|
||||
BuildCacheReclaimable int64 `json:"buildCacheReclaimable"`
|
||||
}
|
||||
type ContainerListStats struct {
|
||||
ContainerID string `json:"containerID"`
|
||||
|
||||
@@ -153,6 +171,7 @@ type PortHelper struct {
|
||||
}
|
||||
|
||||
type ContainerOperation struct {
|
||||
TaskID string `json:"taskID"`
|
||||
Names []string `json:"names" validate:"required"`
|
||||
Operation string `json:"operation" validate:"required,oneof=up start stop restart kill pause unpause remove"`
|
||||
}
|
||||
|
||||
@@ -53,6 +53,12 @@ type CronjobOperate struct {
|
||||
AlertCount uint `json:"alertCount"`
|
||||
AlertTitle string `json:"alertTitle"`
|
||||
AlertMethod string `json:"alertMethod"`
|
||||
|
||||
CleanLogConfig
|
||||
}
|
||||
|
||||
type CleanLogConfig struct {
|
||||
Scopes []string `json:"scopes"`
|
||||
}
|
||||
|
||||
type SnapshotRule struct {
|
||||
|
||||
+18
-10
@@ -13,15 +13,17 @@ type DashboardBase struct {
|
||||
Platform string `json:"platform"`
|
||||
PlatformFamily string `json:"platformFamily"`
|
||||
PlatformVersion string `json:"platformVersion"`
|
||||
PrettyDistro string `json:"prettyDistro"`
|
||||
KernelArch string `json:"kernelArch"`
|
||||
KernelVersion string `json:"kernelVersion"`
|
||||
VirtualizationSystem string `json:"virtualizationSystem"`
|
||||
IpV4Addr string `json:"ipV4Addr"`
|
||||
SystemProxy string `json:"systemProxy"`
|
||||
|
||||
CPUCores int `json:"cpuCores"`
|
||||
CPULogicalCores int `json:"cpuLogicalCores"`
|
||||
CPUModelName string `json:"cpuModelName"`
|
||||
CPUCores int `json:"cpuCores"`
|
||||
CPULogicalCores int `json:"cpuLogicalCores"`
|
||||
CPUModelName string `json:"cpuModelName"`
|
||||
CPUMhz float64 `json:"cpuMhz"`
|
||||
|
||||
QuickJumps []QuickJump `json:"quickJump"`
|
||||
CurrentInfo DashboardCurrent `json:"currentInfo"`
|
||||
@@ -48,6 +50,7 @@ type OsInfo struct {
|
||||
PlatformFamily string `json:"platformFamily"`
|
||||
KernelArch string `json:"kernelArch"`
|
||||
KernelVersion string `json:"kernelVersion"`
|
||||
PrettyDistro string `json:"prettyDistro"`
|
||||
|
||||
DiskSize int64 `json:"diskSize"`
|
||||
}
|
||||
@@ -58,9 +61,10 @@ type NodeCurrent struct {
|
||||
Load15 float64 `json:"load15"`
|
||||
LoadUsagePercent float64 `json:"loadUsagePercent"`
|
||||
|
||||
CPUUsedPercent float64 `json:"cpuUsedPercent"`
|
||||
CPUUsed float64 `json:"cpuUsed"`
|
||||
CPUTotal int `json:"cpuTotal"`
|
||||
CPUUsedPercent float64 `json:"cpuUsedPercent"`
|
||||
CPUUsed float64 `json:"cpuUsed"`
|
||||
CPUTotal int `json:"cpuTotal"`
|
||||
CPUDetailedPercent []float64 `json:"cpuDetailedPercent"`
|
||||
|
||||
MemoryTotal uint64 `json:"memoryTotal"`
|
||||
MemoryAvailable uint64 `json:"memoryAvailable"`
|
||||
@@ -84,10 +88,11 @@ type DashboardCurrent struct {
|
||||
Load15 float64 `json:"load15"`
|
||||
LoadUsagePercent float64 `json:"loadUsagePercent"`
|
||||
|
||||
CPUPercent []float64 `json:"cpuPercent"`
|
||||
CPUUsedPercent float64 `json:"cpuUsedPercent"`
|
||||
CPUUsed float64 `json:"cpuUsed"`
|
||||
CPUTotal int `json:"cpuTotal"`
|
||||
CPUPercent []float64 `json:"cpuPercent"`
|
||||
CPUUsedPercent float64 `json:"cpuUsedPercent"`
|
||||
CPUUsed float64 `json:"cpuUsed"`
|
||||
CPUTotal int `json:"cpuTotal"`
|
||||
CPUDetailedPercent []float64 `json:"cpuDetailedPercent"`
|
||||
|
||||
MemoryTotal uint64 `json:"memoryTotal"`
|
||||
MemoryUsed uint64 `json:"memoryUsed"`
|
||||
@@ -116,6 +121,9 @@ type DashboardCurrent struct {
|
||||
GPUData []GPUInfo `json:"gpuData"`
|
||||
XPUData []XPUInfo `json:"xpuData"`
|
||||
|
||||
TopCPUItems []Process `json:"topCPUItems"`
|
||||
TopMemItems []Process `json:"topMemItems"`
|
||||
|
||||
ShotTime time.Time `json:"shotTime"`
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ type MysqlDBInfo struct {
|
||||
From string `json:"from"`
|
||||
MysqlName string `json:"mysqlName"`
|
||||
Format string `json:"format"`
|
||||
Collation string `json:"collation"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Permission string `json:"permission"`
|
||||
@@ -57,13 +58,19 @@ type MysqlDBCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
Format string `json:"format" validate:"required,oneof=utf8mb4 utf8 gbk big5"`
|
||||
Format string `json:"format" validate:"required"`
|
||||
Collation string `json:"collation"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Permission string `json:"permission" validate:"required"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type MysqlFormatCollationOption struct {
|
||||
Format string `json:"format"`
|
||||
Collations []string `json:"collations"`
|
||||
}
|
||||
|
||||
type BindUser struct {
|
||||
Database string `json:"database" validate:"required"`
|
||||
DB string `json:"db" validate:"required"`
|
||||
@@ -281,14 +288,15 @@ type DatabaseItem struct {
|
||||
}
|
||||
|
||||
type DatabaseCreate struct {
|
||||
Name string `json:"name" validate:"required,max=256"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Version string `json:"version" validate:"required"`
|
||||
Address string `json:"address"`
|
||||
Port uint `json:"port"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password"`
|
||||
Name string `json:"name" validate:"required,max=256"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Version string `json:"version" validate:"required"`
|
||||
Address string `json:"address"`
|
||||
Port uint `json:"port"`
|
||||
InitialDB string `json:"initialDB"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password"`
|
||||
|
||||
SSL bool `json:"ssl"`
|
||||
RootCert string `json:"rootCert"`
|
||||
|
||||
@@ -4,6 +4,8 @@ type FirewallBaseInfo struct {
|
||||
Name string `json:"name"`
|
||||
IsExist bool `json:"isExist"`
|
||||
IsActive bool `json:"isActive"`
|
||||
IsInit bool `json:"isInit"`
|
||||
IsBind bool `json:"isBind"`
|
||||
Version string `json:"version"`
|
||||
PingStatus string `json:"pingStatus"`
|
||||
}
|
||||
@@ -22,7 +24,9 @@ type FirewallOperation struct {
|
||||
}
|
||||
|
||||
type PortRuleOperate struct {
|
||||
ID uint `json:"id"`
|
||||
Operation string `json:"operation" validate:"required,oneof=add remove"`
|
||||
Chain string `json:"chain"`
|
||||
Address string `json:"address"`
|
||||
Port string `json:"port" validate:"required"`
|
||||
Protocol string `json:"protocol" validate:"required,oneof=tcp udp tcp/udp"`
|
||||
@@ -46,8 +50,11 @@ type ForwardRuleOperate struct {
|
||||
|
||||
type UpdateFirewallDescription struct {
|
||||
Type string `json:"type"`
|
||||
Address string `json:"address"`
|
||||
Port string `json:"port"`
|
||||
Chain string `json:"chain"`
|
||||
SrcIP string `json:"srcIP"`
|
||||
DstIP string `json:"dstIP"`
|
||||
SrcPort string `json:"srcPort"`
|
||||
DstPort string `json:"dstPort"`
|
||||
Protocol string `json:"protocol"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=accept drop"`
|
||||
|
||||
@@ -55,6 +62,7 @@ type UpdateFirewallDescription struct {
|
||||
}
|
||||
|
||||
type AddrRuleOperate struct {
|
||||
ID uint `json:"id"`
|
||||
Operation string `json:"operation" validate:"required,oneof=add remove"`
|
||||
Address string `json:"address" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=accept drop"`
|
||||
@@ -76,3 +84,30 @@ type BatchRuleOperate struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
Rules []PortRuleOperate `json:"rules"`
|
||||
}
|
||||
|
||||
type IptablesOp struct {
|
||||
Name string `json:"name" validate:"required,oneof=1PANEL_INPUT 1PANEL_OUTPUT 1PANEL_BASIC"`
|
||||
Operate string `json:"operate" validate:"required,oneof=init-base init-forward init-advance bind-base unbind-base bind unbind"`
|
||||
}
|
||||
|
||||
type IptablesRuleOp struct {
|
||||
Operation string `json:"operation" validate:"required,oneof=add remove"`
|
||||
ID uint `json:"id"`
|
||||
Chain string `json:"chain" validate:"required,oneof=1PANEL_BASIC 1PANEL_BASIC_BEFORE 1PANEL_INPUT 1PANEL_OUTPUT"`
|
||||
Protocol string `json:"protocol"`
|
||||
SrcIP string `json:"srcIP"`
|
||||
SrcPort uint `json:"srcPort"`
|
||||
DstIP string `json:"dstIP"`
|
||||
DstPort uint `json:"dstPort"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=accept drop reject"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type IptablesBatchOperate struct {
|
||||
Rules []IptablesRuleOp `json:"rules"`
|
||||
}
|
||||
|
||||
type IptablesChainStatus struct {
|
||||
IsBind bool `json:"isBind"`
|
||||
DefaultStrategy string `json:"defaultStrategy"`
|
||||
}
|
||||
|
||||
@@ -15,10 +15,14 @@ type ImageInfo struct {
|
||||
IsUsed bool `json:"isUsed"`
|
||||
Tags []string `json:"tags"`
|
||||
Size int64 `json:"size"`
|
||||
|
||||
IsPinned bool `json:"isPinned"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type ImageLoad struct {
|
||||
Path string `json:"path" validate:"required"`
|
||||
TaskID string `json:"taskID"`
|
||||
Path string `json:"path" validate:"required"`
|
||||
}
|
||||
|
||||
type ImageBuild struct {
|
||||
@@ -27,6 +31,7 @@ type ImageBuild struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Dockerfile string `json:"dockerfile" validate:"required"`
|
||||
Tags []string `json:"tags"`
|
||||
Args []string `json:"args"`
|
||||
}
|
||||
|
||||
type ImagePull struct {
|
||||
@@ -48,6 +53,7 @@ type ImagePush struct {
|
||||
}
|
||||
|
||||
type ImageSave struct {
|
||||
TaskID string `json:"taskID"`
|
||||
TagName string `json:"tagName" validate:"required"`
|
||||
Path string `json:"path" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
|
||||
@@ -4,25 +4,67 @@ import "time"
|
||||
|
||||
type MonitorSearch struct {
|
||||
Param string `json:"param" validate:"required,oneof=all cpu memory load io network"`
|
||||
Info string `json:"info"`
|
||||
IO string `json:"io"`
|
||||
Network string `json:"network"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
EndTime time.Time `json:"endTime"`
|
||||
}
|
||||
|
||||
type MonitorData struct {
|
||||
Param string `json:"param" validate:"required,oneof=cpu memory load io network"`
|
||||
Param string `json:"param"`
|
||||
Date []time.Time `json:"date"`
|
||||
Value []interface{} `json:"value"`
|
||||
}
|
||||
|
||||
type Process struct {
|
||||
Name string `json:"name"`
|
||||
Pid int32 `json:"pid"`
|
||||
Percent float64 `json:"percent"`
|
||||
Memory uint64 `json:"memory"`
|
||||
Cmd string `json:"cmd"`
|
||||
User string `json:"user"`
|
||||
}
|
||||
|
||||
type MonitorSetting struct {
|
||||
MonitorStatus string `json:"monitorStatus"`
|
||||
MonitorStoreDays string `json:"monitorStoreDays"`
|
||||
MonitorInterval string `json:"monitorInterval"`
|
||||
DefaultNetwork string `json:"defaultNetwork"`
|
||||
DefaultIO string `json:"defaultIO"`
|
||||
}
|
||||
|
||||
type MonitorSettingUpdate struct {
|
||||
Key string `json:"key" validate:"required,oneof=MonitorStatus MonitorStoreDays MonitorInterval DefaultNetwork"`
|
||||
Key string `json:"key" validate:"required,oneof=MonitorStatus MonitorStoreDays MonitorInterval DefaultNetwork DefaultIO"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type MonitorGPUOptions struct {
|
||||
GPUType string `json:"gpuType"`
|
||||
Options []string `json:"options"`
|
||||
}
|
||||
type MonitorGPUSearch struct {
|
||||
ProductName string `json:"productName"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
EndTime time.Time `json:"endTime"`
|
||||
}
|
||||
type MonitorGPUData struct {
|
||||
Date []time.Time `json:"date"`
|
||||
GPUValue []float64 `json:"gpuValue"`
|
||||
TemperatureValue []float64 `json:"temperatureValue"`
|
||||
PowerTotal []float64 `json:"powerTotal"`
|
||||
PowerUsed []float64 `json:"powerUsed"`
|
||||
PowerPercent []float64 `json:"powerPercent"`
|
||||
MemoryTotal []float64 `json:"memoryTotal"`
|
||||
MemoryUsed []float64 `json:"memoryUsed"`
|
||||
MemoryPercent []float64 `json:"memoryPercent"`
|
||||
SpeedValue []int `json:"speedValue"`
|
||||
|
||||
GPUProcesses [][]GPUProcess `json:"gpuProcesses"`
|
||||
}
|
||||
|
||||
type GPUProcess struct {
|
||||
Pid string `json:"pid"`
|
||||
Type string `json:"type"`
|
||||
ProcessName string `json:"processName"`
|
||||
UsedMemory string `json:"usedMemory"`
|
||||
}
|
||||
|
||||
@@ -73,12 +73,13 @@ type NginxUpstream struct {
|
||||
}
|
||||
|
||||
type NginxUpstreamServer struct {
|
||||
Server string `json:"server"`
|
||||
Weight int `json:"weight"`
|
||||
FailTimeout string `json:"failTimeout"`
|
||||
MaxFails int `json:"maxFails"`
|
||||
MaxConns int `json:"maxConns"`
|
||||
Flag string `json:"flag"`
|
||||
Server string `json:"server"`
|
||||
Weight int `json:"weight"`
|
||||
FailTimeout int `json:"failTimeout"`
|
||||
FailTimeoutUnit string `json:"failTimeoutUnit"`
|
||||
MaxFails int `json:"maxFails"`
|
||||
MaxConns int `json:"maxConns"`
|
||||
Flag string `json:"flag"`
|
||||
}
|
||||
|
||||
var LBAlgorithms = map[string]struct{}{"ip_hash": {}, "least_conn": {}}
|
||||
|
||||
@@ -130,3 +130,8 @@ type NginxModuleUpdate struct {
|
||||
type NginxOperateReq struct {
|
||||
Operate string `json:"operate" validate:"required,oneof=enable disable"`
|
||||
}
|
||||
|
||||
type NginxDefaultHTTPSUpdate struct {
|
||||
Operate string `json:"operate" validate:"required,oneof=enable disable"`
|
||||
SSLRejectHandshake bool `json:"sslRejectHandshake"`
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ type NodeConfig struct {
|
||||
ExposedPorts []ExposedPort `json:"exposedPorts"`
|
||||
Environments []Environment `json:"environments"`
|
||||
Volumes []Volume `json:"volumes"`
|
||||
ExtraHosts []ExtraHost `json:"extraHosts"`
|
||||
}
|
||||
|
||||
type Environment struct {
|
||||
@@ -49,6 +50,11 @@ type ExposedPort struct {
|
||||
HostIP string `json:"hostIP"`
|
||||
}
|
||||
|
||||
type ExtraHost struct {
|
||||
Hostname string `json:"hostname"`
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
type RuntimeDelete struct {
|
||||
ID uint `json:"id"`
|
||||
ForceDelete bool `json:"forceDelete"`
|
||||
@@ -135,6 +141,7 @@ type PHPContainerConfig struct {
|
||||
ExposedPorts []ExposedPort `json:"exposedPorts"`
|
||||
Environments []Environment `json:"environments"`
|
||||
Volumes []Volume `json:"volumes"`
|
||||
ExtraHosts []ExtraHost `json:"extraHosts"`
|
||||
}
|
||||
|
||||
type RuntimeRemark struct {
|
||||
|
||||
@@ -14,6 +14,8 @@ type TensorRTLLMCreate struct {
|
||||
ModelDir string `json:"modelDir" validate:"required"`
|
||||
Image string `json:"image" validate:"required"`
|
||||
Command string `json:"command" validate:"required"`
|
||||
ModelType string `json:"modelType"`
|
||||
ModelSpeedup bool `json:"modelSpeedup"`
|
||||
DockerConfig
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,11 @@ type BatchWebsiteOp struct {
|
||||
TaskID string `json:"taskID" validate:"required"`
|
||||
}
|
||||
|
||||
type BatchWebsiteGroup struct {
|
||||
IDs []uint `json:"ids" validate:"required"`
|
||||
GroupID uint `json:"groupID" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteRedirectUpdate struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Key string `json:"key" validate:"required"`
|
||||
|
||||
@@ -33,16 +33,10 @@ type AppItem struct {
|
||||
Key string `json:"key"`
|
||||
ID uint `json:"id"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
Resource string `json:"resource"`
|
||||
Installed bool `json:"installed"`
|
||||
Versions []string `json:"versions"`
|
||||
Limit int `json:"limit"`
|
||||
Tags []TagDTO `json:"tags"`
|
||||
Github string `json:"github"`
|
||||
Website string `json:"website"`
|
||||
Tags []string `json:"tags"`
|
||||
GpuSupport bool `json:"gpuSupport"`
|
||||
Recommend int `json:"recommend"`
|
||||
}
|
||||
@@ -128,6 +122,9 @@ type AppInstallDTO struct {
|
||||
Favorite bool `json:"favorite"`
|
||||
App AppDetail `json:"app"`
|
||||
Container string `json:"container"`
|
||||
IsEdit bool `json:"isEdit"`
|
||||
LinkDB bool `json:"linkDB"`
|
||||
ServiceName string `json:"serviceName"`
|
||||
}
|
||||
|
||||
type AppInstallInfo struct {
|
||||
@@ -139,6 +136,8 @@ type AppInstallInfo struct {
|
||||
HttpPort int `json:"HttpPort"`
|
||||
Container string `json:"container"`
|
||||
ComposePath string `json:"composePath"`
|
||||
AppKey string `json:"appKey"`
|
||||
AppPorts []int `json:"appPorts"`
|
||||
|
||||
Env map[string]interface{} `json:"env"`
|
||||
}
|
||||
@@ -176,6 +175,7 @@ type AppParam struct {
|
||||
}
|
||||
|
||||
type AppConfig struct {
|
||||
Params []AppParam `json:"params"`
|
||||
Params []AppParam `json:"params"`
|
||||
RawCompose string `json:"rawCompose"`
|
||||
request.AppContainerConfig
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ type DiskBasicInfo struct {
|
||||
type CompleteDiskInfo struct {
|
||||
Disks []DiskInfo `json:"disks"`
|
||||
UnpartitionedDisks []DiskBasicInfo `json:"unpartitionedDisks"`
|
||||
SystemDisk *DiskInfo `json:"systemDisk"`
|
||||
SystemDisks []DiskInfo `json:"systemDisks"`
|
||||
TotalDisks int `json:"totalDisks"`
|
||||
TotalCapacity int64 `json:"totalCapacity"`
|
||||
}
|
||||
|
||||
@@ -82,5 +82,6 @@ type NginxBuildConfig struct {
|
||||
}
|
||||
|
||||
type NginxConfigRes struct {
|
||||
Https bool `json:"https"`
|
||||
Https bool `json:"https"`
|
||||
SSLRejectHandshake bool `json:"sslRejectHandshake"`
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ type RuntimeDTO struct {
|
||||
ExposedPorts []request.ExposedPort `json:"exposedPorts"`
|
||||
Environments []request.Environment `json:"environments"`
|
||||
Volumes []request.Volume `json:"volumes"`
|
||||
ExtraHosts []request.ExtraHost `json:"extraHosts"`
|
||||
ContainerStatus string `json:"containerStatus"`
|
||||
Container string `json:"container"`
|
||||
Remark string `json:"remark"`
|
||||
|
||||
@@ -10,6 +10,7 @@ type SettingInfo struct {
|
||||
NtpSite string `json:"ntpSite"`
|
||||
|
||||
DefaultNetwork string `json:"defaultNetwork"`
|
||||
DefaultIO string `json:"defaultIO"`
|
||||
LastCleanTime string `json:"lastCleanTime"`
|
||||
LastCleanSize string `json:"lastCleanSize"`
|
||||
LastCleanData string `json:"lastCleanData"`
|
||||
@@ -80,3 +81,11 @@ type SystemProxy struct {
|
||||
User string `json:"user"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type CommonDescription struct {
|
||||
ID string `json:"id" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
DetailType string `json:"detailType"`
|
||||
IsPinned bool `json:"isPinned"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ type Cronjob struct {
|
||||
EntryIDs string `json:"entryIDs"`
|
||||
Records []JobRecords `json:"records"`
|
||||
Secret string `json:"secret"`
|
||||
|
||||
Config string `json:"config"`
|
||||
}
|
||||
|
||||
type JobRecords struct {
|
||||
|
||||
@@ -9,6 +9,7 @@ type Database struct {
|
||||
From string `json:"from" gorm:"not null"`
|
||||
Address string `json:"address" gorm:"not null"`
|
||||
Port uint `json:"port" gorm:"not null"`
|
||||
InitialDB string `json:"initialDB"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ type DatabaseMysql struct {
|
||||
From string `json:"from" gorm:"not null;default:local"`
|
||||
MysqlName string `json:"mysqlName" gorm:"not null"`
|
||||
Format string `json:"format" gorm:"not null"`
|
||||
Collation string `json:"collation"`
|
||||
Username string `json:"username" gorm:"not null"`
|
||||
Password string `json:"password" gorm:"not null"`
|
||||
Permission string `json:"permission" gorm:"not null"`
|
||||
|
||||
+11
-15
@@ -3,20 +3,16 @@ package model
|
||||
type Firewall struct {
|
||||
BaseModel
|
||||
|
||||
Type string `gorm:"not null" json:"type"`
|
||||
Port string `gorm:"not null" json:"port"`
|
||||
Protocol string `gorm:"not null" json:"protocol"`
|
||||
Address string `gorm:"not null" json:"address"`
|
||||
Type string `json:"type"`
|
||||
Port string `json:"port"` // Deprecated
|
||||
Address string `json:"address"` // Deprecated
|
||||
|
||||
Chain string `json:"chain"`
|
||||
Protocol string `json:"protocol"`
|
||||
SrcIP string `json:"srcIP"`
|
||||
SrcPort string `json:"srcPort"`
|
||||
DstIP string `json:"dstIP"`
|
||||
DstPort string `json:"dstPort"`
|
||||
Strategy string `gorm:"not null" json:"strategy"`
|
||||
Description string `gorm:"not null" json:"description"`
|
||||
}
|
||||
|
||||
type Forward struct {
|
||||
BaseModel
|
||||
|
||||
Protocol string `gorm:"not null" json:"protocol"`
|
||||
Port string `gorm:"not null" json:"port"`
|
||||
TargetIP string `gorm:"not null" json:"targetIP"`
|
||||
TargetPort string `gorm:"not null" json:"targetPort"`
|
||||
Interface string `json:"interface"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
@@ -2,14 +2,18 @@ package model
|
||||
|
||||
type MonitorBase struct {
|
||||
BaseModel
|
||||
Cpu float64 `json:"cpu"`
|
||||
Cpu float64 `json:"cpu"`
|
||||
TopCPU string `json:"topCPU"`
|
||||
TopCPUItems interface{} `gorm:"-" json:"topCPUItems"`
|
||||
|
||||
Memory float64 `json:"memory"`
|
||||
TopMem string `json:"topMem"`
|
||||
TopMemItems interface{} `gorm:"-" json:"topMemItems"`
|
||||
|
||||
LoadUsage float64 `json:"loadUsage"`
|
||||
CpuLoad1 float64 `json:"cpuLoad1"`
|
||||
CpuLoad5 float64 `json:"cpuLoad5"`
|
||||
CpuLoad15 float64 `json:"cpuLoad15"`
|
||||
|
||||
Memory float64 `json:"memory"`
|
||||
}
|
||||
|
||||
type MonitorIO struct {
|
||||
@@ -27,3 +31,16 @@ type MonitorNetwork struct {
|
||||
Up float64 `json:"up"`
|
||||
Down float64 `json:"down"`
|
||||
}
|
||||
|
||||
type MonitorGPU struct {
|
||||
BaseModel
|
||||
ProductName string `json:"productName"`
|
||||
GPUUtil float64 `json:"gpuUtil"`
|
||||
Temperature float64 `json:"temperature"`
|
||||
PowerDraw float64 `json:"powerDraw"`
|
||||
MaxPowerLimit float64 `json:"maxPowerLimit"`
|
||||
MemUsed float64 `json:"memUsed"`
|
||||
MemTotal float64 `json:"memTotal"`
|
||||
FanSpeed int `json:"fanSpeed"`
|
||||
Processes string `json:"processes"`
|
||||
}
|
||||
|
||||
@@ -7,6 +7,14 @@ type Setting struct {
|
||||
About string `json:"about"`
|
||||
}
|
||||
|
||||
type CommonDescription struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
DetailType string `json:"detailType"`
|
||||
IsPinned bool `json:"isPinned"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type NodeInfo struct {
|
||||
Scope string `json:"scope"`
|
||||
BaseDir string `json:"baseDir"`
|
||||
|
||||
@@ -6,8 +6,9 @@ type TensorRTLLM struct {
|
||||
DockerCompose string `json:"dockerCompose"`
|
||||
ContainerName string `json:"containerName"`
|
||||
Message string `json:"message"`
|
||||
//Port int `json:"port"`
|
||||
Status string `json:"status"`
|
||||
Env string `json:"env"`
|
||||
TaskID string `json:"taskID"`
|
||||
Status string `json:"status"`
|
||||
Env string `json:"env"`
|
||||
TaskID string `json:"taskID"`
|
||||
ModelType string `json:"modelType"`
|
||||
ModelSpeedup bool `json:"modelSpeedup"`
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ type IAppRepo interface {
|
||||
GetFirst(opts ...DBOption) (model.App, error)
|
||||
GetBy(opts ...DBOption) ([]model.App, error)
|
||||
BatchCreate(ctx context.Context, apps []model.App) error
|
||||
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
|
||||
@@ -144,14 +143,6 @@ func (a AppRepo) BatchCreate(ctx context.Context, apps []model.App) error {
|
||||
return getTx(ctx).Omit(clause.Associations).Create(&apps).Error
|
||||
}
|
||||
|
||||
func (a AppRepo) GetByKey(ctx context.Context, key string) (model.App, error) {
|
||||
var app model.App
|
||||
if err := getTx(ctx).Where("key = ?", key).First(&app).Error; err != nil {
|
||||
return app, err
|
||||
}
|
||||
return app, nil
|
||||
}
|
||||
|
||||
func (a AppRepo) Create(ctx context.Context, app *model.App) error {
|
||||
return getTx(ctx).Omit(clause.Associations).Create(app).Error
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ type IBackupRepo interface {
|
||||
WithByPublic(isPublic bool) DBOption
|
||||
|
||||
ListRecord(opts ...DBOption) ([]model.BackupRecord, error)
|
||||
GetRecord(opts ...DBOption) (*model.BackupRecord, error)
|
||||
PageRecord(page, size int, opts ...DBOption) (int64, []model.BackupRecord, error)
|
||||
CreateRecord(record *model.BackupRecord) error
|
||||
DeleteRecord(ctx context.Context, opts ...DBOption) error
|
||||
@@ -160,7 +161,7 @@ func (u *BackupRepo) WithByCronID(cronjobID uint) DBOption {
|
||||
}
|
||||
|
||||
func (u *BackupRepo) GetRecord(opts ...DBOption) (*model.BackupRecord, error) {
|
||||
var record *model.BackupRecord
|
||||
record := &model.BackupRecord{}
|
||||
db := global.DB.Model(&model.BackupRecord{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
|
||||
@@ -77,6 +77,11 @@ func WithByType(tp string) DBOption {
|
||||
return g.Where("`type` = ?", tp)
|
||||
}
|
||||
}
|
||||
func WithByDetailType(tp string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("`detail_type` = ?", tp)
|
||||
}
|
||||
}
|
||||
|
||||
func WithTypes(types []string) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
|
||||
@@ -173,7 +173,7 @@ func (u *CronjobRepo) AddFailedRecord(cronjobID uint, message string) {
|
||||
var record model.JobRecords
|
||||
record.StartTime = time.Now()
|
||||
record.CronjobID = cronjobID
|
||||
record.Status = constant.StatusFailed
|
||||
record.Status = constant.StatusUnexecuted
|
||||
record.Message = message
|
||||
if err := global.DB.Create(&record).Error; err != nil {
|
||||
global.LOG.Errorf("create record status failed, err: %v", err)
|
||||
|
||||
+40
-21
@@ -4,16 +4,16 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type HostRepo struct{}
|
||||
|
||||
type IHostRepo interface {
|
||||
GetFirewallRecord(opts ...DBOption) (model.Firewall, error)
|
||||
ListFirewallRecord() ([]model.Firewall, error)
|
||||
ListFirewallRecord(opts ...DBOption) ([]model.Firewall, error)
|
||||
SaveFirewallRecord(firewall *model.Firewall) error
|
||||
DeleteFirewallRecordByID(id uint) error
|
||||
DeleteFirewallRecord(fType, port, protocol, address, strategy string) error
|
||||
|
||||
SyncCert(data []model.RootCert) error
|
||||
GetCert(opts ...DBOption) (model.RootCert, error)
|
||||
@@ -22,6 +22,8 @@ type IHostRepo interface {
|
||||
SaveCert(cert *model.RootCert) error
|
||||
UpdateCert(id uint, vars map[string]interface{}) error
|
||||
DeleteCert(opts ...DBOption) error
|
||||
|
||||
WithByChain(chain string) DBOption
|
||||
}
|
||||
|
||||
func NewIHostRepo() IHostRepo {
|
||||
@@ -38,12 +40,16 @@ func (h *HostRepo) GetFirewallRecord(opts ...DBOption) (model.Firewall, error) {
|
||||
return firewall, err
|
||||
}
|
||||
|
||||
func (h *HostRepo) ListFirewallRecord() ([]model.Firewall, error) {
|
||||
var datas []model.Firewall
|
||||
if err := global.DB.Find(&datas).Error; err != nil {
|
||||
return datas, nil
|
||||
func (h *HostRepo) ListFirewallRecord(opts ...DBOption) ([]model.Firewall, error) {
|
||||
var firewalls []model.Firewall
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
return datas, nil
|
||||
if err := global.DB.Find(&firewalls).Error; err != nil {
|
||||
return firewalls, nil
|
||||
}
|
||||
return firewalls, nil
|
||||
}
|
||||
|
||||
func (h *HostRepo) SaveFirewallRecord(firewall *model.Firewall) error {
|
||||
@@ -51,16 +57,27 @@ func (h *HostRepo) SaveFirewallRecord(firewall *model.Firewall) error {
|
||||
return global.DB.Save(firewall).Error
|
||||
}
|
||||
var data model.Firewall
|
||||
if firewall.Type == "port" {
|
||||
_ = global.DB.Where("type = ? AND port = ? AND protocol = ? AND address = ? AND strategy = ?", "port", firewall.Port, firewall.Protocol, firewall.Address, firewall.Strategy).First(&data)
|
||||
if data.ID != 0 {
|
||||
firewall.ID = data.ID
|
||||
}
|
||||
} else {
|
||||
_ = global.DB.Where("type = ? AND address = ? AND strategy = ?", "address", firewall.Address, firewall.Strategy).First(&data)
|
||||
if data.ID != 0 {
|
||||
firewall.ID = data.ID
|
||||
}
|
||||
switch firewall.Type {
|
||||
case "port":
|
||||
_ = global.DB.Where("type = ? AND dst_port = ? AND protocol = ? AND src_ip = ? AND strategy = ?", "port",
|
||||
firewall.DstPort,
|
||||
firewall.Protocol,
|
||||
firewall.SrcIP,
|
||||
firewall.Strategy,
|
||||
).First(&data).Error
|
||||
case "ip":
|
||||
_ = global.DB.Where("type = ? AND src_ip = ? AND strategy = ?", "address", firewall.SrcIP, firewall.Strategy).First(&data)
|
||||
default:
|
||||
_ = global.DB.Where("type = ? AND chain = ? AND src_port = ? AND dst_port = ? AND protocol = ? AND src_ip = ? AND dst_ip = ? AND strategy = ?",
|
||||
firewall.Type,
|
||||
firewall.Chain,
|
||||
firewall.SrcPort,
|
||||
firewall.DstPort,
|
||||
firewall.Protocol,
|
||||
firewall.SrcIP,
|
||||
firewall.DstIP,
|
||||
firewall.Strategy,
|
||||
).First(&data).Error
|
||||
}
|
||||
return global.DB.Save(firewall).Error
|
||||
}
|
||||
@@ -69,10 +86,6 @@ func (h *HostRepo) DeleteFirewallRecordByID(id uint) error {
|
||||
return global.DB.Where("id = ?", id).Delete(&model.Firewall{}).Error
|
||||
}
|
||||
|
||||
func (h *HostRepo) DeleteFirewallRecord(fType, port, protocol, address, strategy string) error {
|
||||
return global.DB.Where("type = ? AND port = ? AND protocol = ? AND address = ? AND strategy = ?", fType, port, protocol, address, strategy).Delete(&model.Firewall{}).Error
|
||||
}
|
||||
|
||||
func (u *HostRepo) GetCert(opts ...DBOption) (model.RootCert, error) {
|
||||
var cert model.RootCert
|
||||
db := global.DB
|
||||
@@ -151,3 +164,9 @@ func (u *HostRepo) SyncCert(data []model.RootCert) error {
|
||||
tx.Commit()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *HostRepo) WithByChain(chain string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("chain = ?", chain)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,21 +5,27 @@ import (
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type MonitorRepo struct{}
|
||||
|
||||
type IMonitorRepo interface {
|
||||
GetBase(opts ...DBOption) ([]model.MonitorBase, error)
|
||||
GetGPU(opts ...DBOption) ([]model.MonitorGPU, error)
|
||||
GetIO(opts ...DBOption) ([]model.MonitorIO, error)
|
||||
GetNetwork(opts ...DBOption) ([]model.MonitorNetwork, error)
|
||||
|
||||
CreateMonitorBase(model model.MonitorBase) error
|
||||
BatchCreateMonitorGPU(list []model.MonitorGPU) error
|
||||
BatchCreateMonitorIO(ioList []model.MonitorIO) error
|
||||
BatchCreateMonitorNet(ioList []model.MonitorNetwork) error
|
||||
DelMonitorBase(timeForDelete time.Time) error
|
||||
DelMonitorGPU(timeForDelete time.Time) error
|
||||
DelMonitorIO(timeForDelete time.Time) error
|
||||
DelMonitorNet(timeForDelete time.Time) error
|
||||
|
||||
WithByProductName(name string) DBOption
|
||||
}
|
||||
|
||||
func NewIMonitorRepo() IMonitorRepo {
|
||||
@@ -53,10 +59,22 @@ func (u *MonitorRepo) GetNetwork(opts ...DBOption) ([]model.MonitorNetwork, erro
|
||||
err := db.Find(&data).Error
|
||||
return data, err
|
||||
}
|
||||
func (u *MonitorRepo) GetGPU(opts ...DBOption) ([]model.MonitorGPU, error) {
|
||||
var data []model.MonitorGPU
|
||||
db := global.GPUMonitorDB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(&data).Error
|
||||
return data, err
|
||||
}
|
||||
|
||||
func (u *MonitorRepo) CreateMonitorBase(model model.MonitorBase) error {
|
||||
return global.MonitorDB.Create(&model).Error
|
||||
}
|
||||
func (s *MonitorRepo) BatchCreateMonitorGPU(list []model.MonitorGPU) error {
|
||||
return global.GPUMonitorDB.CreateInBatches(&list, len(list)).Error
|
||||
}
|
||||
func (u *MonitorRepo) BatchCreateMonitorIO(ioList []model.MonitorIO) error {
|
||||
return global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error
|
||||
}
|
||||
@@ -72,3 +90,12 @@ func (u *MonitorRepo) DelMonitorIO(timeForDelete time.Time) error {
|
||||
func (u *MonitorRepo) DelMonitorNet(timeForDelete time.Time) error {
|
||||
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error
|
||||
}
|
||||
func (s *MonitorRepo) DelMonitorGPU(timeForDelete time.Time) error {
|
||||
return global.GPUMonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorGPU{}).Error
|
||||
}
|
||||
|
||||
func (s *MonitorRepo) WithByProductName(name string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("product_name = ?", name)
|
||||
}
|
||||
}
|
||||
|
||||
+40
-26
@@ -2,7 +2,6 @@ package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
@@ -19,13 +18,14 @@ type ISettingRepo interface {
|
||||
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
|
||||
UpdateOrCreate(key, value string) error
|
||||
|
||||
GetDescription(opts ...DBOption) (model.CommonDescription, error)
|
||||
GetDescriptionList(opts ...DBOption) ([]model.CommonDescription, error)
|
||||
CreateDescription(data *model.CommonDescription) error
|
||||
UpdateDescription(id string, val map[string]interface{}) error
|
||||
DelDescription(id string) error
|
||||
WithByDescriptionID(id string) DBOption
|
||||
}
|
||||
|
||||
func NewISettingRepo() ISettingRepo {
|
||||
@@ -78,25 +78,6 @@ func (s *SettingRepo) Update(key, value string) error {
|
||||
return global.DB.Model(&model.Setting{}).Where("key = ?", key).Updates(map[string]interface{}{"value": value}).Error
|
||||
}
|
||||
|
||||
func (s *SettingRepo) CreateMonitorBase(model model.MonitorBase) error {
|
||||
return global.MonitorDB.Create(&model).Error
|
||||
}
|
||||
func (s *SettingRepo) BatchCreateMonitorIO(ioList []model.MonitorIO) error {
|
||||
return global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error
|
||||
}
|
||||
func (s *SettingRepo) BatchCreateMonitorNet(ioList []model.MonitorNetwork) error {
|
||||
return global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error
|
||||
}
|
||||
func (s *SettingRepo) DelMonitorBase(timeForDelete time.Time) error {
|
||||
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorBase{}).Error
|
||||
}
|
||||
func (s *SettingRepo) DelMonitorIO(timeForDelete time.Time) error {
|
||||
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorIO{}).Error
|
||||
}
|
||||
func (s *SettingRepo) DelMonitorNet(timeForDelete time.Time) error {
|
||||
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error
|
||||
}
|
||||
|
||||
func (s *SettingRepo) UpdateOrCreate(key, value string) error {
|
||||
var setting model.Setting
|
||||
result := global.DB.Where("key = ?", key).First(&setting)
|
||||
@@ -108,3 +89,36 @@ func (s *SettingRepo) UpdateOrCreate(key, value string) error {
|
||||
}
|
||||
return global.DB.Model(&setting).UpdateColumn("value", value).Error
|
||||
}
|
||||
|
||||
func (s *SettingRepo) GetDescriptionList(opts ...DBOption) ([]model.CommonDescription, error) {
|
||||
var lists []model.CommonDescription
|
||||
db := global.DB.Model(&model.CommonDescription{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(&lists).Error
|
||||
return lists, err
|
||||
}
|
||||
func (s *SettingRepo) GetDescription(opts ...DBOption) (model.CommonDescription, error) {
|
||||
var data model.CommonDescription
|
||||
db := global.DB.Model(&model.CommonDescription{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.First(&data).Error
|
||||
return data, err
|
||||
}
|
||||
func (s *SettingRepo) CreateDescription(data *model.CommonDescription) error {
|
||||
return global.DB.Create(data).Error
|
||||
}
|
||||
func (s *SettingRepo) UpdateDescription(id string, val map[string]interface{}) error {
|
||||
return global.DB.Model(&model.CommonDescription{}).Where("id = ?", id).Updates(val).Error
|
||||
}
|
||||
func (s *SettingRepo) DelDescription(id string) error {
|
||||
return global.DB.Where("id = ?", id).Delete(&model.CommonDescription{}).Error
|
||||
}
|
||||
func (s *SettingRepo) WithByDescriptionID(id string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("id = ?", id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,9 +134,8 @@ func (u *AIToolService) Close(name string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s ollama stop %s", containerName, name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("handle ollama stop %s failed, stdout: %s, err: %v", name, stdout, err)
|
||||
if err := cmd.RunDefaultBashCf("docker exec %s ollama stop %s", containerName, name); err != nil {
|
||||
return fmt.Errorf("handle ollama stop %s failed, %v", name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -193,9 +192,8 @@ func (u *AIToolService) Delete(req dto.ForceDelete) error {
|
||||
}
|
||||
for _, item := range ollamaList {
|
||||
if item.Status != constant.StatusDeleted {
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s ollama rm %s", containerName, item.Name)
|
||||
if err != nil && !req.ForceDelete {
|
||||
return fmt.Errorf("handle ollama rm %s failed, stdout: %s, err: %v", item.Name, stdout, err)
|
||||
if err := cmd.RunDefaultBashCf("docker exec %s ollama rm %s", containerName, item.Name); err != nil && !req.ForceDelete {
|
||||
return fmt.Errorf("handle ollama rm %s failed, %v", item.Name, err)
|
||||
}
|
||||
}
|
||||
_ = aiRepo.Delete(repo.WithByID(item.ID))
|
||||
|
||||
@@ -486,9 +486,10 @@ func (a AlertService) TestAlertConfig(req dto.AlertConfigTest) (bool, error) {
|
||||
cfg := email.SMTPConfig{
|
||||
Host: req.Host,
|
||||
Port: req.Port,
|
||||
Sender: req.Sender,
|
||||
Username: username,
|
||||
Password: req.Password,
|
||||
From: fmt.Sprintf("%s <%s>", req.DisplayName, req.Sender),
|
||||
From: fmt.Sprintf(`"%s" <%s>`, req.DisplayName, req.Sender),
|
||||
Encryption: req.Encryption,
|
||||
Recipient: req.Recipient,
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
alertUtil "github.com/1Panel-dev/1Panel/agent/utils/alert"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/psutil"
|
||||
versionUtil "github.com/1Panel-dev/1Panel/agent/utils/version"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||
"github.com/shirou/gopsutil/v4/cpu"
|
||||
@@ -352,7 +353,7 @@ func loadLoadInfo(alert dto.AlertDTO) {
|
||||
return
|
||||
}
|
||||
var loadValue float64
|
||||
CPUTotal, _ := cpu.Counts(true)
|
||||
CPUTotal, _ := psutil.CPUInfo.GetLogicalCores(false)
|
||||
switch alert.Cycle {
|
||||
case 1:
|
||||
loadValue = avgStat.Load1 / (float64(CPUTotal*2) * 0.75) * 100
|
||||
@@ -839,7 +840,7 @@ func processSingleDisk(alert dto.AlertDTO) error {
|
||||
}
|
||||
|
||||
func checkAndCreateDiskAlert(alert dto.AlertDTO, path string) (bool, error) {
|
||||
usageStat, err := disk.Usage(path)
|
||||
usageStat, err := psutil.DISK.GetUsage(path, false)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("error getting disk usage for %s, err: %v", path, err)
|
||||
return false, err
|
||||
|
||||
+29
-33
@@ -37,7 +37,7 @@ type AppService struct {
|
||||
}
|
||||
|
||||
type IAppService interface {
|
||||
PageApp(ctx *gin.Context, req request.AppSearch) (interface{}, error)
|
||||
PageApp(ctx *gin.Context, req request.AppSearch) (*response.AppRes, error)
|
||||
GetAppTags(ctx *gin.Context) ([]response.TagDTO, error)
|
||||
GetApp(ctx *gin.Context, key string) (*response.AppDTO, error)
|
||||
GetAppDetail(appId uint, version, appType string) (response.AppDetailDTO, error)
|
||||
@@ -46,13 +46,14 @@ type IAppService interface {
|
||||
GetAppUpdate() (*response.AppUpdateRes, error)
|
||||
GetAppDetailByID(id uint) (*response.AppDetailDTO, error)
|
||||
SyncAppListFromLocal(taskID string)
|
||||
GetAppIcon(appID uint) ([]byte, error)
|
||||
}
|
||||
|
||||
func NewIAppService() IAppService {
|
||||
return &AppService{}
|
||||
}
|
||||
|
||||
func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{}, error) {
|
||||
func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (*response.AppRes, error) {
|
||||
var opts []repo.DBOption
|
||||
opts = append(opts, appRepo.OrderByRecommend())
|
||||
if req.Name != "" {
|
||||
@@ -98,7 +99,7 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
|
||||
}
|
||||
opts = append(opts, repo.WithByIDs(appIds))
|
||||
}
|
||||
var res response.AppRes
|
||||
res := &response.AppRes{}
|
||||
|
||||
total, apps, err := appRepo.Page(req.Page, req.PageSize, opts...)
|
||||
if err != nil {
|
||||
@@ -120,12 +121,7 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
|
||||
ID: ap.ID,
|
||||
Name: ap.Name,
|
||||
Key: ap.Key,
|
||||
Type: ap.Type,
|
||||
Icon: ap.Icon,
|
||||
Resource: ap.Resource,
|
||||
Limit: ap.Limit,
|
||||
Website: ap.Website,
|
||||
Github: ap.Github,
|
||||
GpuSupport: ap.GpuSupport,
|
||||
Recommend: ap.Recommend,
|
||||
Description: ap.GetDescription(ctx),
|
||||
@@ -135,7 +131,9 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
appDTO.Tags = tags
|
||||
for _, tag := range tags {
|
||||
appDTO.Tags = append(appDTO.Tags, tag.Name)
|
||||
}
|
||||
if ap.Type == constant.RuntimePHP || ap.Type == constant.RuntimeGo || ap.Type == constant.RuntimeNode || ap.Type == constant.RuntimePython || ap.Type == constant.RuntimeJava || ap.Type == constant.RuntimeDotNet {
|
||||
details, _ := appDetailRepo.GetBy(appDetailRepo.WithAppId(ap.ID))
|
||||
var ids []uint
|
||||
@@ -195,24 +193,7 @@ func (a AppService) GetApp(ctx *gin.Context, key string) (*response.AppDTO, erro
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var versionsRaw []string
|
||||
hasLatest := false
|
||||
latestVersion := ""
|
||||
for _, detail := range details {
|
||||
if strings.Contains(detail.Version, "latest") {
|
||||
hasLatest = true
|
||||
latestVersion = detail.Version
|
||||
continue
|
||||
}
|
||||
if key == "openresty" && !common.CompareAppVersion(detail.Version, "1.27") {
|
||||
continue
|
||||
}
|
||||
versionsRaw = append(versionsRaw, detail.Version)
|
||||
}
|
||||
appDTO.Versions = common.GetSortedVersions(versionsRaw)
|
||||
if hasLatest {
|
||||
appDTO.Versions = append([]string{latestVersion}, appDTO.Versions...)
|
||||
}
|
||||
appDTO.Versions = getAppVersions(key, details)
|
||||
tags, err := getAppTags(app.ID, strings.ToLower(common.GetLang(ctx)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -450,10 +431,11 @@ func (a AppService) Install(req request.AppInstallCreate) (appInstall *model.App
|
||||
}
|
||||
index++
|
||||
}
|
||||
if app.Limit == 0 && appInstall.Name != serviceName && len(servicesMap) == 1 {
|
||||
servicesMap[appInstall.Name] = servicesMap[serviceName]
|
||||
newServiceName := strings.ToLower(appInstall.Name)
|
||||
if app.Limit == 0 && newServiceName != serviceName && len(servicesMap) == 1 {
|
||||
servicesMap[newServiceName] = servicesMap[serviceName]
|
||||
delete(servicesMap, serviceName)
|
||||
serviceName = appInstall.Name
|
||||
serviceName = newServiceName
|
||||
}
|
||||
appInstall.ServiceName = serviceName
|
||||
|
||||
@@ -863,13 +845,15 @@ func getAppList() (*dto.AppList, error) {
|
||||
return nil, err
|
||||
}
|
||||
listFile := filepath.Join(global.Dir.ResourceDir, "1panel.json")
|
||||
content, err := os.ReadFile(listFile)
|
||||
file, err := os.Open(listFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = json.Unmarshal(content, list); err != nil {
|
||||
defer file.Close()
|
||||
if err = json.NewDecoder(file).Decode(list); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
@@ -1152,7 +1136,7 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
|
||||
}, nil)
|
||||
|
||||
go func() {
|
||||
if err = syncTask.Execute(); err != nil {
|
||||
if err := syncTask.Execute(); err != nil {
|
||||
_ = NewISettingService().Update("AppStoreLastModified", "0")
|
||||
_ = NewISettingService().Update("AppStoreSyncStatus", constant.StatusError)
|
||||
}
|
||||
@@ -1160,3 +1144,15 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AppService) GetAppIcon(appID uint) ([]byte, error) {
|
||||
app, err := appRepo.GetFirst(repo.WithByID(appID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iconBytes, err := base64.StdEncoding.DecodeString(app.Icon)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iconBytes, nil
|
||||
}
|
||||
|
||||
@@ -796,6 +796,9 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
||||
res.RestartPolicy = getRestartPolicy(install.DockerCompose)
|
||||
res.WebUI = install.WebUI
|
||||
res.Type = install.App.Type
|
||||
if rawCompose, err := getUpgradeCompose(install, detail); err == nil {
|
||||
res.RawCompose = rawCompose
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
@@ -940,8 +943,26 @@ func (a *AppInstallService) GetAppInstallInfo(installID uint) (*response.AppInst
|
||||
HttpPort: appInstall.HttpPort,
|
||||
Status: appInstall.Status,
|
||||
Message: appInstall.Message,
|
||||
AppKey: appInstall.App.Key,
|
||||
Env: envMap,
|
||||
ComposePath: appInstall.GetComposePath(),
|
||||
}
|
||||
for k, v := range envMap {
|
||||
if strings.Contains(strings.ToUpper(k), "PANEL_APP_PORT") {
|
||||
var port int
|
||||
switch val := v.(type) {
|
||||
case int:
|
||||
port = val
|
||||
case string:
|
||||
port, err = strconv.Atoi(val)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
res.AppPorts = append(res.AppPorts, port)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
+196
-128
@@ -15,7 +15,6 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -40,6 +39,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/re"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
@@ -134,6 +134,76 @@ var ToolKeys = map[string]uint{
|
||||
"minio": 9001,
|
||||
}
|
||||
|
||||
func CreateDB(ctx context.Context, app model.App, appInstall *model.AppInstall, params map[string]interface{}) error {
|
||||
database := &model.Database{
|
||||
AppInstallID: appInstall.ID,
|
||||
Name: appInstall.Name,
|
||||
Type: app.Key,
|
||||
Version: appInstall.Version,
|
||||
From: "local",
|
||||
Address: appInstall.ServiceName,
|
||||
Port: DatabaseKeys[app.Key],
|
||||
}
|
||||
detail, err := appDetailRepo.GetFirst(repo.WithByID(appInstall.AppDetailId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
formFields := &dto.AppForm{}
|
||||
if err := json.Unmarshal([]byte(detail.Params), formFields); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, form := range formFields.FormFields {
|
||||
if form.EnvKey == "PANEL_APP_PORT_HTTP" {
|
||||
portFloat, ok := form.Default.(float64)
|
||||
if ok {
|
||||
database.Port = uint(int(portFloat))
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch app.Key {
|
||||
case constant.AppMysql, constant.AppMariaDB, constant.AppPostgresql, constant.AppMongodb, constant.AppMysqlCluster, constant.AppPostgresqlCluster:
|
||||
if password, ok := params["PANEL_DB_ROOT_PASSWORD"]; ok {
|
||||
if password != "" {
|
||||
database.Password = password.(string)
|
||||
if app.Key == "mysql" || app.Key == "mariadb" || app.Key == constant.AppMysqlCluster {
|
||||
database.Username = "root"
|
||||
}
|
||||
if rootUser, ok := params["PANEL_DB_ROOT_USER"]; ok {
|
||||
database.Username = rootUser.(string)
|
||||
}
|
||||
authParam := dto.AuthParam{
|
||||
RootPassword: password.(string),
|
||||
RootUser: database.Username,
|
||||
}
|
||||
authByte, err := json.Marshal(authParam)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appInstall.Param = string(authByte)
|
||||
|
||||
}
|
||||
}
|
||||
case constant.AppRedis, constant.AppRedisCluster:
|
||||
if password, ok := params["PANEL_REDIS_ROOT_PASSWORD"]; ok {
|
||||
authParam := dto.RedisAuthParam{
|
||||
RootPassword: "",
|
||||
}
|
||||
if password != "" {
|
||||
authParam.RootPassword = password.(string)
|
||||
database.Password = password.(string)
|
||||
}
|
||||
authByte, err := json.Marshal(authParam)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appInstall.Param = string(authByte)
|
||||
}
|
||||
}
|
||||
return databaseRepo.Create(ctx, database)
|
||||
}
|
||||
|
||||
func createLink(ctx context.Context, installTask *task.Task, app model.App, appInstall *model.AppInstall, params map[string]interface{}) error {
|
||||
deleteAppLink := func(t *task.Task) {
|
||||
del := dto.DelAppLink{
|
||||
@@ -145,74 +215,7 @@ func createLink(ctx context.Context, installTask *task.Task, app model.App, appI
|
||||
}
|
||||
if DatabaseKeys[app.Key] > 0 {
|
||||
handleDataBaseApp := func(task *task.Task) error {
|
||||
database := &model.Database{
|
||||
AppInstallID: appInstall.ID,
|
||||
Name: appInstall.Name,
|
||||
Type: app.Key,
|
||||
Version: appInstall.Version,
|
||||
From: "local",
|
||||
Address: appInstall.ServiceName,
|
||||
Port: DatabaseKeys[app.Key],
|
||||
}
|
||||
detail, err := appDetailRepo.GetFirst(repo.WithByID(appInstall.AppDetailId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
formFields := &dto.AppForm{}
|
||||
if err := json.Unmarshal([]byte(detail.Params), formFields); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, form := range formFields.FormFields {
|
||||
if form.EnvKey == "PANEL_APP_PORT_HTTP" {
|
||||
portFloat, ok := form.Default.(float64)
|
||||
if ok {
|
||||
database.Port = uint(int(portFloat))
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch app.Key {
|
||||
case constant.AppMysql, constant.AppMariaDB, constant.AppPostgresql, constant.AppMongodb, constant.AppMysqlCluster, constant.AppPostgresqlCluster:
|
||||
if password, ok := params["PANEL_DB_ROOT_PASSWORD"]; ok {
|
||||
if password != "" {
|
||||
database.Password = password.(string)
|
||||
if app.Key == "mysql" || app.Key == "mariadb" || app.Key == constant.AppMysqlCluster {
|
||||
database.Username = "root"
|
||||
}
|
||||
if rootUser, ok := params["PANEL_DB_ROOT_USER"]; ok {
|
||||
database.Username = rootUser.(string)
|
||||
}
|
||||
authParam := dto.AuthParam{
|
||||
RootPassword: password.(string),
|
||||
RootUser: database.Username,
|
||||
}
|
||||
authByte, err := json.Marshal(authParam)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appInstall.Param = string(authByte)
|
||||
|
||||
}
|
||||
}
|
||||
case constant.AppRedis, constant.AppRedisCluster:
|
||||
if password, ok := params["PANEL_REDIS_ROOT_PASSWORD"]; ok {
|
||||
authParam := dto.RedisAuthParam{
|
||||
RootPassword: "",
|
||||
}
|
||||
if password != "" {
|
||||
authParam.RootPassword = password.(string)
|
||||
database.Password = password.(string)
|
||||
}
|
||||
authByte, err := json.Marshal(authParam)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appInstall.Param = string(authByte)
|
||||
}
|
||||
}
|
||||
return databaseRepo.Create(ctx, database)
|
||||
return CreateDB(ctx, app, appInstall, params)
|
||||
}
|
||||
installTask.AddSubTask(i18n.GetMsgByKey("HandleDatabaseApp"), handleDataBaseApp, deleteAppLink)
|
||||
}
|
||||
@@ -297,10 +300,11 @@ func createLink(ctx context.Context, installTask *task.Task, app model.App, appI
|
||||
createMysql.Name = dbConfig.DbName
|
||||
createMysql.Username = dbConfig.DbUser
|
||||
createMysql.Database = database.Name
|
||||
createMysql.Format = "utf8mb4"
|
||||
createMysql.Format = dbConfig.Format
|
||||
createMysql.Permission = "%"
|
||||
createMysql.Password = dbConfig.Password
|
||||
createMysql.From = database.From
|
||||
createMysql.Collation = dbConfig.Collation
|
||||
mysqldb, err := NewIMysqlService().Create(ctx, createMysql)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -537,8 +541,18 @@ func handleUpgradeCompose(install model.AppInstall, detail model.AppDetail) (map
|
||||
if !ok || oldValue == nil {
|
||||
return nil, buserr.New("ErrFileParse")
|
||||
}
|
||||
oldValueMap := oldValue.(map[string]interface{})
|
||||
oldServiceValue := oldValueMap[install.ServiceName].(map[string]interface{})
|
||||
oldValueMap, ok := oldValue.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, buserr.New("ErrFileParse")
|
||||
}
|
||||
oldServiceValueInterface, ok := oldValueMap[install.ServiceName]
|
||||
if !ok || oldServiceValueInterface == nil {
|
||||
return nil, buserr.New("ErrFileParse")
|
||||
}
|
||||
oldServiceValue, ok := oldServiceValueInterface.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, buserr.New("ErrFileParse")
|
||||
}
|
||||
if oldServiceValue["deploy"] != nil {
|
||||
serviceValue["deploy"] = oldServiceValue["deploy"]
|
||||
}
|
||||
@@ -1082,11 +1096,7 @@ func runScript(task *task.Task, appInstall *model.AppInstall, operate string) er
|
||||
task.LogStart(logStr)
|
||||
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute), cmd.WithWorkDir(workDir))
|
||||
out, err := cmdMgr.RunWithStdoutBashC(scriptPath)
|
||||
if err != nil {
|
||||
if out != "" {
|
||||
err = errors.New(out)
|
||||
}
|
||||
if err := cmdMgr.RunBashC(scriptPath); err != nil {
|
||||
task.LogFailedWithErr(logStr, err)
|
||||
return err
|
||||
}
|
||||
@@ -1413,6 +1423,9 @@ func handleLocalApp(appDir string) (app *model.App, err error) {
|
||||
desc, _ := json.Marshal(appDefine.Description)
|
||||
app.Description = string(desc)
|
||||
app.Key = "local" + appDefine.Key
|
||||
app.Architectures = strings.Join(appDefine.Architectures, ",")
|
||||
app.GpuSupport = appDefine.GpuSupport
|
||||
app.MemoryRequired = appDefine.MemoryRequired
|
||||
|
||||
app.Resource = constant.AppResourceLocal
|
||||
app.Status = constant.AppNormal
|
||||
@@ -1554,7 +1567,6 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool)
|
||||
Message: installed.Message,
|
||||
HttpPort: installed.HttpPort,
|
||||
HttpsPort: installed.HttpsPort,
|
||||
Icon: installed.App.Icon,
|
||||
AppName: installed.App.Name,
|
||||
AppKey: installed.App.Key,
|
||||
AppType: installed.App.Type,
|
||||
@@ -1566,41 +1578,41 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool)
|
||||
Website: installed.App.Website,
|
||||
Document: installed.App.Document,
|
||||
},
|
||||
Favorite: installed.Favorite,
|
||||
Container: installed.ContainerName,
|
||||
Favorite: installed.Favorite,
|
||||
Container: installed.ContainerName,
|
||||
ServiceName: strings.ToLower(installed.ServiceName),
|
||||
}
|
||||
if updated {
|
||||
installDTO.DockerCompose = installed.DockerCompose
|
||||
if !updated {
|
||||
installDTO.LinkDB = hasLinkDB(installed.ID)
|
||||
res = append(res, installDTO)
|
||||
continue
|
||||
}
|
||||
app, err := appRepo.GetFirst(repo.WithByID(installed.AppId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if installed.Version == "latest" {
|
||||
continue
|
||||
}
|
||||
details, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(app.ID))
|
||||
installDTO.DockerCompose = installed.DockerCompose
|
||||
installDTO.IsEdit = isEditCompose(installed)
|
||||
details, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(installed.App.ID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var versions []string
|
||||
for _, detail := range details {
|
||||
ignores, _ := appIgnoreUpgradeRepo.List(runtimeRepo.WithDetailId(detail.ID), appIgnoreUpgradeRepo.WithScope("version"))
|
||||
if len(ignores) > 0 || installed.Version == "latest" {
|
||||
for _, appDetail := range details {
|
||||
ignores, _ := appIgnoreUpgradeRepo.List(runtimeRepo.WithDetailId(appDetail.ID), appIgnoreUpgradeRepo.WithScope("version"))
|
||||
if len(ignores) > 0 {
|
||||
continue
|
||||
}
|
||||
if common.IsCrossVersion(installed.Version, detail.Version) && !app.CrossVersionUpdate {
|
||||
if common.IsCrossVersion(installed.Version, appDetail.Version) && !installed.App.CrossVersionUpdate {
|
||||
continue
|
||||
}
|
||||
versions = append(versions, detail.Version)
|
||||
versions = append(versions, appDetail.Version)
|
||||
}
|
||||
versions = common.GetSortedVersions(versions)
|
||||
if len(versions) == 0 {
|
||||
if !updated {
|
||||
installDTO.CanUpdate = false
|
||||
res = append(res, installDTO)
|
||||
}
|
||||
continue
|
||||
}
|
||||
versions = common.GetSortedVersions(versions)
|
||||
lastVersion := versions[0]
|
||||
if app.Key == constant.AppMysql || app.Key == constant.AppMysqlCluster {
|
||||
if installed.App.Key == constant.AppMysql || installed.App.Key == constant.AppMysqlCluster {
|
||||
for _, version := range versions {
|
||||
majorVersion := getMajorVersion(installed.Version)
|
||||
if !strings.HasPrefix(version, majorVersion) {
|
||||
@@ -1612,15 +1624,11 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool)
|
||||
}
|
||||
}
|
||||
if common.IsCrossVersion(installed.Version, lastVersion) {
|
||||
installDTO.CanUpdate = app.CrossVersionUpdate
|
||||
installDTO.CanUpdate = installed.App.CrossVersionUpdate
|
||||
} else {
|
||||
installDTO.CanUpdate = common.CompareVersion(lastVersion, installed.Version)
|
||||
}
|
||||
if updated {
|
||||
if installDTO.CanUpdate {
|
||||
res = append(res, installDTO)
|
||||
}
|
||||
} else {
|
||||
if installDTO.CanUpdate {
|
||||
res = append(res, installDTO)
|
||||
}
|
||||
}
|
||||
@@ -1824,8 +1832,7 @@ func getAppCommonConfig(envs map[string]interface{}) request.AppContainerConfig
|
||||
config.CpuQuota = 0
|
||||
}
|
||||
if memLimit, ok := envs[constant.MemoryLimit]; ok {
|
||||
re := regexp.MustCompile(`(\d+)([A-Za-z]+)`)
|
||||
matches := re.FindStringSubmatch(memLimit.(string))
|
||||
matches := re.GetRegex(re.NumberAlphaPattern).FindStringSubmatch(memLimit.(string))
|
||||
if len(matches) == 3 {
|
||||
num, err := strconv.ParseFloat(matches[1], 64)
|
||||
if err == nil {
|
||||
@@ -1999,7 +2006,7 @@ func handleOpenrestyFile(appInstall *model.AppInstall) error {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err := handleSSLConfig(appInstall, hasDefaultWebsite); err != nil {
|
||||
if err := handleSSLConfig(appInstall, hasDefaultWebsite, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(websites) == 0 {
|
||||
@@ -2028,34 +2035,37 @@ func handleDefaultServer(appInstall *model.AppInstall) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleSSLConfig(appInstall *model.AppInstall, hasDefaultWebsite bool) error {
|
||||
func handleSSLConfig(appInstall *model.AppInstall, hasDefaultWebsite bool, sslRejectHandshake bool) error {
|
||||
sslDir := path.Join(appInstall.GetPath(), "conf", "ssl")
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(sslDir) {
|
||||
return errors.New("ssl dir not found")
|
||||
}
|
||||
ca, _ := websiteCARepo.GetFirst(repo.WithByName("1Panel"))
|
||||
if ca.ID == 0 {
|
||||
global.LOG.Errorf("create openresty default ssl failed ca not found")
|
||||
return nil
|
||||
hasDefaultSSL := fileOp.Stat(path.Join(sslDir, "fullchain.pem")) && fileOp.Stat(path.Join(sslDir, "privkey.pem")) && fileOp.Stat(path.Join(sslDir, "root_ssl.conf"))
|
||||
if !hasDefaultSSL {
|
||||
ca, _ := websiteCARepo.GetFirst(repo.WithByName("1Panel"))
|
||||
if ca.ID == 0 {
|
||||
global.LOG.Errorf("create openresty default ssl failed ca not found")
|
||||
return nil
|
||||
}
|
||||
caService := NewIWebsiteCAService()
|
||||
caRequest := request.WebsiteCAObtain{
|
||||
ID: ca.ID,
|
||||
Domains: "localhost",
|
||||
KeyType: "4096",
|
||||
Time: 99,
|
||||
Unit: "year",
|
||||
Dir: sslDir,
|
||||
PushDir: true,
|
||||
}
|
||||
websiteSSL, err := caService.ObtainSSL(caRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = NewIWebsiteSSLService().Delete([]uint{websiteSSL.ID})
|
||||
}()
|
||||
}
|
||||
caService := NewIWebsiteCAService()
|
||||
caRequest := request.WebsiteCAObtain{
|
||||
ID: ca.ID,
|
||||
Domains: "localhost",
|
||||
KeyType: "4096",
|
||||
Time: 99,
|
||||
Unit: "year",
|
||||
Dir: sslDir,
|
||||
PushDir: true,
|
||||
}
|
||||
websiteSSL, err := caService.ObtainSSL(caRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = NewIWebsiteSSLService().Delete([]uint{websiteSSL.ID})
|
||||
}()
|
||||
defaultConfigPath := path.Join(appInstall.GetPath(), "conf", "default", "00.default.conf")
|
||||
content, err := os.ReadFile(defaultConfigPath)
|
||||
if err != nil {
|
||||
@@ -2070,6 +2080,11 @@ func handleSSLConfig(appInstall *model.AppInstall, hasDefaultWebsite bool) error
|
||||
updateDefaultServer(defaultServer, appInstall.HttpPort, appInstall.HttpsPort, !hasDefaultWebsite, true)
|
||||
defaultServer.UpdateDirective("include", []string{"/usr/local/openresty/nginx/conf/ssl/root_ssl.conf"})
|
||||
defaultServer.UpdateDirective("http2", []string{"on"})
|
||||
if sslRejectHandshake {
|
||||
defaultServer.UpdateDirective("ssl_reject_handshake", []string{"on"})
|
||||
} else {
|
||||
defaultServer.RemoveDirective("ssl_reject_handshake", []string{})
|
||||
}
|
||||
if err = nginx.WriteConfig(defaultConfig, nginx.IndentedStyle); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -2131,3 +2146,56 @@ func needsUpdate(localTag *model.Tag, remoteTag dto.Tag, translations string) bo
|
||||
localTag.Sort != remoteTag.Sort ||
|
||||
localTag.Translations != translations
|
||||
}
|
||||
|
||||
func hasLinkDB(installID uint) bool {
|
||||
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(installID))
|
||||
hasDB := false
|
||||
if len(resources) > 0 {
|
||||
for _, resource := range resources {
|
||||
if resource.Key == constant.AppPostgres || resource.Key == constant.AppMysql ||
|
||||
resource.Key == constant.AppMariaDB || resource.Key == constant.AppMysqlCluster ||
|
||||
resource.Key == constant.AppPostgresql || resource.Key == constant.AppPostgresqlCluster {
|
||||
hasDB = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasDB
|
||||
}
|
||||
|
||||
func isEditCompose(installed model.AppInstall) bool {
|
||||
detail, _ := appDetailRepo.GetFirst(repo.WithByID(installed.AppDetailId))
|
||||
rawCompose, err := getUpgradeCompose(installed, detail)
|
||||
if rawCompose == "" || err != nil {
|
||||
return false
|
||||
}
|
||||
if rawCompose != installed.DockerCompose {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getAppVersions(key string, details []model.AppDetail) []string {
|
||||
var (
|
||||
versionsRaw []string
|
||||
versions []string
|
||||
)
|
||||
hasLatest := false
|
||||
latestVersion := ""
|
||||
for _, detail := range details {
|
||||
if key != "mssql" && strings.Contains(detail.Version, "latest") {
|
||||
hasLatest = true
|
||||
latestVersion = detail.Version
|
||||
continue
|
||||
}
|
||||
if key == "openresty" && !common.CompareAppVersion(detail.Version, "1.27") {
|
||||
continue
|
||||
}
|
||||
versionsRaw = append(versionsRaw, detail.Version)
|
||||
}
|
||||
versions = common.GetSortedVersions(versionsRaw)
|
||||
if hasLatest {
|
||||
versions = append([]string{latestVersion}, versions...)
|
||||
}
|
||||
return versions
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ func backupDatabaseWithTask(parentTask *task.Task, resourceKey, tmpDir, name str
|
||||
}
|
||||
parentTask.LogStart(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))
|
||||
databaseHelper := DatabaseHelper{Database: db.MysqlName, DBType: resourceKey, Name: db.Name}
|
||||
if err := handleMysqlBackup(databaseHelper, parentTask, 0, tmpDir, fmt.Sprintf("%s.sql.gz", name), ""); err != nil {
|
||||
if err := handleMysqlBackup(databaseHelper, parentTask, 0, tmpDir, fmt.Sprintf("%s.sql.gz", name), "", ""); err != nil {
|
||||
return err
|
||||
}
|
||||
parentTask.LogSuccess(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))
|
||||
@@ -113,7 +113,7 @@ func backupDatabaseWithTask(parentTask *task.Task, resourceKey, tmpDir, name str
|
||||
}
|
||||
parentTask.LogStart(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))
|
||||
databaseHelper := DatabaseHelper{Database: db.PostgresqlName, DBType: resourceKey, Name: db.Name}
|
||||
if err := handlePostgresqlBackup(databaseHelper, parentTask, 0, tmpDir, fmt.Sprintf("%s.sql.gz", name), ""); err != nil {
|
||||
if err := handlePostgresqlBackup(databaseHelper, parentTask, 0, tmpDir, fmt.Sprintf("%s.sql.gz", name), "", ""); err != nil {
|
||||
return err
|
||||
}
|
||||
parentTask.LogSuccess(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))
|
||||
@@ -260,7 +260,7 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
|
||||
Name: newDB.MysqlName,
|
||||
DetailName: newDB.Name,
|
||||
File: fmt.Sprintf("%s/%s.sql.gz", tmpPath, install.Name),
|
||||
}, parentTask, true, ""); err != nil {
|
||||
}, parentTask, true); err != nil {
|
||||
t.LogFailedWithErr(taskName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,17 +8,14 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/mysql/client"
|
||||
@@ -48,7 +45,7 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
|
||||
}
|
||||
|
||||
databaseHelper := DatabaseHelper{Database: req.Name, DBType: req.Type, Name: req.DetailName}
|
||||
if err := handleMysqlBackup(databaseHelper, nil, record.ID, targetDir, fileName, req.TaskID); err != nil {
|
||||
if err := handleMysqlBackup(databaseHelper, nil, record.ID, targetDir, fileName, req.TaskID, req.Secret); err != nil {
|
||||
backupRepo.UpdateRecordByMap(record.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||
return err
|
||||
}
|
||||
@@ -56,7 +53,7 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
|
||||
}
|
||||
|
||||
func (u *BackupService) MysqlRecover(req dto.CommonRecover) error {
|
||||
return handleMysqlRecover(req, nil, false, req.TaskID)
|
||||
return handleMysqlRecover(req, nil, false)
|
||||
}
|
||||
|
||||
func (u *BackupService) MysqlRecoverByUpload(req dto.CommonRecover) error {
|
||||
@@ -66,13 +63,13 @@ func (u *BackupService) MysqlRecoverByUpload(req dto.CommonRecover) error {
|
||||
}
|
||||
req.File = recoveFile
|
||||
|
||||
if err := handleMysqlRecover(req, nil, false, req.TaskID); err != nil {
|
||||
if err := handleMysqlRecover(req, nil, false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleMysqlBackup(db DatabaseHelper, parentTask *task.Task, recordID uint, targetDir, fileName, taskID string) error {
|
||||
func handleMysqlBackup(db DatabaseHelper, parentTask *task.Task, recordID uint, targetDir, fileName, taskID, secret string) error {
|
||||
var (
|
||||
err error
|
||||
backupTask *task.Task
|
||||
@@ -90,11 +87,11 @@ func handleMysqlBackup(db DatabaseHelper, parentTask *task.Task, recordID uint,
|
||||
}
|
||||
}
|
||||
|
||||
itemHandler := func() error { return doMysqlBackup(db, targetDir, fileName) }
|
||||
itemHandler := func() error { return doMysqlBackup(db, targetDir, fileName, secret) }
|
||||
if parentTask != nil {
|
||||
return itemHandler()
|
||||
}
|
||||
backupTask.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 3, time.Hour)
|
||||
backupTask.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 0, 3*time.Hour)
|
||||
go func() {
|
||||
if err := backupTask.Execute(); err != nil {
|
||||
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||
@@ -105,7 +102,7 @@ func handleMysqlBackup(db DatabaseHelper, parentTask *task.Task, recordID uint,
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback bool, taskID string) error {
|
||||
func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback bool) error {
|
||||
var (
|
||||
err error
|
||||
itemTask *task.Task
|
||||
@@ -117,7 +114,7 @@ func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback
|
||||
}
|
||||
itemName := fmt.Sprintf("%s[%s] - %s", req.Name, req.Type, req.DetailName)
|
||||
if parentTask == nil {
|
||||
itemTask, err = task.NewTaskWithOps(itemName, task.TaskRecover, task.TaskScopeBackup, taskID, dbInfo.ID)
|
||||
itemTask, err = task.NewTaskWithOps(itemName, task.TaskRecover, task.TaskScopeBackup, req.TaskID, dbInfo.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -170,6 +167,15 @@ func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback
|
||||
}
|
||||
}()
|
||||
}
|
||||
if len(req.Secret) != 0 {
|
||||
err = files.OpensslDecrypt(req.File, req.Secret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.File = path.Join(path.Dir(req.File), "tmp_"+path.Base(req.File))
|
||||
defer os.Remove(req.File)
|
||||
t.LogWithStatus(i18n.GetMsgByKey("Decrypt"), err)
|
||||
}
|
||||
if err := cli.Recover(client.RecoverInfo{
|
||||
Name: req.DetailName,
|
||||
Type: req.Type,
|
||||
@@ -187,14 +193,14 @@ func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback
|
||||
return recoverDatabase(parentTask)
|
||||
}
|
||||
|
||||
itemTask.AddSubTaskWithOps(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil, 3, time.Hour)
|
||||
itemTask.AddSubTaskWithOps(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil, 0, 3*time.Hour)
|
||||
go func() {
|
||||
_ = itemTask.Execute()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func doMysqlBackup(db DatabaseHelper, targetDir, fileName string) error {
|
||||
func doMysqlBackup(db DatabaseHelper, targetDir, fileName, secret string) error {
|
||||
dbInfo, err := mysqlRepo.Get(repo.WithByName(db.Name), mysqlRepo.WithByMysqlName(db.Database))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -211,7 +217,13 @@ func doMysqlBackup(db DatabaseHelper, targetDir, fileName string) error {
|
||||
TargetDir: targetDir,
|
||||
FileName: fileName,
|
||||
}
|
||||
return cli.Backup(backupInfo)
|
||||
if err := cli.Backup(backupInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(secret) != 0 {
|
||||
return files.OpensslEncrypt(path.Join(targetDir, fileName), secret)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadSqlFile(file string) (string, error) {
|
||||
|
||||
@@ -6,21 +6,18 @@ import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
pgclient "github.com/1Panel-dev/1Panel/agent/utils/postgresql/client"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/postgresql/client"
|
||||
pgclient "github.com/1Panel-dev/1Panel/agent/utils/postgresql/client"
|
||||
)
|
||||
|
||||
func (u *BackupService) PostgresqlBackup(req dto.CommonBackup) error {
|
||||
@@ -46,7 +43,7 @@ func (u *BackupService) PostgresqlBackup(req dto.CommonBackup) error {
|
||||
}
|
||||
|
||||
databaseHelper := DatabaseHelper{Database: req.Name, DBType: req.Type, Name: req.DetailName}
|
||||
if err := handlePostgresqlBackup(databaseHelper, nil, record.ID, targetDir, fileName, req.TaskID); err != nil {
|
||||
if err := handlePostgresqlBackup(databaseHelper, nil, record.ID, targetDir, fileName, req.TaskID, req.Secret); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -71,7 +68,7 @@ func (u *BackupService) PostgresqlRecoverByUpload(req dto.CommonRecover) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func handlePostgresqlBackup(db DatabaseHelper, parentTask *task.Task, recordID uint, targetDir, fileName, taskID string) error {
|
||||
func handlePostgresqlBackup(db DatabaseHelper, parentTask *task.Task, recordID uint, targetDir, fileName, taskID, secret string) error {
|
||||
var (
|
||||
err error
|
||||
backupTask *task.Task
|
||||
@@ -85,11 +82,11 @@ func handlePostgresqlBackup(db DatabaseHelper, parentTask *task.Task, recordID u
|
||||
}
|
||||
}
|
||||
|
||||
itemHandler := func() error { return doPostgresqlgBackup(db, targetDir, fileName) }
|
||||
itemHandler := func() error { return doPostgresqlgBackup(db, targetDir, fileName, secret, backupTask) }
|
||||
if parentTask != nil {
|
||||
return itemHandler()
|
||||
}
|
||||
backupTask.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 3, time.Hour)
|
||||
backupTask.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 0, 3*time.Hour)
|
||||
go func() {
|
||||
if err := backupTask.Execute(); err != nil {
|
||||
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||
@@ -133,10 +130,12 @@ func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRol
|
||||
if !isRollback {
|
||||
rollbackFile := path.Join(global.Dir.TmpDir, fmt.Sprintf("database/%s/%s_%s.sql.gz", req.Type, req.DetailName, time.Now().Format(constant.DateTimeSlimLayout)))
|
||||
if err := cli.Backup(client.BackupInfo{
|
||||
Database: req.Name,
|
||||
Name: req.DetailName,
|
||||
TargetDir: path.Dir(rollbackFile),
|
||||
FileName: path.Base(rollbackFile),
|
||||
|
||||
Task: t,
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("backup postgresql db %s for rollback before recover failed, err: %v", req.DetailName, err)
|
||||
@@ -145,9 +144,11 @@ func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRol
|
||||
if !isOk {
|
||||
global.LOG.Info("recover failed, start to rollback now")
|
||||
if err := cli.Recover(client.RecoverInfo{
|
||||
Database: req.Name,
|
||||
Name: req.DetailName,
|
||||
SourceFile: rollbackFile,
|
||||
|
||||
Task: t,
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
global.LOG.Errorf("rollback postgresql db %s from %s failed, err: %v", req.DetailName, rollbackFile, err)
|
||||
@@ -160,10 +161,21 @@ func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRol
|
||||
}
|
||||
}()
|
||||
}
|
||||
if len(req.Secret) != 0 {
|
||||
err = files.OpensslDecrypt(req.File, req.Secret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.File = path.Join(path.Dir(req.File), "tmp_"+path.Base(req.File))
|
||||
defer os.Remove(req.File)
|
||||
t.LogWithStatus(i18n.GetMsgByKey("Decrypt"), err)
|
||||
}
|
||||
if err := cli.Recover(client.RecoverInfo{
|
||||
Database: req.Name,
|
||||
Name: req.DetailName,
|
||||
SourceFile: req.File,
|
||||
Username: dbInfo.Username,
|
||||
Task: t,
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
global.LOG.Errorf("recover postgresql db %s from %s failed, err: %v", req.DetailName, req.File, err)
|
||||
@@ -176,25 +188,33 @@ func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRol
|
||||
return recoverDatabase(parentTask)
|
||||
}
|
||||
|
||||
itemTask.AddSubTaskWithOps(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil, 3, time.Hour)
|
||||
itemTask.AddSubTaskWithOps(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil, 0, 3*time.Hour)
|
||||
go func() {
|
||||
_ = itemTask.Execute()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func doPostgresqlgBackup(db DatabaseHelper, targetDir, fileName string) error {
|
||||
func doPostgresqlgBackup(db DatabaseHelper, targetDir, fileName, secret string, task *task.Task) error {
|
||||
cli, err := LoadPostgresqlClientByFrom(db.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
backupInfo := pgclient.BackupInfo{
|
||||
Database: db.Database,
|
||||
Name: db.Name,
|
||||
TargetDir: targetDir,
|
||||
FileName: fileName,
|
||||
|
||||
Task: task,
|
||||
Timeout: 300,
|
||||
}
|
||||
return cli.Backup(backupInfo)
|
||||
if err := cli.Backup(backupInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(secret) != 0 {
|
||||
return files.OpensslEncrypt(path.Join(targetDir, fileName), secret)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (u *BackupService) RedisBackup(req dto.CommonBackup) error {
|
||||
@@ -98,9 +97,8 @@ func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, recordID
|
||||
}
|
||||
}
|
||||
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s redis-cli -a %s --no-auth-warning save", redisInfo.ContainerName, redisInfo.Password)
|
||||
if err != nil {
|
||||
return errors.New(string(stdout))
|
||||
if err := cmd.RunDefaultBashCf("docker exec %s redis-cli -a %s --no-auth-warning save", redisInfo.ContainerName, redisInfo.Password); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if strings.HasSuffix(fileName, ".tar.gz") {
|
||||
@@ -111,16 +109,14 @@ func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, recordID
|
||||
return nil
|
||||
}
|
||||
if strings.HasSuffix(fileName, ".aof") {
|
||||
stdout1, err := cmd.RunDefaultWithStdoutBashCf("docker cp %s:/data/appendonly.aof %s/%s", redisInfo.ContainerName, backupDir, fileName)
|
||||
if err != nil {
|
||||
return errors.New(stdout1)
|
||||
if err := cmd.RunDefaultBashCf("docker cp %s:/data/appendonly.aof %s/%s", redisInfo.ContainerName, backupDir, fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
stdout1, err1 := cmd.RunDefaultWithStdoutBashCf("docker cp %s:/data/dump.rdb %s/%s", redisInfo.ContainerName, backupDir, fileName)
|
||||
if err1 != nil {
|
||||
return errors.New(stdout1)
|
||||
if err := cmd.RunDefaultBashCf("docker cp %s:/data/dump.rdb %s/%s", redisInfo.ContainerName, backupDir, fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/compose"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
@@ -179,9 +178,8 @@ func handleWebsiteRecover(website *model.Website, parentTask *task.Task, recover
|
||||
if err = fileOp.TarGzExtractPro(fmt.Sprintf("%s/%s.web.tar.gz", tmpPath, website.Alias), GetOpenrestyDir(SitesRootDir), ""); err != nil {
|
||||
return err
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker exec -i %s nginx -s reload", nginxInfo.ContainerName)
|
||||
if err != nil {
|
||||
return errors.New(stdout)
|
||||
if err := cmd.RunDefaultBashCf("docker exec -i %s nginx -s reload", nginxInfo.ContainerName); err != nil {
|
||||
return err
|
||||
}
|
||||
oldWebsite.ID = website.ID
|
||||
if err := websiteRepo.SaveWithoutCtx(&oldWebsite); err != nil {
|
||||
@@ -365,7 +363,7 @@ func recoverWebsiteDatabase(t *task.Task, dbID uint, dbType, tmpPath, websiteKey
|
||||
Name: db.MysqlName,
|
||||
DetailName: db.Name,
|
||||
File: fmt.Sprintf("%s/%s.sql.gz", tmpPath, websiteKey),
|
||||
}, t, true, ""); err != nil {
|
||||
}, t, true); err != nil {
|
||||
t.LogFailedWithErr(taskName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
+76
-90
@@ -21,14 +21,15 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/clam"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/systemctl"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/controller"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
type ClamService struct {
|
||||
serviceName string
|
||||
serviceName string
|
||||
freshClamService string
|
||||
}
|
||||
|
||||
type IClamService interface {
|
||||
@@ -56,23 +57,31 @@ func (c *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) {
|
||||
var baseInfo dto.ClamBaseInfo
|
||||
baseInfo.Version = "-"
|
||||
baseInfo.FreshVersion = "-"
|
||||
exist1, _ := systemctl.IsExist(constant.ClamServiceNameCentOs)
|
||||
if exist1 {
|
||||
c.serviceName = constant.ClamServiceNameCentOs
|
||||
baseInfo.IsExist = true
|
||||
baseInfo.IsActive, _ = systemctl.IsActive(constant.ClamServiceNameCentOs)
|
||||
|
||||
clamSvc, err := controller.LoadServiceName("clam")
|
||||
if err != nil {
|
||||
baseInfo.IsExist = false
|
||||
return baseInfo, nil
|
||||
}
|
||||
exist2, _ := systemctl.IsExist(constant.ClamServiceNameUbuntu)
|
||||
if exist2 {
|
||||
c.serviceName = constant.ClamServiceNameUbuntu
|
||||
c.serviceName = clamSvc
|
||||
exist, _ := controller.CheckExist(clamSvc)
|
||||
if exist {
|
||||
baseInfo.IsExist = true
|
||||
baseInfo.IsActive, _ = systemctl.IsActive(constant.ClamServiceNameUbuntu)
|
||||
baseInfo.IsActive, _ = controller.CheckActive(clamSvc)
|
||||
}
|
||||
freshExist, _ := systemctl.IsExist(constant.FreshClamService)
|
||||
|
||||
freshSvc, err := controller.LoadServiceName("freshclam")
|
||||
if err != nil {
|
||||
baseInfo.FreshIsExist = false
|
||||
return baseInfo, nil
|
||||
}
|
||||
c.freshClamService = freshSvc
|
||||
freshExist, _ := controller.CheckExist(freshSvc)
|
||||
if freshExist {
|
||||
baseInfo.FreshIsExist = true
|
||||
baseInfo.FreshIsActive, _ = systemctl.IsActive(constant.FreshClamService)
|
||||
baseInfo.FreshIsActive, _ = controller.CheckActive(freshSvc)
|
||||
}
|
||||
|
||||
if !cmd.Which("clamdscan") {
|
||||
baseInfo.IsActive = false
|
||||
}
|
||||
@@ -87,7 +96,7 @@ func (c *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_ = clam.CheckClamIsActive(false, clamRepo)
|
||||
_ = clam.StopAllClamJob(false, clamRepo)
|
||||
}
|
||||
if baseInfo.FreshIsActive {
|
||||
version, err := cmd.RunDefaultWithStdoutBashC("freshclam --version")
|
||||
@@ -105,15 +114,13 @@ func (c *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) {
|
||||
func (c *ClamService) Operate(operate string) error {
|
||||
switch operate {
|
||||
case "start", "restart", "stop":
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("systemctl %s %s", operate, c.serviceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s the %s failed, err: %s", operate, c.serviceName, stdout)
|
||||
if err := controller.Handle(operate, c.serviceName); err != nil {
|
||||
return fmt.Errorf("%s the %s failed, err: %s", operate, c.serviceName, err)
|
||||
}
|
||||
return nil
|
||||
case "fresh-start", "fresh-restart", "fresh-stop":
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("systemctl %s %s", strings.TrimPrefix(operate, "fresh-"), constant.FreshClamService)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s the %s failed, err: %s", operate, c.serviceName, stdout)
|
||||
if err := controller.Handle(strings.TrimPrefix(operate, "fresh-"), c.freshClamService); err != nil {
|
||||
return fmt.Errorf("%s the %s failed, err: %s", operate, c.serviceName, err)
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
@@ -306,7 +313,7 @@ func (c *ClamService) Delete(req dto.ClamDelete) error {
|
||||
}
|
||||
|
||||
func (c *ClamService) HandleOnce(id uint) error {
|
||||
if active := clam.CheckClamIsActive(true, clamRepo); !active {
|
||||
if active := clam.StopAllClamJob(true, clamRepo); !active {
|
||||
return buserr.New("ErrClamdscanNotFound")
|
||||
}
|
||||
clamItem, _ := clamRepo.Get(repo.WithByID(id))
|
||||
@@ -379,37 +386,13 @@ func (c *ClamService) LoadFile(req dto.ClamFileReq) (string, error) {
|
||||
filePath := ""
|
||||
switch req.Name {
|
||||
case "clamd":
|
||||
if c.serviceName == constant.ClamServiceNameUbuntu {
|
||||
filePath = "/etc/clamav/clamd.conf"
|
||||
} else {
|
||||
filePath = "/etc/clamd.d/scan.conf"
|
||||
}
|
||||
filePath = c.loadConfigPath("clamd")
|
||||
case "clamd-log":
|
||||
filePath = c.loadLogPath("clamd-log")
|
||||
if len(filePath) != 0 {
|
||||
break
|
||||
}
|
||||
if c.serviceName == constant.ClamServiceNameUbuntu {
|
||||
filePath = "/var/log/clamav/clamav.log"
|
||||
} else {
|
||||
filePath = "/var/log/clamd.scan"
|
||||
}
|
||||
case "freshclam":
|
||||
if c.serviceName == constant.ClamServiceNameUbuntu {
|
||||
filePath = "/etc/clamav/freshclam.conf"
|
||||
} else {
|
||||
filePath = "/etc/freshclam.conf"
|
||||
}
|
||||
filePath = c.loadConfigPath("freshclam")
|
||||
case "freshclam-log":
|
||||
filePath = c.loadLogPath("freshclam-log")
|
||||
if len(filePath) != 0 {
|
||||
break
|
||||
}
|
||||
if c.serviceName == constant.ClamServiceNameUbuntu {
|
||||
filePath = "/var/log/clamav/freshclam.log"
|
||||
} else {
|
||||
filePath = "/var/log/freshclam.log"
|
||||
}
|
||||
default:
|
||||
return "", fmt.Errorf("not support such type")
|
||||
}
|
||||
@@ -432,23 +415,11 @@ func (c *ClamService) LoadFile(req dto.ClamFileReq) (string, error) {
|
||||
|
||||
func (c *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error {
|
||||
filePath := ""
|
||||
service := ""
|
||||
switch req.Name {
|
||||
case "clamd":
|
||||
if c.serviceName == constant.ClamServiceNameUbuntu {
|
||||
service = constant.ClamServiceNameUbuntu
|
||||
filePath = "/etc/clamav/clamd.conf"
|
||||
} else {
|
||||
service = constant.ClamServiceNameCentOs
|
||||
filePath = "/etc/clamd.d/scan.conf"
|
||||
}
|
||||
filePath = c.loadConfigPath("clamd")
|
||||
case "freshclam":
|
||||
if c.serviceName == constant.ClamServiceNameUbuntu {
|
||||
filePath = "/etc/clamav/freshclam.conf"
|
||||
} else {
|
||||
filePath = "/etc/freshclam.conf"
|
||||
}
|
||||
service = "clamav-freshclam.service"
|
||||
filePath = c.loadConfigPath("freshclam")
|
||||
default:
|
||||
return fmt.Errorf("not support such type")
|
||||
}
|
||||
@@ -461,50 +432,65 @@ func (c *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error {
|
||||
_, _ = write.WriteString(req.File)
|
||||
write.Flush()
|
||||
|
||||
_ = systemctl.Restart(service)
|
||||
_ = controller.HandleRestart(c.serviceName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClamService) loadLogPath(name string) string {
|
||||
confPath := ""
|
||||
if name == "clamd-log" {
|
||||
if c.serviceName == constant.ClamServiceNameUbuntu {
|
||||
confPath = "/etc/clamav/clamd.conf"
|
||||
} else {
|
||||
confPath = "/etc/clamd.d/scan.conf"
|
||||
}
|
||||
} else {
|
||||
if c.serviceName == constant.ClamServiceNameUbuntu {
|
||||
confPath = "/etc/clamav/freshclam.conf"
|
||||
} else {
|
||||
confPath = "/etc/freshclam.conf"
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(confPath); err != nil {
|
||||
return ""
|
||||
configKey := "clamd"
|
||||
searchPrefix := "LogFile "
|
||||
if name != "clamd-log" {
|
||||
configKey = "freshclam"
|
||||
searchPrefix = "UpdateLogFile "
|
||||
}
|
||||
confPath := c.loadConfigPath(configKey)
|
||||
content, err := os.ReadFile(confPath)
|
||||
if err != nil {
|
||||
global.LOG.Debugf("read config of %s failed, err: %v", configKey, err)
|
||||
return ""
|
||||
}
|
||||
lines := strings.Split(string(content), "\n")
|
||||
if name == "clamd-log" {
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "LogFile ") {
|
||||
return strings.Trim(strings.ReplaceAll(line, "LogFile ", ""), " ")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "UpdateLogFile ") {
|
||||
return strings.Trim(strings.ReplaceAll(line, "UpdateLogFile ", ""), " ")
|
||||
}
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, searchPrefix) {
|
||||
return strings.Trim(strings.ReplaceAll(line, searchPrefix, ""), " ")
|
||||
}
|
||||
}
|
||||
if configKey == "clamd" {
|
||||
if _, err := os.Stat("/var/log/clamav/clamav.log"); err == nil {
|
||||
return "/var/log/clamav/clamav.log"
|
||||
}
|
||||
if _, err := os.Stat("/var/log/clamd.scan"); err == nil {
|
||||
return "/var/log/clamd.scan"
|
||||
}
|
||||
}
|
||||
if configKey == "freshclam" {
|
||||
if _, err := os.Stat("/var/log/clamav/freshclam.log"); err == nil {
|
||||
return "/var/log/clamav/freshclam.log"
|
||||
}
|
||||
if _, err := os.Stat("/var/log/freshclam.log"); err == nil {
|
||||
return "/var/log/freshclam.log"
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *ClamService) loadConfigPath(confType string) string {
|
||||
switch confType {
|
||||
case "clamd":
|
||||
if _, err := os.Stat("/etc/clamav/clamd.conf"); err == nil {
|
||||
return "/etc/clamav/clamd.conf"
|
||||
}
|
||||
return "/etc/clamd.d/scan.conf"
|
||||
case "freshclam":
|
||||
if _, err := os.Stat("/etc/clamav/freshclam.conf"); err == nil {
|
||||
return "/etc/clamav/freshclam.conf"
|
||||
}
|
||||
return "/etc/freshclam.conf"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func handleAlert(infectedFiles, clamName string, clamId uint) {
|
||||
itemInfected, _ := strconv.Atoi(strings.TrimSpace(infectedFiles))
|
||||
if itemInfected < 0 {
|
||||
|
||||
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/jinzhu/copier"
|
||||
@@ -12,8 +13,9 @@ type ComposeTemplateService struct{}
|
||||
type IComposeTemplateService interface {
|
||||
List() ([]dto.ComposeTemplateInfo, error)
|
||||
SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error)
|
||||
Create(composeDto dto.ComposeTemplateCreate) error
|
||||
Create(req dto.ComposeTemplateCreate) error
|
||||
Update(id uint, upMap map[string]interface{}) error
|
||||
Batch(req dto.ComposeTemplateBatch) error
|
||||
Delete(ids []uint) error
|
||||
}
|
||||
|
||||
@@ -64,6 +66,22 @@ func (u *ComposeTemplateService) Create(composeDto dto.ComposeTemplateCreate) er
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ComposeTemplateService) Batch(req dto.ComposeTemplateBatch) error {
|
||||
for _, item := range req.Templates {
|
||||
template, _ := composeRepo.Get(repo.WithByName(item.Name))
|
||||
if template.ID == 0 {
|
||||
if err := composeRepo.Create(&model.ComposeTemplate{Name: item.Name, Content: item.Content, Description: item.Description}); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := composeRepo.Update(template.ID, map[string]interface{}{"content": item.Content, "description": item.Description}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ComposeTemplateService) Delete(ids []uint) error {
|
||||
if len(ids) == 1 {
|
||||
compose, _ := composeRepo.Get(repo.WithByID(ids[0]))
|
||||
|
||||
+239
-161
@@ -12,7 +12,6 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -34,6 +33,8 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/re"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/build"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
@@ -69,6 +70,7 @@ type IContainerService interface {
|
||||
ContainerUpgrade(req dto.ContainerUpgrade) error
|
||||
ContainerInfo(req dto.OperationWithName) (*dto.ContainerOperate, error)
|
||||
ContainerListStats() ([]dto.ContainerListStats, error)
|
||||
ContainerItemStats(req dto.OperationWithName) (dto.ContainerItemStats, error)
|
||||
LoadResourceLimit() (*dto.ResourceLimit, error)
|
||||
ContainerRename(req dto.ContainerRename) error
|
||||
ContainerCommit(req dto.ContainerCommit) error
|
||||
@@ -95,18 +97,12 @@ func NewIContainerService() IContainerService {
|
||||
}
|
||||
|
||||
func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, error) {
|
||||
var (
|
||||
records []container.Summary
|
||||
list []container.Summary
|
||||
)
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer client.Close()
|
||||
options := container.ListOptions{
|
||||
All: true,
|
||||
}
|
||||
options := container.ListOptions{All: true}
|
||||
if len(req.Filters) != 0 {
|
||||
options.Filters = filters.NewArgs()
|
||||
options.Filters.Add("label", req.Filters)
|
||||
@@ -115,112 +111,32 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if req.ExcludeAppStore {
|
||||
for _, item := range containers {
|
||||
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
|
||||
continue
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
} else {
|
||||
list = containers
|
||||
}
|
||||
records := searchWithFilter(req, containers)
|
||||
|
||||
if len(req.Name) != 0 {
|
||||
length, count := len(list), 0
|
||||
for count < length {
|
||||
if !strings.Contains(list[count].Names[0][1:], req.Name) && !strings.Contains(list[count].Image, req.Name) {
|
||||
list = append(list[:count], list[(count+1):]...)
|
||||
length--
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
if req.State != "all" {
|
||||
length, count := len(list), 0
|
||||
for count < length {
|
||||
if list[count].State != req.State {
|
||||
list = append(list[:count], list[(count+1):]...)
|
||||
length--
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
switch req.OrderBy {
|
||||
case "name":
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
if req.Order == constant.OrderAsc {
|
||||
return list[i].Names[0][1:] < list[j].Names[0][1:]
|
||||
}
|
||||
return list[i].Names[0][1:] > list[j].Names[0][1:]
|
||||
})
|
||||
default:
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
if req.Order == constant.OrderAsc {
|
||||
return list[i].Created < list[j].Created
|
||||
}
|
||||
return list[i].Created > list[j].Created
|
||||
})
|
||||
}
|
||||
|
||||
total, start, end := len(list), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||
var backData []dto.ContainerInfo
|
||||
total, start, end := len(records), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||
if start > total {
|
||||
records = make([]container.Summary, 0)
|
||||
backData = make([]dto.ContainerInfo, 0)
|
||||
} else {
|
||||
if end >= total {
|
||||
end = total
|
||||
}
|
||||
records = list[start:end]
|
||||
backData = records[start:end]
|
||||
}
|
||||
|
||||
backDatas := make([]dto.ContainerInfo, len(records))
|
||||
for i := 0; i < len(records); i++ {
|
||||
item := records[i]
|
||||
IsFromCompose := false
|
||||
if _, ok := item.Labels[composeProjectLabel]; ok {
|
||||
IsFromCompose = true
|
||||
}
|
||||
IsFromApp := false
|
||||
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
|
||||
IsFromApp = true
|
||||
}
|
||||
|
||||
exposePorts := transPortToStr(records[i].Ports)
|
||||
info := dto.ContainerInfo{
|
||||
ContainerID: item.ID,
|
||||
CreateTime: time.Unix(item.Created, 0).Format(constant.DateTimeLayout),
|
||||
Name: item.Names[0][1:],
|
||||
ImageId: strings.Split(item.ImageID, ":")[1],
|
||||
ImageName: item.Image,
|
||||
State: item.State,
|
||||
RunTime: item.Status,
|
||||
Ports: exposePorts,
|
||||
IsFromApp: IsFromApp,
|
||||
IsFromCompose: IsFromCompose,
|
||||
}
|
||||
install, _ := appInstallRepo.GetFirst(appInstallRepo.WithContainerName(info.Name))
|
||||
for i := 0; i < len(backData); i++ {
|
||||
install, _ := appInstallRepo.GetFirst(appInstallRepo.WithContainerName(backData[i].Name))
|
||||
if install.ID > 0 {
|
||||
info.AppInstallName = install.Name
|
||||
info.AppName = install.App.Name
|
||||
backData[i].AppInstallName = install.Name
|
||||
backData[i].AppName = install.App.Name
|
||||
websites, _ := websiteRepo.GetBy(websiteRepo.WithAppInstallId(install.ID))
|
||||
for _, website := range websites {
|
||||
info.Websites = append(info.Websites, website.PrimaryDomain)
|
||||
backData[i].Websites = append(backData[i].Websites, website.PrimaryDomain)
|
||||
}
|
||||
}
|
||||
backDatas[i] = info
|
||||
if item.NetworkSettings != nil && len(item.NetworkSettings.Networks) > 0 {
|
||||
networks := make([]string, 0, len(item.NetworkSettings.Networks))
|
||||
for key := range item.NetworkSettings.Networks {
|
||||
networks = append(networks, item.NetworkSettings.Networks[key].IPAddress)
|
||||
}
|
||||
sort.Strings(networks)
|
||||
backDatas[i].Network = networks
|
||||
}
|
||||
}
|
||||
|
||||
return int64(total), backDatas, nil
|
||||
return int64(total), backData, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) List() []dto.ContainerOptions {
|
||||
@@ -282,10 +198,8 @@ func (u *ContainerService) LoadStatus() (dto.ContainerStatus, error) {
|
||||
}
|
||||
defer client.Close()
|
||||
c := context.Background()
|
||||
images, _ := client.ImageList(c, image.ListOptions{})
|
||||
for _, image := range images {
|
||||
data.ImageSize += uint64(image.Size)
|
||||
}
|
||||
|
||||
images, _ := client.ImageList(c, image.ListOptions{All: true})
|
||||
data.ImageCount = len(images)
|
||||
repo, _ := imageRepoRepo.List()
|
||||
data.RepoCount = len(repo)
|
||||
@@ -296,11 +210,8 @@ func (u *ContainerService) LoadStatus() (dto.ContainerStatus, error) {
|
||||
volumes, _ := client.VolumeList(c, volume.ListOptions{})
|
||||
data.VolumeCount = len(volumes.Volumes)
|
||||
data.ComposeCount = loadComposeCount(client)
|
||||
containers, err := client.ContainerList(c, container.ListOptions{All: true})
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
data.All = uint(len(containers))
|
||||
containers, _ := client.ContainerList(c, container.ListOptions{All: true})
|
||||
data.ContainerCount = len(containers)
|
||||
for _, item := range containers {
|
||||
switch item.State {
|
||||
case "created":
|
||||
@@ -319,7 +230,50 @@ func (u *ContainerService) LoadStatus() (dto.ContainerStatus, error) {
|
||||
data.Removing++
|
||||
}
|
||||
}
|
||||
data.ContainerCount = int(data.All)
|
||||
return data, nil
|
||||
}
|
||||
func (u *ContainerService) ContainerItemStats(req dto.OperationWithName) (dto.ContainerItemStats, error) {
|
||||
var data dto.ContainerItemStats
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
if req.Name != "system" {
|
||||
defer client.Close()
|
||||
containerInfo, _, err := client.ContainerInspectWithRaw(context.Background(), req.Name, true)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
data.SizeRw = *containerInfo.SizeRw
|
||||
data.SizeRootFs = *containerInfo.SizeRootFs
|
||||
return data, nil
|
||||
}
|
||||
|
||||
usage, err := client.DiskUsage(context.Background(), types.DiskUsageOptions{})
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
for _, item := range usage.Images {
|
||||
data.ImageUsage += item.Size
|
||||
if item.Containers < 1 {
|
||||
data.ImageReclaimable += item.Size
|
||||
}
|
||||
}
|
||||
for _, item := range usage.Containers {
|
||||
data.ContainerUsage += item.SizeRw
|
||||
if item.State != "running" {
|
||||
data.ContainerReclaimable += item.SizeRw
|
||||
}
|
||||
}
|
||||
for _, item := range usage.Volumes {
|
||||
data.VolumeUsage += item.UsageData.Size
|
||||
if item.UsageData.RefCount == 0 {
|
||||
data.VolumeReclaimable += item.UsageData.Size
|
||||
}
|
||||
}
|
||||
for _, item := range usage.BuildCache {
|
||||
data.BuildCacheUsage += item.Size
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
func (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error) {
|
||||
@@ -478,6 +432,7 @@ func (u *ContainerService) Prune(req dto.ContainerPrune) error {
|
||||
pruneFilters.Add("until", "24h")
|
||||
}
|
||||
}
|
||||
taskItem.Log(i18n.GetMsgByKey("PruneStart"))
|
||||
DeletedNumber := 0
|
||||
SpaceReclaimed := 0
|
||||
switch req.PruneType {
|
||||
@@ -643,27 +598,27 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
|
||||
data.Name = strings.ReplaceAll(oldContainer.Name, "/", "")
|
||||
data.Image = oldContainer.Config.Image
|
||||
if oldContainer.NetworkSettings != nil {
|
||||
for network := range oldContainer.NetworkSettings.Networks {
|
||||
data.Network = network
|
||||
break
|
||||
for net, val := range oldContainer.NetworkSettings.Networks {
|
||||
netItem := dto.ContainerNetwork{
|
||||
Network: net,
|
||||
MacAddr: val.MacAddress,
|
||||
}
|
||||
if val.IPAMConfig != nil {
|
||||
if netItem.Network != "bridge" {
|
||||
netItem.Ipv4 = val.IPAMConfig.IPv4Address
|
||||
netItem.Ipv6 = val.IPAMConfig.IPv6Address
|
||||
}
|
||||
} else {
|
||||
if netItem.Network != "bridge" {
|
||||
netItem.Ipv4 = val.IPAddress
|
||||
}
|
||||
}
|
||||
data.Networks = append(data.Networks, netItem)
|
||||
}
|
||||
}
|
||||
|
||||
exposePorts, _ := loadPortByInspect(oldContainer.ID, client)
|
||||
data.ExposedPorts = loadContainerPortForInfo(exposePorts)
|
||||
networkSettings := oldContainer.NetworkSettings
|
||||
bridgeNetworkSettings := networkSettings.Networks[data.Network]
|
||||
if bridgeNetworkSettings.IPAMConfig != nil {
|
||||
data.MacAddr = bridgeNetworkSettings.MacAddress
|
||||
if data.Network != "bridge" {
|
||||
data.Ipv4 = bridgeNetworkSettings.IPAMConfig.IPv4Address
|
||||
data.Ipv6 = bridgeNetworkSettings.IPAMConfig.IPv6Address
|
||||
}
|
||||
} else {
|
||||
if data.Network != "bridge" {
|
||||
data.Ipv4 = bridgeNetworkSettings.IPAddress
|
||||
}
|
||||
}
|
||||
data.Hostname = oldContainer.Config.Hostname
|
||||
data.DNS = oldContainer.HostConfig.DNS
|
||||
data.DomainName = oldContainer.Config.Domainname
|
||||
@@ -895,25 +850,36 @@ func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
for _, item := range req.Names {
|
||||
global.LOG.Infof("start container %s operation %s", item, req.Operation)
|
||||
switch req.Operation {
|
||||
case constant.ContainerOpStart:
|
||||
err = client.ContainerStart(ctx, item, container.StartOptions{})
|
||||
case constant.ContainerOpStop:
|
||||
err = client.ContainerStop(ctx, item, container.StopOptions{})
|
||||
case constant.ContainerOpRestart:
|
||||
err = client.ContainerRestart(ctx, item, container.StopOptions{})
|
||||
case constant.ContainerOpKill:
|
||||
err = client.ContainerKill(ctx, item, "SIGKILL")
|
||||
case constant.ContainerOpPause:
|
||||
err = client.ContainerPause(ctx, item)
|
||||
case constant.ContainerOpUnpause:
|
||||
err = client.ContainerUnpause(ctx, item)
|
||||
case constant.ContainerOpRemove:
|
||||
err = client.ContainerRemove(ctx, item, container.RemoveOptions{RemoveVolumes: true, Force: true})
|
||||
}
|
||||
taskItem, err := task.NewTaskWithOps(strings.Join(req.Names, " "), req.Operation, task.TaskScopeContainer, req.TaskID, 1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("new task for container commit failed, err: %v", err)
|
||||
}
|
||||
|
||||
for _, item := range req.Names {
|
||||
taskItem.AddSubTask(item, func(t *task.Task) error {
|
||||
switch req.Operation {
|
||||
case constant.ContainerOpStart:
|
||||
err = client.ContainerStart(ctx, item, container.StartOptions{})
|
||||
case constant.ContainerOpStop:
|
||||
err = client.ContainerStop(ctx, item, container.StopOptions{})
|
||||
case constant.ContainerOpRestart:
|
||||
err = client.ContainerRestart(ctx, item, container.StopOptions{})
|
||||
case constant.ContainerOpKill:
|
||||
err = client.ContainerKill(ctx, item, "SIGKILL")
|
||||
case constant.ContainerOpPause:
|
||||
err = client.ContainerPause(ctx, item)
|
||||
case constant.ContainerOpUnpause:
|
||||
err = client.ContainerUnpause(ctx, item)
|
||||
case constant.ContainerOpRemove:
|
||||
err = client.ContainerRemove(ctx, item, container.RemoveOptions{RemoveVolumes: true, Force: true})
|
||||
}
|
||||
return err
|
||||
}, nil)
|
||||
}
|
||||
|
||||
go func() {
|
||||
_ = taskItem.Execute()
|
||||
}()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1132,7 +1098,7 @@ func (u *ContainerService) DownloadContainerLogs(containerType, container, since
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
var ansiRegex = regexp.MustCompile(`\x1b\[[0-9;?]*[A-Za-z]|\x1b=|\x1b>`)
|
||||
var ansiRegex = re.GetRegex(re.AnsiEscapePattern)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
cleanLine := ansiRegex.ReplaceAllString(line, "")
|
||||
@@ -1222,6 +1188,16 @@ func stringsToMap(list []string) map[string]string {
|
||||
}
|
||||
return labelMap
|
||||
}
|
||||
func stringsToMap2(list []string) map[string]*string {
|
||||
var labelMap = make(map[string]*string)
|
||||
for _, label := range list {
|
||||
if strings.Contains(label, "=") {
|
||||
sps := strings.SplitN(label, "=", 2)
|
||||
labelMap[sps[0]] = &sps[1]
|
||||
}
|
||||
}
|
||||
return labelMap
|
||||
}
|
||||
|
||||
func calculateCPUPercentUnix(stats *container.StatsResponse) float64 {
|
||||
cpuPercent := 0.0
|
||||
@@ -1460,25 +1436,25 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *conta
|
||||
config.User = req.User
|
||||
config.WorkingDir = req.WorkingDir
|
||||
|
||||
if len(req.Network) != 0 {
|
||||
switch req.Network {
|
||||
case "host", "none", "bridge":
|
||||
hostConf.NetworkMode = container.NetworkMode(req.Network)
|
||||
}
|
||||
if req.Ipv4 != "" || req.Ipv6 != "" {
|
||||
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: {
|
||||
IPAMConfig: &network.EndpointIPAMConfig{
|
||||
IPv4Address: req.Ipv4,
|
||||
IPv6Address: req.Ipv6,
|
||||
}, MacAddress: req.MacAddr}}
|
||||
} else {
|
||||
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: {}}
|
||||
if len(req.Networks) != 0 {
|
||||
networkConf.EndpointsConfig = make(map[string]*network.EndpointSettings)
|
||||
for _, item := range req.Networks {
|
||||
switch item.Network {
|
||||
case "host", "none", "bridge":
|
||||
hostConf.NetworkMode = container.NetworkMode(item.Network)
|
||||
}
|
||||
if item.Ipv4 != "" || item.Ipv6 != "" {
|
||||
networkConf.EndpointsConfig[item.Network] = &network.EndpointSettings{
|
||||
IPAMConfig: &network.EndpointIPAMConfig{
|
||||
IPv4Address: item.Ipv4,
|
||||
IPv6Address: item.Ipv6,
|
||||
}, MacAddress: item.MacAddr}
|
||||
} else {
|
||||
networkConf.EndpointsConfig[item.Network] = &network.EndpointSettings{}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if req.Ipv4 != "" || req.Ipv6 != "" {
|
||||
return nil, nil, nil, fmt.Errorf("please set up the network")
|
||||
}
|
||||
networkConf = network.NetworkingConfig{}
|
||||
return nil, nil, nil, fmt.Errorf("please set up the network")
|
||||
}
|
||||
|
||||
hostConf.Privileged = req.Privileged
|
||||
@@ -1726,3 +1702,105 @@ func loadContainerPortForInfo(itemPorts []container.Port) []dto.PortHelper {
|
||||
}
|
||||
return exposedPorts
|
||||
}
|
||||
|
||||
func searchWithFilter(req dto.PageContainer, containers []container.Summary) []dto.ContainerInfo {
|
||||
var (
|
||||
records []dto.ContainerInfo
|
||||
list []container.Summary
|
||||
)
|
||||
|
||||
if req.ExcludeAppStore {
|
||||
for _, item := range containers {
|
||||
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
|
||||
continue
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
} else {
|
||||
list = containers
|
||||
}
|
||||
|
||||
if len(req.Name) != 0 {
|
||||
length, count := len(list), 0
|
||||
for count < length {
|
||||
if !strings.Contains(list[count].Names[0][1:], req.Name) && !strings.Contains(list[count].Image, req.Name) {
|
||||
list = append(list[:count], list[(count+1):]...)
|
||||
length--
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
if req.State != "all" {
|
||||
length, count := len(list), 0
|
||||
for count < length {
|
||||
if list[count].State != req.State {
|
||||
list = append(list[:count], list[(count+1):]...)
|
||||
length--
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
switch req.OrderBy {
|
||||
case "name":
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
if req.Order == constant.OrderAsc {
|
||||
return list[i].Names[0][1:] < list[j].Names[0][1:]
|
||||
}
|
||||
return list[i].Names[0][1:] > list[j].Names[0][1:]
|
||||
})
|
||||
default:
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
if req.Order == constant.OrderAsc {
|
||||
return list[i].Created < list[j].Created
|
||||
}
|
||||
return list[i].Created > list[j].Created
|
||||
})
|
||||
}
|
||||
for _, item := range list {
|
||||
IsFromCompose := false
|
||||
if _, ok := item.Labels[composeProjectLabel]; ok {
|
||||
IsFromCompose = true
|
||||
}
|
||||
IsFromApp := false
|
||||
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
|
||||
IsFromApp = true
|
||||
}
|
||||
exposePorts := transPortToStr(item.Ports)
|
||||
info := dto.ContainerInfo{
|
||||
ContainerID: item.ID,
|
||||
CreateTime: time.Unix(item.Created, 0).Format(constant.DateTimeLayout),
|
||||
Name: item.Names[0][1:],
|
||||
Ports: exposePorts,
|
||||
ImageId: strings.Split(item.ImageID, ":")[1],
|
||||
ImageName: item.Image,
|
||||
State: item.State,
|
||||
RunTime: item.Status,
|
||||
IsFromApp: IsFromApp,
|
||||
IsFromCompose: IsFromCompose,
|
||||
}
|
||||
if item.NetworkSettings != nil && len(item.NetworkSettings.Networks) > 0 {
|
||||
networks := make([]string, 0, len(item.NetworkSettings.Networks))
|
||||
for key := range item.NetworkSettings.Networks {
|
||||
networks = append(networks, item.NetworkSettings.Networks[key].IPAddress)
|
||||
}
|
||||
sort.Strings(networks)
|
||||
info.Network = networks
|
||||
}
|
||||
records = append(records, info)
|
||||
}
|
||||
descriptions, _ := settingRepo.GetDescriptionList(repo.WithByType("container"))
|
||||
for i := 0; i < len(records); i++ {
|
||||
for _, desc := range descriptions {
|
||||
if desc.ID == records[i].ContainerID {
|
||||
records[i].Description = desc.Description
|
||||
records[i].IsPinned = desc.IsPinned
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Slice(records, func(i, j int) bool {
|
||||
return records[i].IsPinned && !records[j].IsPinned
|
||||
})
|
||||
return records
|
||||
}
|
||||
|
||||
@@ -203,14 +203,13 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error {
|
||||
}
|
||||
go func() {
|
||||
taskItem.AddSubTask(i18n.GetMsgByKey("ComposeCreate"), func(t *task.Task) error {
|
||||
cmd := getComposeCmd(req.Path, "up")
|
||||
out, err := cmd.CombinedOutput()
|
||||
taskItem.Log(i18n.GetWithName("ComposeCreateRes", string(out)))
|
||||
err := compose.UpWithTask(req.Path, t)
|
||||
t.LogWithStatus(i18n.GetMsgByKey("ComposeCreate"), err)
|
||||
if err != nil {
|
||||
_, _ = compose.Down(req.Path)
|
||||
return err
|
||||
}
|
||||
_ = composeRepo.CreateRecord(&model.Compose{Name: req.Name, Path: req.Path})
|
||||
_ = composeRepo.CreateRecord(&model.Compose{Name: strings.ToLower(req.Name), Path: req.Path})
|
||||
return nil
|
||||
}, nil)
|
||||
_ = taskItem.Execute()
|
||||
|
||||
@@ -117,10 +117,8 @@ func (u *ContainerService) CreateNetwork(req dto.NetworkCreate) error {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
var (
|
||||
ipams []network.IPAMConfig
|
||||
enableV6 bool
|
||||
)
|
||||
var ipams []network.IPAMConfig
|
||||
|
||||
if req.Ipv4 {
|
||||
var itemIpam network.IPAMConfig
|
||||
if len(req.AuxAddress) != 0 {
|
||||
@@ -141,7 +139,6 @@ func (u *ContainerService) CreateNetwork(req dto.NetworkCreate) error {
|
||||
ipams = append(ipams, itemIpam)
|
||||
}
|
||||
if req.Ipv6 {
|
||||
enableV6 = true
|
||||
var itemIpam network.IPAMConfig
|
||||
if len(req.AuxAddress) != 0 {
|
||||
itemIpam.AuxAddress = make(map[string]string)
|
||||
@@ -162,7 +159,7 @@ func (u *ContainerService) CreateNetwork(req dto.NetworkCreate) error {
|
||||
}
|
||||
|
||||
options := network.CreateOptions{
|
||||
EnableIPv6: &enableV6,
|
||||
EnableIPv6: &req.Ipv6,
|
||||
Driver: req.Driver,
|
||||
Options: stringsToMap(req.Options),
|
||||
Labels: stringsToMap(req.Labels),
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
@@ -57,7 +56,7 @@ func (u *ContainerService) PageVolume(req dto.SearchWithPage) (int64, interface{
|
||||
var volume dto.Volume
|
||||
volume.Driver = item.Driver
|
||||
volume.Mountpoint = item.Mountpoint
|
||||
volume.Name = simplifyVolumeName(item.Name)
|
||||
volume.Name = item.Name
|
||||
for key, val := range item.Labels {
|
||||
volume.Labels = append(volume.Labels, dto.VolumeOption{Key: key, Value: val})
|
||||
}
|
||||
@@ -143,16 +142,3 @@ func (u *ContainerService) CreateVolume(req dto.VolumeCreate) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func simplifyVolumeName(name string) string {
|
||||
if len(name) != 64 {
|
||||
return name
|
||||
}
|
||||
|
||||
for _, char := range name {
|
||||
if !unicode.Is(unicode.ASCII_Hex_Digit, char) {
|
||||
return name
|
||||
}
|
||||
}
|
||||
return name[:12]
|
||||
}
|
||||
|
||||
@@ -108,6 +108,9 @@ func (u *CronjobService) LoadInfo(req dto.OperateByID) (*dto.CronjobOperate, err
|
||||
if cronjob.Type == "snapshot" && len(cronjob.SnapshotRule) != 0 {
|
||||
_ = json.Unmarshal([]byte(cronjob.SnapshotRule), &item.SnapshotRule)
|
||||
}
|
||||
if cronjob.Type == "cleanLog" {
|
||||
_ = json.Unmarshal([]byte(cronjob.Config), &item.CleanLogConfig)
|
||||
}
|
||||
return &item, err
|
||||
}
|
||||
|
||||
@@ -577,6 +580,10 @@ func (u *CronjobService) Create(req dto.CronjobOperate) error {
|
||||
}
|
||||
cronjob.Spec = spec
|
||||
cronjob.EntryIDs = entryIDs
|
||||
if req.Type == "cleanLog" {
|
||||
config, _ := json.Marshal(req.CleanLogConfig)
|
||||
cronjob.Config = string(config)
|
||||
}
|
||||
if err := cronjobRepo.Create(&cronjob); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
@@ -21,7 +22,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
|
||||
@@ -32,7 +32,7 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, t
|
||||
}
|
||||
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
|
||||
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
return buserr.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
}
|
||||
for _, app := range apps {
|
||||
retry := 0
|
||||
@@ -90,7 +90,7 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
|
||||
}
|
||||
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
|
||||
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
return buserr.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
}
|
||||
for _, web := range webs {
|
||||
retry := 0
|
||||
@@ -149,7 +149,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
|
||||
}
|
||||
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
|
||||
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
return buserr.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
}
|
||||
for _, dbInfo := range dbs {
|
||||
retry := 0
|
||||
@@ -167,7 +167,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
|
||||
backupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf("tmp/database/%s/%s/%s", dbInfo.DBType, record.Name, dbInfo.Name))
|
||||
record.FileName = simplifiedFileName(fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)))
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" || cronjob.DBType == "mysql-cluster" {
|
||||
if err := doMysqlBackup(dbInfo, backupDir, record.FileName); err != nil {
|
||||
if err := doMysqlBackup(dbInfo, backupDir, record.FileName, cronjob.Secret); err != nil {
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
@@ -178,7 +178,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := doPostgresqlgBackup(dbInfo, backupDir, record.FileName); err != nil {
|
||||
if err := doPostgresqlgBackup(dbInfo, backupDir, record.FileName, cronjob.Secret, taskItem); err != nil {
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
@@ -217,7 +217,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
|
||||
func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
|
||||
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
|
||||
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
return buserr.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
}
|
||||
taskItem.AddSubTaskWithOps(task.GetTaskName(cronjob.SourceDir, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
|
||||
fileName := fmt.Sprintf("%s.tar.gz", startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(2))
|
||||
@@ -267,7 +267,7 @@ func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.T
|
||||
func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
|
||||
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
|
||||
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
return buserr.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
}
|
||||
taskItem.AddSubTaskWithOps(task.GetTaskName(i18n.GetMsgByKey("SystemLog"), task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
|
||||
nameItem := startTime.Format(constant.DateTimeSlimLayout) + common.RandStrAndNum(5)
|
||||
@@ -303,7 +303,7 @@ func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.T
|
||||
func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.JobRecords, taskItem *task.Task) error {
|
||||
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
|
||||
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
return buserr.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
}
|
||||
var record model.BackupRecord
|
||||
record.Status = constant.StatusSuccess
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
pathUtils "path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/alert_push"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
@@ -127,6 +131,10 @@ func (u *CronjobService) loadTask(cronjob *model.Cronjob, record *model.JobRecor
|
||||
err = u.handleDirectory(*cronjob, record.StartTime, taskItem)
|
||||
case "log":
|
||||
err = u.handleSystemLog(*cronjob, record.StartTime, taskItem)
|
||||
case "syncIpGroup":
|
||||
u.handleSyncIpGroup(*cronjob, taskItem)
|
||||
case "cleanLog":
|
||||
u.handleCleanLog(*cronjob, taskItem)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -138,7 +146,7 @@ func (u *CronjobService) handleShell(cronjob model.Cronjob, taskItem *task.Task)
|
||||
if len(cronjob.ContainerName) != 0 {
|
||||
scriptItem := cronjob.Script
|
||||
if cronjob.ScriptMode == "select" {
|
||||
scriptItem = path.Join("/tmp", path.Base(cronjob.Script))
|
||||
scriptItem = pathUtils.Join("/tmp", pathUtils.Base(cronjob.Script))
|
||||
if err := cmdMgr.Run("docker", "cp", cronjob.Script, cronjob.ContainerName+":"+scriptItem); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -186,10 +194,17 @@ func (u *CronjobService) handleShell(cronjob model.Cronjob, taskItem *task.Task)
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleCurl(cronjob model.Cronjob, taskItem *task.Task) {
|
||||
taskItem.AddSubTaskWithOps(i18n.GetWithName("HandleShell", cronjob.Name), func(t *task.Task) error {
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem))
|
||||
return cmdMgr.Run("curl", cronjob.URL)
|
||||
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
|
||||
urls := strings.Split(cronjob.URL, ",")
|
||||
for _, url := range urls {
|
||||
if len(strings.TrimSpace(url)) == 0 {
|
||||
continue
|
||||
}
|
||||
taskItem.AddSubTaskWithOps(i18n.GetWithName("HandleCurl", url), func(t *task.Task) error {
|
||||
taskItem.LogStart(i18n.GetWithName("HandleCurl", url))
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem))
|
||||
return cmdMgr.Run("curl", url)
|
||||
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleNtpSync(cronjob model.Cronjob, taskItem *task.Task) {
|
||||
@@ -210,10 +225,120 @@ func (u *CronjobService) handleNtpSync(cronjob model.Cronjob, taskItem *task.Tas
|
||||
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleCleanLog(cronjob model.Cronjob, taskItem *task.Task) {
|
||||
taskItem.AddSubTaskWithOps(i18n.GetWithName("CleanLog", cronjob.Name), func(t *task.Task) error {
|
||||
config := GetCleanLogConfig(cronjob)
|
||||
for _, scope := range config.Scopes {
|
||||
switch scope {
|
||||
case "website":
|
||||
websites, _ := websiteRepo.List()
|
||||
for _, website := range websites {
|
||||
curStr := i18n.GetWithName("CleanLogByName", website.PrimaryDomain)
|
||||
t.LogStart(curStr)
|
||||
accessLogPath := GetSitePath(website, SiteAccessLog)
|
||||
if err := os.Truncate(accessLogPath, 0); err != nil {
|
||||
t.LogFailedWithErr(curStr, err)
|
||||
continue
|
||||
}
|
||||
errLogPath := GetSitePath(website, SiteErrorLog)
|
||||
if err := os.Truncate(errLogPath, 0); err != nil {
|
||||
t.LogFailedWithErr(curStr, err)
|
||||
continue
|
||||
}
|
||||
t.LogSuccess(curStr)
|
||||
}
|
||||
appInstall, _ := getAppInstallByKey(constant.AppOpenresty)
|
||||
if appInstall.ID > 0 {
|
||||
curStr := i18n.GetWithName("CleanLogByName", "OpenResty")
|
||||
t.LogStart(curStr)
|
||||
accessLogPath := pathUtils.Join(appInstall.GetPath(), "log", "access.log")
|
||||
if err := os.Truncate(accessLogPath, 0); err != nil {
|
||||
t.LogFailedWithErr(curStr, err)
|
||||
}
|
||||
errLogPath := pathUtils.Join(appInstall.GetPath(), "log", "error.log")
|
||||
if err := os.Truncate(errLogPath, 0); err != nil {
|
||||
t.LogFailedWithErr(curStr, err)
|
||||
}
|
||||
t.LogSuccess(curStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleSyncIpGroup(cronjob model.Cronjob, taskItem *task.Task) {
|
||||
taskItem.AddSubTaskWithOps(i18n.GetWithName("SyncIpGroup", cronjob.Name), func(t *task.Task) error {
|
||||
appInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ipGroupDir := pathUtils.Join(appInstall.GetPath(), "1pwaf", "data", "rules", "ip_group")
|
||||
urlDir := pathUtils.Join(ipGroupDir, "ip_group_url")
|
||||
|
||||
urlsFiles, err := os.ReadDir(urlDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, file := range urlsFiles {
|
||||
if file.IsDir() || !strings.HasSuffix(file.Name(), "_url") {
|
||||
continue
|
||||
}
|
||||
urlFilePath := filepath.Join(urlDir, file.Name())
|
||||
|
||||
urlContent, err := os.ReadFile(urlFilePath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
remoteURL := strings.TrimSpace(string(urlContent))
|
||||
if remoteURL == "" {
|
||||
continue
|
||||
}
|
||||
resp, err := http.Get(remoteURL)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
ipGroupFile := strings.TrimSuffix(file.Name(), "_url")
|
||||
ipGroupPath := filepath.Join(ipGroupDir, ipGroupFile)
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
resp.Body.Close()
|
||||
taskItem.Logf("get remote ip group %s failed %s status code %d", ipGroupFile, remoteURL, resp.StatusCode)
|
||||
continue
|
||||
}
|
||||
|
||||
outFile, err := os.OpenFile(ipGroupPath, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
resp.Body.Close()
|
||||
taskItem.Logf("sync %s failed %v", ipGroupFile, err)
|
||||
continue
|
||||
}
|
||||
|
||||
writer := bufio.NewWriter(outFile)
|
||||
_, err = io.Copy(writer, resp.Body)
|
||||
if err != nil {
|
||||
outFile.Close()
|
||||
resp.Body.Close()
|
||||
taskItem.Logf("sync %s failed , write file failed %v", ipGroupFile, err)
|
||||
continue
|
||||
}
|
||||
writer.Flush()
|
||||
outFile.Close()
|
||||
resp.Body.Close()
|
||||
taskItem.LogSuccess(i18n.Get("TaskSync") + " " + ipGroupFile)
|
||||
}
|
||||
if err := opNginx(appInstall.ContainerName, constant.NginxReload); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime time.Time, taskItem *task.Task) error {
|
||||
clientMap := NewBackupClientMap([]string{fmt.Sprintf("%v", cronjob.DownloadAccountID)})
|
||||
if !clientMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
|
||||
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", clientMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
return buserr.New(i18n.GetMsgWithDetail("LoadBackupFailed", clientMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
}
|
||||
taskItem.AddSubTaskWithOps(i18n.GetWithName("CutWebsiteLog", cronjob.Name), func(t *task.Task) error {
|
||||
websites := loadWebsForJob(*cronjob)
|
||||
@@ -343,9 +468,9 @@ func (u *CronjobService) removeExpiredLog(cronjob model.Cronjob) {
|
||||
_ = os.Remove(file)
|
||||
}
|
||||
}
|
||||
_ = cronjobRepo.DeleteRecord(repo.WithByID(uint(records[i].ID)))
|
||||
_ = cronjobRepo.DeleteRecord(repo.WithByID(records[i].ID))
|
||||
_ = taskRepo.Delete(taskRepo.WithByID(records[i].TaskID))
|
||||
_ = os.Remove(path.Join(global.CONF.Base.InstallDir, "1panel/log/task/Cronjob", records[i].TaskID+".log"))
|
||||
_ = os.Remove(pathUtils.Join(global.CONF.Base.InstallDir, "1panel/log/task/Cronjob", records[i].TaskID+".log"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,3 +487,9 @@ func handleCronJobAlert(cronjob *model.Cronjob) {
|
||||
}
|
||||
_ = alert_push.PushAlert(pushAlert)
|
||||
}
|
||||
|
||||
func GetCleanLogConfig(cronJob model.Cronjob) dto.CleanLogConfig {
|
||||
config := &dto.CleanLogConfig{}
|
||||
_ = json.Unmarshal([]byte(cronJob.Config), config)
|
||||
return *config
|
||||
}
|
||||
|
||||
+109
-72
@@ -1,6 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -21,11 +22,11 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/xpu"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/controller"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/copier"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/psutil"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/shirou/gopsutil/v4/cpu"
|
||||
"github.com/shirou/gopsutil/v4/disk"
|
||||
"github.com/shirou/gopsutil/v4/host"
|
||||
"github.com/shirou/gopsutil/v4/load"
|
||||
"github.com/shirou/gopsutil/v4/mem"
|
||||
"github.com/shirou/gopsutil/v4/net"
|
||||
@@ -38,6 +39,8 @@ type IDashboardService interface {
|
||||
LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error)
|
||||
LoadCurrentInfoForNode() *dto.NodeCurrent
|
||||
LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent
|
||||
LoadTopCPU() []dto.Process
|
||||
LoadTopMem() []dto.Process
|
||||
|
||||
LoadQuickOptions() []dto.QuickJump
|
||||
ChangeQuick(req dto.ChangeQuicks) error
|
||||
@@ -53,28 +56,30 @@ func NewIDashboardService() IDashboardService {
|
||||
}
|
||||
|
||||
func (u *DashboardService) Restart(operation string) error {
|
||||
if operation != "1panel" && operation != "system" && operation != "1panel-agent" {
|
||||
switch operation {
|
||||
case "system":
|
||||
{
|
||||
go func() {
|
||||
if err := cmd.RunDefaultBashCf("%s reboot", cmd.SudoHandleCmd()); err != nil {
|
||||
global.LOG.Errorf("handle reboot failed, %v", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
case "1panel-agent":
|
||||
controller.RestartPanel(false, true, false)
|
||||
return nil
|
||||
case "1panel":
|
||||
controller.RestartPanel(true, true, false)
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("handle restart operation %s failed, err: nonsupport such operation", operation)
|
||||
}
|
||||
itemCmd := fmt.Sprintf("%s systemctl restart 1panel-agent.service && %s systemctl restart 1panel-core.service", cmd.SudoHandleCmd(), cmd.SudoHandleCmd())
|
||||
if operation == "system" {
|
||||
itemCmd = fmt.Sprintf("%s reboot", cmd.SudoHandleCmd())
|
||||
}
|
||||
if operation == "1panel-agent" {
|
||||
itemCmd = fmt.Sprintf("%s systemctl restart 1panel-agent.service", cmd.SudoHandleCmd())
|
||||
}
|
||||
go func() {
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashC(itemCmd)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("handle %s failed, err: %v", itemCmd, stdout)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *DashboardService) LoadOsInfo() (*dto.OsInfo, error) {
|
||||
var baseInfo dto.OsInfo
|
||||
hostInfo, err := host.Info()
|
||||
hostInfo, err := psutil.HOST.GetHostInfo(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -83,8 +88,9 @@ func (u *DashboardService) LoadOsInfo() (*dto.OsInfo, error) {
|
||||
baseInfo.PlatformFamily = hostInfo.PlatformFamily
|
||||
baseInfo.KernelArch = hostInfo.KernelArch
|
||||
baseInfo.KernelVersion = hostInfo.KernelVersion
|
||||
baseInfo.PrettyDistro = psutil.HOST.GetDistro()
|
||||
|
||||
diskInfo, err := disk.Usage(global.Dir.BaseDir)
|
||||
diskInfo, err := psutil.DISK.GetUsage(global.Dir.BaseDir, false)
|
||||
if err == nil {
|
||||
baseInfo.DiskSize = int64(diskInfo.Free)
|
||||
}
|
||||
@@ -101,12 +107,17 @@ func (u *DashboardService) LoadOsInfo() (*dto.OsInfo, error) {
|
||||
func (u *DashboardService) LoadCurrentInfoForNode() *dto.NodeCurrent {
|
||||
var currentInfo dto.NodeCurrent
|
||||
|
||||
currentInfo.CPUTotal, _ = cpu.Counts(true)
|
||||
totalPercent, _ := cpu.Percent(100*time.Millisecond, false)
|
||||
if len(totalPercent) == 1 {
|
||||
currentInfo.CPUUsedPercent = totalPercent[0]
|
||||
currentInfo.CPUUsed = currentInfo.CPUUsedPercent * 0.01 * float64(currentInfo.CPUTotal)
|
||||
currentInfo.CPUTotal, _ = psutil.CPUInfo.GetLogicalCores(false)
|
||||
|
||||
cpuUsedPercent, perCore, cpuDetailedPercent := psutil.CPU.GetCPUUsage()
|
||||
if len(perCore) == 0 {
|
||||
currentInfo.CPUTotal = psutil.CPU.NumCPU()
|
||||
} else {
|
||||
currentInfo.CPUTotal = len(perCore)
|
||||
}
|
||||
currentInfo.CPUUsedPercent = cpuUsedPercent
|
||||
currentInfo.CPUUsed = cpuUsedPercent * 0.01 * float64(currentInfo.CPUTotal)
|
||||
currentInfo.CPUDetailedPercent = cpuDetailedPercent
|
||||
|
||||
loadInfo, _ := load.Avg()
|
||||
currentInfo.Load1 = loadInfo.Load1
|
||||
@@ -117,7 +128,7 @@ func (u *DashboardService) LoadCurrentInfoForNode() *dto.NodeCurrent {
|
||||
memoryInfo, _ := mem.VirtualMemory()
|
||||
currentInfo.MemoryTotal = memoryInfo.Total
|
||||
currentInfo.MemoryAvailable = memoryInfo.Available
|
||||
currentInfo.MemoryUsed = memoryInfo.Used + memoryInfo.Shared
|
||||
currentInfo.MemoryUsed = memoryInfo.Used
|
||||
currentInfo.MemoryUsedPercent = memoryInfo.UsedPercent
|
||||
|
||||
swapInfo, _ := mem.SwapMemory()
|
||||
@@ -131,38 +142,39 @@ func (u *DashboardService) LoadCurrentInfoForNode() *dto.NodeCurrent {
|
||||
|
||||
func (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error) {
|
||||
var baseInfo dto.DashboardBase
|
||||
hostInfo, err := host.Info()
|
||||
hostInfo, err := psutil.HOST.GetHostInfo(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
baseInfo.Hostname = hostInfo.Hostname
|
||||
baseInfo.OS = hostInfo.OS
|
||||
baseInfo.Platform = hostInfo.Platform
|
||||
baseInfo.PlatformFamily = hostInfo.PlatformFamily
|
||||
baseInfo.PlatformVersion = hostInfo.PlatformVersion
|
||||
baseInfo.KernelArch = hostInfo.KernelArch
|
||||
baseInfo.KernelVersion = hostInfo.KernelVersion
|
||||
ss, _ := json.Marshal(hostInfo)
|
||||
baseInfo.VirtualizationSystem = string(ss)
|
||||
baseInfo.IpV4Addr = loadOutboundIP()
|
||||
httpProxy := os.Getenv("http_proxy")
|
||||
if httpProxy == "" {
|
||||
httpProxy = os.Getenv("HTTP_PROXY")
|
||||
baseInfo = dto.DashboardBase{
|
||||
Hostname: hostInfo.Hostname,
|
||||
OS: hostInfo.OS,
|
||||
Platform: hostInfo.Platform,
|
||||
PlatformFamily: hostInfo.PlatformFamily,
|
||||
PlatformVersion: hostInfo.PlatformVersion,
|
||||
PrettyDistro: psutil.HOST.GetDistro(),
|
||||
KernelArch: hostInfo.KernelArch,
|
||||
KernelVersion: hostInfo.KernelVersion,
|
||||
VirtualizationSystem: string(ss),
|
||||
IpV4Addr: loadOutboundIP(),
|
||||
SystemProxy: "noProxy",
|
||||
}
|
||||
if httpProxy != "" {
|
||||
baseInfo.SystemProxy = httpProxy
|
||||
|
||||
if proxy := cmp.Or(os.Getenv("http_proxy"), os.Getenv("HTTP_PROXY")); proxy != "" {
|
||||
baseInfo.SystemProxy = proxy
|
||||
}
|
||||
baseInfo.SystemProxy = "noProxy"
|
||||
|
||||
loadQuickJump(&baseInfo)
|
||||
|
||||
cpuInfo, err := cpu.Info()
|
||||
if err == nil {
|
||||
cpuInfo, err := psutil.CPUInfo.GetCPUInfo(false)
|
||||
if err == nil && len(cpuInfo) > 0 {
|
||||
baseInfo.CPUModelName = cpuInfo[0].ModelName
|
||||
}
|
||||
|
||||
baseInfo.CPUCores, _ = cpu.Counts(false)
|
||||
baseInfo.CPULogicalCores, _ = cpu.Counts(true)
|
||||
baseInfo.CPUCores, _ = psutil.CPUInfo.GetPhysicalCores(false)
|
||||
baseInfo.CPULogicalCores, _ = psutil.CPUInfo.GetLogicalCores(false)
|
||||
baseInfo.CPUMhz = cpuInfo[0].Mhz
|
||||
|
||||
baseInfo.CurrentInfo = *u.LoadCurrentInfo(ioOption, netOption)
|
||||
return &baseInfo, nil
|
||||
@@ -170,18 +182,22 @@ func (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto
|
||||
|
||||
func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent {
|
||||
var currentInfo dto.DashboardCurrent
|
||||
hostInfo, _ := host.Info()
|
||||
hostInfo, _ := psutil.HOST.GetHostInfo(false)
|
||||
currentInfo.Uptime = hostInfo.Uptime
|
||||
currentInfo.TimeSinceUptime = time.Now().Add(-time.Duration(hostInfo.Uptime) * time.Second).Format(constant.DateTimeLayout)
|
||||
currentInfo.TimeSinceUptime = time.Unix(int64(hostInfo.BootTime), 0).Format(constant.DateTimeLayout)
|
||||
currentInfo.Procs = hostInfo.Procs
|
||||
currentInfo.CPUTotal, _ = psutil.CPUInfo.GetLogicalCores(false)
|
||||
|
||||
currentInfo.CPUTotal, _ = cpu.Counts(true)
|
||||
totalPercent, _ := cpu.Percent(100*time.Millisecond, false)
|
||||
if len(totalPercent) == 1 {
|
||||
currentInfo.CPUUsedPercent = totalPercent[0]
|
||||
currentInfo.CPUUsed = currentInfo.CPUUsedPercent * 0.01 * float64(currentInfo.CPUTotal)
|
||||
cpuUsedPercent, perCore, cpuDetailedPercent := psutil.CPU.GetCPUUsage()
|
||||
if len(perCore) == 0 {
|
||||
currentInfo.CPUTotal = psutil.CPU.NumCPU()
|
||||
} else {
|
||||
currentInfo.CPUTotal = len(perCore)
|
||||
}
|
||||
currentInfo.CPUPercent, _ = cpu.Percent(100*time.Millisecond, true)
|
||||
currentInfo.CPUPercent = perCore
|
||||
currentInfo.CPUUsedPercent = cpuUsedPercent
|
||||
currentInfo.CPUUsed = cpuUsedPercent * 0.01 * float64(currentInfo.CPUTotal)
|
||||
currentInfo.CPUDetailedPercent = cpuDetailedPercent
|
||||
|
||||
loadInfo, _ := load.Avg()
|
||||
currentInfo.Load1 = loadInfo.Load1
|
||||
@@ -191,7 +207,7 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
|
||||
|
||||
memoryInfo, _ := mem.VirtualMemory()
|
||||
currentInfo.MemoryTotal = memoryInfo.Total
|
||||
currentInfo.MemoryUsed = memoryInfo.Used + memoryInfo.Shared
|
||||
currentInfo.MemoryUsed = memoryInfo.Used
|
||||
currentInfo.MemoryFree = memoryInfo.Free
|
||||
currentInfo.MemoryCache = memoryInfo.Cached + memoryInfo.Buffers
|
||||
currentInfo.MemoryShard = memoryInfo.Shared
|
||||
@@ -240,6 +256,7 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
|
||||
if state.Name == netOption {
|
||||
currentInfo.NetBytesSent = state.BytesSent
|
||||
currentInfo.NetBytesRecv = state.BytesRecv
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -248,6 +265,14 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
|
||||
return ¤tInfo
|
||||
}
|
||||
|
||||
func (u *DashboardService) LoadTopCPU() []dto.Process {
|
||||
return loadTopCPU()
|
||||
}
|
||||
|
||||
func (u *DashboardService) LoadTopMem() []dto.Process {
|
||||
return loadTopMem()
|
||||
}
|
||||
|
||||
func (u *DashboardService) LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher, error) {
|
||||
var data []dto.AppLauncher
|
||||
appInstalls, err := appInstallRepo.ListBy(context.Background())
|
||||
@@ -401,10 +426,11 @@ type diskInfo struct {
|
||||
func loadDiskInfo() []dto.DiskInfo {
|
||||
var datas []dto.DiskInfo
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(2 * time.Second))
|
||||
stdout, err := cmdMgr.RunWithStdoutBashC("timeout 2 df -hT -P | awk 'NR>1 && !/tmpfs|snap\\/core|udev/ {print}'")
|
||||
format := `awk 'NR>1 && !/tmpfs|snap\/core|udev/ {printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", $1, $2, $3, $4, $5, $6, $7}'`
|
||||
stdout, err := cmdMgr.RunWithStdout("bash", "-c", `timeout 2 df -hT -P | `+format)
|
||||
if err != nil {
|
||||
cmdMgr2 := cmd.NewCommandMgr(cmd.WithTimeout(1 * time.Second))
|
||||
stdout, err = cmdMgr2.RunWithStdoutBashC("timeout 1 df -lhT -P | awk 'NR>1 && !/tmpfs|snap\\/core|udev/ {print}'")
|
||||
stdout, err = cmdMgr2.RunWithStdout("bash", "-c", `timeout 1 df -lhT -P | `+format)
|
||||
if err != nil {
|
||||
return datas
|
||||
}
|
||||
@@ -414,7 +440,7 @@ func loadDiskInfo() []dto.DiskInfo {
|
||||
var mounts []diskInfo
|
||||
var excludes = []string{"/mnt/cdrom", "/boot", "/boot/efi", "/dev", "/dev/shm", "/run/lock", "/run", "/run/shm", "/run/user"}
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
fields := strings.Split(line, "\t")
|
||||
if len(fields) < 7 {
|
||||
continue
|
||||
}
|
||||
@@ -448,41 +474,52 @@ func loadDiskInfo() []dto.DiskInfo {
|
||||
)
|
||||
wg.Add(len(mounts))
|
||||
for i := 0; i < len(mounts); i++ {
|
||||
go func(timeoutCh <-chan time.Time, mount diskInfo) {
|
||||
go func(mount diskInfo) {
|
||||
defer wg.Done()
|
||||
|
||||
var itemData dto.DiskInfo
|
||||
itemData.Path = mount.Mount
|
||||
itemData.Type = mount.Type
|
||||
itemData.Device = mount.Device
|
||||
|
||||
type diskResult struct {
|
||||
state *disk.UsageStat
|
||||
err error
|
||||
}
|
||||
resultCh := make(chan diskResult, 1)
|
||||
|
||||
go func() {
|
||||
state, err := psutil.DISK.GetUsage(mount.Mount, false)
|
||||
resultCh <- diskResult{state: state, err: err}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-timeoutCh:
|
||||
case <-time.After(5 * time.Second):
|
||||
mu.Lock()
|
||||
datas = append(datas, itemData)
|
||||
mu.Unlock()
|
||||
global.LOG.Errorf("load disk info from %s failed, err: timeout", mount.Mount)
|
||||
default:
|
||||
state, err := disk.Usage(mount.Mount)
|
||||
if err != nil {
|
||||
case result := <-resultCh:
|
||||
if result.err != nil {
|
||||
mu.Lock()
|
||||
datas = append(datas, itemData)
|
||||
mu.Unlock()
|
||||
global.LOG.Errorf("load disk info from %s failed, err: %v", mount.Mount, err)
|
||||
global.LOG.Errorf("load disk info from %s failed, err: %v", mount.Mount, result.err)
|
||||
return
|
||||
}
|
||||
itemData.Total = state.Total
|
||||
itemData.Free = state.Free
|
||||
itemData.Used = state.Used
|
||||
itemData.UsedPercent = state.UsedPercent
|
||||
itemData.InodesTotal = state.InodesTotal
|
||||
itemData.InodesUsed = state.InodesUsed
|
||||
itemData.InodesFree = state.InodesFree
|
||||
itemData.InodesUsedPercent = state.InodesUsedPercent
|
||||
itemData.Total = result.state.Total
|
||||
itemData.Free = result.state.Free
|
||||
itemData.Used = result.state.Used
|
||||
itemData.UsedPercent = result.state.UsedPercent
|
||||
itemData.InodesTotal = result.state.InodesTotal
|
||||
itemData.InodesUsed = result.state.InodesUsed
|
||||
itemData.InodesFree = result.state.InodesFree
|
||||
itemData.InodesUsedPercent = result.state.InodesUsedPercent
|
||||
mu.Lock()
|
||||
datas = append(datas, itemData)
|
||||
mu.Unlock()
|
||||
}
|
||||
}(time.After(5*time.Second), mounts[i])
|
||||
}(mounts[i])
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
|
||||
@@ -122,12 +122,13 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
||||
switch req.Type {
|
||||
case constant.AppPostgresql:
|
||||
_, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: req.Timeout,
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
InitialDB: req.InitialDB,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: req.Timeout,
|
||||
})
|
||||
return err == nil
|
||||
case constant.AppRedis:
|
||||
|
||||
@@ -5,8 +5,10 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
@@ -51,7 +53,14 @@ func (u *DBCommonService) LoadDatabaseFile(req dto.OperationWithNameAndType) (st
|
||||
case "mariadb-conf":
|
||||
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/mariadb/%s/conf/my.cnf", req.Name))
|
||||
case "postgresql-conf":
|
||||
database, err := databaseRepo.Get(repo.WithByName(req.Name))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/postgresql/%s/data/postgresql.conf", req.Name))
|
||||
if strings.HasPrefix(database.Version, "18.") {
|
||||
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/postgresql/%s/data/18/docker/postgresql.conf", req.Name))
|
||||
}
|
||||
case "redis-conf":
|
||||
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/redis/%s/conf/redis.conf", req.Name))
|
||||
case "redis-cluster-conf":
|
||||
@@ -76,8 +85,13 @@ func (u *DBCommonService) UpdateConfByFile(req dto.DBConfUpdateByFile) error {
|
||||
switch req.Type {
|
||||
case constant.AppMariaDB, constant.AppMysql, constant.AppMysqlCluster:
|
||||
path = fmt.Sprintf("%s/%s/%s/conf/my.cnf", global.Dir.AppInstallDir, req.Type, app.Name)
|
||||
case constant.AppPostgresql, constant.AppPostgresqlCluster:
|
||||
case constant.AppPostgresqlCluster:
|
||||
path = fmt.Sprintf("%s/%s/%s/data/postgresql.conf", global.Dir.AppInstallDir, req.Type, app.Name)
|
||||
case constant.AppPostgresql:
|
||||
path = fmt.Sprintf("%s/%s/%s/data/postgresql.conf", global.Dir.AppInstallDir, req.Type, app.Name)
|
||||
if strings.HasPrefix(app.Version, "18.") {
|
||||
path = fmt.Sprintf("%s/%s/%s/data/18/docker/postgresql.conf", global.Dir.AppInstallDir, req.Type, app.Name)
|
||||
}
|
||||
case constant.AppRedis, constant.AppRedisCluster:
|
||||
path = fmt.Sprintf("%s/%s/%s/conf/redis.conf", global.Dir.AppInstallDir, req.Type, app.Name)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -25,6 +24,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/mysql"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/mysql/client"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/re"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
@@ -45,6 +45,7 @@ type IMysqlService interface {
|
||||
DeleteCheck(req dto.MysqlDBDeleteCheck) ([]dto.DBResource, error)
|
||||
Delete(ctx context.Context, req dto.MysqlDBDelete) error
|
||||
|
||||
LoadFormatOption(req dto.OperationWithName) []dto.MysqlFormatCollationOption
|
||||
LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlStatus, error)
|
||||
LoadVariables(req dto.OperationWithNameAndType) (*dto.MysqlVariables, error)
|
||||
LoadRemoteAccess(req dto.OperationWithNameAndType) (bool, error)
|
||||
@@ -99,7 +100,7 @@ func (u *MysqlService) ListDBOption() ([]dto.MysqlOption, error) {
|
||||
}
|
||||
|
||||
func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error) {
|
||||
if cmd.CheckIllegal(req.Name, req.Username, req.Password, req.Format, req.Permission) {
|
||||
if cmd.CheckIllegal(req.Name, req.Username, req.Password, req.Format, req.Collation, req.Permission) {
|
||||
return nil, buserr.New("ErrCmdIllegal")
|
||||
}
|
||||
|
||||
@@ -126,6 +127,7 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
|
||||
if err := cli.Create(client.CreateInfo{
|
||||
Name: req.Name,
|
||||
Format: req.Format,
|
||||
Collation: req.Collation,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Permission: req.Permission,
|
||||
@@ -584,6 +586,19 @@ func (u *MysqlService) LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlS
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) LoadFormatOption(req dto.OperationWithName) []dto.MysqlFormatCollationOption {
|
||||
defaultList := []dto.MysqlFormatCollationOption{{Format: "utf8mb4"}, {Format: "utf8mb3"}, {Format: "gbk"}, {Format: "big5"}}
|
||||
client, _, err := LoadMysqlClientByFrom(req.Name)
|
||||
if err != nil {
|
||||
return defaultList
|
||||
}
|
||||
options, err := client.LoadFormatCollation(3)
|
||||
if err != nil {
|
||||
return defaultList
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
func executeSqlForMaps(containerName, dbType, password, command string) (map[string]string, error) {
|
||||
if dbType == "mysql-cluster" {
|
||||
dbType = "mysql"
|
||||
@@ -623,7 +638,7 @@ func updateMyCnf(oldFiles []string, group string, param string, value interface{
|
||||
isOn := false
|
||||
hasGroup := false
|
||||
hasKey := false
|
||||
regItem, _ := regexp.Compile(`\[*\]`)
|
||||
regItem := re.GetRegex(re.MysqlGroupPattern)
|
||||
var newFiles []string
|
||||
i := 0
|
||||
for _, line := range oldFiles {
|
||||
|
||||
@@ -80,9 +80,9 @@ func (u *RedisService) CheckHasCli() bool {
|
||||
|
||||
func (u *RedisService) InstallCli() error {
|
||||
item := dto.ContainerOperate{
|
||||
Name: "1Panel-redis-cli-tools",
|
||||
Image: "redis:7.4.4",
|
||||
Network: "1panel-network",
|
||||
Name: "1Panel-redis-cli-tools",
|
||||
Image: "redis:7.4.4",
|
||||
Networks: []dto.ContainerNetwork{{Network: "1panel-network"}},
|
||||
}
|
||||
return NewIContainerService().ContainerCreate(item, false)
|
||||
}
|
||||
|
||||
+19
-24
@@ -2,7 +2,6 @@ package service
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
@@ -19,7 +18,9 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/controller"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ntp"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/psutil"
|
||||
"github.com/shirou/gopsutil/v4/mem"
|
||||
)
|
||||
|
||||
@@ -122,7 +123,7 @@ func (u *DeviceService) Update(key, value string) error {
|
||||
if err := ntp.UpdateSystemTimeZone(value); err != nil {
|
||||
return err
|
||||
}
|
||||
go common.RestartService(global.IsMaster, true, false)
|
||||
go controller.RestartPanel(global.IsMaster, true, false)
|
||||
case "DNS":
|
||||
if err := updateDNS(strings.Split(value, ",")); err != nil {
|
||||
return err
|
||||
@@ -131,10 +132,10 @@ func (u *DeviceService) Update(key, value string) error {
|
||||
if cmd.CheckIllegal(value) {
|
||||
return buserr.New("ErrCmdIllegal")
|
||||
}
|
||||
std, err := cmd.RunDefaultWithStdoutBashCf("%s hostnamectl set-hostname %s", cmd.SudoHandleCmd(), value)
|
||||
if err != nil {
|
||||
return errors.New(std)
|
||||
if err := cmd.RunDefaultBashCf("%s hostnamectl set-hostname %s", cmd.SudoHandleCmd(), value); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = psutil.HOST.GetHostInfo(true)
|
||||
case "Ntp", "LocalTime":
|
||||
if cmd.CheckIllegal(value) {
|
||||
return buserr.New("ErrCmdIllegal")
|
||||
@@ -222,12 +223,11 @@ func (u *DeviceService) UpdatePasswd(req dto.ChangePasswd) error {
|
||||
if cmd.CheckIllegal(req.User, req.Passwd) {
|
||||
return buserr.New("ErrCmdIllegal")
|
||||
}
|
||||
std, err := cmd.RunDefaultWithStdoutBashCf("%s echo '%s:%s' | %s chpasswd", cmd.SudoHandleCmd(), req.User, req.Passwd, cmd.SudoHandleCmd())
|
||||
if err != nil {
|
||||
if err := cmd.RunDefaultBashCf("%s echo '%s:%s' | %s chpasswd", cmd.SudoHandleCmd(), req.User, req.Passwd, cmd.SudoHandleCmd()); err != nil {
|
||||
if strings.Contains(err.Error(), "does not exist") {
|
||||
return buserr.New("ErrNotExistUser")
|
||||
}
|
||||
return errors.New(std)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -245,9 +245,8 @@ func (u *DeviceService) UpdateSwap(req dto.SwapHelper) error {
|
||||
}
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem))
|
||||
if !req.IsNew {
|
||||
std, err := cmdMgr.RunWithStdoutBashCf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("handle swapoff %s failed, err: %s", req.Path, std)
|
||||
if err := cmdMgr.RunBashCf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path); err != nil {
|
||||
return fmt.Errorf("handle swapoff %s failed, %v", req.Path, err)
|
||||
}
|
||||
}
|
||||
if req.Size == 0 {
|
||||
@@ -257,27 +256,23 @@ func (u *DeviceService) UpdateSwap(req dto.SwapHelper) error {
|
||||
return operateSwapWithFile(true, req)
|
||||
}
|
||||
taskItem.LogStart(i18n.GetMsgByKey("CreateSwap"))
|
||||
stdDD, err := cmdMgr.RunWithStdoutBashCf("%s dd if=/dev/zero of=%s bs=1024 count=%d", cmd.SudoHandleCmd(), req.Path, req.Size)
|
||||
if err != nil {
|
||||
return fmt.Errorf("handle dd %s failed, std: %s, err: %s", req.Path, stdDD, err)
|
||||
if err := cmdMgr.RunBashCf("%s dd if=/dev/zero of=%s bs=1024 count=%d", cmd.SudoHandleCmd(), req.Path, req.Size); err != nil {
|
||||
return fmt.Errorf("handle dd %s failed, %v", req.Path, err)
|
||||
}
|
||||
|
||||
taskItem.Log("chmod 0600 " + req.Path)
|
||||
stdChmod, err := cmdMgr.RunWithStdoutBashCf("%s chmod 0600 %s", cmd.SudoHandleCmd(), req.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("handle chmod 0600 %s failed,std: %s, err: %s", req.Path, stdChmod, err)
|
||||
if err := cmdMgr.RunBashCf("%s chmod 0600 %s", cmd.SudoHandleCmd(), req.Path); err != nil {
|
||||
return fmt.Errorf("handle chmod 0600 %s failed, %v", req.Path, err)
|
||||
}
|
||||
taskItem.LogStart(i18n.GetMsgByKey("FormatSwap"))
|
||||
stdMkswap, err := cmdMgr.RunWithStdoutBashCf("%s mkswap -f %s", cmd.SudoHandleCmd(), req.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("handle mkswap -f %s failed, std: %s, err: %s", req.Path, stdMkswap, err)
|
||||
if err := cmdMgr.RunBashCf("%s mkswap -f %s", cmd.SudoHandleCmd(), req.Path); err != nil {
|
||||
return fmt.Errorf("handle mkswap -f %s failed, %v", req.Path, err)
|
||||
}
|
||||
|
||||
taskItem.LogStart(i18n.GetMsgByKey("EnableSwap"))
|
||||
stdSwapon, err := cmdMgr.RunWithStdoutBashCf("%s swapon %s", cmd.SudoHandleCmd(), req.Path)
|
||||
if err != nil {
|
||||
_, _ = cmdMgr.RunWithStdoutBashCf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
|
||||
return fmt.Errorf("handle swapoff %s failed,std: %s, err: %s", req.Path, stdSwapon, err)
|
||||
if err := cmdMgr.RunBashCf("%s swapon %s", cmd.SudoHandleCmd(), req.Path); err != nil {
|
||||
_ = cmdMgr.RunBashCf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
|
||||
return fmt.Errorf("handle swapoff %s failed, %v", req.Path, err)
|
||||
}
|
||||
return operateSwapWithFile(false, req)
|
||||
}, nil)
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/controller"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/build"
|
||||
@@ -260,7 +261,7 @@ func (u *DeviceService) Clean(req []dto.Clean) {
|
||||
_ = settingRepo.Update("LastCleanData", fmt.Sprintf("%v", len(req)))
|
||||
|
||||
if restart {
|
||||
go common.RestartService(false, true, false)
|
||||
go controller.RestartPanel(false, true, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -646,11 +647,13 @@ func dropTaskLog(logDir string) {
|
||||
if err == nil {
|
||||
_ = xpackDB.Table("tampers").Where("task_id != ?", "").Select("task_id").Find(&usedTasks).Error
|
||||
}
|
||||
defer common.CloseDB(xpackDB)
|
||||
case "System":
|
||||
xpackDB, err := common.LoadDBConnByPathWithErr(path.Join(global.CONF.Base.InstallDir, "1panel/db/xpack.db"), "xpack.db")
|
||||
if err == nil {
|
||||
_ = xpackDB.Model("nodes").Where("task_id != ?", "").Select("task_id").Find(&usedTasks).Error
|
||||
}
|
||||
defer common.CloseDB(xpackDB)
|
||||
default:
|
||||
dropFileOrDir(logDir)
|
||||
_ = taskRepo.Delete(repo.WithByType(taskType))
|
||||
|
||||
@@ -28,16 +28,23 @@ func NewIDiskService() IDiskService {
|
||||
}
|
||||
|
||||
func (s *DiskService) GetCompleteDiskInfo() (*response.CompleteDiskInfo, error) {
|
||||
output, err := cmd.RunDefaultWithStdoutBashC("lsblk -P -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL,TRAN,ROTA")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute lsblk command: %v", err)
|
||||
var diskInfos []response.DiskBasicInfo
|
||||
output, err := cmd.RunDefaultWithStdoutBashC("lsblk -J -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL,TRAN,ROTA")
|
||||
if err == nil {
|
||||
diskInfos, err = parseLsblkJsonOutput(output)
|
||||
if err == nil {
|
||||
result := organizeDiskInfo(diskInfos)
|
||||
return &result, nil
|
||||
}
|
||||
}
|
||||
|
||||
diskInfos, err := parseLsblkOutput(output)
|
||||
output, err = cmd.RunDefaultWithStdoutBashC("lsblk -P -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL,TRAN,ROTA")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to run lsblk command: %v", err)
|
||||
}
|
||||
diskInfos, err = parseLsblkOutput(output)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse lsblk output: %v", err)
|
||||
}
|
||||
|
||||
result := organizeDiskInfo(diskInfos)
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
+151
-21
@@ -2,21 +2,146 @@ package service
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/re"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type LsblkRaw struct {
|
||||
Blockdevices []LsblkDevice `json:"blockdevices"`
|
||||
}
|
||||
|
||||
type LsblkDevice struct {
|
||||
Name string `json:"name"`
|
||||
Size string `json:"size"`
|
||||
Type string `json:"type"`
|
||||
Mountpoint string `json:"mountpoint"`
|
||||
Fstype string `json:"fstype"`
|
||||
Model string `json:"model"`
|
||||
Serial string `json:"serial"`
|
||||
Tran string `json:"tran"`
|
||||
Rota bool `json:"rota"`
|
||||
Children []LsblkDevice `json:"children"`
|
||||
}
|
||||
|
||||
func parseDevice(dev LsblkDevice) []response.DiskBasicInfo {
|
||||
var list []response.DiskBasicInfo
|
||||
|
||||
if strings.HasPrefix(dev.Name, "loop") || strings.HasPrefix(dev.Name, "dm-") || dev.Type == "rom" {
|
||||
return list
|
||||
}
|
||||
|
||||
if dev.Type == "lvm" {
|
||||
return list
|
||||
}
|
||||
|
||||
diskType := "Unknown"
|
||||
if dev.Type == "disk" || dev.Type == "part" {
|
||||
if dev.Rota {
|
||||
diskType = "HDD"
|
||||
} else {
|
||||
diskType = "SSD"
|
||||
}
|
||||
}
|
||||
|
||||
mountPoint := dev.Mountpoint
|
||||
filesystem := dev.Fstype
|
||||
size := dev.Size
|
||||
|
||||
var used, avail, totalSize string
|
||||
var usePercent int
|
||||
isMounted := mountPoint != ""
|
||||
isSystem := false
|
||||
|
||||
if dev.Fstype == "LVM2_member" && len(dev.Children) > 0 {
|
||||
for _, child := range dev.Children {
|
||||
if child.Type == "lvm" && child.Mountpoint != "" {
|
||||
devicePath := "/dev/mapper/" + child.Name
|
||||
totalSize, used, avail, usePercent, _ := getDiskUsageInfo(devicePath)
|
||||
|
||||
childInfo := response.DiskBasicInfo{
|
||||
Device: dev.Name,
|
||||
Size: totalSize,
|
||||
Model: dev.Model,
|
||||
DiskType: diskType,
|
||||
Filesystem: child.Fstype,
|
||||
MountPoint: child.Mountpoint,
|
||||
IsMounted: true,
|
||||
UsePercent: usePercent,
|
||||
Used: used,
|
||||
Avail: avail,
|
||||
Serial: dev.Serial,
|
||||
IsRemovable: dev.Tran == "usb",
|
||||
IsSystem: isSystemDisk(child.Mountpoint),
|
||||
}
|
||||
list = append(list, childInfo)
|
||||
}
|
||||
}
|
||||
return list
|
||||
} else if isMounted {
|
||||
isSystem = isSystemDisk(mountPoint)
|
||||
devicePath := "/dev/" + dev.Name
|
||||
totalSize, used, avail, usePercent, _ = getDiskUsageInfo(devicePath)
|
||||
if totalSize != "" {
|
||||
size = totalSize
|
||||
}
|
||||
}
|
||||
|
||||
info := response.DiskBasicInfo{
|
||||
Device: dev.Name,
|
||||
Size: size,
|
||||
Model: dev.Model,
|
||||
DiskType: diskType,
|
||||
Filesystem: filesystem,
|
||||
MountPoint: mountPoint,
|
||||
IsMounted: isMounted,
|
||||
UsePercent: usePercent,
|
||||
Used: used,
|
||||
Avail: avail,
|
||||
Serial: dev.Serial,
|
||||
IsRemovable: dev.Tran == "usb",
|
||||
IsSystem: isSystem,
|
||||
}
|
||||
|
||||
list = append(list, info)
|
||||
|
||||
for _, child := range dev.Children {
|
||||
childList := parseDevice(child)
|
||||
list = append(list, childList...)
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
func parseLsblkJsonOutput(output string) ([]response.DiskBasicInfo, error) {
|
||||
raw := &LsblkRaw{}
|
||||
if err := json.Unmarshal([]byte(output), raw); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse lsblk json output: %v", err)
|
||||
}
|
||||
var disks []response.DiskBasicInfo
|
||||
|
||||
for _, dev := range raw.Blockdevices {
|
||||
if strings.HasPrefix(dev.Name, "loop") ||
|
||||
strings.HasPrefix(dev.Name, "dm-") {
|
||||
continue
|
||||
}
|
||||
devList := parseDevice(dev)
|
||||
disks = append(disks, devList...)
|
||||
}
|
||||
return disks, nil
|
||||
}
|
||||
|
||||
func organizeDiskInfo(diskInfos []response.DiskBasicInfo) response.CompleteDiskInfo {
|
||||
var result response.CompleteDiskInfo
|
||||
var systemDisk *response.DiskInfo
|
||||
diskMap := make(map[string]*response.DiskInfo)
|
||||
partitions := make(map[string][]response.DiskBasicInfo)
|
||||
|
||||
@@ -54,7 +179,7 @@ func organizeDiskInfo(diskInfos []response.DiskBasicInfo) response.CompleteDiskI
|
||||
totalCapacity += capacity
|
||||
|
||||
if disk.IsSystem {
|
||||
systemDisk = disk
|
||||
result.SystemDisks = append(result.SystemDisks, *disk)
|
||||
} else if len(disk.Partitions) == 0 {
|
||||
result.UnpartitionedDisks = append(result.UnpartitionedDisks, disk.DiskBasicInfo)
|
||||
} else {
|
||||
@@ -62,7 +187,6 @@ func organizeDiskInfo(diskInfos []response.DiskBasicInfo) response.CompleteDiskI
|
||||
}
|
||||
}
|
||||
|
||||
result.SystemDisk = systemDisk
|
||||
result.TotalDisks = len(diskMap)
|
||||
result.TotalCapacity = totalCapacity
|
||||
|
||||
@@ -161,17 +285,25 @@ func parseLsblkOutput(output string) ([]response.DiskBasicInfo, error) {
|
||||
if fsType == "LVM2_member" {
|
||||
for _, lvmInfo := range lvmMap {
|
||||
if lvmInfo.IsMounted {
|
||||
actualMountPoint = lvmInfo.MountPoint
|
||||
actualFsType = lvmInfo.Filesystem
|
||||
actualUsed = lvmInfo.Used
|
||||
size = lvmInfo.Size
|
||||
actualAvail = lvmInfo.Avail
|
||||
actualUsePercent = lvmInfo.UsePercent
|
||||
isMounted = true
|
||||
isSystemPartition = lvmInfo.IsSystem
|
||||
break
|
||||
lvmDiskInfo := response.DiskBasicInfo{
|
||||
Device: name,
|
||||
Size: lvmInfo.Size,
|
||||
Model: model,
|
||||
DiskType: "LVM",
|
||||
IsRemovable: tran == "usb",
|
||||
IsSystem: lvmInfo.IsSystem,
|
||||
Filesystem: lvmInfo.Filesystem,
|
||||
Used: lvmInfo.Used,
|
||||
Avail: lvmInfo.Avail,
|
||||
UsePercent: lvmInfo.UsePercent,
|
||||
MountPoint: lvmInfo.MountPoint,
|
||||
IsMounted: true,
|
||||
Serial: serial,
|
||||
}
|
||||
diskInfos = append(diskInfos, lvmDiskInfo)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
info := response.DiskBasicInfo{
|
||||
@@ -205,12 +337,10 @@ func getDiskType(rota string) string {
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
var kvRe = regexp.MustCompile(`([A-Za-z0-9_]+)=("([^"\\]|\\.)*"|[^ \t]+)`)
|
||||
|
||||
func parseKeyValuePairs(line string) map[string]string {
|
||||
fields := make(map[string]string)
|
||||
|
||||
matches := kvRe.FindAllStringSubmatch(line, -1)
|
||||
matches := re.GetRegex(re.DiskKeyValuePattern).FindAllStringSubmatch(line, -1)
|
||||
for _, m := range matches {
|
||||
key := m[1]
|
||||
raw := m[2]
|
||||
@@ -394,7 +524,7 @@ func isSystemDisk(mountPoint string) bool {
|
||||
"/boot/efi",
|
||||
"/usr",
|
||||
"/var",
|
||||
"/home",
|
||||
"/etc",
|
||||
}
|
||||
|
||||
for _, sysMount := range systemMountPoints {
|
||||
|
||||
+16
-48
@@ -8,15 +8,13 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/controller"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/systemctl"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type DockerService struct{}
|
||||
@@ -236,7 +234,9 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate, withRestart bool) erro
|
||||
}
|
||||
|
||||
if withRestart {
|
||||
return restartDocker()
|
||||
if err := controller.HandleRestart("docker"); err != nil {
|
||||
return fmt.Errorf("failed to restart Docker: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -280,8 +280,8 @@ func (u *DockerService) UpdateLogOption(req dto.LogOption) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := restartDocker(); err != nil {
|
||||
return err
|
||||
if err := controller.HandleRestart("docker"); err != nil {
|
||||
return fmt.Errorf("failed to restart Docker: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -319,8 +319,8 @@ func (u *DockerService) UpdateIpv6Option(req dto.Ipv6Option) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := restartDocker(); err != nil {
|
||||
return err
|
||||
if err := controller.HandleRestart("docker"); err != nil {
|
||||
return fmt.Errorf("failed to restart Docker: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -343,25 +343,19 @@ func (u *DockerService) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := restartDocker(); err != nil {
|
||||
return err
|
||||
if err := controller.HandleRestart("docker"); err != nil {
|
||||
return fmt.Errorf("failed to restart Docker: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *DockerService) OperateDocker(req dto.DockerOperation) error {
|
||||
service := "docker"
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
dockerCmd, err := getDockerRestartCommand()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req.Operation == "stop" {
|
||||
isSocketActive, _ := systemctl.IsActive("docker.socket")
|
||||
isSocketActive, _ := controller.CheckExist("docker.socket")
|
||||
if isSocketActive {
|
||||
std, err := cmd.RunDefaultWithStdoutBashCf("%s systemctl stop docker.socket", sudo)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("handle systemctl stop docker.socket failed, err: %v", std)
|
||||
if err := controller.HandleStop("docker.socket"); err != nil {
|
||||
global.LOG.Errorf("handle stop docker.socket failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -370,9 +364,8 @@ func (u *DockerService) OperateDocker(req dto.DockerOperation) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("%s %s %s ", dockerCmd, req.Operation, service)
|
||||
if err != nil {
|
||||
return errors.New(string(stdout))
|
||||
if err := controller.Handle(req.Operation, service); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -435,32 +428,7 @@ func validateDockerConfig() error {
|
||||
return nil
|
||||
}
|
||||
if err != nil || (stdout != "" && strings.TrimSpace(stdout) != "configuration OK") {
|
||||
return fmt.Errorf("Docker configuration validation failed, err: %v", stdout)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getDockerRestartCommand() (string, error) {
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashC("which docker")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to find docker: %v", err)
|
||||
}
|
||||
dockerPath := stdout
|
||||
if strings.Contains(dockerPath, "snap") {
|
||||
return "snap", nil
|
||||
}
|
||||
return "systemctl", nil
|
||||
}
|
||||
|
||||
func restartDocker() error {
|
||||
global.LOG.Info("restart docker")
|
||||
restartCmd, err := getDockerRestartCommand()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stdout, err := cmd.NewCommandMgr(cmd.WithTimeout(3*time.Minute)).RunWithStdoutBashCf("%s restart docker", restartCmd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to restart Docker: %s", stdout)
|
||||
return fmt.Errorf("Docker configuration validation failed, %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,10 +5,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/convert"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ini_conf"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
@@ -21,6 +17,11 @@ import (
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/convert"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ini_conf"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/jinzhu/copier"
|
||||
"golang.org/x/text/encoding"
|
||||
@@ -241,6 +242,8 @@ func (f *FileService) Create(op request.FileCreate) error {
|
||||
if err := fo.LinkFile(op.LinkPath, op.Path, op.IsSymlink); err != nil {
|
||||
return err
|
||||
}
|
||||
handleDefaultOwn(op.Path)
|
||||
return nil
|
||||
}
|
||||
if err := fo.CreateFileWithMode(op.Path, fs.FileMode(mode)); err != nil {
|
||||
return err
|
||||
@@ -591,7 +594,7 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
|
||||
scope string
|
||||
logFileRes *dto.LogFileRes
|
||||
)
|
||||
if stat.Size() > 500*1024*1024 {
|
||||
if stat.Size() > files.MaxReadFileSize {
|
||||
lines, err = files.TailFromEnd(logFilePath, req.PageSize)
|
||||
isEndOfFile = true
|
||||
scope = "tail"
|
||||
|
||||
+145
-70
@@ -16,17 +16,20 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/controller"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/firewall"
|
||||
fireClient "github.com/1Panel-dev/1Panel/agent/utils/firewall/client"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/firewall/client/iptables"
|
||||
"github.com/jinzhu/copier"
|
||||
)
|
||||
|
||||
const confPath = "/etc/sysctl.conf"
|
||||
const panelSysctlPath = "/etc/sysctl.d/98-onepanel.conf"
|
||||
|
||||
type FirewallService struct{}
|
||||
|
||||
type IFirewallService interface {
|
||||
LoadBaseInfo() (dto.FirewallBaseInfo, error)
|
||||
LoadBaseInfo(tab string) (dto.FirewallBaseInfo, error)
|
||||
SearchWithPage(search dto.RuleSearch) (int64, interface{}, error)
|
||||
OperateFirewall(req dto.FirewallOperation) error
|
||||
OperatePortRule(req dto.PortRuleOperate, reload bool) error
|
||||
@@ -42,7 +45,7 @@ func NewIFirewallService() IFirewallService {
|
||||
return &FirewallService{}
|
||||
}
|
||||
|
||||
func (u *FirewallService) LoadBaseInfo() (dto.FirewallBaseInfo, error) {
|
||||
func (u *FirewallService) LoadBaseInfo(tab string) (dto.FirewallBaseInfo, error) {
|
||||
var baseInfo dto.FirewallBaseInfo
|
||||
baseInfo.Version = "-"
|
||||
baseInfo.Name = "-"
|
||||
@@ -56,18 +59,16 @@ func (u *FirewallService) LoadBaseInfo() (dto.FirewallBaseInfo, error) {
|
||||
baseInfo.Name = client.Name()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(3)
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
baseInfo.PingStatus = u.pingStatus()
|
||||
baseInfo.Version, _ = client.Version()
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
baseInfo.IsActive, _ = client.Status()
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
baseInfo.Version, _ = client.Version()
|
||||
baseInfo.IsInit, baseInfo.IsBind = iptables.LoadInitStatus(baseInfo.Name, tab)
|
||||
}()
|
||||
wg.Wait()
|
||||
return baseInfo, nil
|
||||
@@ -157,15 +158,17 @@ func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}
|
||||
if req.Type != des.Type {
|
||||
continue
|
||||
}
|
||||
if backDatas[i].Port == des.Port &&
|
||||
if backDatas[i].Port == des.DstPort &&
|
||||
req.Type == "port" &&
|
||||
backDatas[i].Protocol == des.Protocol &&
|
||||
backDatas[i].Strategy == des.Strategy &&
|
||||
backDatas[i].Address == des.Address {
|
||||
backDatas[i].Address == des.SrcIP {
|
||||
backDatas[i].ID = des.ID
|
||||
backDatas[i].Description = des.Description
|
||||
break
|
||||
}
|
||||
if req.Type == "address" && backDatas[i].Strategy == des.Strategy && backDatas[i].Address == des.Address {
|
||||
if req.Type == "address" && backDatas[i].Strategy == des.Strategy && backDatas[i].Address == des.SrcIP {
|
||||
backDatas[i].ID = des.ID
|
||||
backDatas[i].Description = des.Description
|
||||
break
|
||||
}
|
||||
@@ -211,8 +214,8 @@ func (u *FirewallService) OperateFirewall(req dto.FirewallOperation) error {
|
||||
return fmt.Errorf("not supported operation: %s", req.Operation)
|
||||
}
|
||||
if needRestartDocker && req.WithDockerRestart {
|
||||
if err := restartDocker(); err != nil {
|
||||
return err
|
||||
if err := controller.HandleRestart("docker"); err != nil {
|
||||
return fmt.Errorf("failed to restart Docker: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -223,6 +226,9 @@ func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate, reload bool)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(req.Chain) == 0 && client.Name() == "iptables" {
|
||||
req.Chain = iptables.Chain1PanelBasic
|
||||
}
|
||||
protos := strings.Split(req.Protocol, "/")
|
||||
itemAddress := strings.Split(strings.TrimSuffix(req.Address, ","), ",")
|
||||
|
||||
@@ -403,7 +409,10 @@ func (u *FirewallService) OperateAddressRule(req dto.AddrRuleOperate, reload boo
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chain := ""
|
||||
if client.Name() == "iptables" {
|
||||
chain = iptables.Chain1PanelBasic
|
||||
}
|
||||
var fireInfo fireClient.FireInfo
|
||||
if err := copier.Copy(&fireInfo, &req); err != nil {
|
||||
return err
|
||||
@@ -419,7 +428,7 @@ func (u *FirewallService) OperateAddressRule(req dto.AddrRuleOperate, reload boo
|
||||
return err
|
||||
}
|
||||
req.Address = addressList[i]
|
||||
if err := u.addAddressRecord(req); err != nil {
|
||||
if err := u.addAddressRecord(chain, req); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -458,10 +467,18 @@ func (u *FirewallService) UpdateAddrRule(req dto.AddrRuleUpdate) error {
|
||||
}
|
||||
|
||||
func (u *FirewallService) UpdateDescription(req dto.UpdateFirewallDescription) error {
|
||||
var firewall model.Firewall
|
||||
if err := copier.Copy(&firewall, &req); err != nil {
|
||||
return buserr.WithDetail("ErrStructTransform", err.Error(), nil)
|
||||
firewall := model.Firewall{
|
||||
Type: req.Type,
|
||||
Chain: req.Chain,
|
||||
SrcIP: req.SrcIP,
|
||||
DstIP: req.DstIP,
|
||||
SrcPort: req.SrcPort,
|
||||
DstPort: req.DstPort,
|
||||
Protocol: req.Protocol,
|
||||
Strategy: req.Strategy,
|
||||
Description: req.Description,
|
||||
}
|
||||
|
||||
return hostRepo.SaveFirewallRecord(&firewall)
|
||||
}
|
||||
|
||||
@@ -561,7 +578,7 @@ func (u *FirewallService) cleanUnUsedData(client firewall.FirewallClient) {
|
||||
}
|
||||
for _, item := range list {
|
||||
for i := 0; i < len(records); i++ {
|
||||
if records[i].Port == item.Port && records[i].Protocol == item.Protocol && records[i].Strategy == item.Strategy && records[i].Address == item.Address {
|
||||
if records[i].DstPort == item.Port && records[i].Protocol == item.Protocol && records[i].Strategy == item.Strategy && records[i].SrcIP == item.Address {
|
||||
records = append(records[:i], records[i+1:]...)
|
||||
}
|
||||
}
|
||||
@@ -571,62 +588,101 @@ func (u *FirewallService) cleanUnUsedData(client firewall.FirewallClient) {
|
||||
_ = hostRepo.DeleteFirewallRecordByID(record.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *FirewallService) pingStatus() string {
|
||||
if _, err := os.Stat("/etc/sysctl.conf"); err != nil {
|
||||
data, err := os.ReadFile("/proc/sys/net/ipv4/icmp_echo_ignore_all")
|
||||
if err != nil {
|
||||
return constant.StatusNone
|
||||
}
|
||||
stdout, _ := cmd.RunDefaultWithStdoutBashCf("%s sysctl -a 2>/dev/null | grep 'net.ipv4.icmp.echo_ignore_all'", cmd.SudoHandleCmd())
|
||||
if stdout == "net.ipv4.icmp_echo_ignore_all = 1\n" {
|
||||
return constant.StatusEnable
|
||||
v6Data, v6err := os.ReadFile("/proc/sys/net/ipv6/icmp/echo_ignore_all")
|
||||
if v6err != nil {
|
||||
if strings.TrimSpace(string(data)) == "1" {
|
||||
return constant.StatusEnable
|
||||
}
|
||||
return constant.StatusDisable
|
||||
} else {
|
||||
if strings.TrimSpace(string(data)) == "1" && strings.TrimSpace(string(v6Data)) == "1" {
|
||||
return constant.StatusEnable
|
||||
}
|
||||
return constant.StatusDisable
|
||||
}
|
||||
return constant.StatusDisable
|
||||
|
||||
}
|
||||
|
||||
func (u *FirewallService) updatePingStatus(enable string) error {
|
||||
lineBytes, err := os.ReadFile(confPath)
|
||||
if err != nil {
|
||||
return err
|
||||
var targetPath string
|
||||
var applyCmd string
|
||||
|
||||
if _, err := os.Stat(confPath); os.IsNotExist(err) {
|
||||
// Debian 13
|
||||
targetPath = panelSysctlPath
|
||||
applyCmd = fmt.Sprintf("%s sysctl --system", cmd.SudoHandleCmd())
|
||||
if err := cmd.RunDefaultBashCf("%s mkdir -p /etc/sysctl.d", cmd.SudoHandleCmd()); err != nil {
|
||||
return fmt.Errorf("failed to create directory /etc/sysctl.d: %v", err)
|
||||
}
|
||||
} else {
|
||||
targetPath = confPath
|
||||
applyCmd = fmt.Sprintf("%s sysctl -p", cmd.SudoHandleCmd())
|
||||
}
|
||||
files := strings.Split(string(lineBytes), "\n")
|
||||
var newFiles []string
|
||||
|
||||
lineBytes, err := os.ReadFile(targetPath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to read %s: %v", targetPath, err)
|
||||
}
|
||||
|
||||
if err := cmd.RunDefaultBashCf("echo %s | %s tee /proc/sys/net/ipv4/icmp_echo_ignore_all > /dev/null", enable, cmd.SudoHandleCmd()); err != nil {
|
||||
return fmt.Errorf("failed to apply ipv4 ping status temporarily: %v", err)
|
||||
}
|
||||
|
||||
var hasIpv6 bool
|
||||
ipv6Status, _ := cmd.RunDefaultWithStdoutBashC("sysctl -a 2>/dev/null | grep 'net.ipv6.icmp.echo_ignore_all'")
|
||||
if len(strings.ReplaceAll(ipv6Status, "\n", "")) != 0 {
|
||||
if _, err := os.Stat("/proc/sys/net/ipv6/icmp/echo_ignore_all"); err == nil {
|
||||
hasIpv6 = true
|
||||
if err := cmd.RunDefaultBashCf("echo %s | %s tee /proc/sys/net/ipv6/icmp/echo_ignore_all > /dev/null", enable, cmd.SudoHandleCmd()); err != nil {
|
||||
global.LOG.Warnf("failed to apply ipv6 ping status temporarily: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var files []string
|
||||
if err == nil {
|
||||
files = strings.Split(string(lineBytes), "\n")
|
||||
}
|
||||
|
||||
var newFiles []string
|
||||
hasIPv4Line, hasIPv6Line := false, false
|
||||
|
||||
for _, line := range files {
|
||||
if strings.Contains(line, "net/ipv4/icmp_echo_ignore_all") || strings.Contains(line, "net.ipv4.icmp_echo_ignore_all") {
|
||||
if strings.HasPrefix(strings.TrimSpace(line), "net.ipv4.icmp_echo_ignore_all") {
|
||||
newFiles = append(newFiles, "net.ipv4.icmp_echo_ignore_all="+enable)
|
||||
hasIPv4Line = true
|
||||
continue
|
||||
}
|
||||
if hasIpv6 && strings.Contains(line, "net/ipv6/icmp/echo_ignore_all") || strings.Contains(line, "net.ipv6.icmp.echo_ignore_all") {
|
||||
if strings.HasPrefix(strings.TrimSpace(line), "net.ipv6.icmp.echo_ignore_all") {
|
||||
newFiles = append(newFiles, "net.ipv6.icmp.echo_ignore_all="+enable)
|
||||
hasIPv6Line = true
|
||||
continue
|
||||
}
|
||||
newFiles = append(newFiles, line)
|
||||
}
|
||||
|
||||
if !hasIPv4Line {
|
||||
newFiles = append(newFiles, "net.ipv4.icmp_echo_ignore_all="+enable)
|
||||
}
|
||||
if !hasIPv6Line {
|
||||
if hasIpv6 && !hasIPv6Line {
|
||||
newFiles = append(newFiles, "net.ipv6.icmp.echo_ignore_all="+enable)
|
||||
}
|
||||
file, err := os.OpenFile(confPath, os.O_WRONLY|os.O_TRUNC, constant.FilePerm)
|
||||
|
||||
file, err := os.OpenFile(targetPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, constant.FilePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to open %s: %v", targetPath, err)
|
||||
}
|
||||
defer file.Close()
|
||||
_, err = file.WriteString(strings.Join(newFiles, "\n"))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
if _, err = file.WriteString(strings.Join(newFiles, "\n")); err != nil {
|
||||
return fmt.Errorf("failed to write to %s: %v", targetPath, err)
|
||||
}
|
||||
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("%s sysctl -p", cmd.SudoHandleCmd())
|
||||
if err != nil {
|
||||
return fmt.Errorf("update ping status failed, err: %v", stdout)
|
||||
if err := cmd.RunDefaultBashC(applyCmd); err != nil {
|
||||
global.LOG.Warnf("failed to apply persistent config with '%s': %v", applyCmd, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -646,7 +702,7 @@ func (u *FirewallService) addPortsBeforeStart(client firewall.FirewallClient) er
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := client.Port(fireClient.FireInfo{Port: "22", Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
|
||||
if err := client.Port(fireClient.FireInfo{Port: loadSSHPort(), Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := client.Port(fireClient.FireInfo{Port: "80", Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
|
||||
@@ -661,14 +717,21 @@ func (u *FirewallService) addPortsBeforeStart(client firewall.FirewallClient) er
|
||||
|
||||
func (u *FirewallService) addPortRecord(req dto.PortRuleOperate) error {
|
||||
if req.Operation == "remove" {
|
||||
return hostRepo.DeleteFirewallRecord("port", req.Port, req.Protocol, req.Address, req.Strategy)
|
||||
if req.ID != 0 {
|
||||
return hostRepo.DeleteFirewallRecordByID(req.ID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(req.Description) == 0 {
|
||||
return nil
|
||||
}
|
||||
if err := hostRepo.SaveFirewallRecord(&model.Firewall{
|
||||
Type: "port",
|
||||
Port: req.Port,
|
||||
Chain: req.Chain,
|
||||
DstPort: req.Port,
|
||||
Protocol: req.Protocol,
|
||||
Address: req.Address,
|
||||
SrcIP: req.Address,
|
||||
Strategy: req.Strategy,
|
||||
Description: req.Description,
|
||||
}); err != nil {
|
||||
@@ -678,13 +741,18 @@ func (u *FirewallService) addPortRecord(req dto.PortRuleOperate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *FirewallService) addAddressRecord(req dto.AddrRuleOperate) error {
|
||||
func (u *FirewallService) addAddressRecord(chain string, req dto.AddrRuleOperate) error {
|
||||
if req.Operation == "remove" {
|
||||
return hostRepo.DeleteFirewallRecord("address", "", "", req.Address, req.Strategy)
|
||||
if req.ID != 0 {
|
||||
return hostRepo.DeleteFirewallRecordByID(req.ID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := hostRepo.SaveFirewallRecord(&model.Firewall{
|
||||
Type: "address",
|
||||
Address: req.Address,
|
||||
Chain: chain,
|
||||
SrcIP: req.Address,
|
||||
Strategy: req.Strategy,
|
||||
Description: req.Description,
|
||||
}); err != nil {
|
||||
@@ -695,29 +763,36 @@ func (u *FirewallService) addAddressRecord(req dto.AddrRuleOperate) error {
|
||||
|
||||
func checkPortUsed(ports, proto string, apps []portOfApp) string {
|
||||
var portList []int
|
||||
if strings.Contains(ports, "-") || strings.Contains(ports, ",") {
|
||||
if strings.Contains(ports, "-") {
|
||||
port1, err := strconv.Atoi(strings.Split(ports, "-")[0])
|
||||
if err != nil {
|
||||
global.LOG.Errorf(" convert string %s to int failed, err: %v", strings.Split(ports, "-")[0], err)
|
||||
return ""
|
||||
}
|
||||
port2, err := strconv.Atoi(strings.Split(ports, "-")[1])
|
||||
if err != nil {
|
||||
global.LOG.Errorf(" convert string %s to int failed, err: %v", strings.Split(ports, "-")[1], err)
|
||||
return ""
|
||||
}
|
||||
for i := port1; i <= port2; i++ {
|
||||
portList = append(portList, i)
|
||||
}
|
||||
} else {
|
||||
portLists := strings.Split(ports, ",")
|
||||
for _, item := range portLists {
|
||||
portItem, _ := strconv.Atoi(item)
|
||||
portList = append(portList, portItem)
|
||||
}
|
||||
rangeSplit := ""
|
||||
if strings.Contains(ports, "-") {
|
||||
rangeSplit = "-"
|
||||
}
|
||||
if strings.Contains(ports, ":") {
|
||||
rangeSplit = ":"
|
||||
}
|
||||
if len(rangeSplit) != 0 {
|
||||
port1, err := strconv.Atoi(strings.Split(ports, rangeSplit)[0])
|
||||
if err != nil {
|
||||
global.LOG.Errorf(" convert string %s to int failed, err: %v", strings.Split(ports, rangeSplit)[0], err)
|
||||
return ""
|
||||
}
|
||||
|
||||
port2, err := strconv.Atoi(strings.Split(ports, rangeSplit)[1])
|
||||
if err != nil {
|
||||
global.LOG.Errorf(" convert string %s to int failed, err: %v", strings.Split(ports, rangeSplit)[1], err)
|
||||
return ""
|
||||
}
|
||||
for i := port1; i <= port2; i++ {
|
||||
portList = append(portList, i)
|
||||
}
|
||||
}
|
||||
if strings.Contains(ports, ",") {
|
||||
portLists := strings.Split(ports, ",")
|
||||
for _, item := range portLists {
|
||||
portItem, _ := strconv.Atoi(item)
|
||||
portList = append(portList, portItem)
|
||||
}
|
||||
}
|
||||
if len(portList) != 0 {
|
||||
var usedPorts []string
|
||||
for _, port := range portList {
|
||||
portItem := fmt.Sprintf("%v", port)
|
||||
|
||||
@@ -17,9 +17,9 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/controller"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ini_conf"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/systemctl"
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
@@ -52,9 +52,9 @@ func (h *HostToolService) GetToolStatus(req request.HostToolReq) (*response.Host
|
||||
return res, nil
|
||||
}
|
||||
supervisorConfig.IsExist = true
|
||||
serviceExist, _ := systemctl.IsExist(constant.Supervisord)
|
||||
serviceExist, _ := controller.CheckExist(constant.Supervisord)
|
||||
if !serviceExist {
|
||||
serviceExist, _ = systemctl.IsExist(constant.Supervisor)
|
||||
serviceExist, _ = controller.CheckExist(constant.Supervisor)
|
||||
if !serviceExist {
|
||||
supervisorConfig.IsExist = false
|
||||
res.Config = supervisorConfig
|
||||
@@ -76,7 +76,7 @@ func (h *HostToolService) GetToolStatus(req request.HostToolReq) (*response.Host
|
||||
_, ctlRrr := exec.LookPath("supervisorctl")
|
||||
supervisorConfig.CtlExist = ctlRrr == nil
|
||||
|
||||
active, _ := systemctl.IsActive(supervisorConfig.ServiceName)
|
||||
active, _ := controller.CheckActive(supervisorConfig.ServiceName)
|
||||
if active {
|
||||
supervisorConfig.Status = "running"
|
||||
} else {
|
||||
@@ -193,7 +193,7 @@ func (h *HostToolService) CreateToolConfig(req request.HostToolCreate) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = systemctl.Restart(req.ServiceName); err != nil {
|
||||
if err = controller.HandleRestart(req.ServiceName); err != nil {
|
||||
global.LOG.Errorf("[init] restart %s failed err %s", req.ServiceName, err.Error())
|
||||
return err
|
||||
}
|
||||
@@ -209,7 +209,7 @@ func (h *HostToolService) OperateTool(req request.HostToolReq) error {
|
||||
serviceName = serviceNameSet.Value
|
||||
}
|
||||
}
|
||||
return systemctl.Operate(req.Operate, serviceName)
|
||||
return controller.Handle(req.Operate, serviceName)
|
||||
}
|
||||
|
||||
func (h *HostToolService) OperateToolConfig(req request.HostToolConfig) (*response.HostToolConfig, error) {
|
||||
@@ -251,7 +251,7 @@ func (h *HostToolService) OperateToolConfig(req request.HostToolConfig) (*respon
|
||||
if err = fileOp.WriteFile(configPath, strings.NewReader(req.Content), fileInfo.Mode()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = systemctl.Restart(serviceName); err != nil {
|
||||
if err = controller.HandleRestart(serviceName); err != nil {
|
||||
_ = fileOp.WriteFile(configPath, bytes.NewReader(oldContent), fileInfo.Mode())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+70
-36
@@ -128,6 +128,18 @@ func (u *ImageService) Page(req dto.PageImage) (int64, interface{}, error) {
|
||||
})
|
||||
}
|
||||
|
||||
imageDescriptions, _ := settingRepo.GetDescriptionList(repo.WithByType("image"))
|
||||
for i := 0; i < len(list); i++ {
|
||||
for _, desc := range imageDescriptions {
|
||||
if "sha256:"+desc.ID == records[i].ID {
|
||||
records[i].Description = desc.Description
|
||||
records[i].IsPinned = desc.IsPinned
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Slice(records, func(i, j int) bool {
|
||||
return records[i].IsPinned && !records[j].IsPinned
|
||||
})
|
||||
total, start, end := len(records), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||
if start > total {
|
||||
backDatas = make([]dto.ImageInfo, 0)
|
||||
@@ -228,6 +240,7 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) error {
|
||||
Tags: []string{req.Name},
|
||||
Remove: true,
|
||||
Labels: stringsToMap(req.Tags),
|
||||
BuildArgs: stringsToMap2(req.Args),
|
||||
}
|
||||
taskItem, err := task.NewTaskWithOps(req.Name, task.TaskBuild, task.TaskScopeImage, req.TaskID, 1)
|
||||
if err != nil {
|
||||
@@ -308,51 +321,72 @@ func (u *ImageService) ImagePull(req dto.ImagePull) error {
|
||||
}
|
||||
|
||||
func (u *ImageService) ImageLoad(req dto.ImageLoad) error {
|
||||
file, err := os.Open(req.Path)
|
||||
taskItem, err := task.NewTaskWithOps(req.Path, task.TaskImport, task.TaskScopeImage, req.TaskID, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
res, err := client.ImageLoad(context.TODO(), file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
content, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.Contains(string(content), "Error") {
|
||||
return errors.New(string(content))
|
||||
return fmt.Errorf("new task for image import failed, err: %v", err)
|
||||
}
|
||||
taskItem.AddSubTask(i18n.GetWithName("TaskImport", req.Path), func(t *task.Task) error {
|
||||
file, err := os.Open(req.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
res, err := client.ImageLoad(context.TODO(), file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
content, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.Contains(string(content), "Error") {
|
||||
return errors.New(string(content))
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
go func() {
|
||||
_ = taskItem.Execute()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ImageService) ImageSave(req dto.ImageSave) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
taskItem, err := task.NewTaskWithOps(req.Name, task.TaskExport, task.TaskScopeImage, req.TaskID, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("new task for image export failed, err: %v", err)
|
||||
}
|
||||
defer client.Close()
|
||||
taskItem.AddSubTask(i18n.GetWithName("TaskExport", req.Name), func(t *task.Task) error {
|
||||
taskItem.Log(req.TagName)
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
out, err := client.ImageSave(context.TODO(), []string{req.TagName})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
file, err := os.OpenFile(fmt.Sprintf("%s/%s.tar", req.Path, req.Name), os.O_WRONLY|os.O_CREATE|os.O_EXCL, constant.FilePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
if _, err = io.Copy(file, out); err != nil {
|
||||
return err
|
||||
}
|
||||
out, err := client.ImageSave(context.TODO(), []string{req.TagName})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
file, err := os.OpenFile(fmt.Sprintf("%s/%s.tar", req.Path, req.Name), os.O_WRONLY|os.O_CREATE|os.O_EXCL, constant.FilePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
if _, err = io.Copy(file, out); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
go func() {
|
||||
_ = taskItem.Execute()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/controller"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -134,7 +135,7 @@ func (u *ImageRepoService) Delete(req dto.OperateByID) error {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
_ = restartDocker()
|
||||
_ = controller.HandleRestart("docker")
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
@@ -262,8 +263,8 @@ func stopBeforeUpdateRepo() error {
|
||||
if err := validateDockerConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := restartDocker(); err != nil {
|
||||
return err
|
||||
if err := controller.HandleRestart("docker"); err != nil {
|
||||
return fmt.Errorf("failed to restart Docker: %v", err)
|
||||
}
|
||||
ticker := time.NewTicker(3 * time.Second)
|
||||
defer ticker.Stop()
|
||||
@@ -275,8 +276,8 @@ func stopBeforeUpdateRepo() error {
|
||||
cancel()
|
||||
return errors.New("the docker service cannot be restarted")
|
||||
default:
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashC("systemctl is-active docker")
|
||||
if string(stdout) == "active\n" && err == nil {
|
||||
active, err := controller.CheckActive("docker")
|
||||
if active && err != nil {
|
||||
global.LOG.Info("docker restart with new conf successful!")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,386 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/firewall/client"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/firewall/client/iptables"
|
||||
)
|
||||
|
||||
type IIptablesService interface {
|
||||
Search(req dto.SearchPageWithType) (int64, interface{}, error)
|
||||
OperateRule(req dto.IptablesRuleOp, withSave bool) error
|
||||
BatchOperate(req dto.IptablesBatchOperate) error
|
||||
LoadChainStatus(req dto.OperationWithName) dto.IptablesChainStatus
|
||||
|
||||
Operate(req dto.IptablesOp) error
|
||||
}
|
||||
|
||||
type IptablesService struct{}
|
||||
|
||||
func NewIIptablesService() IIptablesService {
|
||||
return &IptablesService{}
|
||||
}
|
||||
|
||||
func (s *IptablesService) Search(req dto.SearchPageWithType) (int64, interface{}, error) {
|
||||
rules, err := iptables.ReadFilterRulesByChain(req.Type)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("failed to read iptables rules: %w", err)
|
||||
}
|
||||
var records []iptables.FilterRules
|
||||
total, start, end := len(rules), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||
if start > total {
|
||||
records = make([]iptables.FilterRules, 0)
|
||||
} else {
|
||||
if end >= total {
|
||||
end = total
|
||||
}
|
||||
records = rules[start:end]
|
||||
}
|
||||
|
||||
rulesInDB, _ := hostRepo.ListFirewallRecord(hostRepo.WithByChain(req.Type))
|
||||
|
||||
for i := 0; i < len(records); i++ {
|
||||
for _, item := range rulesInDB {
|
||||
if records[i].Strategy == item.Strategy &&
|
||||
records[i].DstIP == item.DstIP &&
|
||||
fmt.Sprintf("%v", records[i].DstPort) == item.DstPort &&
|
||||
records[i].Protocol == item.Protocol &&
|
||||
records[i].SrcIP == item.SrcIP &&
|
||||
fmt.Sprintf("%v", records[i].SrcPort) == item.SrcPort {
|
||||
records[i].ID = item.ID
|
||||
records[i].Description = item.Description
|
||||
}
|
||||
}
|
||||
}
|
||||
return int64(total), records, nil
|
||||
}
|
||||
|
||||
func (s *IptablesService) OperateRule(req dto.IptablesRuleOp, withSave bool) error {
|
||||
action := "ACCEPT"
|
||||
if req.Strategy == "drop" {
|
||||
action = "DROP"
|
||||
}
|
||||
policy := iptables.FilterRules{
|
||||
Protocol: req.Protocol,
|
||||
SrcIP: req.SrcIP,
|
||||
DstIP: req.DstIP,
|
||||
Strategy: action,
|
||||
}
|
||||
if req.SrcPort != 0 {
|
||||
policy.SrcPort = fmt.Sprintf("%v", req.SrcPort)
|
||||
}
|
||||
if req.DstPort != 0 {
|
||||
policy.DstPort = fmt.Sprintf("%v", req.DstPort)
|
||||
}
|
||||
|
||||
name := iptables.InputFileName
|
||||
if req.Chain == iptables.Chain1PanelOutput {
|
||||
name = iptables.OutputFileName
|
||||
}
|
||||
switch req.Operation {
|
||||
case "add":
|
||||
if err := s.validateRuleInput(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := iptables.AddFilterRule(req.Chain, policy); err != nil {
|
||||
return fmt.Errorf("failed to add iptables rule: %w", err)
|
||||
}
|
||||
|
||||
if len(req.Description) != 0 {
|
||||
rule := &model.Firewall{
|
||||
Chain: req.Chain,
|
||||
Protocol: req.Protocol,
|
||||
SrcIP: req.SrcIP,
|
||||
SrcPort: policy.SrcPort,
|
||||
DstIP: req.DstIP,
|
||||
DstPort: policy.DstPort,
|
||||
Strategy: req.Strategy,
|
||||
Description: req.Description,
|
||||
}
|
||||
|
||||
if err := hostRepo.SaveFirewallRecord(rule); err != nil {
|
||||
return fmt.Errorf("failed to save rule to database: %w", err)
|
||||
}
|
||||
}
|
||||
case "remove":
|
||||
if err := iptables.DeleteFilterRule(req.Chain, policy); err != nil {
|
||||
return fmt.Errorf("failed to remove iptables rule: %w", err)
|
||||
}
|
||||
if req.ID != 0 {
|
||||
if err := hostRepo.DeleteFirewallRecordByID(req.ID); err != nil {
|
||||
return fmt.Errorf("failed to delete rule from database: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !withSave {
|
||||
return nil
|
||||
}
|
||||
if err := iptables.SaveRulesToFile(iptables.FilterTab, req.Chain, name); err != nil {
|
||||
global.LOG.Errorf("persistence for %s failed, err: %v", iptables.Chain1PanelBasic, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *IptablesService) BatchOperate(req dto.IptablesBatchOperate) error {
|
||||
if len(req.Rules) == 0 {
|
||||
return errors.New("no rules to operate")
|
||||
}
|
||||
for _, rule := range req.Rules {
|
||||
if err := s.OperateRule(rule, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
chain := iptables.Chain1PanelInput
|
||||
fileName := iptables.InputFileName
|
||||
if req.Rules[0].Chain == iptables.Chain1PanelOutput {
|
||||
chain = iptables.Chain1PanelOutput
|
||||
fileName = iptables.OutputFileName
|
||||
}
|
||||
if err := iptables.SaveRulesToFile(iptables.FilterTab, chain, fileName); err != nil {
|
||||
global.LOG.Errorf("persistence for %s failed, err: %v", iptables.Chain1PanelBasic, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *IptablesService) Operate(req dto.IptablesOp) error {
|
||||
targetChain := iptables.ChainInput
|
||||
if req.Name == iptables.Chain1PanelOutput {
|
||||
targetChain = iptables.ChainOutput
|
||||
}
|
||||
switch req.Operate {
|
||||
case "init-base":
|
||||
if ok := cmd.Which("iptables"); !ok {
|
||||
return fmt.Errorf("failed to find iptables")
|
||||
}
|
||||
if err := iptables.AddChain(iptables.FilterTab, iptables.Chain1PanelBasicBefore); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.AddChain(iptables.FilterTab, iptables.Chain1PanelBasic); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.AddChain(iptables.FilterTab, iptables.Chain1PanelBasicAfter); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := initPreRules(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicBefore, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasic, 2); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter, 3); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.SaveRulesToFile(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.BasicBeforeFileName); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.SaveRulesToFile(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.BasicAfterFileName); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = settingRepo.Update("IptablesStatus", constant.StatusEnable)
|
||||
return nil
|
||||
case "init-forward":
|
||||
if err := client.EnableIptablesForward(); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = settingRepo.Update("IptablesForwardStatus", constant.StatusEnable)
|
||||
return nil
|
||||
case "init-advance":
|
||||
if err := iptables.AddChain(iptables.FilterTab, iptables.Chain1PanelInput); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.AddChain(iptables.FilterTab, iptables.Chain1PanelOutput); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainOutput, iptables.Chain1PanelOutput, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
number := loadBindNumber(iptables.Chain1PanelInput)
|
||||
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelInput, number); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = settingRepo.Update("IptablesInputStatus", constant.StatusEnable)
|
||||
_ = settingRepo.Update("IptablesOutputStatus", constant.StatusEnable)
|
||||
return nil
|
||||
case "bind-base":
|
||||
if err := initPreRules(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicBefore, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasic, 2); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter, 3); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = settingRepo.Update("IptablesStatus", constant.StatusEnable)
|
||||
return nil
|
||||
case "unbind-base":
|
||||
if err := iptables.UnbindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.UnbindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicBefore); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.UnbindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasic); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = settingRepo.Update("IptablesStatus", constant.StatusDisable)
|
||||
return nil
|
||||
case "bind":
|
||||
if err := iptables.BindChain(iptables.FilterTab, targetChain, req.Name, loadBindNumber(req.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.Name == iptables.Chain1PanelInput {
|
||||
_ = settingRepo.Update("IptablesInputStatus", constant.StatusEnable)
|
||||
}
|
||||
if req.Name == iptables.Chain1PanelOutput {
|
||||
_ = settingRepo.Update("IptablesOutputStatus", constant.StatusEnable)
|
||||
}
|
||||
return nil
|
||||
case "unbind":
|
||||
if err := iptables.UnbindChain(iptables.FilterTab, targetChain, req.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.Name == iptables.Chain1PanelInput {
|
||||
_ = settingRepo.Update("IptablesInputStatus", constant.StatusDisable)
|
||||
}
|
||||
if req.Name == iptables.Chain1PanelOutput {
|
||||
_ = settingRepo.Update("IptablesOutputStatus", constant.StatusDisable)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *IptablesService) LoadChainStatus(req dto.OperationWithName) dto.IptablesChainStatus {
|
||||
var data dto.IptablesChainStatus
|
||||
var err error
|
||||
data.DefaultStrategy, err = iptables.LoadDefaultStrategy(req.Name)
|
||||
if err != nil {
|
||||
global.LOG.Error(err)
|
||||
}
|
||||
switch req.Name {
|
||||
case iptables.Chain1PanelBasic:
|
||||
data.IsBind, err = iptables.CheckChainBind(iptables.FilterTab, iptables.ChainInput, req.Name)
|
||||
case iptables.Chain1PanelInput:
|
||||
data.IsBind, err = iptables.CheckChainBind(iptables.FilterTab, iptables.ChainInput, req.Name)
|
||||
case iptables.Chain1PanelOutput:
|
||||
data.IsBind, err = iptables.CheckChainBind(iptables.FilterTab, iptables.ChainOutput, req.Name)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (s *IptablesService) validateRuleInput(req *dto.IptablesRuleOp) error {
|
||||
if req.Protocol != "" {
|
||||
validProtocols := map[string]bool{"tcp": true, "udp": true, "icmp": true, "all": true}
|
||||
if !validProtocols[strings.ToLower(req.Protocol)] {
|
||||
return fmt.Errorf("invalid protocol: %s, must be tcp, udp, icmp or all", req.Protocol)
|
||||
}
|
||||
}
|
||||
if req.SrcIP != "" {
|
||||
if err := s.validateIPOrCIDR(req.SrcIP); err != nil {
|
||||
return fmt.Errorf("invalid source IP: %w", err)
|
||||
}
|
||||
}
|
||||
if req.DstIP != "" {
|
||||
if err := s.validateIPOrCIDR(req.DstIP); err != nil {
|
||||
return fmt.Errorf("invalid destination IP: %w", err)
|
||||
}
|
||||
}
|
||||
if req.SrcPort > 65535 {
|
||||
return fmt.Errorf("invalid source port: %d, must be between 1 and 65535", req.SrcPort)
|
||||
}
|
||||
if req.DstPort > 65535 {
|
||||
return fmt.Errorf("invalid destination port: %d, must be between 1 and 65535", req.DstPort)
|
||||
}
|
||||
if (req.SrcPort > 0 || req.DstPort > 0) && req.Protocol == "" {
|
||||
return fmt.Errorf("port specification requires protocol (tcp/udp)")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *IptablesService) validateIPOrCIDR(ipStr string) error {
|
||||
if strings.Contains(ipStr, "/") {
|
||||
_, _, err := net.ParseCIDR(ipStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid CIDR format: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip == nil {
|
||||
return fmt.Errorf("invalid IP address format")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadBindNumber(chain string) int {
|
||||
if chain == iptables.Chain1PanelOutput {
|
||||
return 1
|
||||
}
|
||||
number := 1
|
||||
if exist, _ := iptables.CheckChainExist(iptables.FilterTab, iptables.Chain1PanelBasicBefore); exist {
|
||||
number++
|
||||
}
|
||||
if exist, _ := iptables.CheckChainExist(iptables.FilterTab, iptables.Chain1PanelBasic); exist {
|
||||
number++
|
||||
}
|
||||
return number
|
||||
}
|
||||
|
||||
func initPreRules() error {
|
||||
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.IoRuleIn); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.EstablishedRule); err != nil {
|
||||
return err
|
||||
}
|
||||
panelPort := LoadPanelPort()
|
||||
if len(panelPort) == 0 {
|
||||
return errors.New("find 1panel service port failed")
|
||||
}
|
||||
ports := []string{"80", "443", panelPort, loadSSHPort()}
|
||||
for _, item := range ports {
|
||||
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, fmt.Sprintf("-p tcp -m tcp --dport %v -j ACCEPT", item)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.DropAllTcp); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.DropAllUdp); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadPanelPort() string {
|
||||
if !global.IsMaster {
|
||||
return global.CONF.Base.Port
|
||||
} else {
|
||||
var portSetting model.Setting
|
||||
_ = global.CoreDB.Where("key = ?", "ServerPort").First(&portSetting).Error
|
||||
if len(portSetting.Value) != 0 {
|
||||
return portSetting.Value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
+327
-19
@@ -4,7 +4,9 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
@@ -14,13 +16,17 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/xpu"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/psutil"
|
||||
"github.com/robfig/cron/v3"
|
||||
"github.com/shirou/gopsutil/v4/cpu"
|
||||
"github.com/shirou/gopsutil/v4/disk"
|
||||
"github.com/shirou/gopsutil/v4/load"
|
||||
"github.com/shirou/gopsutil/v4/mem"
|
||||
"github.com/shirou/gopsutil/v4/net"
|
||||
"github.com/shirou/gopsutil/v4/process"
|
||||
)
|
||||
|
||||
type MonitorService struct {
|
||||
@@ -33,6 +39,8 @@ var monitorCancel context.CancelFunc
|
||||
type IMonitorService interface {
|
||||
Run()
|
||||
LoadMonitorData(req dto.MonitorSearch) ([]dto.MonitorData, error)
|
||||
LoadGPUOptions() dto.MonitorGPUOptions
|
||||
LoadGPUMonitorData(req dto.MonitorGPUSearch) (dto.MonitorGPUData, error)
|
||||
LoadSetting() (*dto.MonitorSetting, error)
|
||||
UpdateSetting(key, value string) error
|
||||
CleanData() error
|
||||
@@ -64,12 +72,24 @@ func (m *MonitorService) LoadMonitorData(req dto.MonitorSearch) ([]dto.MonitorDa
|
||||
itemData.Param = "base"
|
||||
for _, base := range bases {
|
||||
itemData.Date = append(itemData.Date, base.CreatedAt)
|
||||
if req.Param == "all" || req.Param == "cpu" {
|
||||
var processes []dto.Process
|
||||
_ = json.Unmarshal([]byte(base.TopCPU), &processes)
|
||||
base.TopCPUItems = processes
|
||||
base.TopCPU = ""
|
||||
}
|
||||
if req.Param == "all" || req.Param == "mem" {
|
||||
var processes []dto.Process
|
||||
_ = json.Unmarshal([]byte(base.TopMem), &processes)
|
||||
base.TopMemItems = processes
|
||||
base.TopMem = ""
|
||||
}
|
||||
itemData.Value = append(itemData.Value, base)
|
||||
}
|
||||
data = append(data, itemData)
|
||||
}
|
||||
if req.Param == "all" || req.Param == "io" {
|
||||
bases, err := monitorRepo.GetIO(repo.WithByCreatedAt(req.StartTime, req.EndTime))
|
||||
bases, err := monitorRepo.GetIO(repo.WithByName(req.IO), repo.WithByCreatedAt(req.StartTime, req.EndTime))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -83,7 +103,7 @@ func (m *MonitorService) LoadMonitorData(req dto.MonitorSearch) ([]dto.MonitorDa
|
||||
data = append(data, itemData)
|
||||
}
|
||||
if req.Param == "all" || req.Param == "network" {
|
||||
bases, err := monitorRepo.GetNetwork(repo.WithByName(req.Info), repo.WithByCreatedAt(req.StartTime, req.EndTime))
|
||||
bases, err := monitorRepo.GetNetwork(repo.WithByName(req.Network), repo.WithByCreatedAt(req.StartTime, req.EndTime))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -99,6 +119,78 @@ func (m *MonitorService) LoadMonitorData(req dto.MonitorSearch) ([]dto.MonitorDa
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (m *MonitorService) LoadGPUOptions() dto.MonitorGPUOptions {
|
||||
var data dto.MonitorGPUOptions
|
||||
gpuExist, gpuClient := gpu.New()
|
||||
xpuExist, xpuClient := xpu.New()
|
||||
if !gpuExist && !xpuExist {
|
||||
return data
|
||||
}
|
||||
if gpuExist {
|
||||
data.GPUType = "gpu"
|
||||
gpuInfo, err := gpuClient.LoadGpuInfo()
|
||||
if err != nil || len(gpuInfo.GPUs) == 0 {
|
||||
global.LOG.Error("Load GPU info failed or no GPU found, err: ", err)
|
||||
return data
|
||||
}
|
||||
sort.Slice(gpuInfo.GPUs, func(i, j int) bool {
|
||||
return gpuInfo.GPUs[i].Index < gpuInfo.GPUs[j].Index
|
||||
})
|
||||
for _, item := range gpuInfo.GPUs {
|
||||
data.Options = append(data.Options, fmt.Sprintf("%d - %s", item.Index, item.ProductName))
|
||||
}
|
||||
return data
|
||||
} else {
|
||||
data.GPUType = "xpu"
|
||||
var err error
|
||||
data.Options, err = xpuClient.LoadDeviceList()
|
||||
if err != nil || len(data.Options) == 0 {
|
||||
global.LOG.Error("Load XPU info failed or no XPU found, err: ", err)
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MonitorService) LoadGPUMonitorData(req dto.MonitorGPUSearch) (dto.MonitorGPUData, error) {
|
||||
loc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
|
||||
req.StartTime = req.StartTime.In(loc)
|
||||
req.EndTime = req.EndTime.In(loc)
|
||||
var data dto.MonitorGPUData
|
||||
gpuList, err := monitorRepo.GetGPU(repo.WithByCreatedAt(req.StartTime, req.EndTime), monitorRepo.WithByProductName(req.ProductName))
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
for _, gpu := range gpuList {
|
||||
data.Date = append(data.Date, gpu.CreatedAt)
|
||||
data.GPUValue = append(data.GPUValue, gpu.GPUUtil)
|
||||
data.TemperatureValue = append(data.TemperatureValue, gpu.Temperature)
|
||||
data.PowerUsed = append(data.PowerUsed, gpu.PowerDraw)
|
||||
data.PowerTotal = append(data.PowerTotal, gpu.MaxPowerLimit)
|
||||
if gpu.MaxPowerLimit != 0 {
|
||||
data.PowerPercent = append(data.PowerPercent, gpu.PowerDraw/gpu.MaxPowerLimit*100)
|
||||
} else {
|
||||
data.PowerPercent = append(data.PowerPercent, float64(0))
|
||||
}
|
||||
|
||||
data.MemoryTotal = append(data.MemoryTotal, gpu.MemTotal)
|
||||
data.MemoryUsed = append(data.MemoryUsed, gpu.MemUsed)
|
||||
if gpu.MemTotal != 0 {
|
||||
data.MemoryPercent = append(data.MemoryPercent, gpu.MemUsed/gpu.MemTotal*100)
|
||||
} else {
|
||||
data.MemoryPercent = append(data.MemoryPercent, float64(0))
|
||||
}
|
||||
var process []dto.GPUProcess
|
||||
if err := json.Unmarshal([]byte(gpu.Processes), &process); err == nil {
|
||||
data.GPUProcesses = append(data.GPUProcesses, process)
|
||||
} else {
|
||||
data.GPUProcesses = append(data.GPUProcesses, []dto.GPUProcess{})
|
||||
}
|
||||
data.SpeedValue = append(data.SpeedValue, gpu.FanSpeed)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (m *MonitorService) LoadSetting() (*dto.MonitorSetting, error) {
|
||||
setting, err := settingRepo.GetList()
|
||||
if err != nil {
|
||||
@@ -160,17 +252,26 @@ func (m *MonitorService) CleanData() error {
|
||||
if err := global.MonitorDB.Exec("DELETE FROM monitor_networks").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
_ = global.GPUMonitorDB.Exec("DELETE FROM monitor_gpus").Error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MonitorService) Run() {
|
||||
saveGPUDataToDB()
|
||||
saveXPUDataToDB()
|
||||
var itemModel model.MonitorBase
|
||||
totalPercent, _ := cpu.Percent(3*time.Second, false)
|
||||
if len(totalPercent) == 1 {
|
||||
itemModel.Cpu = totalPercent[0]
|
||||
}
|
||||
cpuCount, _ := cpu.Counts(false)
|
||||
|
||||
topCPU := loadTopCPU()
|
||||
if len(topCPU) != 0 {
|
||||
topItemCPU, err := json.Marshal(topCPU)
|
||||
if err == nil {
|
||||
itemModel.TopCPU = string(topItemCPU)
|
||||
}
|
||||
}
|
||||
cpuCount, _ := psutil.CPUInfo.GetPhysicalCores(false)
|
||||
loadInfo, _ := load.Avg()
|
||||
itemModel.CpuLoad1 = loadInfo.Load1
|
||||
itemModel.CpuLoad5 = loadInfo.Load5
|
||||
@@ -179,8 +280,15 @@ func (m *MonitorService) Run() {
|
||||
|
||||
memoryInfo, _ := mem.VirtualMemory()
|
||||
itemModel.Memory = memoryInfo.UsedPercent
|
||||
topMem := loadTopMem()
|
||||
if len(topMem) != 0 {
|
||||
topMemItem, err := json.Marshal(topMem)
|
||||
if err == nil {
|
||||
itemModel.TopMem = string(topMemItem)
|
||||
}
|
||||
}
|
||||
|
||||
if err := settingRepo.CreateMonitorBase(itemModel); err != nil {
|
||||
if err := monitorRepo.CreateMonitorBase(itemModel); err != nil {
|
||||
global.LOG.Errorf("Insert basic monitoring data failed, err: %v", err)
|
||||
}
|
||||
|
||||
@@ -193,17 +301,26 @@ func (m *MonitorService) Run() {
|
||||
}
|
||||
storeDays, _ := strconv.Atoi(MonitorStoreDays.Value)
|
||||
timeForDelete := time.Now().AddDate(0, 0, -storeDays)
|
||||
_ = settingRepo.DelMonitorBase(timeForDelete)
|
||||
_ = settingRepo.DelMonitorIO(timeForDelete)
|
||||
_ = settingRepo.DelMonitorNet(timeForDelete)
|
||||
_ = monitorRepo.DelMonitorBase(timeForDelete)
|
||||
_ = monitorRepo.DelMonitorIO(timeForDelete)
|
||||
_ = monitorRepo.DelMonitorNet(timeForDelete)
|
||||
}
|
||||
|
||||
func (m *MonitorService) loadDiskIO() {
|
||||
ioStat, _ := disk.IOCounters()
|
||||
var diskIOList []disk.IOCountersStat
|
||||
var ioStatAll disk.IOCountersStat
|
||||
for _, io := range ioStat {
|
||||
ioStatAll.Name = "all"
|
||||
ioStatAll.ReadBytes += io.ReadBytes
|
||||
ioStatAll.WriteBytes += io.WriteBytes
|
||||
ioStatAll.ReadTime += io.ReadTime
|
||||
ioStatAll.WriteTime += io.WriteTime
|
||||
ioStatAll.WriteCount += io.WriteCount
|
||||
ioStatAll.ReadCount += io.ReadCount
|
||||
diskIOList = append(diskIOList, io)
|
||||
}
|
||||
diskIOList = append(diskIOList, ioStatAll)
|
||||
m.DiskIO <- diskIOList
|
||||
}
|
||||
|
||||
@@ -234,29 +351,29 @@ func (m *MonitorService) saveIODataToDB(ctx context.Context, interval float64) {
|
||||
var itemIO model.MonitorIO
|
||||
itemIO.Name = io1.Name
|
||||
if io2.ReadBytes != 0 && io1.ReadBytes != 0 && io2.ReadBytes > io1.ReadBytes {
|
||||
itemIO.Read = uint64(float64(io2.ReadBytes-io1.ReadBytes) / interval / 60)
|
||||
itemIO.Read = uint64(float64(io2.ReadBytes-io1.ReadBytes) / interval)
|
||||
}
|
||||
if io2.WriteBytes != 0 && io1.WriteBytes != 0 && io2.WriteBytes > io1.WriteBytes {
|
||||
itemIO.Write = uint64(float64(io2.WriteBytes-io1.WriteBytes) / interval / 60)
|
||||
itemIO.Write = uint64(float64(io2.WriteBytes-io1.WriteBytes) / interval)
|
||||
}
|
||||
|
||||
if io2.ReadCount != 0 && io1.ReadCount != 0 && io2.ReadCount > io1.ReadCount {
|
||||
itemIO.Count = uint64(float64(io2.ReadCount-io1.ReadCount) / interval / 60)
|
||||
itemIO.Count = uint64(float64(io2.ReadCount-io1.ReadCount) / interval)
|
||||
}
|
||||
writeCount := uint64(0)
|
||||
if io2.WriteCount != 0 && io1.WriteCount != 0 && io2.WriteCount > io1.WriteCount {
|
||||
writeCount = uint64(float64(io2.WriteCount-io1.WriteCount) / interval * 60)
|
||||
writeCount = uint64(float64(io2.WriteCount-io1.WriteCount) / interval)
|
||||
}
|
||||
if writeCount > itemIO.Count {
|
||||
itemIO.Count = writeCount
|
||||
}
|
||||
|
||||
if io2.ReadTime != 0 && io1.ReadTime != 0 && io2.ReadTime > io1.ReadTime {
|
||||
itemIO.Time = uint64(float64(io2.ReadTime-io1.ReadTime) / interval / 60)
|
||||
itemIO.Time = uint64(float64(io2.ReadTime-io1.ReadTime) / interval)
|
||||
}
|
||||
writeTime := uint64(0)
|
||||
if io2.WriteTime != 0 && io1.WriteTime != 0 && io2.WriteTime > io1.WriteTime {
|
||||
writeTime = uint64(float64(io2.WriteTime-io1.WriteTime) / interval / 60)
|
||||
writeTime = uint64(float64(io2.WriteTime-io1.WriteTime) / interval)
|
||||
}
|
||||
if writeTime > itemIO.Time {
|
||||
itemIO.Time = writeTime
|
||||
@@ -266,7 +383,7 @@ func (m *MonitorService) saveIODataToDB(ctx context.Context, interval float64) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := settingRepo.BatchCreateMonitorIO(ioList); err != nil {
|
||||
if err := monitorRepo.BatchCreateMonitorIO(ioList); err != nil {
|
||||
global.LOG.Errorf("Insert io monitoring data failed, err: %v", err)
|
||||
}
|
||||
m.DiskIO <- ioStat2
|
||||
@@ -294,10 +411,10 @@ func (m *MonitorService) saveNetDataToDB(ctx context.Context, interval float64)
|
||||
itemNet.Name = net1.Name
|
||||
|
||||
if net2.BytesSent != 0 && net1.BytesSent != 0 && net2.BytesSent > net1.BytesSent {
|
||||
itemNet.Up = float64(net2.BytesSent-net1.BytesSent) / 1024 / interval / 60
|
||||
itemNet.Up = float64(net2.BytesSent-net1.BytesSent) / 1024 / interval
|
||||
}
|
||||
if net2.BytesRecv != 0 && net1.BytesRecv != 0 && net2.BytesRecv > net1.BytesRecv {
|
||||
itemNet.Down = float64(net2.BytesRecv-net1.BytesRecv) / 1024 / interval / 60
|
||||
itemNet.Down = float64(net2.BytesRecv-net1.BytesRecv) / 1024 / interval
|
||||
}
|
||||
netList = append(netList, itemNet)
|
||||
break
|
||||
@@ -305,7 +422,7 @@ func (m *MonitorService) saveNetDataToDB(ctx context.Context, interval float64)
|
||||
}
|
||||
}
|
||||
|
||||
if err := settingRepo.BatchCreateMonitorNet(netList); err != nil {
|
||||
if err := monitorRepo.BatchCreateMonitorNet(netList); err != nil {
|
||||
global.LOG.Errorf("Insert network monitoring data failed, err: %v", err)
|
||||
}
|
||||
m.NetIO <- netStat2
|
||||
@@ -314,6 +431,108 @@ func (m *MonitorService) saveNetDataToDB(ctx context.Context, interval float64)
|
||||
}
|
||||
}
|
||||
|
||||
func loadTopCPU() []dto.Process {
|
||||
processes, err := process.Processes()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
top5 := make([]dto.Process, 0, 5)
|
||||
for _, p := range processes {
|
||||
percent, err := p.CPUPercent()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
minIndex := 0
|
||||
if len(top5) >= 5 {
|
||||
minCPU := top5[0].Percent
|
||||
for i := 1; i < len(top5); i++ {
|
||||
if top5[i].Percent < minCPU {
|
||||
minCPU = top5[i].Percent
|
||||
minIndex = i
|
||||
}
|
||||
}
|
||||
if percent < minCPU {
|
||||
continue
|
||||
}
|
||||
}
|
||||
name, err := p.Name()
|
||||
if err != nil {
|
||||
name = "undifine"
|
||||
}
|
||||
cmd, err := p.Cmdline()
|
||||
if err != nil {
|
||||
cmd = "undifine"
|
||||
}
|
||||
user, err := p.Username()
|
||||
if err != nil {
|
||||
user = "undifine"
|
||||
}
|
||||
if len(top5) == 5 {
|
||||
top5[minIndex] = dto.Process{Percent: percent, Pid: p.Pid, User: user, Name: name, Cmd: cmd}
|
||||
} else {
|
||||
top5 = append(top5, dto.Process{Percent: percent, Pid: p.Pid, User: user, Name: name, Cmd: cmd})
|
||||
}
|
||||
}
|
||||
sort.Slice(top5, func(i, j int) bool {
|
||||
return top5[i].Percent > top5[j].Percent
|
||||
})
|
||||
|
||||
return top5
|
||||
}
|
||||
|
||||
func loadTopMem() []dto.Process {
|
||||
processes, err := process.Processes()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
top5 := make([]dto.Process, 0, 5)
|
||||
for _, p := range processes {
|
||||
stat, err := p.MemoryInfo()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
memItem := stat.RSS
|
||||
minIndex := 0
|
||||
if len(top5) >= 5 {
|
||||
min := top5[0].Memory
|
||||
for i := 1; i < len(top5); i++ {
|
||||
if top5[i].Memory < min {
|
||||
min = top5[i].Memory
|
||||
minIndex = i
|
||||
}
|
||||
}
|
||||
if memItem < min {
|
||||
continue
|
||||
}
|
||||
}
|
||||
name, err := p.Name()
|
||||
if err != nil {
|
||||
name = "undifine"
|
||||
}
|
||||
cmd, err := p.Cmdline()
|
||||
if err != nil {
|
||||
cmd = "undifine"
|
||||
}
|
||||
user, err := p.Username()
|
||||
if err != nil {
|
||||
user = "undifine"
|
||||
}
|
||||
percent, _ := p.MemoryPercent()
|
||||
if len(top5) == 5 {
|
||||
top5[minIndex] = dto.Process{Percent: float64(percent), Pid: p.Pid, User: user, Name: name, Cmd: cmd, Memory: memItem}
|
||||
} else {
|
||||
top5 = append(top5, dto.Process{Percent: float64(percent), Pid: p.Pid, User: user, Name: name, Cmd: cmd, Memory: memItem})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(top5, func(i, j int) bool {
|
||||
return top5[i].Memory > top5[j].Memory
|
||||
})
|
||||
return top5
|
||||
}
|
||||
|
||||
func StartMonitor(removeBefore bool, interval string) error {
|
||||
if removeBefore {
|
||||
monitorCancel()
|
||||
@@ -330,7 +549,7 @@ func StartMonitor(removeBefore bool, interval string) error {
|
||||
now := time.Now()
|
||||
nextMinute := now.Truncate(time.Minute).Add(time.Minute)
|
||||
time.AfterFunc(time.Until(nextMinute), func() {
|
||||
monitorID, err := global.Cron.AddJob(fmt.Sprintf("@every %sm", interval), service)
|
||||
monitorID, err := global.Cron.AddJob(fmt.Sprintf("@every %ss", interval), service)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -344,3 +563,92 @@ func StartMonitor(removeBefore bool, interval string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func saveGPUDataToDB() {
|
||||
exist, client := gpu.New()
|
||||
if !exist {
|
||||
return
|
||||
}
|
||||
gpuInfo, err := client.LoadGpuInfo()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var list []model.MonitorGPU
|
||||
for _, gpuItem := range gpuInfo.GPUs {
|
||||
item := model.MonitorGPU{
|
||||
ProductName: fmt.Sprintf("%d - %s", gpuItem.Index, gpuItem.ProductName),
|
||||
GPUUtil: loadGPUInfoFloat(gpuItem.GPUUtil),
|
||||
Temperature: loadGPUInfoFloat(gpuItem.Temperature),
|
||||
PowerDraw: loadGPUInfoFloat(gpuItem.PowerDraw),
|
||||
MaxPowerLimit: loadGPUInfoFloat(gpuItem.MaxPowerLimit),
|
||||
MemUsed: loadGPUInfoFloat(gpuItem.MemUsed),
|
||||
MemTotal: loadGPUInfoFloat(gpuItem.MemTotal),
|
||||
FanSpeed: loadGPUInfoInt(gpuItem.FanSpeed),
|
||||
}
|
||||
process, _ := json.Marshal(gpuItem.Processes)
|
||||
if len(process) != 0 {
|
||||
item.Processes = string(process)
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
if err := repo.NewIMonitorRepo().BatchCreateMonitorGPU(list); err != nil {
|
||||
global.LOG.Errorf("batch create gpu monitor data failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
func saveXPUDataToDB() {
|
||||
exist, client := xpu.New()
|
||||
if !exist {
|
||||
return
|
||||
}
|
||||
xpuInfo, err := client.LoadGpuInfo()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var list []model.MonitorGPU
|
||||
for _, xpuItem := range xpuInfo.Xpu {
|
||||
item := model.MonitorGPU{
|
||||
ProductName: fmt.Sprintf("%d - %s", xpuItem.Basic.DeviceID, xpuItem.Basic.DeviceName),
|
||||
Temperature: loadGPUInfoFloat(xpuItem.Stats.Temperature),
|
||||
PowerDraw: loadGPUInfoFloat(xpuItem.Stats.Power),
|
||||
MemUsed: loadGPUInfoFloat(xpuItem.Stats.MemoryUsed),
|
||||
MemTotal: loadGPUInfoFloat(xpuItem.Basic.Memory),
|
||||
}
|
||||
if len(xpuItem.Processes) != 0 {
|
||||
var processItem []dto.GPUProcess
|
||||
for _, ps := range xpuItem.Processes {
|
||||
processItem = append(processItem, dto.GPUProcess{
|
||||
Pid: fmt.Sprintf("%v", ps.PID),
|
||||
Type: ps.SHR,
|
||||
ProcessName: ps.Command,
|
||||
UsedMemory: ps.Memory,
|
||||
})
|
||||
}
|
||||
process, _ := json.Marshal(processItem)
|
||||
if len(process) != 0 {
|
||||
item.Processes = string(process)
|
||||
}
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
if err := repo.NewIMonitorRepo().BatchCreateMonitorGPU(list); err != nil {
|
||||
global.LOG.Errorf("batch create gpu monitor data failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
func loadGPUInfoInt(val string) int {
|
||||
val = strings.TrimSuffix(val, "%")
|
||||
val = strings.TrimSpace(val)
|
||||
data, _ := strconv.Atoi(val)
|
||||
return data
|
||||
}
|
||||
func loadGPUInfoFloat(val string) float64 {
|
||||
val = strings.TrimSpace(val)
|
||||
suffixes := []string{"W", "MB", "MiB", "°C", "C", "%"}
|
||||
for _, suffix := range suffixes {
|
||||
val = strings.TrimSuffix(val, suffix)
|
||||
}
|
||||
val = strings.TrimSpace(val)
|
||||
data, _ := strconv.ParseFloat(val, 64)
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ type INginxService interface {
|
||||
GetModules() (*response.NginxBuildConfig, error)
|
||||
UpdateModule(req request.NginxModuleUpdate) error
|
||||
|
||||
OperateDefaultHTTPs(req request.NginxOperateReq) error
|
||||
OperateDefaultHTTPs(req request.NginxDefaultHTTPSUpdate) error
|
||||
GetDefaultHttpsStatus() (*response.NginxConfigRes, error)
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ func (n NginxService) UpdateModule(req request.NginxModuleUpdate) error {
|
||||
return fileOp.SaveFileWithByte(moduleConfigPath, moduleByte, constant.DirPerm)
|
||||
}
|
||||
|
||||
func (n NginxService) OperateDefaultHTTPs(req request.NginxOperateReq) error {
|
||||
func (n NginxService) OperateDefaultHTTPs(req request.NginxDefaultHTTPSUpdate) error {
|
||||
appInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -372,11 +372,18 @@ func (n NginxService) OperateDefaultHTTPs(req request.NginxOperateReq) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req.Operate == "enable" {
|
||||
if err := handleSSLConfig(&appInstall, hasDefaultWebsite); err != nil {
|
||||
switch req.Operate {
|
||||
case "enable":
|
||||
if req.SSLRejectHandshake {
|
||||
defaultWebsite, _ := websiteRepo.GetFirst(websiteRepo.WithDefaultServer())
|
||||
if defaultWebsite.ID > 0 {
|
||||
return buserr.New("ErrDefaultWebsite")
|
||||
}
|
||||
}
|
||||
if err := handleSSLConfig(&appInstall, hasDefaultWebsite, req.SSLRejectHandshake); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if req.Operate == "disable" {
|
||||
case "disable":
|
||||
defaultConfig, err := parser.NewStringParser(string(content)).Parse()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -387,6 +394,7 @@ func (n NginxService) OperateDefaultHTTPs(req request.NginxOperateReq) error {
|
||||
defaultServer.RemoveListen(fmt.Sprintf("[::]:%d", appInstall.HttpsPort))
|
||||
defaultServer.RemoveDirective("include", []string{"/usr/local/openresty/nginx/conf/ssl/root_ssl.conf"})
|
||||
defaultServer.RemoveDirective("http2", []string{"on"})
|
||||
defaultServer.RemoveDirective("ssl_reject_handshake", []string{"on"})
|
||||
if err = nginx.WriteConfig(defaultConfig, nginx.IndentedStyle); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -413,9 +421,11 @@ func (n NginxService) GetDefaultHttpsStatus() (*response.NginxConfigRes, error)
|
||||
res := &response.NginxConfigRes{}
|
||||
for _, directive := range defaultServer.GetDirectives() {
|
||||
if directive.GetName() == "include" && directive.GetParameters()[0] == "/usr/local/openresty/nginx/conf/ssl/root_ssl.conf" {
|
||||
return &response.NginxConfigRes{
|
||||
Https: true,
|
||||
}, nil
|
||||
res.Https = true
|
||||
}
|
||||
if directive.GetName() == "ssl_reject_handshake" && directive.GetParameters()[0] == "on" {
|
||||
res.Https = true
|
||||
res.SSLRejectHandshake = true
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
@@ -122,9 +121,11 @@ func updateDefaultServerConfig(enable bool) error {
|
||||
|
||||
includeSSL := false
|
||||
for _, dir := range defaultServer.GetDirectives() {
|
||||
if dir.GetName() == "ssl_reject_handshake" && dir.GetParameters()[0] == "on" {
|
||||
defaultServer.RemoveDirective("ssl_reject_handshake", []string{"on"})
|
||||
}
|
||||
if dir.GetName() == "include" && dir.GetParameters()[0] == "/usr/local/openresty/nginx/conf/ssl/root_ssl.conf" {
|
||||
includeSSL = true
|
||||
break
|
||||
}
|
||||
}
|
||||
updateDefaultServer(defaultServer, nginxInstall.HttpPort, nginxInstall.HttpsPort, enable, includeSSL)
|
||||
@@ -238,10 +239,6 @@ func getNginxParamsFromStaticFile(scope dto.NginxKey, newParams []dto.NginxParam
|
||||
}
|
||||
|
||||
func opNginx(containerName, operate string) error {
|
||||
var (
|
||||
out string
|
||||
err error
|
||||
)
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second))
|
||||
cmdStr := fmt.Sprintf("docker exec -i %s nginx ", containerName)
|
||||
if operate == constant.NginxCheck {
|
||||
@@ -249,11 +246,7 @@ func opNginx(containerName, operate string) error {
|
||||
} else {
|
||||
cmdStr = cmdStr + "-s reload"
|
||||
}
|
||||
out, err = cmdMgr.RunWithStdoutBashC(cmdStr)
|
||||
if err != nil {
|
||||
if out != "" {
|
||||
return errors.New(out)
|
||||
}
|
||||
if err := cmdMgr.RunBashC(cmdStr); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
+167
-18
@@ -1,11 +1,15 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/websocket"
|
||||
"github.com/shirou/gopsutil/v4/process"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -103,24 +107,20 @@ func (ps *ProcessService) GetProcessInfoByPID(pid int32) (*websocket.PsProcessDa
|
||||
data.CmdLine = cmdline
|
||||
}
|
||||
|
||||
if memInfo, err := p.MemoryInfo(); err == nil {
|
||||
data.Rss = common.FormatBytes(memInfo.RSS)
|
||||
data.VMS = common.FormatBytes(memInfo.VMS)
|
||||
data.HWM = common.FormatBytes(memInfo.HWM)
|
||||
data.Data = common.FormatBytes(memInfo.Data)
|
||||
data.Stack = common.FormatBytes(memInfo.Stack)
|
||||
data.Locked = common.FormatBytes(memInfo.Locked)
|
||||
data.Swap = common.FormatBytes(memInfo.Swap)
|
||||
data.RssValue = memInfo.RSS
|
||||
} else {
|
||||
data.Rss = "--"
|
||||
data.Data = "--"
|
||||
data.VMS = "--"
|
||||
data.HWM = "--"
|
||||
data.Stack = "--"
|
||||
data.Locked = "--"
|
||||
data.Swap = "--"
|
||||
data.RssValue = 0
|
||||
if memDetail, err := getMemoryDetail(p.Pid); err == nil {
|
||||
data.Rss = common.FormatBytes(memDetail.RSS)
|
||||
data.VMS = common.FormatBytes(memDetail.VMS)
|
||||
data.HWM = common.FormatBytes(memDetail.HWM)
|
||||
data.Data = common.FormatBytes(memDetail.Data)
|
||||
data.Stack = common.FormatBytes(memDetail.Stack)
|
||||
data.Locked = common.FormatBytes(memDetail.Locked)
|
||||
data.Swap = common.FormatBytes(memDetail.Swap)
|
||||
data.Dirty = common.FormatBytes(memDetail.Dirty)
|
||||
data.RssValue = memDetail.RSS
|
||||
data.PSS = common.FormatBytes(memDetail.PSS)
|
||||
data.USS = common.FormatBytes(memDetail.USS)
|
||||
data.Shared = common.FormatBytes(memDetail.Shared)
|
||||
data.Text = common.FormatBytes(memDetail.Text)
|
||||
}
|
||||
|
||||
if envs, err := p.Environ(); err == nil {
|
||||
@@ -133,3 +133,152 @@ func (ps *ProcessService) GetProcessInfoByPID(pid int32) (*websocket.PsProcessDa
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
type MemoryDetail struct {
|
||||
RSS uint64
|
||||
VMS uint64
|
||||
HWM uint64
|
||||
Data uint64
|
||||
Stack uint64
|
||||
Locked uint64
|
||||
Swap uint64
|
||||
|
||||
PSS uint64
|
||||
USS uint64
|
||||
Shared uint64
|
||||
Text uint64
|
||||
Dirty uint64
|
||||
}
|
||||
|
||||
func getMemoryDetail(pid int32) (*MemoryDetail, error) {
|
||||
mem := &MemoryDetail{}
|
||||
|
||||
if err := readStatus(pid, mem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := readSmapsRollup(pid, mem); err != nil {
|
||||
if err := readSmaps(pid, mem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return mem, nil
|
||||
}
|
||||
|
||||
func readStatus(pid int32, mem *MemoryDetail) error {
|
||||
filePath := fmt.Sprintf("/proc/%d/status", pid)
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.TrimSuffix(fields[0], ":")
|
||||
value, _ := strconv.ParseUint(fields[1], 10, 64)
|
||||
value *= 1024
|
||||
|
||||
switch key {
|
||||
case "VmRSS":
|
||||
mem.RSS = value
|
||||
case "VmSize":
|
||||
mem.VMS = value
|
||||
case "VmData":
|
||||
mem.Data = value
|
||||
case "VmSwap":
|
||||
mem.Swap = value
|
||||
case "VmExe":
|
||||
mem.Text = value
|
||||
case "RssShmem":
|
||||
mem.Shared = value
|
||||
case "VmHWM":
|
||||
mem.HWM = value
|
||||
case "VmStk":
|
||||
mem.Stack = value
|
||||
case "VmLck":
|
||||
mem.Locked = value
|
||||
}
|
||||
}
|
||||
|
||||
return scanner.Err()
|
||||
}
|
||||
|
||||
func readSmapsRollup(pid int32, mem *MemoryDetail) error {
|
||||
filePath := fmt.Sprintf("/proc/%d/smaps_rollup", pid)
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.TrimSuffix(fields[0], ":")
|
||||
value, _ := strconv.ParseUint(fields[1], 10, 64)
|
||||
value *= 1024
|
||||
|
||||
switch key {
|
||||
case "Pss":
|
||||
mem.PSS = value
|
||||
case "Private_Clean", "Private_Dirty":
|
||||
mem.USS += value
|
||||
case "Shared_Clean", "Shared_Dirty":
|
||||
if mem.Shared == 0 {
|
||||
mem.Shared = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return scanner.Err()
|
||||
}
|
||||
|
||||
func readSmaps(pid int32, mem *MemoryDetail) error {
|
||||
filePath := fmt.Sprintf("/proc/%d/smaps", pid)
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.TrimSuffix(fields[0], ":")
|
||||
value, _ := strconv.ParseUint(fields[1], 10, 64)
|
||||
value *= 1024
|
||||
|
||||
switch key {
|
||||
case "Pss":
|
||||
mem.PSS += value
|
||||
case "Private_Clean", "Private_Dirty":
|
||||
mem.USS += value
|
||||
case "Shared_Clean", "Shared_Dirty":
|
||||
if mem.Shared == 0 {
|
||||
mem.Shared += value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return scanner.Err()
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -17,6 +16,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/re"
|
||||
"github.com/shirou/gopsutil/v4/disk"
|
||||
)
|
||||
|
||||
@@ -76,6 +76,9 @@ func (r RecycleBinService) Page(search dto.PageInfo) (int64, []response.RecycleB
|
||||
}
|
||||
|
||||
func (r RecycleBinService) Create(create request.RecycleBinCreate) error {
|
||||
if files.IsProtected(create.SourcePath) {
|
||||
return buserr.New("ErrPathNotDelete")
|
||||
}
|
||||
op := files.NewFileOp()
|
||||
if !op.Stat(create.SourcePath) {
|
||||
return buserr.New("ErrLinkPathNotFound")
|
||||
@@ -184,8 +187,7 @@ func createClashDir(clashDir string) error {
|
||||
}
|
||||
|
||||
func getRecycleBinDTOFromName(filename string) (*response.RecycleBinDTO, error) {
|
||||
r := regexp.MustCompile(`_1p_file_1p_(.+)_p_(\d+)_(\d+)`)
|
||||
matches := r.FindStringSubmatch(filename)
|
||||
matches := re.GetRegex(re.RecycleBinFilePattern).FindStringSubmatch(filename)
|
||||
if len(matches) != 4 {
|
||||
return nil, fmt.Errorf("invalid filename format")
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user