mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-10 16:00:31 +00:00
Compare commits
219
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da23b8e6d9 | ||
|
|
a1799f5b4b | ||
|
|
34496f6a00 | ||
|
|
5d8208a061 | ||
|
|
f94e60c53c | ||
|
|
dd70be51b3 | ||
|
|
12e1a6a855 | ||
|
|
07648367c1 | ||
|
|
5b86ca38e0 | ||
|
|
79d9d9dfc2 | ||
|
|
e667477f4b | ||
|
|
0367149386 | ||
|
|
3d0ce2add4 | ||
|
|
17224b8920 | ||
|
|
05d125c762 | ||
|
|
e1a268b21d | ||
|
|
c486e8a354 | ||
|
|
eba74d91b3 | ||
|
|
613f8c2b57 | ||
|
|
16e2ad64a2 | ||
|
|
3274895dcf | ||
|
|
7367d47d27 | ||
|
|
4ed2a03222 | ||
|
|
586df4627a | ||
|
|
25181b6c27 | ||
|
|
72e6b9b188 | ||
|
|
e2efa20582 | ||
|
|
2cebb1beca | ||
|
|
fb24a699e6 | ||
|
|
1c16a0a69f | ||
|
|
26e480f242 | ||
|
|
fcf864156d | ||
|
|
d773391763 | ||
|
|
b47d63330e | ||
|
|
026ed1eb42 | ||
|
|
fb80724a96 | ||
|
|
a7ab866e95 | ||
|
|
06f636c9aa | ||
|
|
bbc5b8b731 | ||
|
|
cd154b13a9 | ||
|
|
f71a8a1603 | ||
|
|
d2ecae7e97 | ||
|
|
649e7b1912 | ||
|
|
2a97b0a9de | ||
|
|
5ca02a1dcf | ||
|
|
f088cec4d5 | ||
|
|
dde40064d2 | ||
|
|
597000ce52 | ||
|
|
4597cb26a1 | ||
|
|
e16ab09253 | ||
|
|
595be6f470 | ||
|
|
7afe9ac878 | ||
|
|
1f53b64b08 | ||
|
|
49ca336dd6 | ||
|
|
7cabb54c4e | ||
|
|
f55841f41c | ||
|
|
8983cb6186 | ||
|
|
222181baff | ||
|
|
02c002bda6 | ||
|
|
a5fbd01ea5 | ||
|
|
3652e07988 | ||
|
|
6ed000985c | ||
|
|
4a8b524a7c | ||
|
|
39509ad9a8 | ||
|
|
f977a2488d | ||
|
|
e7b1aadb0e | ||
|
|
339b085db7 | ||
|
|
a7c00900d8 | ||
|
|
99023569fc | ||
|
|
e4478da423 | ||
|
|
d0bb9165bf | ||
|
|
72a0e0200e | ||
|
|
5d83f21a26 | ||
|
|
56936aa6cd | ||
|
|
f4a649dc2f | ||
|
|
464ca8d272 | ||
|
|
7897bdabc4 | ||
|
|
3675c2f3f3 | ||
|
|
4eb264bea4 | ||
|
|
129e864975 | ||
|
|
7f05c1ef33 | ||
|
|
97d6d9c69f | ||
|
|
8937acee13 | ||
|
|
7518c5fe95 | ||
|
|
45fbb9f010 | ||
|
|
eff55da020 | ||
|
|
e87bf0b3fc | ||
|
|
3f141346fa | ||
|
|
34b39f6eb8 | ||
|
|
b8fd8ee0d9 | ||
|
|
b8895f215c | ||
|
|
c4b3efeb9d | ||
|
|
8f0c84c2a5 | ||
|
|
90b9c53420 | ||
|
|
46f9f4f561 | ||
|
|
de265a19b2 | ||
|
|
8af6868694 | ||
|
|
b1dde54e38 | ||
|
|
0a6163c89b | ||
|
|
07d0b37692 | ||
|
|
ac9881dd4c | ||
|
|
280c1b4fea | ||
|
|
6b24ee23e6 | ||
|
|
d93f8c5e6e | ||
|
|
384f065040 | ||
|
|
672fedc7e5 | ||
|
|
0433a26a07 | ||
|
|
4ebd4bddee | ||
|
|
030ab743f3 | ||
|
|
d4623a68dd | ||
|
|
a38d1c96d8 | ||
|
|
61ddf89fdd | ||
|
|
5c7d9b4ffe | ||
|
|
563ea985a4 | ||
|
|
657b2ddc8a | ||
|
|
50221c2ace | ||
|
|
b0bf3be707 | ||
|
|
caaf05897a | ||
|
|
0354b11ddb | ||
|
|
bdcf9b075e | ||
|
|
ab77aa2b5b | ||
|
|
1d54dcc95c | ||
|
|
0138d481a6 | ||
|
|
ba6cdc6e10 | ||
|
|
389d9c652f | ||
|
|
4942c6c206 | ||
|
|
40e264e0d4 | ||
|
|
b73b851245 | ||
|
|
49a777e2d8 | ||
|
|
7df43aeb59 | ||
|
|
86047c48e4 | ||
|
|
6000a206a8 | ||
|
|
da75f050e2 | ||
|
|
7ed81996b8 | ||
|
|
2040f8f3b0 | ||
|
|
c216726449 | ||
|
|
a33b17d5da | ||
|
|
58c994a3e2 | ||
|
|
98d01556c2 | ||
|
|
fd21e8de14 | ||
|
|
37ef2059f1 | ||
|
|
41c49ca95f | ||
|
|
1999f102bf | ||
|
|
aa91bb88bc | ||
|
|
7410a41331 | ||
|
|
a987370657 | ||
|
|
1883b05f26 | ||
|
|
c408619042 | ||
|
|
1181b13c83 | ||
|
|
c1aef03f09 | ||
|
|
6609871b1e | ||
|
|
813f1718fb | ||
|
|
49bcc14df7 | ||
|
|
28108a1c02 | ||
|
|
c169dfa395 | ||
|
|
3b26a692b6 | ||
|
|
9a6b51b092 | ||
|
|
c3121fcd38 | ||
|
|
7e8581a7de | ||
|
|
0ba83d1353 | ||
|
|
a302e100ec | ||
|
|
4f58fd492d | ||
|
|
0b06a4fac6 | ||
|
|
4e1a7bf42c | ||
|
|
ef2fb1c62e | ||
|
|
f5fcbcd87a | ||
|
|
986bffaf2b | ||
|
|
766a2a0546 | ||
|
|
291b594ad9 | ||
|
|
7742e3da38 | ||
|
|
c5f70eae4c | ||
|
|
8bdf02c4c0 | ||
|
|
8abe132c25 | ||
|
|
3a447b02a1 | ||
|
|
aa718eef88 | ||
|
|
60b1c9ba32 | ||
|
|
9b50581fd4 | ||
|
|
ee33fdab89 | ||
|
|
c8f19467ba | ||
|
|
1fc0f1c71b | ||
|
|
2258ba108d | ||
|
|
d21f4ffae6 | ||
|
|
4bd9c4c0fc | ||
|
|
29165f0f6f | ||
|
|
f75b786916 | ||
|
|
011914339e | ||
|
|
08a07214ef | ||
|
|
f682f07dbc | ||
|
|
50e99f1f4c | ||
|
|
feabc488fe | ||
|
|
72d3f5071b | ||
|
|
27dc662430 | ||
|
|
80e233c4a6 | ||
|
|
a099308a82 | ||
|
|
6f0673f184 | ||
|
|
b428e699db | ||
|
|
f4334eadc0 | ||
|
|
a2a6503030 | ||
|
|
9c22005b79 | ||
|
|
7448953b7d | ||
|
|
18214095ab | ||
|
|
bcfc78e510 | ||
|
|
cc6b506176 | ||
|
|
c61828fb39 | ||
|
|
23876ed186 | ||
|
|
ac4962cf19 | ||
|
|
d9e7c8d880 | ||
|
|
ea25b800d2 | ||
|
|
7ba06e73d7 | ||
|
|
39cf26628f | ||
|
|
ea7f324a9d | ||
|
|
789d451ea2 | ||
|
|
7313f3d591 | ||
|
|
1662546bef | ||
|
|
53f75086ec | ||
|
|
81fcc61ec9 | ||
|
|
324a47966b | ||
|
|
5a0c50b9a2 | ||
|
|
66465beb39 |
@@ -12,7 +12,7 @@ jobs:
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.2'
|
||||
node-version: '22.19.0'
|
||||
- name: Build Web
|
||||
run: |
|
||||
cd frontend && npm install && npm run build:pro
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.2'
|
||||
node-version: '22.19.0'
|
||||
- name: Build Web
|
||||
run: |
|
||||
cd frontend && npm install && npm run build:pro
|
||||
|
||||
@@ -60,7 +60,7 @@ func (b *BaseApi) RecreateOllamaModel(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/ollama/model/close [post]
|
||||
// @Router /ai/ollama/close [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"关闭 Ollama 模型连接 [name]","formatEN":"close conn for Ollama model [name]"}
|
||||
func (b *BaseApi) CloseOllamaModel(c *gin.Context) {
|
||||
var req dto.OllamaModelName
|
||||
|
||||
@@ -109,7 +109,7 @@ func (b *BaseApi) LoadPort(c *gin.Context) {
|
||||
// @Success 200 {object} response.DatabaseConn
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /apps/installed/conninfo/:key [get]
|
||||
// @Router /apps/installed/conninfo [POST]
|
||||
func (b *BaseApi) LoadConnInfo(c *gin.Context) {
|
||||
var req dto.OperationWithNameAndType
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
|
||||
@@ -74,7 +74,7 @@ func (b *BaseApi) RefreshToken(c *gin.Context) {
|
||||
// @Success 200 {array} object
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /buckets [post]
|
||||
// @Router /backups/buckets [post]
|
||||
func (b *BaseApi) ListBuckets(c *gin.Context) {
|
||||
var req dto.ForBuckets
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -133,6 +133,28 @@ func (b *BaseApi) UpdateBackup(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Upload file for recover
|
||||
// @Accept json
|
||||
// @Param request body dto.UploadForRecover true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /backups/upload [post]
|
||||
// @x-panel-log {"bodyKeys":["filePath"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"上传备份文件 [filePath]","formatEN":"upload backup file [filePath]"}
|
||||
func (b *BaseApi) UploadForRecover(c *gin.Context) {
|
||||
var req dto.UploadForRecover
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := backupService.UploadForRecover(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Load backup account options
|
||||
// @Accept json
|
||||
@@ -273,7 +295,7 @@ func (b *BaseApi) SearchBackupRecordsByCronjob(c *gin.Context) {
|
||||
// @Success 200 {string} filePath
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /backup/record/download [post]
|
||||
// @Router /backups/record/download [post]
|
||||
// @x-panel-log {"bodyKeys":["source","fileName"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"下载备份记录 [source][fileName]","formatEN":"download backup records [source][fileName]"}
|
||||
func (b *BaseApi) DownloadRecord(c *gin.Context) {
|
||||
var req dto.DownloadRecord
|
||||
@@ -296,7 +318,7 @@ func (b *BaseApi) DownloadRecord(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /backup/record/description/update [post]
|
||||
// @Router /backups/record/description/update [post]
|
||||
func (b *BaseApi) UpdateRecordDescription(c *gin.Context) {
|
||||
var req dto.UpdateDescription
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -317,7 +339,7 @@ func (b *BaseApi) UpdateRecordDescription(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /record/del [post]
|
||||
// @Router /backups/record/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"backup_records","output_column":"file_name","output_value":"files"}],"formatZH":"删除备份记录 [files]","formatEN":"delete backup records [files]"}
|
||||
func (b *BaseApi) DeleteBackupRecord(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
|
||||
@@ -104,7 +104,7 @@ func (b *BaseApi) SearchClam(c *gin.Context) {
|
||||
// @Success 200 {object} dto.ClamBaseInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /toolbox/clam/base [get]
|
||||
// @Router /toolbox/clam/base [post]
|
||||
func (b *BaseApi) LoadClamBaseInfo(c *gin.Context) {
|
||||
info, err := clamService.LoadBaseInfo()
|
||||
if err != nil {
|
||||
@@ -152,7 +152,7 @@ func (b *BaseApi) CleanClamRecord(c *gin.Context) {
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := clamService.CleanRecord(req); err != nil {
|
||||
if err := clamService.CleanRecord(req.ID); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
@@ -174,7 +174,7 @@ func (b *BaseApi) SearchClamRecord(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
total, list, err := clamService.LoadRecords(req)
|
||||
total, list, err := clamService.SearchRecords(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
@@ -186,29 +186,6 @@ func (b *BaseApi) SearchClamRecord(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Clam
|
||||
// @Summary Load clam record detail
|
||||
// @Accept json
|
||||
// @Param request body dto.ClamLogReq true "request"
|
||||
// @Success 200 {string} content
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /toolbox/clam/record/log [post]
|
||||
func (b *BaseApi) LoadClamRecordLog(c *gin.Context) {
|
||||
var req dto.ClamLogReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
content, err := clamService.LoadRecordLog(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, content)
|
||||
}
|
||||
|
||||
// @Tags Clam
|
||||
// @Summary Load clam file
|
||||
// @Accept json
|
||||
@@ -289,7 +266,7 @@ func (b *BaseApi) HandleClamScan(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := clamService.HandleOnce(req); err != nil {
|
||||
if err := clamService.HandleOnce(req.ID); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -697,7 +697,7 @@ func (b *BaseApi) ComposeUpdate(c *gin.Context) {
|
||||
// @Param tail query string false "显示行号"
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/search/log [post]
|
||||
// @Router /containers/search/log [get]
|
||||
func (b *BaseApi) ContainerStreamLogs(c *gin.Context) {
|
||||
c.Header("Content-Type", "text/event-stream")
|
||||
c.Header("Cache-Control", "no-cache")
|
||||
|
||||
@@ -226,6 +226,28 @@ func (b *BaseApi) CleanRecord(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Cronjob
|
||||
// @Summary Handle stop job
|
||||
// @Accept json
|
||||
// @Param request body dto.OperateByID true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /cronjobs/stop [post]
|
||||
func (b *BaseApi) StopCronJob(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := cronjobService.HandleStop(req.ID); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Cronjob
|
||||
// @Summary Delete cronjob
|
||||
// @Accept json
|
||||
|
||||
@@ -83,6 +83,38 @@ func (b *BaseApi) UpdateAppLauncher(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Dashboard
|
||||
// @Summary Load quick jump options
|
||||
// @Success 200 {Array} dto.QuickJump
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /dashboard/quick/option [get]
|
||||
func (b *BaseApi) LoadQuickOption(c *gin.Context) {
|
||||
helper.SuccessWithData(c, dashboardService.LoadQuickOptions())
|
||||
}
|
||||
|
||||
// @Tags Dashboard
|
||||
// @Summary Update quick jump
|
||||
// @Accept json
|
||||
// @Param request body dto.ChangeQuicks true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /dashboard/quick/change [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"切换快速跳转","formatEN":"change quick jump"}
|
||||
func (b *BaseApi) UpdateQuickJump(c *gin.Context) {
|
||||
var req dto.ChangeQuicks
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := dashboardService.ChangeQuick(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Dashboard
|
||||
// @Summary Load dashboard base info
|
||||
// @Accept json
|
||||
|
||||
@@ -160,7 +160,7 @@ func (b *BaseApi) GetDatabase(c *gin.Context) {
|
||||
// @Success 200 {array} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /db/remote/del/check [post]
|
||||
// @Router /databases/db/del/check [post]
|
||||
func (b *BaseApi) DeleteCheckDatabase(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags Disk Management
|
||||
// @Summary Get complete disk information
|
||||
// @Description Get information about all disks including partitioned and unpartitioned disks
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.CompleteDiskInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/disks [get]
|
||||
func (b *BaseApi) GetCompleteDiskInfo(c *gin.Context) {
|
||||
diskInfo, err := diskService.GetCompleteDiskInfo()
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, diskInfo)
|
||||
}
|
||||
|
||||
// @Tags Disk Management
|
||||
// @Summary Partition disk
|
||||
// @Description Create partition and format disk with specified filesystem
|
||||
// @Accept json
|
||||
// @Param request body request.DiskPartitionRequest true "partition request"
|
||||
// @Success 200 {string} string "Partition created successfully"
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/disks/partition [post]
|
||||
// @x-panel-log {"bodyKeys":["device", "filesystem", "mountPoint"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"对磁盘 [device] 进行分区,文件系统 [filesystem],挂载点 [mountPoint]","formatEN":"Partition disk [device] with filesystem [filesystem], mount point [mountPoint]"}
|
||||
func (b *BaseApi) PartitionDisk(c *gin.Context) {
|
||||
var req request.DiskPartitionRequest
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result, err := diskService.PartitionDisk(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, result)
|
||||
}
|
||||
|
||||
// @Tags Disk Management
|
||||
// @Summary Mount disk
|
||||
// @Description Mount partition to specified mount point
|
||||
// @Accept json
|
||||
// @Param request body request.DiskMountRequest true "mount request"
|
||||
// @Success 200 {string} string "Disk mounted successfully"
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/disks/mount [post]
|
||||
// @x-panel-log {"bodyKeys":["device", "mountPoint"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"挂载磁盘 [device] 到 [mountPoint]","formatEN":"Mount disk [device] to [mountPoint]"}
|
||||
func (b *BaseApi) MountDisk(c *gin.Context) {
|
||||
var req request.DiskMountRequest
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err := diskService.MountDisk(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Disk Management
|
||||
// @Summary Unmount disk
|
||||
// @Description Unmount partition from mount point
|
||||
// @Accept json
|
||||
// @Param request body request.DiskUnmountRequest true "unmount request"
|
||||
// @Success 200 {string} string "Disk unmounted successfully"
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/disks/unmount [post]
|
||||
// @x-panel-log {"bodyKeys":["device", "mountPoint"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"卸载磁盘 [device] 从 [mountPoint]","formatEN":"Unmount disk [device] from [mountPoint]"}
|
||||
func (b *BaseApi) UnmountDisk(c *gin.Context) {
|
||||
var req request.DiskUnmountRequest
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err := diskService.UnmountDisk(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.Success(c)
|
||||
}
|
||||
@@ -17,8 +17,9 @@ var (
|
||||
appInstallService = service.NewIAppInstalledService()
|
||||
appIgnoreUpgradeService = service.NewIAppIgnoreUpgradeService()
|
||||
|
||||
aiToolService = service.NewIAIToolService()
|
||||
mcpServerService = service.NewIMcpServerService()
|
||||
aiToolService = service.NewIAIToolService()
|
||||
mcpServerService = service.NewIMcpServerService()
|
||||
tensorrtLLMService = service.NewITensorRTLLMService()
|
||||
|
||||
containerService = service.NewIContainerService()
|
||||
composeTemplateService = service.NewIComposeTemplateService()
|
||||
@@ -38,6 +39,7 @@ var (
|
||||
sshService = service.NewISSHService()
|
||||
firewallService = service.NewIFirewallService()
|
||||
monitorService = service.NewIMonitorService()
|
||||
systemService = service.NewISystemService()
|
||||
|
||||
deviceService = service.NewIDeviceService()
|
||||
fail2banService = service.NewIFail2BanService()
|
||||
@@ -71,4 +73,6 @@ var (
|
||||
taskService = service.NewITaskService()
|
||||
groupService = service.NewIGroupService()
|
||||
alertService = service.NewIAlertService()
|
||||
|
||||
diskService = service.NewIDiskService()
|
||||
)
|
||||
|
||||
@@ -880,7 +880,7 @@ func (b *BaseApi) GetPathByType(c *gin.Context) {
|
||||
// @Success 200 {object} dto.DiskInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /mount [post]
|
||||
// @Router /files/mount [post]
|
||||
func (b *BaseApi) GetHostMount(c *gin.Context) {
|
||||
disks := fileService.GetHostMount()
|
||||
helper.SuccessWithData(c, disks)
|
||||
@@ -892,7 +892,7 @@ func (b *BaseApi) GetHostMount(c *gin.Context) {
|
||||
// @Success 200 {object} response.UserGroupResponse
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /user/group [post]
|
||||
// @Router /files/user/group [post]
|
||||
func (b *BaseApi) GetUsersAndGroups(c *gin.Context) {
|
||||
res, err := fileService.GetUsersAndGroups()
|
||||
if err != nil {
|
||||
@@ -901,3 +901,44 @@ func (b *BaseApi) GetUsersAndGroups(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
// @Summary Convert file
|
||||
// @Accept json
|
||||
// @Param request body request.FileConvert true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /files/convert [post]
|
||||
func (b *BaseApi) ConvertFile(c *gin.Context) {
|
||||
var req request.FileConvertRequest
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
fileService.Convert(req)
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
// @Summary Convert file
|
||||
// @Accept json
|
||||
// @Param request body dto.PageInfo true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /files/convert/log [post]
|
||||
func (b *BaseApi) ConvertLog(c *gin.Context) {
|
||||
var req dto.PageInfo
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
total, logs, err := fileService.ConvertLog(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: logs,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ func (b *BaseApi) OperateFirewall(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := firewallService.OperateFirewall(req.Operation); err != nil {
|
||||
if err := firewallService.OperateFirewall(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /agent/groups [post]
|
||||
// @Router /groups [post]
|
||||
// @x-panel-log {"bodyKeys":["name","type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建组 [name][type]","formatEN":"create group [name][type]"}
|
||||
func (b *BaseApi) CreateGroup(c *gin.Context) {
|
||||
var req dto.GroupCreate
|
||||
@@ -35,7 +35,7 @@ func (b *BaseApi) CreateGroup(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /agent/groups/del [post]
|
||||
// @Router /groups/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"groups","output_column":"name","output_value":"name"},{"input_column":"id","input_value":"id","isList":false,"db":"groups","output_column":"type","output_value":"type"}],"formatZH":"删除组 [type][name]","formatEN":"delete group [type][name]"}
|
||||
func (b *BaseApi) DeleteGroup(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
@@ -57,7 +57,7 @@ func (b *BaseApi) DeleteGroup(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /agent/groups/update [post]
|
||||
// @Router /groups/update [post]
|
||||
// @x-panel-log {"bodyKeys":["name","type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新组 [name][type]","formatEN":"update group [name][type]"}
|
||||
func (b *BaseApi) UpdateGroup(c *gin.Context) {
|
||||
var req dto.GroupUpdate
|
||||
@@ -79,7 +79,7 @@ func (b *BaseApi) UpdateGroup(c *gin.Context) {
|
||||
// @Success 200 {array} dto.OperateByType
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /agent/groups/search [post]
|
||||
// @Router /groups/search [post]
|
||||
func (b *BaseApi) ListGroup(c *gin.Context) {
|
||||
var req dto.OperateByType
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
|
||||
@@ -116,7 +116,11 @@ func CheckBind(req interface{}, c *gin.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ErrResponse(ctx *gin.Context, code int) {
|
||||
ctx.JSON(code, nil)
|
||||
ctx.Abort()
|
||||
func GetParamInt32(paramName string, c *gin.Context) (int32, error) {
|
||||
idParam, ok := c.Params.Get(paramName)
|
||||
if !ok {
|
||||
return 0, errors.New("error id in path")
|
||||
}
|
||||
intNum, _ := strconv.Atoi(idParam)
|
||||
return int32(intNum), nil
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ func (b *BaseApi) GetToolStatus(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/tool/create [post]
|
||||
// @Router /hosts/tool/init [post]
|
||||
// @x-panel-log {"bodyKeys":["type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建 [type] 配置","formatEN":"create [type] config"}
|
||||
func (b *BaseApi) InitToolConfig(c *gin.Context) {
|
||||
var req request.HostToolCreate
|
||||
@@ -95,28 +95,6 @@ func (b *BaseApi) OperateToolConfig(c *gin.Context) {
|
||||
helper.SuccessWithData(c, config)
|
||||
}
|
||||
|
||||
// @Tags Host tool
|
||||
// @Summary Get tool logs
|
||||
// @Accept json
|
||||
// @Param request body request.HostToolLogReq true "request"
|
||||
// @Success 200 {string} logContent
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/tool/log [post]
|
||||
func (b *BaseApi) GetToolLog(c *gin.Context) {
|
||||
var req request.HostToolLogReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
logContent, err := hostToolService.GetToolLog(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, logContent)
|
||||
}
|
||||
|
||||
// @Tags Host tool
|
||||
// @Summary Create Supervisor process
|
||||
// @Accept json
|
||||
|
||||
@@ -58,7 +58,7 @@ func (b *BaseApi) ListRepo(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/repo/status [get]
|
||||
// @Router /containers/repo/status [post]
|
||||
func (b *BaseApi) CheckRepoStatus(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// @Success 200 {object} response.McpServersRes
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /mcp/search [post]
|
||||
// @Router /ai/mcp/search [post]
|
||||
func (b *BaseApi) PageMcpServers(c *gin.Context) {
|
||||
var req request.McpServerSearch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -30,7 +30,7 @@ func (b *BaseApi) PageMcpServers(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /mcp/server [post]
|
||||
// @Router /ai/mcp/server [post]
|
||||
func (b *BaseApi) CreateMcpServer(c *gin.Context) {
|
||||
var req request.McpServerCreate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -51,7 +51,7 @@ func (b *BaseApi) CreateMcpServer(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /mcp/server/update [post]
|
||||
// @Router /ai/mcp/server/update [post]
|
||||
func (b *BaseApi) UpdateMcpServer(c *gin.Context) {
|
||||
var req request.McpServerUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -72,7 +72,7 @@ func (b *BaseApi) UpdateMcpServer(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /mcp/server/del [post]
|
||||
// @Router /ai/mcp/server/del [post]
|
||||
func (b *BaseApi) DeleteMcpServer(c *gin.Context) {
|
||||
var req request.McpServerDelete
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -93,7 +93,7 @@ func (b *BaseApi) DeleteMcpServer(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /mcp/server/op [post]
|
||||
// @Router /ai/mcp/server/op [post]
|
||||
func (b *BaseApi) OperateMcpServer(c *gin.Context) {
|
||||
var req request.McpServerOperate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -114,7 +114,7 @@ func (b *BaseApi) OperateMcpServer(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /mcp/domain/bind [post]
|
||||
// @Router /ai/mcp/domain/bind [post]
|
||||
func (b *BaseApi) BindMcpDomain(c *gin.Context) {
|
||||
var req request.McpBindDomain
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -135,7 +135,7 @@ func (b *BaseApi) BindMcpDomain(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /mcp/domain/update [post]
|
||||
// @Router /ai/mcp/domain/update [post]
|
||||
func (b *BaseApi) UpdateMcpBindDomain(c *gin.Context) {
|
||||
var req request.McpBindDomainUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -155,7 +155,7 @@ func (b *BaseApi) UpdateMcpBindDomain(c *gin.Context) {
|
||||
// @Success 200 {object} response.McpBindDomainRes
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /mcp/domain/get [get]
|
||||
// @Router /ai/mcp/domain/get [get]
|
||||
func (b *BaseApi) GetMcpBindDomain(c *gin.Context) {
|
||||
res, err := mcpServerService.GetBindDomain()
|
||||
if err != nil {
|
||||
|
||||
@@ -129,7 +129,7 @@ func (b *BaseApi) BuildNginx(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /openresty/module/update [post]
|
||||
// @Router /openresty/modules/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 OpenResty 模块","formatEN":"Update OpenResty module"}
|
||||
func (b *BaseApi) UpdateNginxModule(c *gin.Context) {
|
||||
var req request.NginxModuleUpdate
|
||||
|
||||
@@ -36,3 +36,24 @@ func (b *BaseApi) StopProcess(c *gin.Context) {
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Process
|
||||
// @Summary Get Process Info By PID
|
||||
// @Param pid path int true "PID"
|
||||
// @Success 200 {object} websocket.PsProcessData
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /process/{pid} [get]
|
||||
func (b *BaseApi) GetProcessInfoByPID(c *gin.Context) {
|
||||
pid, err := helper.GetParamInt32("pid", c)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
data, err := processService.GetProcessInfoByPID(pid)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ func (b *BaseApi) DeleteRuntime(c *gin.Context) {
|
||||
// @Success 200 {array} dto.AppResource
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /installed/delete/check/:id [get]
|
||||
// @Router /runtimes/installed/delete/check/:id [get]
|
||||
func (b *BaseApi) DeleteRuntimeCheck(c *gin.Context) {
|
||||
runTimeId, err := helper.GetIntParamByKey(c, "id")
|
||||
if err != nil {
|
||||
@@ -459,7 +459,7 @@ func (b *BaseApi) GetSupervisorProcess(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /runtimes/supervisor/process/operate [post]
|
||||
// @Router /runtimes/supervisor/process [post]
|
||||
func (b *BaseApi) OperateSupervisorProcess(c *gin.Context) {
|
||||
var req request.PHPSupervisorProcessConfig
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -480,7 +480,7 @@ func (b *BaseApi) OperateSupervisorProcess(c *gin.Context) {
|
||||
// @Success 200 {string} content
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /runtimes/supervisor/process/file/operate [post]
|
||||
// @Router /runtimes/supervisor/process/file [post]
|
||||
func (b *BaseApi) OperateSupervisorProcessFile(c *gin.Context) {
|
||||
var req request.PHPSupervisorProcessFileReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -497,7 +497,7 @@ func (b *BaseApi) OperateSupervisorProcessFile(c *gin.Context) {
|
||||
// @Tags Runtime
|
||||
// @Summary Update PHP container config
|
||||
// @Accept json
|
||||
// @Param request body request.PHPContainerUpdate true "request"
|
||||
// @Param request body request.PHPContainerConfig true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
@@ -518,7 +518,7 @@ func (b *BaseApi) UpdatePHPContainer(c *gin.Context) {
|
||||
// @Summary Get PHP container config
|
||||
// @Accept json
|
||||
// @Param id path integer true "request"
|
||||
// @Success 200 {object} response.PHPContainerConfig
|
||||
// @Success 200 {object} request.PHPContainerConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /runtimes/php/container/:id [get]
|
||||
@@ -539,7 +539,7 @@ func (b *BaseApi) GetPHPContainerConfig(c *gin.Context) {
|
||||
// @Tags Runtime
|
||||
// @Summary Update runtime remark
|
||||
// @Accept json
|
||||
// @Param request body request.RuntimeRemarkUpdate true "request"
|
||||
// @Param request body request.RuntimeRemark true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
|
||||
+31
-35
@@ -2,12 +2,10 @@ package v2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
|
||||
"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/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ssh"
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -71,6 +69,16 @@ func (b *BaseApi) LoadBaseDir(c *gin.Context) {
|
||||
helper.SuccessWithData(c, global.Dir.DataDir)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Load local conn
|
||||
// @Success 200 {object} dto.SSHConnData
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /settings/ssh/conn [get]
|
||||
func (b *BaseApi) LoadLocalConn(c *gin.Context) {
|
||||
helper.SuccessWithData(c, settingService.GetLocalConn())
|
||||
}
|
||||
|
||||
func (b *BaseApi) CheckLocalConn(c *gin.Context) {
|
||||
_, err := loadLocalConn()
|
||||
helper.SuccessWithData(c, err == nil)
|
||||
@@ -87,6 +95,7 @@ func (b *BaseApi) CheckLocalConnByInfo(c *gin.Context) {
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, settingService.TestConnByInfo(req))
|
||||
}
|
||||
|
||||
@@ -96,51 +105,38 @@ func (b *BaseApi) CheckLocalConnByInfo(c *gin.Context) {
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /settings/ssh [post]
|
||||
func (b *BaseApi) SaveLocalConnInfo(c *gin.Context) {
|
||||
func (b *BaseApi) SaveLocalConn(c *gin.Context) {
|
||||
var req dto.SSHConnData
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, settingService.SaveConnInfo(req))
|
||||
|
||||
if err := settingService.SaveConnInfo(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
func loadLocalConn() (*ssh.SSHClient, error) {
|
||||
itemPath := ""
|
||||
currentInfo, _ := user.Current()
|
||||
if len(currentInfo.HomeDir) == 0 {
|
||||
itemPath = "/root/.ssh/id_ed25519_1panel"
|
||||
} else {
|
||||
itemPath = path.Join(currentInfo.HomeDir, ".ssh/id_ed25519_1panel")
|
||||
}
|
||||
if _, err := os.Stat(itemPath); err != nil {
|
||||
_ = sshService.CreateRootCert(dto.CreateRootCert{EncryptionMode: "ed25519", Name: "id_ed25519_1panel", Description: "1Panel Terminal"})
|
||||
}
|
||||
|
||||
privateKey, _ := os.ReadFile(itemPath)
|
||||
connWithKey := ssh.ConnInfo{
|
||||
Addr: "127.0.0.1",
|
||||
User: "root",
|
||||
Port: 22,
|
||||
AuthMode: "key",
|
||||
PrivateKey: privateKey,
|
||||
}
|
||||
client, err := ssh.NewClient(connWithKey)
|
||||
if err == nil {
|
||||
return client, nil
|
||||
}
|
||||
|
||||
connInfoInDB, err := settingService.GetSSHInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
connInfoInDB := settingService.GetSettingByKey("LocalSSHConn")
|
||||
if len(connInfoInDB) == 0 {
|
||||
return nil, errors.New("no such ssh conn info in db!")
|
||||
}
|
||||
var connInDB ssh.ConnInfo
|
||||
var connInDB model.LocalConnInfo
|
||||
if err := json.Unmarshal([]byte(connInfoInDB), &connInDB); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ssh.NewClient(connInDB)
|
||||
sshInfo := ssh.ConnInfo{
|
||||
Addr: connInDB.Addr,
|
||||
Port: int(connInDB.Port),
|
||||
User: connInDB.User,
|
||||
AuthMode: connInDB.AuthMode,
|
||||
Password: connInDB.Password,
|
||||
PrivateKey: []byte(connInDB.PrivateKey),
|
||||
PassPhrase: []byte(connInDB.PassPhrase),
|
||||
}
|
||||
return ssh.NewClient(sshInfo)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
|
||||
@@ -50,7 +50,7 @@ func (b *BaseApi) CreateSnapshot(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /settings/snapshot/recrete [post]
|
||||
// @Router /settings/snapshot/recreate [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"snapshots","output_column":"name","output_value":"name"}],"formatZH":"重试创建快照 [name]","formatEN":"recrete the snapshot [name]"}
|
||||
func (b *BaseApi) RecreateSnapshot(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
|
||||
+75
-41
@@ -68,21 +68,23 @@ func (b *BaseApi) UpdateSSH(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Update host SSH setting by file
|
||||
// @Summary Generate host SSH secret
|
||||
// @Accept json
|
||||
// @Param request body dto.SSHConf true "request"
|
||||
// @Param request body dto.RootCertOperate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/conffile/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 SSH 配置文件","formatEN":"update SSH conf"}
|
||||
func (b *BaseApi) UpdateSSHByfile(c *gin.Context) {
|
||||
var req dto.SSHConf
|
||||
// @Router /hosts/ssh/cert [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"生成 SSH 密钥 ","formatEN":"generate SSH secret"}
|
||||
func (b *BaseApi) CreateRootCert(c *gin.Context) {
|
||||
var req dto.RootCertOperate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := sshService.UpdateByFile(req.File); err != nil {
|
||||
if err := loadCertAfterDecrypt(&req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
}
|
||||
if err := sshService.CreateRootCert(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
@@ -90,45 +92,23 @@ func (b *BaseApi) UpdateSSHByfile(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Generate host SSH secret
|
||||
// @Summary Update host SSH secret
|
||||
// @Accept json
|
||||
// @Param request body dto.CreateRootCert true "request"
|
||||
// @Param request body dto.RootCertOperate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/ssh/cert [post]
|
||||
// @Router /hosts/ssh/cert/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"生成 SSH 密钥 ","formatEN":"generate SSH secret"}
|
||||
func (b *BaseApi) CreateRootCert(c *gin.Context) {
|
||||
var req dto.CreateRootCert
|
||||
func (b *BaseApi) EditRootCert(c *gin.Context) {
|
||||
var req dto.RootCertOperate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if len(req.PassPhrase) != 0 {
|
||||
passPhrase, err := base64.StdEncoding.DecodeString(req.PassPhrase)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
req.PassPhrase = string(passPhrase)
|
||||
if err := loadCertAfterDecrypt(&req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
}
|
||||
if len(req.PrivateKey) != 0 {
|
||||
privateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
req.PrivateKey = string(privateKey)
|
||||
}
|
||||
if len(req.PublicKey) != 0 {
|
||||
publicKey, err := base64.StdEncoding.DecodeString(req.PublicKey)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
req.PublicKey = string(publicKey)
|
||||
}
|
||||
|
||||
if err := sshService.CreateRootCert(req); err != nil {
|
||||
if err := sshService.EditRootCert(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
@@ -246,15 +226,69 @@ func (b *BaseApi) ExportSSHLogs(c *gin.Context) {
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Load host SSH conf
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithName true "request"
|
||||
// @Success 200 {string} conf
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/ssh/conf [get]
|
||||
func (b *BaseApi) LoadSSHConf(c *gin.Context) {
|
||||
data, err := sshService.LoadSSHConf()
|
||||
// @Router /hosts/ssh/file [post]
|
||||
func (b *BaseApi) LoadSSHFile(c *gin.Context) {
|
||||
var req dto.OperationWithName
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := sshService.LoadSSHFile(req.Name)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Update host SSH setting by file
|
||||
// @Accept json
|
||||
// @Param request body dto.SSHConf true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/ssh/file/update [post]
|
||||
// @x-panel-log {"bodyKeys":["key"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 SSH 配置文件 [key]","formatEN":"update SSH conf [key]"}
|
||||
func (b *BaseApi) UpdateSSHByFile(c *gin.Context) {
|
||||
var req dto.SettingUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := sshService.UpdateByFile(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
func loadCertAfterDecrypt(req *dto.RootCertOperate) error {
|
||||
if len(req.PassPhrase) != 0 {
|
||||
passPhrase, err := base64.StdEncoding.DecodeString(req.PassPhrase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.PassPhrase = string(passPhrase)
|
||||
}
|
||||
if len(req.PrivateKey) != 0 {
|
||||
privateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.PrivateKey = string(privateKey)
|
||||
}
|
||||
if len(req.PublicKey) != 0 {
|
||||
publicKey, err := base64.StdEncoding.DecodeString(req.PublicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.PublicKey = string(publicKey)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags Host
|
||||
// @Summary Check if a system component exists
|
||||
// @Accept json
|
||||
// @Param name query string true "Component name to check (e.g., rsync, docker)"
|
||||
// @Success 200 {object} response.ComponentInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/components/{name} [get]
|
||||
func (b *BaseApi) CheckComponentExistence(c *gin.Context) {
|
||||
name := c.Param("name")
|
||||
if name == "" {
|
||||
helper.BadRequest(c, errors.New("empty component name"))
|
||||
return
|
||||
}
|
||||
|
||||
info := systemService.IsComponentExist(name)
|
||||
helper.SuccessWithData(c, info)
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (b *BaseApi) PageTensorRTLLMs(c *gin.Context) {
|
||||
var req request.TensorRTLLMSearch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
list := tensorrtLLMService.Page(req)
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
func (b *BaseApi) CreateTensorRTLLM(c *gin.Context) {
|
||||
var req request.TensorRTLLMCreate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
err := tensorrtLLMService.Create(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
func (b *BaseApi) UpdateTensorRTLLM(c *gin.Context) {
|
||||
var req request.TensorRTLLMUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
err := tensorrtLLMService.Update(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
func (b *BaseApi) DeleteTensorRTLLM(c *gin.Context) {
|
||||
var req request.TensorRTLLMDelete
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
err := tensorrtLLMService.Delete(req.ID)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
func (b *BaseApi) OperateTensorRTLLM(c *gin.Context) {
|
||||
var req request.TensorRTLLMOperate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
err := tensorrtLLMService.Operate(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
@@ -86,15 +85,16 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
source := c.Query("source")
|
||||
var containerID string
|
||||
var initCmd []string
|
||||
switch source {
|
||||
case "redis", "redis-cluster":
|
||||
containerID, initCmd, err = loadRedisInitCmd(c, source)
|
||||
initCmd, err = loadRedisInitCmd(c, source)
|
||||
case "ollama":
|
||||
containerID, initCmd, err = loadOllamaInitCmd(c)
|
||||
initCmd, err = loadOllamaInitCmd(c)
|
||||
case "container":
|
||||
containerID, initCmd, err = loadContainerInitCmd(c)
|
||||
initCmd, err = loadContainerInitCmd(c)
|
||||
case "database":
|
||||
initCmd, err = loadDatabaseInitCmd(c)
|
||||
default:
|
||||
if wshandleError(wsConn, fmt.Errorf("not support such source %s", source)) {
|
||||
return
|
||||
@@ -103,12 +103,10 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
pidMap := loadMapFromDockerTop(containerID)
|
||||
slave, err := terminal.NewCommand("docker", initCmd...)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(containerID, strings.ReplaceAll(strings.Join(initCmd, " "), fmt.Sprintf("exec -it %s ", containerID), ""), pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
|
||||
@@ -127,18 +125,18 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
|
||||
_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)
|
||||
}
|
||||
|
||||
func loadRedisInitCmd(c *gin.Context, redisType string) (string, []string, error) {
|
||||
func loadRedisInitCmd(c *gin.Context, redisType string) ([]string, error) {
|
||||
name := c.Query("name")
|
||||
from := c.Query("from")
|
||||
commands := []string{"exec", "-it"}
|
||||
database, err := databaseService.Get(name)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("no such database in db, err: %v", err)
|
||||
return nil, fmt.Errorf("no such database in db, err: %v", err)
|
||||
}
|
||||
if from == "local" {
|
||||
redisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: name, Type: redisType})
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("no such app in db, err: %v", err)
|
||||
return nil, fmt.Errorf("no such app in db, err: %v", err)
|
||||
}
|
||||
name = redisInfo.ContainerName
|
||||
commands = append(commands, []string{name, "redis-cli"}...)
|
||||
@@ -152,38 +150,61 @@ func loadRedisInitCmd(c *gin.Context, redisType string) (string, []string, error
|
||||
commands = append(commands, []string{"-a", database.Password, "--no-auth-warning"}...)
|
||||
}
|
||||
}
|
||||
return name, commands, nil
|
||||
return commands, nil
|
||||
}
|
||||
|
||||
func loadOllamaInitCmd(c *gin.Context) (string, []string, error) {
|
||||
func loadOllamaInitCmd(c *gin.Context) ([]string, error) {
|
||||
name := c.Query("name")
|
||||
if cmd.CheckIllegal(name) {
|
||||
return "", nil, fmt.Errorf("ollama model %s contains illegal characters", name)
|
||||
return nil, fmt.Errorf("ollama model %s contains illegal characters", name)
|
||||
}
|
||||
ollamaInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: "", Type: "ollama"})
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("no such app in db, err: %v", err)
|
||||
return nil, fmt.Errorf("no such app in db, err: %v", err)
|
||||
}
|
||||
containerName := ollamaInfo.ContainerName
|
||||
return containerName, []string{"exec", "-it", containerName, "ollama", "run", name}, nil
|
||||
return []string{"exec", "-it", containerName, "ollama", "run", name}, nil
|
||||
}
|
||||
|
||||
func loadContainerInitCmd(c *gin.Context) (string, []string, error) {
|
||||
func loadContainerInitCmd(c *gin.Context) ([]string, error) {
|
||||
containerID := c.Query("containerid")
|
||||
command := c.Query("command")
|
||||
user := c.Query("user")
|
||||
if cmd.CheckIllegal(user, containerID, command) {
|
||||
return "", nil, fmt.Errorf("the command contains illegal characters. command: %s, user: %s, containerID: %s", command, user, containerID)
|
||||
return nil, fmt.Errorf("the command contains illegal characters. command: %s, user: %s, containerID: %s", command, user, containerID)
|
||||
}
|
||||
if len(command) == 0 || len(containerID) == 0 {
|
||||
return "", nil, fmt.Errorf("error param of command: %s or containerID: %s", command, containerID)
|
||||
return nil, fmt.Errorf("error param of command: %s or containerID: %s", command, containerID)
|
||||
}
|
||||
commands := []string{"exec", "-it", containerID, command}
|
||||
if len(user) != 0 {
|
||||
commands = []string{"exec", "-it", "-u", user, containerID, command}
|
||||
}
|
||||
|
||||
return containerID, commands, nil
|
||||
return commands, nil
|
||||
}
|
||||
|
||||
func loadDatabaseInitCmd(c *gin.Context) ([]string, error) {
|
||||
database := c.Query("database")
|
||||
databaseType := c.Query("databaseType")
|
||||
if len(database) == 0 || len(databaseType) == 0 {
|
||||
return nil, fmt.Errorf("error param of database: %s or database type: %s", database, databaseType)
|
||||
}
|
||||
databaseConn, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Type: databaseType, Name: database})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("no such database in db, err: %v", err)
|
||||
}
|
||||
commands := []string{"exec", "-it", databaseConn.ContainerName}
|
||||
switch databaseType {
|
||||
case "mysql", "mysql-cluster":
|
||||
commands = append(commands, []string{"mysql", "-uroot", "-p" + databaseConn.Password}...)
|
||||
case "mariadb":
|
||||
commands = append(commands, []string{"mariadb", "-uroot", "-p" + databaseConn.Password}...)
|
||||
case "postgresql", "postgresql-cluster":
|
||||
commands = []string{"exec", "-e", fmt.Sprintf("PGPASSWORD=%s", databaseConn.Password), "-it", databaseConn.ContainerName, "psql", "-t", "-U", databaseConn.Username}
|
||||
}
|
||||
|
||||
return commands, nil
|
||||
}
|
||||
|
||||
func wshandleError(ws *websocket.Conn, err error) bool {
|
||||
@@ -206,42 +227,6 @@ func wshandleError(ws *websocket.Conn, err error) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func loadMapFromDockerTop(containerID string) map[string]string {
|
||||
pidMap := make(map[string]string)
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("%s docker top %s -eo pid,command ", sudo, containerID)
|
||||
if err != nil {
|
||||
return pidMap
|
||||
}
|
||||
lines := strings.Split(stdout, "\n")
|
||||
for _, line := range lines {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
pidMap[parts[0]] = strings.Join(parts[1:], " ")
|
||||
}
|
||||
return pidMap
|
||||
}
|
||||
|
||||
func killBash(containerID, comm string, pidMap map[string]string) {
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
newPidMap := loadMapFromDockerTop(containerID)
|
||||
for pid, command := range newPidMap {
|
||||
isOld := false
|
||||
for pid2 := range pidMap {
|
||||
if pid == pid2 {
|
||||
isOld = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isOld && command == comm {
|
||||
_, _ = cmd.RunDefaultWithStdoutBashCf("%s kill -9 %s", sudo, pid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var upGrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024 * 1024 * 10,
|
||||
|
||||
@@ -527,7 +527,7 @@ func (b *BaseApi) UpdateProxyConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/proxy/file [post]
|
||||
// @Router /websites/proxies/file [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":"Nginx conf proxy file update [domain]"}
|
||||
func (b *BaseApi) UpdateProxyConfigFile(c *gin.Context) {
|
||||
var req request.NginxProxyUpdate
|
||||
@@ -840,7 +840,7 @@ func (b *BaseApi) CreateLoadBalance(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/lbs/delete [post]
|
||||
// @Router /websites/lbs/del [post]
|
||||
func (b *BaseApi) DeleteLoadBalance(c *gin.Context) {
|
||||
var req request.WebsiteLBDelete
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -953,7 +953,7 @@ func (b *BaseApi) GetProxyCache(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/realip [post]
|
||||
// @Router /websites/realip/config [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] 网站真实IP配置 ","formatEN":"Modify the real IP configuration of [domain] website"}
|
||||
func (b *BaseApi) SetRealIPConfig(c *gin.Context) {
|
||||
var req request.WebsiteRealIP
|
||||
@@ -1141,3 +1141,65 @@ func (b *BaseApi) ExecComposer(c *gin.Context) {
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Batch operate websites
|
||||
// @Accept json
|
||||
// @Param request body request.BatchOpWebsite true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/batch/operate [post]
|
||||
func (b *BaseApi) BatchOpWebsites(c *gin.Context) {
|
||||
var req request.BatchWebsiteOp
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := websiteService.BatchOpWebsite(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Get CORS Config
|
||||
// @Accept json
|
||||
// @Param id path int true "id"
|
||||
// @Success 200 {object} request.CorsConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/cors/{id} [get]
|
||||
func (b *BaseApi) GetCORSConfig(c *gin.Context) {
|
||||
id, err := helper.GetParamID(c)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
res, err := websiteService.GetCors(id)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update CORS Config
|
||||
// @Accept json
|
||||
// @Param request body request.CorsConfigReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/cors/update [post]
|
||||
func (b *BaseApi) UpdateCORSConfig(c *gin.Context) {
|
||||
var req request.CorsConfigReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateCors(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package v2
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
@@ -217,6 +219,79 @@ func (b *BaseApi) UploadWebsiteSSL(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Website SSL
|
||||
// @Summary Upload SSL file
|
||||
// @Accept multipart/form-data
|
||||
// @Param type formData string true "type"
|
||||
// @Param description formData string false "description"
|
||||
// @Param sslID formData string false "sslID"
|
||||
// @Param privateKeyFile formData file true "privateKeyFile"
|
||||
// @Param certificateFile formData file true "certificateFile"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/ssl/upload/file [post]
|
||||
// @x-panel-log {"bodyKeys":["type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"上传 ssl 文件 [type]","formatEN":"Upload ssl file [type]"}
|
||||
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 != "" {
|
||||
req.SSLID, _ = strconv.ParseUint(sslID, 10, 64)
|
||||
}
|
||||
|
||||
privateKeyFile, err := c.FormFile("privateKeyFile")
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
certificateFile, err := c.FormFile("certificateFile")
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
privateKeyContent, err := readUploadedFile(privateKeyFile)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
certificateContent, err := readUploadedFile(certificateFile)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
uploadReq := request.WebsiteSSLUpload{
|
||||
Type: "paste",
|
||||
PrivateKey: string(privateKeyContent),
|
||||
Certificate: string(certificateContent),
|
||||
Description: req.Description,
|
||||
SSLID: uint(req.SSLID),
|
||||
}
|
||||
|
||||
if err := websiteSSLService.Upload(uploadReq); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
func readUploadedFile(fileHeader *multipart.FileHeader) ([]byte, error) {
|
||||
file, err := fileHeader.Open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return io.ReadAll(file)
|
||||
}
|
||||
|
||||
// @Tags Website SSL
|
||||
// @Summary Download SSL file
|
||||
// @Accept json
|
||||
|
||||
@@ -292,6 +292,7 @@ type AlertConfigTest struct {
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
Sender string `json:"sender"`
|
||||
UserName string `json:"userName"`
|
||||
Password string `json:"password"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Encryption string `json:"encryption"` // "ssl" / "tls" / "none"
|
||||
@@ -323,6 +324,7 @@ type AlertEmailConfig struct {
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
Sender string `json:"sender"`
|
||||
UserName string `json:"userName"`
|
||||
Password string `json:"password"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Encryption string `json:"encryption"` // "ssl" / "tls" / "none"
|
||||
|
||||
@@ -128,6 +128,7 @@ type Locale struct {
|
||||
Zh string `json:"zh"`
|
||||
Ko string `json:"ko"`
|
||||
Tr string `json:"tr"`
|
||||
Es string `json:"es-es" yaml:"es-es"`
|
||||
}
|
||||
|
||||
type AppForm struct {
|
||||
|
||||
@@ -53,6 +53,11 @@ type BackupOption struct {
|
||||
IsPublic bool `json:"isPublic"`
|
||||
}
|
||||
|
||||
type UploadForRecover struct {
|
||||
FilePath string `json:"filePath"`
|
||||
TargetDir string `json:"targetDir"`
|
||||
}
|
||||
|
||||
type CommonBackup struct {
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql mysql-cluster postgresql-cluster redis-cluster"`
|
||||
Name string `json:"name"`
|
||||
@@ -94,6 +99,9 @@ type BackupRecords struct {
|
||||
DownloadAccountID uint `json:"downloadAccountID"`
|
||||
FileDir string `json:"fileDir"`
|
||||
FileName string `json:"fileName"`
|
||||
TaskID string `json:"taskID"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
|
||||
+16
-9
@@ -30,8 +30,10 @@ type ClamInfo struct {
|
||||
Path string `json:"path"`
|
||||
InfectedStrategy string `json:"infectedStrategy"`
|
||||
InfectedDir string `json:"infectedDir"`
|
||||
LastHandleDate string `json:"lastHandleDate"`
|
||||
LastRecordStatus string `json:"lastRecordStatus"`
|
||||
LastRecordTime string `json:"lastRecordTime"`
|
||||
Spec string `json:"spec"`
|
||||
Timeout uint `json:"timeout"`
|
||||
Description string `json:"description"`
|
||||
AlertCount uint `json:"alertCount"`
|
||||
AlertMethod string `json:"alertMethod"`
|
||||
@@ -41,6 +43,7 @@ type ClamLogSearch struct {
|
||||
PageInfo
|
||||
|
||||
ClamID uint `json:"clamID"`
|
||||
Status string `json:"status"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
EndTime time.Time `json:"endTime"`
|
||||
}
|
||||
@@ -56,13 +59,16 @@ type ClamFileReq struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
|
||||
type ClamLog struct {
|
||||
Name string `json:"name"`
|
||||
ScanDate string `json:"scanDate"`
|
||||
ScanTime string `json:"scanTime"`
|
||||
InfectedFiles string `json:"infectedFiles"`
|
||||
TotalError string `json:"totalError"`
|
||||
Status string `json:"status"`
|
||||
type ClamRecord struct {
|
||||
ID uint `json:"id"`
|
||||
TaskID string `json:"taskID"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
ScanTime string `json:"scanTime"`
|
||||
InfectedFiles string `json:"infectedFiles"`
|
||||
TotalError string `json:"totalError"`
|
||||
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type ClamCreate struct {
|
||||
@@ -72,6 +78,7 @@ type ClamCreate struct {
|
||||
InfectedStrategy string `json:"infectedStrategy"`
|
||||
InfectedDir string `json:"infectedDir"`
|
||||
Spec string `json:"spec"`
|
||||
Timeout uint `json:"timeout"`
|
||||
Description string `json:"description"`
|
||||
AlertCount uint `json:"alertCount"`
|
||||
AlertTitle string `json:"alertTitle"`
|
||||
@@ -86,6 +93,7 @@ type ClamUpdate struct {
|
||||
InfectedStrategy string `json:"infectedStrategy"`
|
||||
InfectedDir string `json:"infectedDir"`
|
||||
Spec string `json:"spec"`
|
||||
Timeout uint `json:"timeout"`
|
||||
Description string `json:"description"`
|
||||
AlertCount uint `json:"alertCount"`
|
||||
AlertTitle string `json:"alertTitle"`
|
||||
@@ -98,7 +106,6 @@ type ClamUpdateStatus struct {
|
||||
}
|
||||
|
||||
type ClamDelete struct {
|
||||
RemoveRecord bool `json:"removeRecord"`
|
||||
RemoveInfected bool `json:"removeInfected"`
|
||||
Ids []uint `json:"ids" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -71,7 +71,6 @@ type ResourceLimit struct {
|
||||
|
||||
type ContainerOperate struct {
|
||||
TaskID string `json:"taskID"`
|
||||
ContainerID string `json:"containerID"`
|
||||
ForcePull bool `json:"forcePull"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Image string `json:"image" validate:"required"`
|
||||
@@ -144,6 +143,7 @@ type VolumeHelper struct {
|
||||
SourceDir string `json:"sourceDir"`
|
||||
ContainerDir string `json:"containerDir"`
|
||||
Mode string `json:"mode"`
|
||||
Shared string `json:"shared"`
|
||||
}
|
||||
type PortHelper struct {
|
||||
HostIP string `json:"hostIP"`
|
||||
@@ -209,11 +209,12 @@ type NetworkCreate struct {
|
||||
}
|
||||
|
||||
type Volume struct {
|
||||
Name string `json:"name"`
|
||||
Labels []string `json:"labels"`
|
||||
Driver string `json:"driver"`
|
||||
Mountpoint string `json:"mountpoint"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Name string `json:"name"`
|
||||
Labels []VolumeOption `json:"labels"`
|
||||
Driver string `json:"driver"`
|
||||
Mountpoint string `json:"mountpoint"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Options []VolumeOption `json:"options"`
|
||||
}
|
||||
type VolumeCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
@@ -221,7 +222,10 @@ type VolumeCreate struct {
|
||||
Options []string `json:"options"`
|
||||
Labels []string `json:"labels"`
|
||||
}
|
||||
|
||||
type VolumeOption struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
type BatchDelete struct {
|
||||
TaskID string `json:"taskID"`
|
||||
Force bool `json:"force"`
|
||||
|
||||
@@ -23,9 +23,25 @@ type DashboardBase struct {
|
||||
CPULogicalCores int `json:"cpuLogicalCores"`
|
||||
CPUModelName string `json:"cpuModelName"`
|
||||
|
||||
QuickJumps []QuickJump `json:"quickJump"`
|
||||
CurrentInfo DashboardCurrent `json:"currentInfo"`
|
||||
}
|
||||
|
||||
type ChangeQuicks struct {
|
||||
Quicks []QuickJump `json:"quicks"`
|
||||
}
|
||||
|
||||
type QuickJump struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Title string `json:"title"`
|
||||
Detail string `json:"detail"`
|
||||
Recommend int `json:"recommend"`
|
||||
IsShow bool `json:"isShow"`
|
||||
Router string `json:"router"`
|
||||
}
|
||||
|
||||
type OsInfo struct {
|
||||
OS string `json:"os"`
|
||||
Platform string `json:"platform"`
|
||||
|
||||
@@ -27,7 +27,8 @@ type SwapHelper struct {
|
||||
Size uint64 `json:"size"`
|
||||
Used string `json:"used"`
|
||||
|
||||
IsNew bool `json:"isNew"`
|
||||
IsNew bool `json:"isNew"`
|
||||
TaskID string `json:"taskID"`
|
||||
}
|
||||
|
||||
type TimeZoneOptions struct {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package dto
|
||||
|
||||
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,omitempty"`
|
||||
}
|
||||
|
||||
type LsblkOutput struct {
|
||||
BlockDevices []LsblkDevice `json:"blockdevices"`
|
||||
}
|
||||
|
||||
type DiskFormatRequest struct {
|
||||
Device string `json:"device" `
|
||||
Filesystem string `json:"filesystem" `
|
||||
Label string `json:"label,omitempty" `
|
||||
QuickFormat bool `json:"quickFormat,omitempty"`
|
||||
}
|
||||
@@ -17,7 +17,8 @@ type RuleSearch struct {
|
||||
}
|
||||
|
||||
type FirewallOperation struct {
|
||||
Operation string `json:"operation" validate:"required,oneof=start stop restart disablePing enablePing"`
|
||||
Operation string `json:"operation" validate:"required,oneof=start stop restart disablePing enablePing"`
|
||||
WithDockerRestart bool `json:"withDockerRestart"`
|
||||
}
|
||||
|
||||
type PortRuleOperate struct {
|
||||
@@ -36,6 +37,7 @@ type ForwardRuleOperate struct {
|
||||
Operation string `json:"operation" validate:"required,oneof=add remove"`
|
||||
Num string `json:"num"`
|
||||
Protocol string `json:"protocol" validate:"required,oneof=tcp udp tcp/udp"`
|
||||
Interface string `json:"interface"`
|
||||
Port string `json:"port" validate:"required"`
|
||||
TargetIP string `json:"targetIP"`
|
||||
TargetPort string `json:"targetPort" validate:"required"`
|
||||
|
||||
@@ -30,14 +30,14 @@ type ImageBuild struct {
|
||||
}
|
||||
|
||||
type ImagePull struct {
|
||||
TaskID string `json:"taskID"`
|
||||
RepoID uint `json:"repoID"`
|
||||
ImageName string `json:"imageName" validate:"required"`
|
||||
TaskID string `json:"taskID"`
|
||||
RepoID uint `json:"repoID"`
|
||||
ImageName []string `json:"imageName" validate:"required"`
|
||||
}
|
||||
|
||||
type ImageTag struct {
|
||||
SourceID string `json:"sourceID" validate:"required"`
|
||||
TargetName string `json:"targetName" validate:"required"`
|
||||
SourceID string `json:"sourceID" validate:"required"`
|
||||
Tags []string `json:"tags" validate:"required"`
|
||||
}
|
||||
|
||||
type ImagePush struct {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package request
|
||||
|
||||
type DiskPartitionRequest struct {
|
||||
Device string `json:"device" validate:"required"`
|
||||
Filesystem string `json:"filesystem" validate:"required,oneof=ext4 xfs"`
|
||||
Label string `json:"label"`
|
||||
AutoMount bool `json:"autoMount"`
|
||||
MountPoint string `json:"mountPoint" validate:"required"`
|
||||
}
|
||||
|
||||
type DiskMountRequest struct {
|
||||
Device string `json:"device" validate:"required"`
|
||||
MountPoint string `json:"mountPoint" validate:"required"`
|
||||
Filesystem string `json:"filesystem" validate:"required,oneof=ext4 xfs"`
|
||||
AutoMount bool `json:"autoMount"`
|
||||
}
|
||||
|
||||
type DiskUnmountRequest struct {
|
||||
MountPoint string `json:"mountPoint" validate:"required"`
|
||||
}
|
||||
@@ -148,3 +148,19 @@ type FileExistReq struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Dir string `json:"dir" validate:"required"`
|
||||
}
|
||||
|
||||
type FileConvert struct {
|
||||
Path string `json:"path" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
InputFile string `json:"inputFile" validate:"required"`
|
||||
Extension string `json:"extension" validate:"required"`
|
||||
OutputFormat string `json:"outputFormat" validate:"required"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type FileConvertRequest struct {
|
||||
Files []FileConvert `json:"files" validate:"required"`
|
||||
OutputPath string `json:"outputPath" validate:"required"`
|
||||
DeleteSource bool `json:"deleteSource"`
|
||||
TaskID string `json:"taskID"`
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ type SupervisorProcessConfig struct {
|
||||
Numprocs string `json:"numprocs"`
|
||||
AutoRestart string `json:"autoRestart"`
|
||||
AutoStart string `json:"autoStart"`
|
||||
Environment string `json:"environment"`
|
||||
}
|
||||
|
||||
type SupervisorProcessFileReq struct {
|
||||
|
||||
@@ -20,6 +20,7 @@ type McpServerCreate struct {
|
||||
HostIP string `json:"hostIP"`
|
||||
StreamableHttpPath string `json:"streamableHttpPath"`
|
||||
OutputTransport string `json:"outputTransport" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
}
|
||||
|
||||
type McpServerUpdate struct {
|
||||
|
||||
@@ -81,8 +81,8 @@ type NginxCommonReq struct {
|
||||
|
||||
type NginxAntiLeechUpdate struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
Extends string `json:"extends" validate:"required"`
|
||||
Return string `json:"return" validate:"required"`
|
||||
Extends string `json:"extends"`
|
||||
Return string `json:"return"`
|
||||
Enable bool `json:"enable" `
|
||||
ServerNames []string `json:"serverNames"`
|
||||
Cache bool `json:"cache"`
|
||||
|
||||
@@ -100,6 +100,7 @@ type PHPConfigUpdate struct {
|
||||
Scope string `json:"scope" validate:"required"`
|
||||
DisableFunctions []string `json:"disableFunctions"`
|
||||
UploadMaxSize string `json:"uploadMaxSize"`
|
||||
MaxExecutionTime string `json:"maxExecutionTime"`
|
||||
}
|
||||
|
||||
type PHPFileUpdate struct {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package request
|
||||
|
||||
import "github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
|
||||
type TensorRTLLMSearch struct {
|
||||
dto.PageInfo
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type TensorRTLLMCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
ContainerName string `json:"containerName" validate:"required"`
|
||||
Version string `json:"version" validate:"required"`
|
||||
ModelDir string `json:"modelDir" validate:"required"`
|
||||
Image string `json:"image" validate:"required"`
|
||||
Command string `json:"command" validate:"required"`
|
||||
DockerConfig
|
||||
}
|
||||
|
||||
type TensorRTLLMUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
TensorRTLLMCreate
|
||||
}
|
||||
|
||||
type TensorRTLLMDelete struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
type TensorRTLLMOperate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Operate string `json:"operate" validate:"required"`
|
||||
}
|
||||
|
||||
type DockerConfig struct {
|
||||
ExposedPorts []ExposedPort `json:"exposedPorts"`
|
||||
Environments []Environment `json:"environments"`
|
||||
Volumes []Volume `json:"volumes"`
|
||||
}
|
||||
@@ -95,6 +95,7 @@ type WebsiteDelete struct {
|
||||
DeleteApp bool `json:"deleteApp"`
|
||||
DeleteBackup bool `json:"deleteBackup"`
|
||||
ForceDelete bool `json:"forceDelete"`
|
||||
DeleteDB bool `json:"deleteDB"`
|
||||
}
|
||||
|
||||
type WebsiteOp struct {
|
||||
@@ -102,6 +103,12 @@ type WebsiteOp struct {
|
||||
Operate string `json:"operate"`
|
||||
}
|
||||
|
||||
type BatchWebsiteOp struct {
|
||||
IDs []uint `json:"ids" validate:"required"`
|
||||
Operate string `json:"operate" validate:"required"`
|
||||
TaskID string `json:"taskID" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteRedirectUpdate struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Key string `json:"key" validate:"required"`
|
||||
@@ -222,6 +229,21 @@ type WebsiteProxyConfig struct {
|
||||
Replaces map[string]string `json:"replaces"`
|
||||
SNI bool `json:"sni"`
|
||||
ProxySSLName string `json:"proxySSLName"`
|
||||
CorsConfig
|
||||
}
|
||||
|
||||
type CorsConfig struct {
|
||||
Cors bool `json:"cors"`
|
||||
AllowOrigins string `json:"allowOrigins"`
|
||||
AllowMethods string `json:"allowMethods"`
|
||||
AllowHeaders string `json:"allowHeaders"`
|
||||
AllowCredentials bool `json:"allowCredentials"`
|
||||
Preflight bool `json:"preflight"`
|
||||
}
|
||||
|
||||
type CorsConfigReq struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
CorsConfig
|
||||
}
|
||||
|
||||
type WebsiteProxyReq struct {
|
||||
|
||||
@@ -6,6 +6,8 @@ type WebsiteSSLSearch struct {
|
||||
dto.PageInfo
|
||||
AcmeAccountID string `json:"acmeAccountID"`
|
||||
Domain string `json:"domain"`
|
||||
OrderBy string `json:"orderBy" validate:"required,oneof=expire_date"`
|
||||
Order string `json:"order" validate:"required,oneof=null ascending descending"`
|
||||
}
|
||||
|
||||
type WebsiteSSLCreate struct {
|
||||
@@ -155,3 +157,9 @@ type WebsiteCAObtain struct {
|
||||
type WebsiteCARenew struct {
|
||||
SSLID uint `json:"SSLID" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteSSLFileUpload struct {
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
SSLID uint64 `json:"sslID"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package response
|
||||
|
||||
type DiskInfo struct {
|
||||
DiskBasicInfo
|
||||
Partitions []DiskBasicInfo `json:"partitions"`
|
||||
}
|
||||
|
||||
type DiskBasicInfo struct {
|
||||
Device string `json:"device"`
|
||||
Size string `json:"size"`
|
||||
Model string `json:"model"`
|
||||
DiskType string `json:"diskType"`
|
||||
IsRemovable bool `json:"isRemovable"`
|
||||
IsSystem bool `json:"isSystem"`
|
||||
Filesystem string `json:"filesystem"`
|
||||
Used string `json:"used"`
|
||||
Avail string `json:"avail"`
|
||||
UsePercent int `json:"usePercent"`
|
||||
MountPoint string `json:"mountPoint"`
|
||||
IsMounted bool `json:"isMounted"`
|
||||
Serial string `json:"serial"`
|
||||
}
|
||||
|
||||
type CompleteDiskInfo struct {
|
||||
Disks []DiskInfo `json:"disks"`
|
||||
UnpartitionedDisks []DiskBasicInfo `json:"unpartitionedDisks"`
|
||||
SystemDisk *DiskInfo `json:"systemDisk"`
|
||||
TotalDisks int `json:"totalDisks"`
|
||||
TotalCapacity int64 `json:"totalCapacity"`
|
||||
}
|
||||
|
||||
type MountInfo struct {
|
||||
Device string `json:"device"`
|
||||
MountPoint string `json:"mountPoint"`
|
||||
Filesystem string `json:"filesystem"`
|
||||
Options string `json:"options"`
|
||||
}
|
||||
@@ -73,3 +73,11 @@ type DepthDirSizeRes struct {
|
||||
Path string `json:"path"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
type FileConvertLog struct {
|
||||
Date string `json:"date"`
|
||||
Type string `json:"type"`
|
||||
Log string `json:"log"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ type SupervisorProcessConfig struct {
|
||||
Status []ProcessStatus `json:"status"`
|
||||
AutoRestart string `json:"autoRestart"`
|
||||
AutoStart string `json:"autoStart"`
|
||||
Environment string `json:"environment"`
|
||||
}
|
||||
|
||||
type ProcessStatus struct {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package response
|
||||
|
||||
type ComponentInfo struct {
|
||||
Exists bool `json:"exists"`
|
||||
Version string `json:"version"`
|
||||
Path string `json:"path"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
)
|
||||
|
||||
type TensorRTLLMsRes struct {
|
||||
Items []TensorRTLLMDTO `json:"items"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
type TensorRTLLMDTO struct {
|
||||
model.TensorRTLLM
|
||||
Version string `json:"version"`
|
||||
Model string `json:"model"`
|
||||
Dir string `json:"dir"`
|
||||
ModelDir string `json:"modelDir"`
|
||||
Image string `json:"image"`
|
||||
Command string `json:"command"`
|
||||
|
||||
ExposedPorts []request.ExposedPort `json:"exposedPorts"`
|
||||
Environments []request.Environment `json:"environments"`
|
||||
Volumes []request.Volume `json:"volumes"`
|
||||
}
|
||||
@@ -59,16 +59,16 @@ type WebsiteNginxConfig struct {
|
||||
}
|
||||
|
||||
type WebsiteHTTPS struct {
|
||||
Enable bool `json:"enable"`
|
||||
HttpConfig string `json:"httpConfig"`
|
||||
SSL model.WebsiteSSL `json:"SSL"`
|
||||
SSLProtocol []string `json:"SSLProtocol"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
Hsts bool `json:"hsts"`
|
||||
HstsIncludeSubDomains bool `json:"hstsIncludeSubDomains"`
|
||||
HttpsPorts []int `json:"httpsPorts"`
|
||||
HttpsPort string `json:"httpsPort"`
|
||||
Http3 bool `json:"http3"`
|
||||
Enable bool `json:"enable"`
|
||||
HttpConfig string `json:"httpConfig"`
|
||||
SSL model.WebsiteSSL `json:"SSL"`
|
||||
SSLProtocol []string `json:"SSLProtocol"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
Hsts bool `json:"hsts"`
|
||||
HstsIncludeSubDomains bool `json:"hstsIncludeSubDomains"`
|
||||
HttpsPorts []int `json:"httpsPorts"`
|
||||
HttpsPort string `json:"httpsPort"`
|
||||
Http3 bool `json:"http3"`
|
||||
}
|
||||
|
||||
type WebsiteLog struct {
|
||||
@@ -82,6 +82,7 @@ type PHPConfig struct {
|
||||
Params map[string]string `json:"params"`
|
||||
DisableFunctions []string `json:"disableFunctions"`
|
||||
UploadMaxSize string `json:"uploadMaxSize"`
|
||||
MaxExecutionTime string `json:"maxExecutionTime"`
|
||||
}
|
||||
|
||||
type NginxRewriteRes struct {
|
||||
|
||||
@@ -69,6 +69,8 @@ type SSHConnData struct {
|
||||
Password string `json:"password"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
PassPhrase string `json:"passPhrase"`
|
||||
|
||||
LocalSSHConnShow string `json:"localSSHConnShow"`
|
||||
}
|
||||
|
||||
type SystemProxy struct {
|
||||
|
||||
@@ -22,7 +22,8 @@ type SSHInfo struct {
|
||||
CurrentUser string `json:"currentUser"`
|
||||
}
|
||||
|
||||
type CreateRootCert struct {
|
||||
type RootCertOperate struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Mode string `json:"mode"`
|
||||
EncryptionMode string `json:"encryptionMode" validate:"required,oneof=rsa ed25519 ecdsa dsa"`
|
||||
|
||||
@@ -43,11 +43,17 @@ type App struct {
|
||||
func (i *App) IsLocalApp() bool {
|
||||
return i.Resource == constant.ResourceLocal
|
||||
}
|
||||
func (i *App) IsCustomApp() bool {
|
||||
return i.Resource == constant.AppResourceCustom
|
||||
}
|
||||
|
||||
func (i *App) GetAppResourcePath() string {
|
||||
if i.IsLocalApp() {
|
||||
//这里要去掉本地应用的local前缀
|
||||
return filepath.Join(global.Dir.LocalAppResourceDir, strings.TrimPrefix(i.Key, "local"))
|
||||
}
|
||||
if i.IsCustomApp() {
|
||||
return filepath.Join(global.Dir.CustomAppResourceDir, i.Key)
|
||||
}
|
||||
return filepath.Join(global.Dir.RemoteAppResourceDir, i.Key)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,3 +4,14 @@ type AppLauncher struct {
|
||||
BaseModel
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
type QuickJump struct {
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Title string `json:"title"`
|
||||
Detail string `json:"detail"`
|
||||
Recommend int `json:"recommend"`
|
||||
IsShow bool `json:"isShow"`
|
||||
Router string `json:"router"`
|
||||
}
|
||||
|
||||
@@ -27,5 +27,8 @@ type BackupRecord struct {
|
||||
FileDir string `json:"fileDir"`
|
||||
FileName string `json:"fileName"`
|
||||
|
||||
TaskID string `json:"taskID"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
+20
-1
@@ -1,14 +1,33 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type Clam struct {
|
||||
BaseModel
|
||||
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Status string `json:"status"`
|
||||
Path string `gorm:"not null" json:"path"`
|
||||
InfectedStrategy string `json:"infectedStrategy"`
|
||||
InfectedDir string `json:"infectedDir"`
|
||||
Spec string `json:"spec"`
|
||||
RetryTimes uint `json:"retryTimes"`
|
||||
Timeout uint `json:"timeout"`
|
||||
EntryID int `json:"entryID"`
|
||||
Description string `json:"description"`
|
||||
|
||||
Status string `json:"status"`
|
||||
IsExecuting bool `json:"isExecuting"`
|
||||
}
|
||||
|
||||
type ClamRecord struct {
|
||||
BaseModel
|
||||
|
||||
ClamID uint `json:"clamID"`
|
||||
TaskID string `json:"taskID"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
ScanTime string `json:"scanTime"`
|
||||
InfectedFiles string `json:"infectedFiles"`
|
||||
TotalError string `json:"totalError"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// BackupAccounts ---> SourceAccountIDs
|
||||
// BackupAccounts ---> DownloadAccountID
|
||||
type Cronjob struct {
|
||||
BaseModel
|
||||
|
||||
@@ -40,10 +38,11 @@ type Cronjob struct {
|
||||
IgnoreErr bool `json:"ignoreErr"`
|
||||
RetainCopies uint64 `json:"retainCopies"`
|
||||
|
||||
Status string `json:"status"`
|
||||
EntryIDs string `json:"entryIDs"`
|
||||
Records []JobRecords `json:"records"`
|
||||
Secret string `json:"secret"`
|
||||
IsExecuting bool `json:"isExecuting"`
|
||||
Status string `json:"status"`
|
||||
EntryIDs string `json:"entryIDs"`
|
||||
Records []JobRecords `json:"records"`
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
type JobRecords struct {
|
||||
|
||||
@@ -18,4 +18,5 @@ type Forward struct {
|
||||
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"`
|
||||
}
|
||||
|
||||
@@ -17,4 +17,5 @@ type McpServer struct {
|
||||
HostIP string `json:"hostIP"`
|
||||
StreamableHttpPath string `json:"streamableHttpPath"`
|
||||
OutputTransport string `json:"outputTransport"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
@@ -49,3 +49,7 @@ func (r *Runtime) GetPHPPath() string {
|
||||
func (r *Runtime) GetLogPath() string {
|
||||
return path.Join(r.GetPath(), "build.log")
|
||||
}
|
||||
|
||||
func (r *Runtime) GetSlowLogPath() string {
|
||||
return path.Join(r.GetPath(), "log", "fpm.slow.log")
|
||||
}
|
||||
|
||||
@@ -15,3 +15,13 @@ type NodeInfo struct {
|
||||
ServerCrt string `json:"serverCrt"`
|
||||
ServerKey string `json:"serverKey"`
|
||||
}
|
||||
|
||||
type LocalConnInfo struct {
|
||||
Addr string `json:"addr"`
|
||||
Port uint `json:"port"`
|
||||
User string `json:"user"`
|
||||
AuthMode string `json:"authMode"`
|
||||
Password string `json:"password"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
PassPhrase string `json:"passPhrase"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package model
|
||||
|
||||
type TensorRTLLM struct {
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
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"`
|
||||
}
|
||||
@@ -10,35 +10,37 @@ import (
|
||||
|
||||
type WebsiteSSL struct {
|
||||
BaseModel
|
||||
PrimaryDomain string `json:"primaryDomain"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
Pem string `json:"pem"`
|
||||
Domains string `json:"domains"`
|
||||
CertURL string `json:"certURL"`
|
||||
Type string `json:"type"`
|
||||
Provider string `json:"provider"`
|
||||
Organization string `json:"organization"`
|
||||
DnsAccountID uint `json:"dnsAccountId"`
|
||||
AcmeAccountID uint `gorm:"column:acme_account_id" json:"acmeAccountId" `
|
||||
CaID uint `json:"caId"`
|
||||
AutoRenew bool `json:"autoRenew"`
|
||||
ExpireDate time.Time `json:"expireDate"`
|
||||
StartDate time.Time `json:"startDate"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
KeyType string `json:"keyType"`
|
||||
PushDir bool `json:"pushDir"`
|
||||
Dir string `json:"dir"`
|
||||
Description string `json:"description"`
|
||||
SkipDNS bool `json:"skipDNS"`
|
||||
Nameserver1 string `json:"nameserver1"`
|
||||
Nameserver2 string `json:"nameserver2"`
|
||||
DisableCNAME bool `json:"disableCNAME"`
|
||||
ExecShell bool `json:"execShell"`
|
||||
Shell string `json:"shell"`
|
||||
MasterSSLID uint `json:"masterSslId"`
|
||||
Nodes string `json:"nodes"`
|
||||
PushNode bool `json:"pushNode"`
|
||||
PrimaryDomain string `json:"primaryDomain"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
Pem string `json:"pem"`
|
||||
Domains string `json:"domains"`
|
||||
CertURL string `json:"certURL"`
|
||||
Type string `json:"type"`
|
||||
Provider string `json:"provider"`
|
||||
Organization string `json:"organization"`
|
||||
DnsAccountID uint `json:"dnsAccountId"`
|
||||
AcmeAccountID uint `gorm:"column:acme_account_id" json:"acmeAccountId" `
|
||||
CaID uint `json:"caId"`
|
||||
AutoRenew bool `json:"autoRenew"`
|
||||
ExpireDate time.Time `json:"expireDate"`
|
||||
StartDate time.Time `json:"startDate"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
KeyType string `json:"keyType"`
|
||||
PushDir bool `json:"pushDir"`
|
||||
Dir string `json:"dir"`
|
||||
Description string `json:"description"`
|
||||
SkipDNS bool `json:"skipDNS"`
|
||||
Nameserver1 string `json:"nameserver1"`
|
||||
Nameserver2 string `json:"nameserver2"`
|
||||
DisableCNAME bool `json:"disableCNAME"`
|
||||
ExecShell bool `json:"execShell"`
|
||||
Shell string `json:"shell"`
|
||||
MasterSSLID uint `json:"masterSslId"`
|
||||
Nodes string `json:"nodes"`
|
||||
PushNode bool `json:"pushNode"`
|
||||
PrivateKeyPath string `json:"privateKeyPath"`
|
||||
CertPath string `json:"certPath"`
|
||||
|
||||
AcmeAccount WebsiteAcmeAccount `json:"acmeAccount" gorm:"-:migration"`
|
||||
DnsAccount WebsiteDnsAccount `json:"dnsAccount" gorm:"-:migration"`
|
||||
|
||||
@@ -13,6 +13,10 @@ type ILauncherRepo interface {
|
||||
Create(launcher *model.AppLauncher) error
|
||||
Save(launcher *model.AppLauncher) error
|
||||
Delete(opts ...DBOption) error
|
||||
|
||||
GetQuickJump(opts ...DBOption) (model.QuickJump, error)
|
||||
ListQuickJump(withAll bool) []model.QuickJump
|
||||
UpdateQuicks(quicks []model.QuickJump) error
|
||||
}
|
||||
|
||||
func NewILauncherRepo() ILauncherRepo {
|
||||
@@ -57,3 +61,46 @@ func (u *LauncherRepo) Delete(opts ...DBOption) error {
|
||||
}
|
||||
return db.Delete(&model.AppLauncher{}).Error
|
||||
}
|
||||
|
||||
func (u *LauncherRepo) GetQuickJump(opts ...DBOption) (model.QuickJump, error) {
|
||||
var launcher model.QuickJump
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.First(&launcher).Error
|
||||
return launcher, err
|
||||
}
|
||||
func (u *LauncherRepo) ListQuickJump(withAll bool) []model.QuickJump {
|
||||
var quicks []model.QuickJump
|
||||
if withAll {
|
||||
_ = global.DB.Find(&quicks).Error
|
||||
} else {
|
||||
_ = global.DB.Where("is_show = ?", true).Find(&quicks).Error
|
||||
}
|
||||
if !withAll && len(quicks) == 0 {
|
||||
return []model.QuickJump{
|
||||
{Name: "Website", Title: "menu.website", Recommend: 10, IsShow: true, Router: "/websites"},
|
||||
{Name: "Database", Title: "menu.database", Recommend: 30, IsShow: true, Router: "/databases"},
|
||||
{Name: "Cronjob", Title: "menu.cronjob", Recommend: 50, IsShow: true, Router: "/cronjobs"},
|
||||
{Name: "AppInstalled", Title: "home.appInstalled", Recommend: 70, IsShow: true, Router: "/apps/installed"},
|
||||
}
|
||||
}
|
||||
|
||||
return quicks
|
||||
}
|
||||
func (u *LauncherRepo) UpdateQuicks(quicks []model.QuickJump) error {
|
||||
tx := global.DB.Begin()
|
||||
for _, item := range quicks {
|
||||
if err := tx.Model(&model.QuickJump{}).Where("id = ?", item.ID).Updates(map[string]interface{}{
|
||||
"is_show": item.IsShow,
|
||||
"detail": item.Detail,
|
||||
"alias": item.Alias,
|
||||
}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ClamRepo struct{}
|
||||
@@ -14,6 +19,14 @@ type IClamRepo interface {
|
||||
Delete(opts ...DBOption) error
|
||||
Get(opts ...DBOption) (model.Clam, error)
|
||||
List(opts ...DBOption) ([]model.Clam, error)
|
||||
|
||||
WithByClamID(id uint) DBOption
|
||||
ListRecord(opts ...DBOption) ([]model.ClamRecord, error)
|
||||
RecordFirst(id uint) (model.ClamRecord, error)
|
||||
DeleteRecord(opts ...DBOption) error
|
||||
StartRecords(clamID uint) model.ClamRecord
|
||||
EndRecords(record model.ClamRecord, status, message string)
|
||||
PageRecords(page, size int, opts ...DBOption) (int64, []model.ClamRecord, error)
|
||||
}
|
||||
|
||||
func NewIClamRepo() IClamRepo {
|
||||
@@ -67,3 +80,69 @@ func (u *ClamRepo) Delete(opts ...DBOption) error {
|
||||
}
|
||||
return db.Delete(&model.Clam{}).Error
|
||||
}
|
||||
|
||||
func (c *ClamRepo) WithByClamID(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("clam_id = ?", id)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *ClamRepo) ListRecord(opts ...DBOption) ([]model.ClamRecord, error) {
|
||||
var record []model.ClamRecord
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(&record).Error
|
||||
return record, err
|
||||
}
|
||||
|
||||
func (u *ClamRepo) RecordFirst(id uint) (model.ClamRecord, error) {
|
||||
var record model.ClamRecord
|
||||
err := global.DB.Where("clam_id = ?", id).Order("created_at desc").First(&record).Error
|
||||
return record, err
|
||||
}
|
||||
|
||||
func (u *ClamRepo) PageRecords(page, size int, opts ...DBOption) (int64, []model.ClamRecord, error) {
|
||||
var records []model.ClamRecord
|
||||
db := global.DB.Model(&model.ClamRecord{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Order("created_at desc").Limit(size).Offset(size * (page - 1)).Find(&records).Error
|
||||
return count, records, err
|
||||
}
|
||||
func (u *ClamRepo) StartRecords(clamID uint) model.ClamRecord {
|
||||
var record model.ClamRecord
|
||||
record.StartTime = time.Now()
|
||||
record.ClamID = clamID
|
||||
record.TaskID = uuid.New().String()
|
||||
record.Status = constant.StatusWaiting
|
||||
if err := global.DB.Create(&record).Error; err != nil {
|
||||
global.LOG.Errorf("create record status failed, err: %v", err)
|
||||
}
|
||||
_ = u.Update(clamID, map[string]interface{}{"is_executing": true})
|
||||
return record
|
||||
}
|
||||
func (u *ClamRepo) EndRecords(record model.ClamRecord, status, message string) {
|
||||
upMap := make(map[string]interface{})
|
||||
upMap["status"] = status
|
||||
upMap["message"] = message
|
||||
upMap["task_id"] = record.TaskID
|
||||
upMap["scan_time"] = record.ScanTime
|
||||
upMap["infected_files"] = record.InfectedFiles
|
||||
upMap["total_error"] = record.TotalError
|
||||
if err := global.DB.Model(&model.ClamRecord{}).Where("id = ?", record.ID).Updates(upMap).Error; err != nil {
|
||||
global.LOG.Errorf("update record status failed, err: %v", err)
|
||||
}
|
||||
_ = u.Update(record.ClamID, map[string]interface{}{"is_executing": false})
|
||||
}
|
||||
func (u *ClamRepo) DeleteRecord(opts ...DBOption) error {
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
return db.Delete(&model.ClamRecord{}).Error
|
||||
}
|
||||
|
||||
@@ -29,9 +29,10 @@ type ICronjobRepo interface {
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
Delete(opts ...DBOption) error
|
||||
DeleteRecord(opts ...DBOption) error
|
||||
StartRecords(cronjobID uint, targetPath, cronjobType string) model.JobRecords
|
||||
StartRecords(cronjobID uint) model.JobRecords
|
||||
UpdateRecords(id uint, vars map[string]interface{}) error
|
||||
EndRecords(record model.JobRecords, status, message, records string)
|
||||
AddFailedRecord(cronjobID uint, message string)
|
||||
PageRecords(page, size int, opts ...DBOption) (int64, []model.JobRecords, error)
|
||||
}
|
||||
|
||||
@@ -143,7 +144,7 @@ func (c *CronjobRepo) WithByRecordDropID(id int) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (u *CronjobRepo) StartRecords(cronjobID uint, targetPath, cronjobType string) model.JobRecords {
|
||||
func (u *CronjobRepo) StartRecords(cronjobID uint) model.JobRecords {
|
||||
var record model.JobRecords
|
||||
record.StartTime = time.Now()
|
||||
record.CronjobID = cronjobID
|
||||
@@ -152,6 +153,7 @@ func (u *CronjobRepo) StartRecords(cronjobID uint, targetPath, cronjobType strin
|
||||
if err := global.DB.Create(&record).Error; err != nil {
|
||||
global.LOG.Errorf("create record status failed, err: %v", err)
|
||||
}
|
||||
_ = u.Update(cronjobID, map[string]interface{}{"is_executing": true})
|
||||
return record
|
||||
}
|
||||
func (u *CronjobRepo) EndRecords(record model.JobRecords, status, message, records string) {
|
||||
@@ -165,6 +167,17 @@ func (u *CronjobRepo) EndRecords(record model.JobRecords, status, message, recor
|
||||
if err := global.DB.Model(&model.JobRecords{}).Where("id = ?", record.ID).Updates(errMap).Error; err != nil {
|
||||
global.LOG.Errorf("update record status failed, err: %v", err)
|
||||
}
|
||||
_ = u.Update(record.CronjobID, map[string]interface{}{"is_executing": false})
|
||||
}
|
||||
func (u *CronjobRepo) AddFailedRecord(cronjobID uint, message string) {
|
||||
var record model.JobRecords
|
||||
record.StartTime = time.Now()
|
||||
record.CronjobID = cronjobID
|
||||
record.Status = constant.StatusFailed
|
||||
record.Message = message
|
||||
if err := global.DB.Create(&record).Error; err != nil {
|
||||
global.LOG.Errorf("create record status failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *CronjobRepo) Save(id uint, cronjob model.Cronjob) error {
|
||||
|
||||
@@ -19,7 +19,7 @@ type IHostRepo interface {
|
||||
GetCert(opts ...DBOption) (model.RootCert, error)
|
||||
PageCert(limit, offset int, opts ...DBOption) (int64, []model.RootCert, error)
|
||||
ListCert(opts ...DBOption) ([]model.RootCert, error)
|
||||
CreateCert(cert *model.RootCert) error
|
||||
SaveCert(cert *model.RootCert) error
|
||||
UpdateCert(id uint, vars map[string]interface{}) error
|
||||
DeleteCert(opts ...DBOption) error
|
||||
}
|
||||
@@ -107,8 +107,8 @@ func (u *HostRepo) ListCert(opts ...DBOption) ([]model.RootCert, error) {
|
||||
return ops, err
|
||||
}
|
||||
|
||||
func (u *HostRepo) CreateCert(cert *model.RootCert) error {
|
||||
return global.DB.Create(cert).Error
|
||||
func (u *HostRepo) SaveCert(cert *model.RootCert) error {
|
||||
return global.DB.Save(cert).Error
|
||||
}
|
||||
|
||||
func (u *HostRepo) UpdateCert(id uint, vars map[string]interface{}) error {
|
||||
|
||||
@@ -24,6 +24,7 @@ type ITaskRepo interface {
|
||||
DeleteAll() error
|
||||
|
||||
WithByID(id string) DBOption
|
||||
WithByIDNotIn(ids []string) DBOption
|
||||
WithResourceID(id uint) DBOption
|
||||
WithOperate(taskOperate string) DBOption
|
||||
WithByStatus(status string) DBOption
|
||||
@@ -58,6 +59,12 @@ func (t TaskRepo) WithByID(id string) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (t TaskRepo) WithByIDNotIn(ids []string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("id not in (?)", ids)
|
||||
}
|
||||
}
|
||||
|
||||
func (t TaskRepo) WithOperate(taskOperate string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("operate = ?", taskOperate)
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package repo
|
||||
|
||||
import "github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
|
||||
type TensorRTLLMRepo struct {
|
||||
}
|
||||
|
||||
type ITensorRTLLMRepo interface {
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.TensorRTLLM, error)
|
||||
GetFirst(opts ...DBOption) (*model.TensorRTLLM, error)
|
||||
Create(tensorrtLLM *model.TensorRTLLM) error
|
||||
Save(tensorrtLLM *model.TensorRTLLM) error
|
||||
DeleteBy(opts ...DBOption) error
|
||||
List(opts ...DBOption) ([]model.TensorRTLLM, error)
|
||||
}
|
||||
|
||||
func NewITensorRTLLMRepo() ITensorRTLLMRepo {
|
||||
return &TensorRTLLMRepo{}
|
||||
}
|
||||
|
||||
func (t TensorRTLLMRepo) Page(page, size int, opts ...DBOption) (int64, []model.TensorRTLLM, error) {
|
||||
var servers []model.TensorRTLLM
|
||||
db := getDb(opts...).Model(&model.TensorRTLLM{})
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&servers).Error
|
||||
return count, servers, err
|
||||
}
|
||||
|
||||
func (t TensorRTLLMRepo) GetFirst(opts ...DBOption) (*model.TensorRTLLM, error) {
|
||||
var tensorrtLLM model.TensorRTLLM
|
||||
if err := getDb(opts...).First(&tensorrtLLM).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tensorrtLLM, nil
|
||||
}
|
||||
|
||||
func (t TensorRTLLMRepo) List(opts ...DBOption) ([]model.TensorRTLLM, error) {
|
||||
var tensorrtLLMs []model.TensorRTLLM
|
||||
if err := getDb(opts...).Find(&tensorrtLLMs).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tensorrtLLMs, nil
|
||||
}
|
||||
|
||||
func (t TensorRTLLMRepo) Create(tensorrtLLM *model.TensorRTLLM) error {
|
||||
return getDb().Create(tensorrtLLM).Error
|
||||
}
|
||||
|
||||
func (t TensorRTLLMRepo) Save(tensorrtLLM *model.TensorRTLLM) error {
|
||||
return getDb().Save(tensorrtLLM).Error
|
||||
}
|
||||
|
||||
func (t TensorRTLLMRepo) DeleteBy(opts ...DBOption) error {
|
||||
return getDb(opts...).Delete(&model.TensorRTLLM{}).Error
|
||||
}
|
||||
@@ -177,7 +177,24 @@ func (a AlertService) UpdateAlert(req dto.AlertUpdate) error {
|
||||
}
|
||||
|
||||
func (a AlertService) DeleteAlert(id uint) error {
|
||||
return alertRepo.Delete(repo.WithByID(id))
|
||||
alertInfo, _ := alertRepo.Get(repo.WithByID(id))
|
||||
if alertInfo.ID == 0 {
|
||||
return buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
err := alertRepo.Delete(repo.WithByID(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
alerts, err := a.GetAlerts()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(alerts) > 0 {
|
||||
NewIAlertTaskHelper().InitTask(alertInfo.Type)
|
||||
} else {
|
||||
NewIAlertTaskHelper().StopTask()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AlertService) GetAlert(id uint) (dto.AlertDTO, error) {
|
||||
@@ -462,10 +479,14 @@ func (a AlertService) DeleteAlertConfig(id uint) error {
|
||||
}
|
||||
|
||||
func (a AlertService) TestAlertConfig(req dto.AlertConfigTest) (bool, error) {
|
||||
username := req.UserName
|
||||
if username == "" {
|
||||
username = req.Sender
|
||||
}
|
||||
cfg := email.SMTPConfig{
|
||||
Host: req.Host,
|
||||
Port: req.Port,
|
||||
Username: req.Sender,
|
||||
Username: username,
|
||||
Password: req.Password,
|
||||
From: fmt.Sprintf("%s <%s>", req.DisplayName, req.Sender),
|
||||
Encryption: req.Encryption,
|
||||
|
||||
@@ -377,16 +377,17 @@ func loadLoadInfo(alert dto.AlertDTO) {
|
||||
|
||||
func loadDiskUsage(alert dto.AlertDTO) {
|
||||
newDate, err := alertRepo.GetTaskLog(alert.Type, alert.ID)
|
||||
if err != nil || !isAlertDue(newDate) {
|
||||
if err != nil {
|
||||
global.LOG.Errorf("record not found, err: %v", err)
|
||||
return
|
||||
}
|
||||
if strings.Contains(alert.Project, "all") {
|
||||
err = processAllDisks(alert)
|
||||
} else {
|
||||
err = processSingleDisk(alert)
|
||||
if isAlertDue(newDate) {
|
||||
if strings.Contains(alert.Project, "all") {
|
||||
err = processAllDisks(alert)
|
||||
} else {
|
||||
err = processSingleDisk(alert)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func loadPanelLogin(alert dto.AlertDTO) {
|
||||
@@ -515,12 +516,14 @@ func loadNodeException(alert dto.AlertDTO) {
|
||||
},
|
||||
}
|
||||
newDate, err := alertRepo.GetTaskLog(alert.Type, alert.ID)
|
||||
if err != nil || !isAlertDue(newDate) {
|
||||
if err != nil {
|
||||
global.LOG.Errorf("record not found, err: %v", err)
|
||||
return
|
||||
}
|
||||
sender := NewAlertSender(alert, quotaType)
|
||||
sender.ResourceSend(strconv.Itoa(int(failCount)), params)
|
||||
if isAlertDue(newDate) {
|
||||
sender := NewAlertSender(alert, quotaType)
|
||||
sender.ResourceSend(strconv.Itoa(int(failCount)), params)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -542,12 +545,14 @@ func loadLicenseException(alert dto.AlertDTO) {
|
||||
},
|
||||
}
|
||||
newDate, err := alertRepo.GetTaskLog(alert.Type, alert.ID)
|
||||
if err != nil || !isAlertDue(newDate) {
|
||||
if err != nil {
|
||||
global.LOG.Errorf("record not found, err: %v", err)
|
||||
return
|
||||
}
|
||||
sender := NewAlertSender(alert, quotaType)
|
||||
sender.ResourceSend(strconv.Itoa(int(failCount)), params)
|
||||
if isAlertDue(newDate) {
|
||||
sender := NewAlertSender(alert, quotaType)
|
||||
sender.ResourceSend(strconv.Itoa(int(failCount)), params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -814,16 +819,11 @@ func processAllDisks(alert dto.AlertDTO) error {
|
||||
global.LOG.Errorf("error getting disk list, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
var flag bool
|
||||
for _, item := range diskList {
|
||||
if success, err := checkAndCreateDiskAlert(alert, item.Path); err == nil && success {
|
||||
flag = true
|
||||
global.LOG.Infof("disk alert pushed successfully for %s", item.Path)
|
||||
}
|
||||
}
|
||||
if flag {
|
||||
global.LOG.Info("all disk alert push successful")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -833,7 +833,7 @@ func processSingleDisk(alert dto.AlertDTO) error {
|
||||
return err
|
||||
}
|
||||
if success {
|
||||
global.LOG.Info("disk alert push successful")
|
||||
global.LOG.Infof("disk alert pushed successfully for %s", alert.Project)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
@@ -72,7 +73,11 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts = append(opts, appRepo.WithArch(info.KernelArch))
|
||||
kernelArch := info.KernelArch
|
||||
if kernelArch == "aarch64" {
|
||||
kernelArch = "arm64"
|
||||
}
|
||||
opts = append(opts, appRepo.WithArch(kernelArch))
|
||||
}
|
||||
if len(req.Tags) != 0 {
|
||||
tags, err := tagRepo.GetByKeys(req.Tags)
|
||||
@@ -107,7 +112,7 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
|
||||
lang := strings.ToLower(common.GetLang(ctx))
|
||||
for _, ap := range apps {
|
||||
if req.Type == "php" {
|
||||
if ap.RequiredPanelVersion == 0 || !common.CompareAppVersion(common.GetSystemVersion(info.SystemVersion), fmt.Sprintf("%f", ap.RequiredPanelVersion)) {
|
||||
if !global.CONF.Base.IsOffLine && (ap.RequiredPanelVersion == 0 || !common.CompareAppVersion(common.GetSystemVersion(info.SystemVersion), fmt.Sprintf("%f", ap.RequiredPanelVersion))) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -480,6 +485,15 @@ func (a AppService) Install(req request.AppInstallCreate) (appInstall *model.App
|
||||
req.Params["RESTY_ADD_PACKAGE_BUILDDEPS"] = ""
|
||||
req.Params["RESTY_CONFIG_OPTIONS_MORE"] = ""
|
||||
}
|
||||
if app.Key == "openresty" && (app.Resource == "remote" || app.Resource == "custom") && common.CompareVersion(appDetail.Version, "1.27") {
|
||||
if dir, ok := req.Params["WEBSITE_DIR"]; ok {
|
||||
siteDir := dir.(string)
|
||||
if siteDir == "" || !strings.HasPrefix(siteDir, "/") {
|
||||
siteDir = path.Join(global.Dir.DataDir, dir.(string))
|
||||
}
|
||||
req.Params["WEBSITE_DIR"] = siteDir
|
||||
}
|
||||
}
|
||||
paramByte, err = json.Marshal(req.Params)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@@ -677,29 +677,29 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
||||
backupFile string
|
||||
)
|
||||
backUpApp := func(t *task.Task) error {
|
||||
if req.Backup {
|
||||
backupService := NewIBackupService()
|
||||
backupRecordService := NewIBackupRecordService()
|
||||
fileName := fmt.Sprintf("upgrade_backup_%s_%s.tar.gz", install.Name, time.Now().Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||
backupRecord, err := backupService.AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name, FileName: fileName})
|
||||
if err == nil {
|
||||
backups, _ := backupRecordService.ListAppRecords(install.App.Key, install.Name, "upgrade_backup")
|
||||
if len(backups) > 3 {
|
||||
backupsToDelete := backups[:len(backups)-3]
|
||||
var deleteIDs []uint
|
||||
for _, backup := range backupsToDelete {
|
||||
deleteIDs = append(deleteIDs, backup.ID)
|
||||
}
|
||||
_ = backupRecordService.BatchDeleteRecord(deleteIDs)
|
||||
backupService := NewIBackupService()
|
||||
backupRecordService := NewIBackupRecordService()
|
||||
fileName := fmt.Sprintf("upgrade_backup_%s_%s.tar.gz", install.Name, time.Now().Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||
backupRecord, err := backupService.AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name, FileName: fileName})
|
||||
if err == nil {
|
||||
backups, _ := backupRecordService.ListAppRecords(install.App.Key, install.Name, "upgrade_backup")
|
||||
if len(backups) > 3 {
|
||||
backupsToDelete := backups[:len(backups)-3]
|
||||
var deleteIDs []uint
|
||||
for _, backup := range backupsToDelete {
|
||||
deleteIDs = append(deleteIDs, backup.ID)
|
||||
}
|
||||
backupFile = path.Join(global.Dir.LocalBackupDir, backupRecord.FileDir, backupRecord.FileName)
|
||||
} else {
|
||||
return buserr.WithNameAndErr("ErrAppBackup", install.Name, err)
|
||||
_ = backupRecordService.BatchDeleteRecord(deleteIDs)
|
||||
}
|
||||
backupFile = path.Join(global.Dir.LocalBackupDir, backupRecord.FileDir, backupRecord.FileName)
|
||||
} else {
|
||||
return buserr.WithNameAndErr("ErrAppBackup", install.Name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
upgradeTask.AddSubTask(task.GetTaskName(install.Name, task.TaskBackup, task.TaskScopeApp), backUpApp, nil)
|
||||
if req.Backup {
|
||||
upgradeTask.AddSubTask(task.GetTaskName(install.Name, task.TaskBackup, task.TaskScopeApp), backUpApp, nil)
|
||||
}
|
||||
|
||||
upgradeApp := func(t *task.Task) error {
|
||||
fileOp := files.NewFileOp()
|
||||
@@ -946,7 +946,7 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
|
||||
}
|
||||
|
||||
func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall, logger *log.Logger) (err error) {
|
||||
if app.IsLocalApp() {
|
||||
if app.IsLocalApp() || app.IsCustomApp() {
|
||||
return nil
|
||||
}
|
||||
appResourceDir := path.Join(global.Dir.AppResourceDir, app.Resource)
|
||||
@@ -1163,7 +1163,11 @@ func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) error
|
||||
appInstall.Message = errMsg + errOur
|
||||
installErr := errors.New(appInstall.Message)
|
||||
task.LogFailedWithErr(i18n.GetMsgByKey("PullImage"), installErr)
|
||||
return installErr
|
||||
if exist, _ := dockerCLi.ImageExists(image); !exist {
|
||||
return installErr
|
||||
} else {
|
||||
task.Log(i18n.GetMsgByKey("UseExistImage"))
|
||||
}
|
||||
} else {
|
||||
task.Log(i18n.GetMsgByKey("PullImageSuccess"))
|
||||
}
|
||||
@@ -1881,6 +1885,10 @@ func ignoreUpdate(installed model.AppInstall) bool {
|
||||
if installed.App.Type == "php" || installed.Status == constant.StatusInstalling {
|
||||
return true
|
||||
}
|
||||
ignores, _ := appIgnoreUpgradeRepo.List(appDetailRepo.WithAppId(installed.AppId), appIgnoreUpgradeRepo.WithScope("all"))
|
||||
if len(ignores) > 0 {
|
||||
return true
|
||||
}
|
||||
if installed.App.Key == constant.AppMysql {
|
||||
majorVersion := getMajorVersion(installed.Version)
|
||||
appDetails, _ := appDetailRepo.GetBy(appDetailRepo.WithAppId(installed.App.ID))
|
||||
@@ -1896,10 +1904,6 @@ func ignoreUpdate(installed model.AppInstall) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
ignores, _ := appIgnoreUpgradeRepo.List(appDetailRepo.WithAppId(installed.AppId), appIgnoreUpgradeRepo.WithScope("all"))
|
||||
if len(ignores) > 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -2063,7 +2067,7 @@ func handleSSLConfig(appInstall *model.AppInstall, hasDefaultWebsite bool) error
|
||||
}
|
||||
defaultConfig.FilePath = defaultConfigPath
|
||||
defaultServer := defaultConfig.FindServers()[0]
|
||||
updateDefaultServer(defaultServer, appInstall.HttpPort, appInstall.HttpsPort, !hasDefaultWebsite)
|
||||
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 err = nginx.WriteConfig(defaultConfig, nginx.IndentedStyle); err != nil {
|
||||
|
||||
@@ -39,6 +39,7 @@ type IBackupService interface {
|
||||
Delete(id uint) error
|
||||
RefreshToken(req dto.OperateByID) error
|
||||
GetLocalDir() (string, error)
|
||||
UploadForRecover(req dto.UploadForRecover) error
|
||||
|
||||
MysqlBackup(db dto.CommonBackup) error
|
||||
PostgresqlBackup(db dto.CommonBackup) error
|
||||
@@ -309,6 +310,16 @@ func (u *BackupService) RefreshToken(req dto.OperateByID) error {
|
||||
return backupRepo.Save(&backup)
|
||||
}
|
||||
|
||||
func (u *BackupService) UploadForRecover(req dto.UploadForRecover) error {
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(req.TargetDir) {
|
||||
if err := fileOp.CreateDir(req.TargetDir, constant.DirPerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return fileOp.Copy(req.FilePath, req.TargetDir)
|
||||
}
|
||||
|
||||
func (u *BackupService) checkBackupConn(backup *model.BackupAccount) (bool, error) {
|
||||
client, err := newClient(backup, false)
|
||||
if err != nil {
|
||||
@@ -445,11 +456,15 @@ func uploadWithMap(taskItem task.Task, accountMap map[string]backupClientHelper,
|
||||
taskItem.LogFailed(i18n.GetMsgWithDetail("LoadBackupFailed", itemBackup.message))
|
||||
continue
|
||||
}
|
||||
name := itemBackup.name
|
||||
if itemBackup.name == "localhost" {
|
||||
name = i18n.GetMsgByKey("Localhost")
|
||||
}
|
||||
taskItem.LogStart(i18n.GetMsgWithMap("UploadFile", map[string]interface{}{
|
||||
"file": path.Join(itemBackup.backupPath, dst),
|
||||
"backup": itemBackup.name,
|
||||
"backup": name,
|
||||
}))
|
||||
for i := 0; i < int(retry); i++ {
|
||||
for i := 0; i < int(retry)+1; i++ {
|
||||
_, err := itemBackup.client.Upload(src, path.Join(itemBackup.backupPath, dst))
|
||||
taskItem.LogWithStatus(i18n.GetMsgByKey("Upload"), err)
|
||||
if err != nil {
|
||||
|
||||
@@ -45,36 +45,29 @@ func (u *BackupService) AppBackup(req dto.CommonBackup) (*model.BackupRecord, er
|
||||
fileName = fmt.Sprintf("%s_%s.tar.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
|
||||
}
|
||||
|
||||
backupApp := func() (*model.BackupRecord, error) {
|
||||
if err = handleAppBackup(&install, nil, backupDir, fileName, "", req.Secret, req.TaskID); err != nil {
|
||||
global.LOG.Errorf("backup app %s failed, err: %v", req.DetailName, err)
|
||||
return nil, err
|
||||
}
|
||||
record := &model.BackupRecord{
|
||||
Type: "app",
|
||||
Name: req.Name,
|
||||
DetailName: req.DetailName,
|
||||
SourceAccountIDs: "1",
|
||||
DownloadAccountID: 1,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
Description: req.Description,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return record, nil
|
||||
record := &model.BackupRecord{
|
||||
Type: "app",
|
||||
Name: req.Name,
|
||||
DetailName: req.DetailName,
|
||||
SourceAccountIDs: "1",
|
||||
DownloadAccountID: 1,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
TaskID: req.TaskID,
|
||||
Status: constant.StatusWaiting,
|
||||
Description: req.Description,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req.TaskID != "" {
|
||||
go func() {
|
||||
_, _ = backupApp()
|
||||
}()
|
||||
} else {
|
||||
return backupApp()
|
||||
if err = handleAppBackup(&install, nil, record.ID, backupDir, fileName, "", req.Secret, req.TaskID); err != nil {
|
||||
global.LOG.Errorf("backup app %s failed, err: %v", req.DetailName, err)
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
return record, nil
|
||||
}
|
||||
|
||||
func (u *BackupService) AppRecover(req dto.CommonRecover) error {
|
||||
@@ -94,11 +87,9 @@ func (u *BackupService) AppRecover(req dto.CommonRecover) error {
|
||||
if _, err := compose.Down(install.GetComposePath()); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
if err := handleAppRecover(&install, nil, req.File, false, req.Secret, req.TaskID); err != nil {
|
||||
global.LOG.Errorf("recover app %s failed, err: %v", req.DetailName, err)
|
||||
}
|
||||
}()
|
||||
if err := handleAppRecover(&install, nil, req.File, false, req.Secret, req.TaskID); err != nil {
|
||||
global.LOG.Errorf("recover app %s failed, err: %v", req.DetailName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -111,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, 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))
|
||||
@@ -122,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, 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))
|
||||
@@ -130,25 +121,34 @@ func backupDatabaseWithTask(parentTask *task.Task, resourceKey, tmpDir, name str
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleAppBackup(install *model.AppInstall, parentTask *task.Task, backupDir, fileName, excludes, secret, taskID string) error {
|
||||
func handleAppBackup(install *model.AppInstall, parentTask *task.Task, recordID uint, backupDir, fileName, excludes, secret, taskID string) error {
|
||||
var (
|
||||
err error
|
||||
backupTask *task.Task
|
||||
)
|
||||
backupTask = parentTask
|
||||
if parentTask == nil {
|
||||
backupTask, err = task.NewTaskWithOps(install.Name, task.TaskBackup, task.TaskScopeApp, taskID, install.ID)
|
||||
backupTask, err = task.NewTaskWithOps(install.Name, task.TaskBackup, task.TaskScopeBackup, taskID, install.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
itemHandler := func() error { return doAppBackup(install, backupTask, backupDir, fileName, excludes, secret) }
|
||||
backupTask.AddSubTask(task.GetTaskName(install.Name, task.TaskBackup, task.TaskScopeApp), func(t *task.Task) error { return itemHandler() }, nil)
|
||||
if parentTask != nil {
|
||||
return itemHandler()
|
||||
}
|
||||
return backupTask.Execute()
|
||||
|
||||
backupTask.AddSubTaskWithOps(task.GetTaskName(install.Name, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 3, time.Hour)
|
||||
go func() {
|
||||
if err := backupTask.Execute(); err != nil {
|
||||
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||
return
|
||||
}
|
||||
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusSuccess})
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverFile string, isRollback bool, secret, taskID string) error {
|
||||
@@ -160,7 +160,7 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
|
||||
)
|
||||
recoverTask = parentTask
|
||||
if parentTask == nil {
|
||||
recoverTask, err = task.NewTaskWithOps(install.Name, task.TaskRecover, task.TaskScopeApp, taskID, install.ID)
|
||||
recoverTask, err = task.NewTaskWithOps(install.Name, task.TaskRecover, task.TaskScopeBackup, taskID, install.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -170,7 +170,7 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
|
||||
fileOp := files.NewFileOp()
|
||||
if !isRollback {
|
||||
rollbackFile = path.Join(global.Dir.TmpDir, fmt.Sprintf("app/%s_%s.tar.gz", install.Name, time.Now().Format(constant.DateTimeSlimLayout)))
|
||||
if err := handleAppBackup(install, recoverTask, path.Dir(rollbackFile), path.Base(rollbackFile), "", "", taskID); err != nil {
|
||||
if err := handleAppBackup(install, recoverTask, 0, path.Dir(rollbackFile), path.Base(rollbackFile), "", "", taskID); err != nil {
|
||||
t.Log(fmt.Sprintf("backup app %s for rollback before recover failed, err: %v", install.Name, err))
|
||||
}
|
||||
}
|
||||
@@ -308,6 +308,10 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
|
||||
return nil
|
||||
}
|
||||
|
||||
if parentTask != nil {
|
||||
return recoverApp(parentTask)
|
||||
}
|
||||
|
||||
rollBackApp := func(t *task.Task) {
|
||||
if isRollback {
|
||||
return
|
||||
@@ -324,12 +328,11 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
}
|
||||
}
|
||||
|
||||
recoverTask.AddSubTask(task.GetTaskName(install.Name, task.TaskRecover, task.TaskScopeApp), recoverApp, rollBackApp)
|
||||
if parentTask != nil {
|
||||
return recoverApp(parentTask)
|
||||
}
|
||||
return recoverTask.Execute()
|
||||
recoverTask.AddSubTask(task.GetTaskName(install.Name, task.TaskRecover, task.TaskScopeBackup), recoverApp, rollBackApp)
|
||||
go func() {
|
||||
_ = recoverTask.Execute()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func doAppBackup(install *model.AppInstall, parentTask *task.Task, backupDir, fileName, excludes, secret string) error {
|
||||
@@ -351,6 +354,7 @@ func doAppBackup(install *model.AppInstall, parentTask *task.Task, backupDir, fi
|
||||
}
|
||||
|
||||
appPath := install.GetPath()
|
||||
parentTask.LogStart(i18n.GetMsgByKey("TaskBackup"))
|
||||
if err := fileOp.TarGzCompressPro(true, appPath, path.Join(tmpDir, "app.tar.gz"), "", excludes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -30,11 +30,6 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
|
||||
targetDir := path.Join(global.Dir.LocalBackupDir, itemDir)
|
||||
fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
|
||||
|
||||
databaseHelper := DatabaseHelper{Database: req.Name, DBType: req.Type, Name: req.DetailName}
|
||||
if err := handleMysqlBackup(databaseHelper, nil, targetDir, fileName, req.TaskID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
record := &model.BackupRecord{
|
||||
Type: req.Type,
|
||||
Name: req.Name,
|
||||
@@ -43,68 +38,41 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
|
||||
DownloadAccountID: 1,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
TaskID: req.TaskID,
|
||||
Status: constant.StatusWaiting,
|
||||
Description: req.Description,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
databaseHelper := DatabaseHelper{Database: req.Name, DBType: req.Type, Name: req.DetailName}
|
||||
if err := handleMysqlBackup(databaseHelper, nil, record.ID, targetDir, fileName, req.TaskID); err != nil {
|
||||
backupRepo.UpdateRecordByMap(record.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *BackupService) MysqlRecover(req dto.CommonRecover) error {
|
||||
if err := handleMysqlRecover(req, nil, false, req.TaskID); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return handleMysqlRecover(req, nil, false, req.TaskID)
|
||||
}
|
||||
|
||||
func (u *BackupService) MysqlRecoverByUpload(req dto.CommonRecover) error {
|
||||
file := req.File
|
||||
fileName := path.Base(req.File)
|
||||
if strings.HasSuffix(fileName, ".tar.gz") {
|
||||
fileNameItem := time.Now().Format(constant.DateTimeSlimLayout)
|
||||
dstDir := fmt.Sprintf("%s/%s", path.Dir(req.File), fileNameItem)
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(dstDir) {
|
||||
if err := fileOp.CreateDir(dstDir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("mkdir %s failed, err: %v", dstDir, err)
|
||||
}
|
||||
}
|
||||
if err := fileOp.TarGzExtractPro(req.File, dstDir, ""); err != nil {
|
||||
_ = os.RemoveAll(dstDir)
|
||||
return err
|
||||
}
|
||||
global.LOG.Infof("decompress file %s successful, now start to check test.sql is exist", req.File)
|
||||
hasTestSql := false
|
||||
_ = filepath.Walk(dstDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if !info.IsDir() && info.Name() == "test.sql" {
|
||||
hasTestSql = true
|
||||
file = path
|
||||
fileName = "test.sql"
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if !hasTestSql {
|
||||
_ = os.RemoveAll(dstDir)
|
||||
return fmt.Errorf("no such file named test.sql in %s", fileName)
|
||||
}
|
||||
defer func() {
|
||||
_ = os.RemoveAll(dstDir)
|
||||
}()
|
||||
recoveFile, err := loadSqlFile(req.File)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.File = recoveFile
|
||||
|
||||
req.File = path.Dir(file) + "/" + fileName
|
||||
if err := handleMysqlRecover(req, nil, false, req.TaskID); err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Info("recover from uploads successful!")
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleMysqlBackup(db DatabaseHelper, parentTask *task.Task, targetDir, fileName, taskID string) error {
|
||||
func handleMysqlBackup(db DatabaseHelper, parentTask *task.Task, recordID uint, targetDir, fileName, taskID string) error {
|
||||
var (
|
||||
err error
|
||||
backupTask *task.Task
|
||||
@@ -116,18 +84,25 @@ func handleMysqlBackup(db DatabaseHelper, parentTask *task.Task, targetDir, file
|
||||
}
|
||||
itemName := fmt.Sprintf("%s[%s] - %s", db.Database, db.DBType, db.Name)
|
||||
if parentTask == nil {
|
||||
backupTask, err = task.NewTaskWithOps(itemName, task.TaskBackup, task.TaskScopeDatabase, taskID, dbInfo.ID)
|
||||
backupTask, err = task.NewTaskWithOps(itemName, task.TaskBackup, task.TaskScopeBackup, taskID, dbInfo.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
itemHandler := func() error { return doMysqlBackup(db, targetDir, fileName) }
|
||||
backupTask.AddSubTask(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeDatabase), func(t *task.Task) error { return itemHandler() }, nil)
|
||||
if parentTask != nil {
|
||||
return itemHandler()
|
||||
}
|
||||
return backupTask.Execute()
|
||||
backupTask.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 3, time.Hour)
|
||||
go func() {
|
||||
if err := backupTask.Execute(); err != nil {
|
||||
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||
return
|
||||
}
|
||||
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusSuccess})
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback bool, taskID string) error {
|
||||
@@ -142,7 +117,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.TaskScopeDatabase, taskID, dbInfo.ID)
|
||||
itemTask, err = task.NewTaskWithOps(itemName, task.TaskRecover, task.TaskScopeBackup, taskID, dbInfo.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -172,8 +147,6 @@ func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback
|
||||
Format: dbInfo.Format,
|
||||
TargetDir: path.Dir(rollbackFile),
|
||||
FileName: path.Base(rollbackFile),
|
||||
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("backup mysql db %s for rollback before recover failed, err: %v", req.DetailName, err)
|
||||
}
|
||||
@@ -186,8 +159,6 @@ func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback
|
||||
Version: version,
|
||||
Format: dbInfo.Format,
|
||||
SourceFile: rollbackFile,
|
||||
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
global.LOG.Errorf("rollback mysql db %s from %s failed, err: %v", req.DetailName, rollbackFile, err)
|
||||
} else {
|
||||
@@ -205,8 +176,6 @@ func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback
|
||||
Version: version,
|
||||
Format: dbInfo.Format,
|
||||
SourceFile: req.File,
|
||||
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
global.LOG.Errorf("recover mysql db %s from %s failed, err: %v", req.DetailName, req.File, err)
|
||||
return err
|
||||
@@ -214,12 +183,15 @@ func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback
|
||||
isOk = true
|
||||
return nil
|
||||
}
|
||||
itemTask.AddSubTask(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil)
|
||||
if parentTask != nil {
|
||||
return recoverDatabase(parentTask)
|
||||
}
|
||||
|
||||
return itemTask.Execute()
|
||||
itemTask.AddSubTaskWithOps(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil, 3, time.Hour)
|
||||
go func() {
|
||||
_ = itemTask.Execute()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func doMysqlBackup(db DatabaseHelper, targetDir, fileName string) error {
|
||||
@@ -238,8 +210,58 @@ func doMysqlBackup(db DatabaseHelper, targetDir, fileName string) error {
|
||||
Format: dbInfo.Format,
|
||||
TargetDir: targetDir,
|
||||
FileName: fileName,
|
||||
|
||||
Timeout: 300,
|
||||
}
|
||||
return cli.Backup(backupInfo)
|
||||
}
|
||||
|
||||
func loadSqlFile(file string) (string, error) {
|
||||
if !strings.HasSuffix(file, ".tar.gz") && !strings.HasSuffix(file, ".zip") {
|
||||
return file, nil
|
||||
}
|
||||
fileName := path.Base(file)
|
||||
fileDir := path.Dir(file)
|
||||
fileNameItem := time.Now().Format(constant.DateTimeSlimLayout)
|
||||
dstDir := fmt.Sprintf("%s/%s", fileDir, fileNameItem)
|
||||
_ = os.Mkdir(dstDir, constant.DirPerm)
|
||||
if strings.HasSuffix(fileName, ".tar.gz") {
|
||||
fileOp := files.NewFileOp()
|
||||
if err := fileOp.TarGzExtractPro(file, dstDir, ""); err != nil {
|
||||
_ = os.RemoveAll(dstDir)
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if strings.HasSuffix(fileName, ".zip") {
|
||||
archiver, err := files.NewShellArchiver(files.Zip)
|
||||
if err != nil {
|
||||
_ = os.RemoveAll(dstDir)
|
||||
return "", err
|
||||
}
|
||||
if err := archiver.Extract(file, dstDir, ""); err != nil {
|
||||
_ = os.RemoveAll(dstDir)
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
global.LOG.Infof("decompress file %s successful, now start to check test.sql is exist", file)
|
||||
var sqlFiles []string
|
||||
hasTestSql := false
|
||||
_ = filepath.Walk(dstDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if !info.IsDir() && strings.HasSuffix(info.Name(), ".sql") {
|
||||
sqlFiles = append(sqlFiles, path)
|
||||
if info.Name() == "test.sql" {
|
||||
hasTestSql = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if len(sqlFiles) == 1 {
|
||||
return sqlFiles[0], nil
|
||||
}
|
||||
if !hasTestSql {
|
||||
_ = os.RemoveAll(dstDir)
|
||||
return "", fmt.Errorf("no such file named test.sql in %s", fileName)
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
@@ -31,11 +29,6 @@ func (u *BackupService) PostgresqlBackup(req dto.CommonBackup) error {
|
||||
targetDir := path.Join(global.Dir.LocalBackupDir, itemDir)
|
||||
fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
|
||||
|
||||
databaseHelper := DatabaseHelper{Database: req.Name, DBType: req.Type, Name: req.DetailName}
|
||||
if err := handlePostgresqlBackup(databaseHelper, nil, targetDir, fileName, req.TaskID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
record := &model.BackupRecord{
|
||||
Type: req.Type,
|
||||
Name: req.Name,
|
||||
@@ -44,11 +37,18 @@ func (u *BackupService) PostgresqlBackup(req dto.CommonBackup) error {
|
||||
DownloadAccountID: 1,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
TaskID: req.TaskID,
|
||||
Status: constant.StatusWaiting,
|
||||
Description: req.Description,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
}
|
||||
|
||||
databaseHelper := DatabaseHelper{Database: req.Name, DBType: req.Type, Name: req.DetailName}
|
||||
if err := handlePostgresqlBackup(databaseHelper, nil, record.ID, targetDir, fileName, req.TaskID); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (u *BackupService) PostgresqlRecover(req dto.CommonRecover) error {
|
||||
@@ -59,44 +59,11 @@ func (u *BackupService) PostgresqlRecover(req dto.CommonRecover) error {
|
||||
}
|
||||
|
||||
func (u *BackupService) PostgresqlRecoverByUpload(req dto.CommonRecover) error {
|
||||
file := req.File
|
||||
fileName := path.Base(req.File)
|
||||
if strings.HasSuffix(fileName, ".tar.gz") {
|
||||
fileNameItem := time.Now().Format(constant.DateTimeSlimLayout)
|
||||
dstDir := fmt.Sprintf("%s/%s", path.Dir(req.File), fileNameItem)
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(dstDir) {
|
||||
if err := fileOp.CreateDir(dstDir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("mkdir %s failed, err: %v", dstDir, err)
|
||||
}
|
||||
}
|
||||
if err := fileOp.TarGzExtractPro(req.File, dstDir, ""); err != nil {
|
||||
_ = os.RemoveAll(dstDir)
|
||||
return err
|
||||
}
|
||||
global.LOG.Infof("decompress file %s successful, now start to check test.sql is exist", req.File)
|
||||
hasTestSql := false
|
||||
_ = filepath.Walk(dstDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if !info.IsDir() && info.Name() == "test.sql" {
|
||||
hasTestSql = true
|
||||
file = path
|
||||
fileName = "test.sql"
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if !hasTestSql {
|
||||
_ = os.RemoveAll(dstDir)
|
||||
return fmt.Errorf("no such file named test.sql in %s", fileName)
|
||||
}
|
||||
defer func() {
|
||||
_ = os.RemoveAll(dstDir)
|
||||
}()
|
||||
recoveFile, err := loadSqlFile(req.File)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.File = path.Dir(file) + "/" + fileName
|
||||
req.File = recoveFile
|
||||
if err := handlePostgresqlRecover(req, nil, false); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -104,7 +71,7 @@ func (u *BackupService) PostgresqlRecoverByUpload(req dto.CommonRecover) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func handlePostgresqlBackup(db DatabaseHelper, parentTask *task.Task, targetDir, fileName, taskID string) error {
|
||||
func handlePostgresqlBackup(db DatabaseHelper, parentTask *task.Task, recordID uint, targetDir, fileName, taskID string) error {
|
||||
var (
|
||||
err error
|
||||
backupTask *task.Task
|
||||
@@ -112,18 +79,25 @@ func handlePostgresqlBackup(db DatabaseHelper, parentTask *task.Task, targetDir,
|
||||
backupTask = parentTask
|
||||
itemName := fmt.Sprintf("%s - %s", db.Database, db.Name)
|
||||
if parentTask == nil {
|
||||
backupTask, err = task.NewTaskWithOps(itemName, task.TaskBackup, task.TaskScopeDatabase, taskID, db.ID)
|
||||
backupTask, err = task.NewTaskWithOps(itemName, task.TaskBackup, task.TaskScopeBackup, taskID, db.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
itemHandler := func() error { return doPostgresqlgBackup(db, targetDir, fileName) }
|
||||
backupTask.AddSubTask(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeDatabase), func(task *task.Task) error { return itemHandler() }, nil)
|
||||
if parentTask != nil {
|
||||
return itemHandler()
|
||||
}
|
||||
return backupTask.Execute()
|
||||
backupTask.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 3, time.Hour)
|
||||
go func() {
|
||||
if err := backupTask.Execute(); err != nil {
|
||||
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||
return
|
||||
}
|
||||
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusSuccess})
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback bool) error {
|
||||
@@ -137,7 +111,7 @@ func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRol
|
||||
}
|
||||
itemTask = parentTask
|
||||
if parentTask == nil {
|
||||
itemTask, err = task.NewTaskWithOps(req.Name, task.TaskRecover, task.TaskScopeDatabase, req.TaskID, dbInfo.ID)
|
||||
itemTask, err = task.NewTaskWithOps(req.Name, task.TaskRecover, task.TaskScopeBackup, req.TaskID, dbInfo.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -198,12 +172,15 @@ func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRol
|
||||
isOk = true
|
||||
return nil
|
||||
}
|
||||
itemTask.AddSubTask(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil)
|
||||
if parentTask != nil {
|
||||
return recoverDatabase(parentTask)
|
||||
}
|
||||
|
||||
return itemTask.Execute()
|
||||
itemTask.AddSubTaskWithOps(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil, 3, time.Hour)
|
||||
go func() {
|
||||
_ = itemTask.Execute()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func doPostgresqlgBackup(db DatabaseHelper, targetDir, fileName string) error {
|
||||
|
||||
@@ -11,6 +11,7 @@ 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/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/jinzhu/copier"
|
||||
)
|
||||
@@ -239,6 +240,9 @@ func (u *BackupRecordService) LoadRecordSize(req dto.SearchForSize) ([]dto.Recor
|
||||
return nil, err
|
||||
}
|
||||
for _, item := range records {
|
||||
if item.Status == constant.StatusWaiting {
|
||||
continue
|
||||
}
|
||||
list = append(list, backupSizeHelper{ID: item.ID, DownloadID: item.DownloadAccountID, FilePath: path.Join(item.FileDir, item.FileName)})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,22 +44,24 @@ func (u *BackupService) RedisBackup(req dto.CommonBackup) error {
|
||||
}
|
||||
itemDir := fmt.Sprintf("database/redis/%s", redisInfo.Name)
|
||||
backupDir := path.Join(global.Dir.LocalBackupDir, itemDir)
|
||||
if err := handleRedisBackup(redisInfo, nil, backupDir, fileName, req.Secret, req.TaskID); err != nil {
|
||||
return err
|
||||
}
|
||||
record := &model.BackupRecord{
|
||||
Type: "redis",
|
||||
Type: req.Type,
|
||||
Name: req.Name,
|
||||
SourceAccountIDs: "1",
|
||||
DownloadAccountID: 1,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
TaskID: req.TaskID,
|
||||
Status: constant.StatusWaiting,
|
||||
Description: req.Description,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
}
|
||||
|
||||
if err := handleRedisBackup(redisInfo, nil, record.ID, backupDir, fileName, req.Secret, req.TaskID); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -75,15 +77,14 @@ func (u *BackupService) RedisRecover(req dto.CommonRecover) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, backupDir, fileName, secret, taskID string) error {
|
||||
|
||||
func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, recordID uint, backupDir, fileName, secret, taskID string) error {
|
||||
var (
|
||||
err error
|
||||
itemTask *task.Task
|
||||
)
|
||||
itemTask = parentTask
|
||||
if parentTask == nil {
|
||||
itemTask, err = task.NewTaskWithOps("Redis", task.TaskBackup, task.TaskScopeDatabase, taskID, redisInfo.ID)
|
||||
itemTask, err = task.NewTaskWithOps("Redis", task.TaskBackup, task.TaskScopeBackup, taskID, redisInfo.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -128,6 +129,14 @@ func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, backupDi
|
||||
return backupDatabase(parentTask)
|
||||
}
|
||||
|
||||
itemTask.AddSubTaskWithOps(i18n.GetMsgByKey("TaskBackup"), backupDatabase, nil, 3, time.Hour)
|
||||
go func() {
|
||||
if err := itemTask.Execute(); err != nil {
|
||||
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||
return
|
||||
}
|
||||
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusSuccess})
|
||||
}()
|
||||
return itemTask.Execute()
|
||||
}
|
||||
|
||||
@@ -138,7 +147,7 @@ func handleRedisRecover(redisInfo *repo.RootInfo, parentTask *task.Task, recover
|
||||
)
|
||||
itemTask = parentTask
|
||||
if parentTask == nil {
|
||||
itemTask, err = task.NewTaskWithOps("Redis", task.TaskRecover, task.TaskScopeDatabase, taskID, redisInfo.ID)
|
||||
itemTask, err = task.NewTaskWithOps("Redis", task.TaskRecover, task.TaskScopeBackup, taskID, redisInfo.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -180,7 +189,7 @@ func handleRedisRecover(redisInfo *repo.RootInfo, parentTask *task.Task, recover
|
||||
}
|
||||
}
|
||||
rollbackFile := path.Join(global.Dir.TmpDir, fmt.Sprintf("database/redis/%s_%s.%s", redisInfo.Name, time.Now().Format(constant.DateTimeSlimLayout), suffix))
|
||||
if err := handleRedisBackup(redisInfo, nil, path.Dir(rollbackFile), path.Base(rollbackFile), secret, ""); err != nil {
|
||||
if err := handleRedisBackup(redisInfo, nil, 0, path.Dir(rollbackFile), path.Base(rollbackFile), secret, ""); err != nil {
|
||||
return fmt.Errorf("backup database %s for rollback before recover failed, err: %v", redisInfo.Name, err)
|
||||
}
|
||||
defer func() {
|
||||
@@ -230,5 +239,8 @@ func handleRedisRecover(redisInfo *repo.RootInfo, parentTask *task.Task, recover
|
||||
return recoverDatabase(parentTask)
|
||||
}
|
||||
|
||||
return itemTask.Execute()
|
||||
go func() {
|
||||
_ = itemTask.Execute()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -38,26 +38,26 @@ func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
|
||||
backupDir := path.Join(global.Dir.LocalBackupDir, itemDir)
|
||||
fileName := fmt.Sprintf("%s_%s.tar.gz", website.Alias, timeNow+common.RandStrAndNum(5))
|
||||
|
||||
go func() {
|
||||
if err = handleWebsiteBackup(&website, nil, backupDir, fileName, "", req.Secret, req.TaskID); err != nil {
|
||||
global.LOG.Errorf("backup website %s failed, err: %v", website.Alias, err)
|
||||
return
|
||||
}
|
||||
record := &model.BackupRecord{
|
||||
Type: "website",
|
||||
Name: website.Alias,
|
||||
DetailName: website.Alias,
|
||||
SourceAccountIDs: "1",
|
||||
DownloadAccountID: 1,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
Description: req.Description,
|
||||
}
|
||||
if err = backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
record := &model.BackupRecord{
|
||||
Type: "website",
|
||||
Name: website.Alias,
|
||||
DetailName: website.Alias,
|
||||
SourceAccountIDs: "1",
|
||||
DownloadAccountID: 1,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
TaskID: req.TaskID,
|
||||
Status: constant.StatusWaiting,
|
||||
Description: req.Description,
|
||||
}
|
||||
if err = backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
if err = handleWebsiteBackup(&website, nil, record.ID, backupDir, fileName, "", req.Secret, req.TaskID); err != nil {
|
||||
global.LOG.Errorf("backup website %s failed, err: %v", website.Alias, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -66,40 +66,34 @@ func (u *BackupService) WebsiteRecover(req dto.CommonRecover) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
if err := handleWebsiteRecover(&website, req.File, false, req.Secret, req.TaskID); err != nil {
|
||||
global.LOG.Errorf("recover website %s failed, err: %v", website.Alias, err)
|
||||
}
|
||||
}()
|
||||
if err := handleWebsiteRecover(&website, nil, req.File, false, req.Secret, req.TaskID); err != nil {
|
||||
global.LOG.Errorf("recover website %s failed, err: %v", website.Alias, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback bool, secret, taskID string) error {
|
||||
recoverTask, err := task.NewTaskWithOps(website.PrimaryDomain, task.TaskRecover, task.TaskScopeWebsite, taskID, website.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
func handleWebsiteRecover(website *model.Website, parentTask *task.Task, recoverFile string, isRollback bool, secret, taskID string) error {
|
||||
var (
|
||||
err error
|
||||
recoverTask *task.Task
|
||||
isOk = false
|
||||
rollbackFile string
|
||||
)
|
||||
recoverTask = parentTask
|
||||
if parentTask == nil {
|
||||
recoverTask, err = task.NewTaskWithOps(website.PrimaryDomain, task.TaskRecover, task.TaskScopeBackup, taskID, website.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
recoverTask.AddSubTask(task.GetTaskName(website.PrimaryDomain, task.TaskRecover, task.TaskScopeWebsite), func(t *task.Task) error {
|
||||
isOk := false
|
||||
recoverWebsite := func(t *task.Task) error {
|
||||
if !isRollback {
|
||||
rollbackFile := path.Join(global.Dir.TmpDir, fmt.Sprintf("website/%s_%s.tar.gz", website.Alias, time.Now().Format(constant.DateTimeSlimLayout)))
|
||||
if err := handleWebsiteBackup(website, recoverTask, path.Dir(rollbackFile), path.Base(rollbackFile), "", "", taskID); err != nil {
|
||||
rollbackFile = path.Join(global.Dir.TmpDir, fmt.Sprintf("website/%s_%s.tar.gz", website.Alias, time.Now().Format(constant.DateTimeSlimLayout)))
|
||||
if err := handleWebsiteBackup(website, recoverTask, 0, path.Dir(rollbackFile), path.Base(rollbackFile), "", "", taskID); err != nil {
|
||||
return fmt.Errorf("backup website %s for rollback before recover failed, err: %v", website.Alias, err)
|
||||
}
|
||||
defer func() {
|
||||
if !isOk {
|
||||
t.LogStart(i18n.GetMsgByKey("Rollback"))
|
||||
if err := handleWebsiteRecover(website, rollbackFile, true, "", taskID); err != nil {
|
||||
t.LogFailedWithErr(i18n.GetMsgByKey("Rollback"), err)
|
||||
return
|
||||
}
|
||||
t.LogSuccess(i18n.GetMsgByKey("Rollback"))
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
} else {
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
tmpPath := strings.ReplaceAll(recoverFile, ".tar.gz", "")
|
||||
t.Log(i18n.GetWithName("DeCompressFile", recoverFile))
|
||||
@@ -151,12 +145,10 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
|
||||
taskName := task.GetTaskName(app.Name, task.TaskRecover, task.TaskScopeApp)
|
||||
t.LogStart(taskName)
|
||||
if err := handleAppRecover(&app, recoverTask, fmt.Sprintf("%s/%s.app.tar.gz", tmpPath, website.Alias), true, "", ""); err != nil {
|
||||
t.LogFailedWithErr(taskName, err)
|
||||
return err
|
||||
}
|
||||
t.LogSuccess(taskName)
|
||||
if _, err = compose.DownAndUp(fmt.Sprintf("%s/%s/%s/docker-compose.yml", global.Dir.AppInstallDir, app.App.Key, app.Name)); err != nil {
|
||||
t.LogFailedWithErr("Run", err)
|
||||
return err
|
||||
}
|
||||
case constant.Runtime:
|
||||
@@ -167,7 +159,6 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
|
||||
taskName := task.GetTaskName(runtime.Name, task.TaskRecover, task.TaskScopeRuntime)
|
||||
t.LogStart(taskName)
|
||||
if err := handleRuntimeRecover(runtime, fmt.Sprintf("%s/%s.runtime.tar.gz", tmpPath, website.Alias), true, ""); err != nil {
|
||||
t.LogFailedWithErr(taskName, err)
|
||||
return err
|
||||
}
|
||||
t.LogSuccess(taskName)
|
||||
@@ -186,7 +177,6 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
|
||||
taskName := i18n.GetMsgByKey("TaskRecover") + i18n.GetMsgByKey("websiteDir")
|
||||
t.Log(taskName)
|
||||
if err = fileOp.TarGzExtractPro(fmt.Sprintf("%s/%s.web.tar.gz", tmpPath, website.Alias), GetOpenrestyDir(SitesRootDir), ""); err != nil {
|
||||
t.LogFailedWithErr(taskName, err)
|
||||
return err
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker exec -i %s nginx -s reload", nginxInfo.ContainerName)
|
||||
@@ -199,28 +189,60 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
|
||||
}
|
||||
isOk = true
|
||||
return nil
|
||||
}, nil)
|
||||
return recoverTask.Execute()
|
||||
}
|
||||
|
||||
if parentTask != nil {
|
||||
return recoverWebsite(parentTask)
|
||||
}
|
||||
|
||||
rollBackWebsite := func(t *task.Task) {
|
||||
if isRollback {
|
||||
return
|
||||
}
|
||||
if !isOk {
|
||||
t.Log(i18n.GetMsgByKey("RecoverFailedStartRollBack"))
|
||||
if err := handleWebsiteRecover(website, t, rollbackFile, true, "", ""); err != nil {
|
||||
t.LogFailedWithErr(i18n.GetMsgByKey("Rollback"), err)
|
||||
return
|
||||
}
|
||||
t.LogSuccess(i18n.GetMsgByKey("Rollback"))
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
} else {
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
}
|
||||
}
|
||||
recoverTask.AddSubTask(task.GetTaskName(website.PrimaryDomain, task.TaskRecover, task.TaskScopeBackup), recoverWebsite, rollBackWebsite)
|
||||
go func() {
|
||||
_ = recoverTask.Execute()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleWebsiteBackup(website *model.Website, parentTask *task.Task, backupDir, fileName, excludes, secret, taskID string) error {
|
||||
func handleWebsiteBackup(website *model.Website, parentTask *task.Task, recordID uint, backupDir, fileName, excludes, secret, taskID string) error {
|
||||
var (
|
||||
err error
|
||||
backupTask *task.Task
|
||||
)
|
||||
backupTask = parentTask
|
||||
if parentTask == nil {
|
||||
backupTask, err = task.NewTaskWithOps(website.Alias, task.TaskBackup, task.TaskScopeWebsite, taskID, website.ID)
|
||||
backupTask, err = task.NewTaskWithOps(website.Alias, task.TaskBackup, task.TaskScopeBackup, taskID, website.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
itemHandler := func() error { return doWebsiteBackup(website, backupTask, backupDir, fileName, excludes, secret) }
|
||||
backupTask.AddSubTask(task.GetTaskName(website.Alias, task.TaskBackup, task.TaskScopeWebsite), func(t *task.Task) error { return itemHandler() }, nil)
|
||||
if parentTask != nil {
|
||||
return itemHandler()
|
||||
}
|
||||
return backupTask.Execute()
|
||||
backupTask.AddSubTaskWithOps(task.GetTaskName(website.Alias, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 3, time.Hour)
|
||||
go func() {
|
||||
if err := backupTask.Execute(); err != nil {
|
||||
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||
return
|
||||
}
|
||||
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusSuccess})
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func doWebsiteBackup(website *model.Website, parentTask *task.Task, backupDir, fileName, excludes, secret string) error {
|
||||
@@ -252,7 +274,7 @@ func doWebsiteBackup(website *model.Website, parentTask *task.Task, backupDir, f
|
||||
return err
|
||||
}
|
||||
parentTask.LogStart(task.GetTaskName(app.Name, task.TaskBackup, task.TaskScopeApp))
|
||||
if err = handleAppBackup(&app, parentTask, tmpDir, fmt.Sprintf("%s.app.tar.gz", website.Alias), excludes, "", ""); err != nil {
|
||||
if err = handleAppBackup(&app, parentTask, 0, tmpDir, fmt.Sprintf("%s.app.tar.gz", website.Alias), excludes, "", ""); err != nil {
|
||||
return err
|
||||
}
|
||||
parentTask.LogSuccess(task.GetTaskName(app.Name, task.TaskBackup, task.TaskScopeApp))
|
||||
|
||||
+102
-230
@@ -6,7 +6,6 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -14,10 +13,12 @@ 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/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/utils/alert_push"
|
||||
"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"
|
||||
@@ -26,13 +27,6 @@ import (
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
clamServiceNameCentOs = "clamd@scan.service"
|
||||
clamServiceNameUbuntu = "clamav-daemon.service"
|
||||
freshClamService = "clamav-freshclam.service"
|
||||
resultDir = "clamav"
|
||||
)
|
||||
|
||||
type ClamService struct {
|
||||
serviceName string
|
||||
}
|
||||
@@ -45,13 +39,13 @@ type IClamService interface {
|
||||
Update(req dto.ClamUpdate) error
|
||||
UpdateStatus(id uint, status string) error
|
||||
Delete(req dto.ClamDelete) error
|
||||
HandleOnce(req dto.OperateByID) error
|
||||
HandleOnce(id uint) error
|
||||
|
||||
LoadFile(req dto.ClamFileReq) (string, error)
|
||||
UpdateFile(req dto.UpdateByNameAndFile) error
|
||||
LoadRecords(req dto.ClamLogSearch) (int64, interface{}, error)
|
||||
CleanRecord(req dto.OperateByID) error
|
||||
|
||||
LoadRecordLog(req dto.ClamLogReq) (string, error)
|
||||
SearchRecords(req dto.ClamLogSearch) (int64, interface{}, error)
|
||||
CleanRecord(id uint) error
|
||||
}
|
||||
|
||||
func NewIClamService() IClamService {
|
||||
@@ -62,22 +56,22 @@ func (c *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) {
|
||||
var baseInfo dto.ClamBaseInfo
|
||||
baseInfo.Version = "-"
|
||||
baseInfo.FreshVersion = "-"
|
||||
exist1, _ := systemctl.IsExist(clamServiceNameCentOs)
|
||||
exist1, _ := systemctl.IsExist(constant.ClamServiceNameCentOs)
|
||||
if exist1 {
|
||||
c.serviceName = clamServiceNameCentOs
|
||||
c.serviceName = constant.ClamServiceNameCentOs
|
||||
baseInfo.IsExist = true
|
||||
baseInfo.IsActive, _ = systemctl.IsActive(clamServiceNameCentOs)
|
||||
baseInfo.IsActive, _ = systemctl.IsActive(constant.ClamServiceNameCentOs)
|
||||
}
|
||||
exist2, _ := systemctl.IsExist(clamServiceNameUbuntu)
|
||||
exist2, _ := systemctl.IsExist(constant.ClamServiceNameUbuntu)
|
||||
if exist2 {
|
||||
c.serviceName = clamServiceNameUbuntu
|
||||
c.serviceName = constant.ClamServiceNameUbuntu
|
||||
baseInfo.IsExist = true
|
||||
baseInfo.IsActive, _ = systemctl.IsActive(clamServiceNameUbuntu)
|
||||
baseInfo.IsActive, _ = systemctl.IsActive(constant.ClamServiceNameUbuntu)
|
||||
}
|
||||
freshExist, _ := systemctl.IsExist(freshClamService)
|
||||
freshExist, _ := systemctl.IsExist(constant.FreshClamService)
|
||||
if freshExist {
|
||||
baseInfo.FreshIsExist = true
|
||||
baseInfo.FreshIsActive, _ = systemctl.IsActive(freshClamService)
|
||||
baseInfo.FreshIsActive, _ = systemctl.IsActive(constant.FreshClamService)
|
||||
}
|
||||
if !cmd.Which("clamdscan") {
|
||||
baseInfo.IsActive = false
|
||||
@@ -93,7 +87,7 @@ func (c *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_ = StopAllCronJob(false)
|
||||
_ = clam.CheckClamIsActive(false, clamRepo)
|
||||
}
|
||||
if baseInfo.FreshIsActive {
|
||||
version, err := cmd.RunDefaultWithStdoutBashC("freshclam --version")
|
||||
@@ -117,7 +111,7 @@ func (c *ClamService) Operate(operate string) error {
|
||||
}
|
||||
return nil
|
||||
case "fresh-start", "fresh-restart", "fresh-stop":
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("systemctl %s %s", strings.TrimPrefix(operate, "fresh-"), freshClamService)
|
||||
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)
|
||||
}
|
||||
@@ -128,31 +122,25 @@ func (c *ClamService) Operate(operate string) error {
|
||||
}
|
||||
|
||||
func (c *ClamService) SearchWithPage(req dto.SearchClamWithPage) (int64, interface{}, error) {
|
||||
total, commands, err := clamRepo.Page(req.Page, req.PageSize, repo.WithByLikeName(req.Info), repo.WithOrderRuleBy(req.OrderBy, req.Order))
|
||||
total, clams, err := clamRepo.Page(req.Page, req.PageSize, repo.WithByLikeName(req.Info), repo.WithOrderRuleBy(req.OrderBy, req.Order))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
var datas []dto.ClamInfo
|
||||
for _, command := range commands {
|
||||
for _, clam := range clams {
|
||||
var item dto.ClamInfo
|
||||
if err := copier.Copy(&item, &command); err != nil {
|
||||
if err := copier.Copy(&item, &clam); err != nil {
|
||||
return 0, nil, buserr.WithDetail("ErrStructTransform", err.Error(), nil)
|
||||
}
|
||||
item.LastHandleDate = "-"
|
||||
datas = append(datas, item)
|
||||
}
|
||||
nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
|
||||
for i := 0; i < len(datas); i++ {
|
||||
logPaths := loadFileByName(datas[i].Name)
|
||||
sort.Slice(logPaths, func(i, j int) bool {
|
||||
return logPaths[i] > logPaths[j]
|
||||
})
|
||||
if len(logPaths) != 0 {
|
||||
t1, err := time.ParseInLocation(constant.DateTimeSlimLayout, logPaths[0], nyc)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
datas[i].LastHandleDate = t1.Format(constant.DateTimeLayout)
|
||||
record, _ := clamRepo.RecordFirst(datas[i].ID)
|
||||
if record.ID != 0 {
|
||||
datas[i].LastRecordStatus = record.Status
|
||||
datas[i].LastRecordTime = record.StartTime.Format(constant.DateTimeLayout)
|
||||
} else {
|
||||
datas[i].LastRecordTime = "-"
|
||||
}
|
||||
alertBase := dto.AlertBase{
|
||||
AlertType: "clams",
|
||||
@@ -174,6 +162,9 @@ func (c *ClamService) Create(req dto.ClamCreate) error {
|
||||
if clam.ID != 0 {
|
||||
return buserr.New("ErrRecordExist")
|
||||
}
|
||||
if cmd.CheckIllegal(req.Path) {
|
||||
return buserr.New("ErrCmdIllegal")
|
||||
}
|
||||
if err := copier.Copy(&clam, &req); err != nil {
|
||||
return buserr.WithDetail("ErrStructTransform", err.Error(), nil)
|
||||
}
|
||||
@@ -181,7 +172,7 @@ func (c *ClamService) Create(req dto.ClamCreate) error {
|
||||
clam.InfectedDir = ""
|
||||
}
|
||||
if len(req.Spec) != 0 {
|
||||
entryID, err := xpack.StartClam(clam, false)
|
||||
entryID, err := xpack.StartClam(&clam, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -209,6 +200,9 @@ func (c *ClamService) Create(req dto.ClamCreate) error {
|
||||
}
|
||||
|
||||
func (c *ClamService) Update(req dto.ClamUpdate) error {
|
||||
if cmd.CheckIllegal(req.Path) {
|
||||
return buserr.New("ErrCmdIllegal")
|
||||
}
|
||||
clam, _ := clamRepo.Get(repo.WithByName(req.Name))
|
||||
if clam.ID == 0 {
|
||||
return buserr.New("ErrRecordNotFound")
|
||||
@@ -231,7 +225,7 @@ func (c *ClamService) Update(req dto.ClamUpdate) error {
|
||||
upMap["entry_id"] = 0
|
||||
}
|
||||
if len(req.Spec) != 0 && clam.Status != constant.StatusDisable {
|
||||
newEntryID, err := xpack.StartClam(clamItem, true)
|
||||
newEntryID, err := xpack.StartClam(&clamItem, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -246,6 +240,7 @@ func (c *ClamService) Update(req dto.ClamUpdate) error {
|
||||
upMap["infected_dir"] = req.InfectedDir
|
||||
upMap["infected_strategy"] = req.InfectedStrategy
|
||||
upMap["spec"] = req.Spec
|
||||
upMap["timeout"] = req.Timeout
|
||||
upMap["description"] = req.Description
|
||||
if err := clamRepo.Update(req.ID, upMap); err != nil {
|
||||
return err
|
||||
@@ -274,7 +269,7 @@ func (c *ClamService) UpdateStatus(id uint, status string) error {
|
||||
err error
|
||||
)
|
||||
if status == constant.StatusEnable {
|
||||
entryID, err = xpack.StartClam(clam, true)
|
||||
entryID, err = xpack.StartClam(&clam, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -292,9 +287,10 @@ func (c *ClamService) Delete(req dto.ClamDelete) error {
|
||||
if clam.ID == 0 {
|
||||
continue
|
||||
}
|
||||
if req.RemoveRecord {
|
||||
_ = os.RemoveAll(path.Join(global.Dir.DataDir, resultDir, clam.Name))
|
||||
if len(clam.Spec) != 0 {
|
||||
global.Cron.Remove(cron.EntryID(clam.EntryID))
|
||||
}
|
||||
_ = c.CleanRecord(clam.ID)
|
||||
if req.RemoveInfected {
|
||||
_ = os.RemoveAll(path.Join(clam.InfectedDir, "1panel-infected", clam.Name))
|
||||
}
|
||||
@@ -309,128 +305,81 @@ func (c *ClamService) Delete(req dto.ClamDelete) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClamService) HandleOnce(req dto.OperateByID) error {
|
||||
if cleaned := StopAllCronJob(true); cleaned {
|
||||
func (c *ClamService) HandleOnce(id uint) error {
|
||||
if active := clam.CheckClamIsActive(true, clamRepo); !active {
|
||||
return buserr.New("ErrClamdscanNotFound")
|
||||
}
|
||||
clam, _ := clamRepo.Get(repo.WithByID(req.ID))
|
||||
if clam.ID == 0 {
|
||||
clamItem, _ := clamRepo.Get(repo.WithByID(id))
|
||||
if clamItem.ID == 0 {
|
||||
return buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
if cmd.CheckIllegal(clam.Path) {
|
||||
return buserr.New("ErrCmdIllegal")
|
||||
}
|
||||
timeNow := time.Now().Format(constant.DateTimeSlimLayout)
|
||||
logFile := path.Join(global.Dir.DataDir, resultDir, clam.Name, timeNow)
|
||||
if _, err := os.Stat(path.Dir(logFile)); err != nil {
|
||||
_ = os.MkdirAll(path.Dir(logFile), os.ModePerm)
|
||||
record := clamRepo.StartRecords(clamItem.ID)
|
||||
taskItem, err := task.NewTaskWithOps("clam-"+clamItem.Name, task.TaskScan, task.TaskScopeClam, record.TaskID, clamItem.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("new task for exec shell failed, err: %v", err)
|
||||
}
|
||||
clam.AddScanTask(taskItem, clamItem, record.StartTime.Format(constant.DateTimeSlimLayout))
|
||||
go func() {
|
||||
strategy := ""
|
||||
switch clam.InfectedStrategy {
|
||||
case "remove":
|
||||
strategy = "--remove"
|
||||
case "move":
|
||||
dir := path.Join(clam.InfectedDir, "1panel-infected", clam.Name, timeNow)
|
||||
strategy = "--move=" + dir
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
_ = os.MkdirAll(dir, os.ModePerm)
|
||||
}
|
||||
case "copy":
|
||||
dir := path.Join(clam.InfectedDir, "1panel-infected", clam.Name, timeNow)
|
||||
strategy = "--copy=" + dir
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
_ = os.MkdirAll(dir, os.ModePerm)
|
||||
}
|
||||
err := taskItem.Execute()
|
||||
taskRepo := repo.NewITaskRepo()
|
||||
taskItem, _ := taskRepo.GetFirst(taskRepo.WithByID(record.TaskID))
|
||||
if len(taskItem.ID) == 0 {
|
||||
record.TaskID = ""
|
||||
}
|
||||
global.LOG.Debugf("clamdscan --fdpass %s %s -l %s", strategy, clam.Path, logFile)
|
||||
stdout, err := cmd.NewCommandMgr(cmd.WithIgnoreExist1(), cmd.WithTimeout(30*time.Minute)).RunWithStdoutBashCf("clamdscan --fdpass %s %s -l %s", strategy, clam.Path, logFile)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("clamdscan failed, stdout: %v, err: %v", stdout, err)
|
||||
clamRepo.EndRecords(record, constant.StatusFailed, err.Error())
|
||||
return
|
||||
}
|
||||
handleAlert(stdout, clam.Name, clam.ID)
|
||||
handleAlert(record.InfectedFiles, clamItem.Name, clamItem.ID)
|
||||
clam.AnalysisFromLog(taskItem.LogFile, &record)
|
||||
clamRepo.EndRecords(record, constant.StatusDone, "")
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClamService) LoadRecords(req dto.ClamLogSearch) (int64, interface{}, error) {
|
||||
func (c *ClamService) SearchRecords(req dto.ClamLogSearch) (int64, interface{}, error) {
|
||||
clam, _ := clamRepo.Get(repo.WithByID(req.ClamID))
|
||||
if clam.ID == 0 {
|
||||
return 0, nil, buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
logPaths := loadFileByName(clam.Name)
|
||||
if len(logPaths) == 0 {
|
||||
return 0, nil, nil
|
||||
}
|
||||
loc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
|
||||
req.StartTime = req.StartTime.In(loc)
|
||||
req.EndTime = req.EndTime.In(loc)
|
||||
|
||||
var filterFiles []string
|
||||
nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
|
||||
for _, item := range logPaths {
|
||||
t1, err := time.ParseInLocation(constant.DateTimeSlimLayout, item, nyc)
|
||||
if err != nil {
|
||||
continue
|
||||
total, records, err := clamRepo.PageRecords(req.Page, req.PageSize, clamRepo.WithByClamID(req.ClamID), repo.WithByStatus(req.Status), repo.WithByCreatedAt(req.StartTime, req.EndTime))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
var datas []dto.ClamRecord
|
||||
for _, record := range records {
|
||||
var item dto.ClamRecord
|
||||
if err := copier.Copy(&item, &record); err != nil {
|
||||
return 0, nil, buserr.WithDetail("ErrStructTransform", err.Error(), nil)
|
||||
}
|
||||
if t1.After(req.StartTime) && t1.Before(req.EndTime) {
|
||||
filterFiles = append(filterFiles, item)
|
||||
}
|
||||
}
|
||||
if len(filterFiles) == 0 {
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
sort.Slice(filterFiles, func(i, j int) bool {
|
||||
return filterFiles[i] > filterFiles[j]
|
||||
})
|
||||
|
||||
var records []string
|
||||
total, start, end := len(filterFiles), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||
if start > total {
|
||||
records = make([]string, 0)
|
||||
} else {
|
||||
if end >= total {
|
||||
end = total
|
||||
}
|
||||
records = filterFiles[start:end]
|
||||
}
|
||||
|
||||
var datas []dto.ClamLog
|
||||
for i := 0; i < len(records); i++ {
|
||||
item := loadResultFromLog(path.Join(global.Dir.DataDir, resultDir, clam.Name, records[i]))
|
||||
datas = append(datas, item)
|
||||
}
|
||||
return int64(total), datas, nil
|
||||
}
|
||||
func (c *ClamService) LoadRecordLog(req dto.ClamLogReq) (string, error) {
|
||||
logPath := path.Join(global.Dir.DataDir, resultDir, req.ClamName, req.RecordName)
|
||||
var tail string
|
||||
if req.Tail != "0" {
|
||||
tail = req.Tail
|
||||
} else {
|
||||
tail = "+1"
|
||||
}
|
||||
cmd := exec.Command("tail", "-n", tail, logPath)
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("tail -n %v failed, err: %v", req.Tail, err)
|
||||
}
|
||||
return string(stdout), nil
|
||||
}
|
||||
|
||||
func (c *ClamService) CleanRecord(req dto.OperateByID) error {
|
||||
clam, _ := clamRepo.Get(repo.WithByID(req.ID))
|
||||
if clam.ID == 0 {
|
||||
return buserr.New("ErrRecordNotFound")
|
||||
func (c *ClamService) CleanRecord(id uint) error {
|
||||
record, _ := clamRepo.ListRecord()
|
||||
for _, item := range record {
|
||||
if len(item.TaskID) != 0 {
|
||||
continue
|
||||
}
|
||||
taskItem, _ := taskRepo.GetFirst(taskRepo.WithByID(item.TaskID))
|
||||
if len(taskItem.LogFile) != 0 {
|
||||
_ = os.Remove(taskItem.LogFile)
|
||||
}
|
||||
}
|
||||
pathItem := path.Join(global.Dir.DataDir, resultDir, clam.Name)
|
||||
_ = os.RemoveAll(pathItem)
|
||||
return nil
|
||||
return clamRepo.DeleteRecord(clamRepo.WithByClamID(id))
|
||||
}
|
||||
|
||||
func (c *ClamService) LoadFile(req dto.ClamFileReq) (string, error) {
|
||||
filePath := ""
|
||||
switch req.Name {
|
||||
case "clamd":
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
if c.serviceName == constant.ClamServiceNameUbuntu {
|
||||
filePath = "/etc/clamav/clamd.conf"
|
||||
} else {
|
||||
filePath = "/etc/clamd.d/scan.conf"
|
||||
@@ -440,13 +389,13 @@ func (c *ClamService) LoadFile(req dto.ClamFileReq) (string, error) {
|
||||
if len(filePath) != 0 {
|
||||
break
|
||||
}
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
if c.serviceName == constant.ClamServiceNameUbuntu {
|
||||
filePath = "/var/log/clamav/clamav.log"
|
||||
} else {
|
||||
filePath = "/var/log/clamd.scan"
|
||||
}
|
||||
case "freshclam":
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
if c.serviceName == constant.ClamServiceNameUbuntu {
|
||||
filePath = "/etc/clamav/freshclam.conf"
|
||||
} else {
|
||||
filePath = "/etc/freshclam.conf"
|
||||
@@ -456,7 +405,7 @@ func (c *ClamService) LoadFile(req dto.ClamFileReq) (string, error) {
|
||||
if len(filePath) != 0 {
|
||||
break
|
||||
}
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
if c.serviceName == constant.ClamServiceNameUbuntu {
|
||||
filePath = "/var/log/clamav/freshclam.log"
|
||||
} else {
|
||||
filePath = "/var/log/freshclam.log"
|
||||
@@ -486,15 +435,15 @@ func (c *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error {
|
||||
service := ""
|
||||
switch req.Name {
|
||||
case "clamd":
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
service = clamServiceNameUbuntu
|
||||
if c.serviceName == constant.ClamServiceNameUbuntu {
|
||||
service = constant.ClamServiceNameUbuntu
|
||||
filePath = "/etc/clamav/clamd.conf"
|
||||
} else {
|
||||
service = clamServiceNameCentOs
|
||||
service = constant.ClamServiceNameCentOs
|
||||
filePath = "/etc/clamd.d/scan.conf"
|
||||
}
|
||||
case "freshclam":
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
if c.serviceName == constant.ClamServiceNameUbuntu {
|
||||
filePath = "/etc/clamav/freshclam.conf"
|
||||
} else {
|
||||
filePath = "/etc/freshclam.conf"
|
||||
@@ -516,86 +465,16 @@ func (c *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func StopAllCronJob(withCheck bool) bool {
|
||||
if withCheck {
|
||||
isActive := false
|
||||
exist1, _ := systemctl.IsExist(clamServiceNameCentOs)
|
||||
if exist1 {
|
||||
isActive, _ = systemctl.IsActive(clamServiceNameCentOs)
|
||||
}
|
||||
exist2, _ := systemctl.IsExist(clamServiceNameUbuntu)
|
||||
if exist2 {
|
||||
isActive, _ = systemctl.IsActive(clamServiceNameUbuntu)
|
||||
}
|
||||
if isActive {
|
||||
return false
|
||||
}
|
||||
}
|
||||
clams, _ := clamRepo.List(repo.WithByStatus(constant.StatusEnable))
|
||||
for i := 0; i < len(clams); i++ {
|
||||
global.Cron.Remove(cron.EntryID(clams[i].EntryID))
|
||||
_ = clamRepo.Update(clams[i].ID, map[string]interface{}{"status": constant.StatusDisable, "entry_id": 0})
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func loadFileByName(name string) []string {
|
||||
var logPaths []string
|
||||
pathItem := path.Join(global.Dir.DataDir, resultDir, name)
|
||||
fileItems, err := os.ReadDir(pathItem)
|
||||
if err != nil {
|
||||
return logPaths
|
||||
}
|
||||
for _, item := range fileItems {
|
||||
if !item.IsDir() {
|
||||
logPaths = append(logPaths, item.Name())
|
||||
}
|
||||
}
|
||||
return logPaths
|
||||
}
|
||||
func loadResultFromLog(pathItem string) dto.ClamLog {
|
||||
var data dto.ClamLog
|
||||
data.Name = path.Base(pathItem)
|
||||
data.Status = constant.StatusWaiting
|
||||
file, err := os.ReadFile(pathItem)
|
||||
if err != nil {
|
||||
return data
|
||||
}
|
||||
lines := strings.Split(string(file), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "- SCAN SUMMARY -") {
|
||||
data.Status = constant.StatusDone
|
||||
}
|
||||
if data.Status != constant.StatusDone {
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case strings.HasPrefix(line, "Infected files:"):
|
||||
data.InfectedFiles = strings.TrimPrefix(line, "Infected files:")
|
||||
case strings.HasPrefix(line, "Total errors:"):
|
||||
data.TotalError = strings.TrimPrefix(line, "Total errors:")
|
||||
case strings.HasPrefix(line, "Time:"):
|
||||
if strings.Contains(line, "(") {
|
||||
data.ScanTime = strings.ReplaceAll(strings.Split(line, "(")[1], ")", "")
|
||||
continue
|
||||
}
|
||||
data.ScanTime = strings.TrimPrefix(line, "Time:")
|
||||
case strings.HasPrefix(line, "Start Date:"):
|
||||
data.ScanDate = strings.TrimPrefix(line, "Start Date:")
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
func (c *ClamService) loadLogPath(name string) string {
|
||||
confPath := ""
|
||||
if name == "clamd-log" {
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
if c.serviceName == constant.ClamServiceNameUbuntu {
|
||||
confPath = "/etc/clamav/clamd.conf"
|
||||
} else {
|
||||
confPath = "/etc/clamd.d/scan.conf"
|
||||
}
|
||||
} else {
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
if c.serviceName == constant.ClamServiceNameUbuntu {
|
||||
confPath = "/etc/clamav/freshclam.conf"
|
||||
} else {
|
||||
confPath = "/etc/freshclam.conf"
|
||||
@@ -626,23 +505,16 @@ func (c *ClamService) loadLogPath(name string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func handleAlert(stdout, clamName string, clamId uint) {
|
||||
if strings.Contains(stdout, "- SCAN SUMMARY -") {
|
||||
lines := strings.Split(stdout, "\n")
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "Infected files: ") {
|
||||
infectedFiles, _ := strconv.Atoi(strings.TrimPrefix(line, "Infected files: "))
|
||||
if infectedFiles > 0 {
|
||||
pushAlert := dto.PushAlert{
|
||||
TaskName: clamName,
|
||||
AlertType: "clams",
|
||||
EntryID: clamId,
|
||||
Param: strconv.Itoa(infectedFiles),
|
||||
}
|
||||
_ = alert_push.PushAlert(pushAlert)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
func handleAlert(infectedFiles, clamName string, clamId uint) {
|
||||
itemInfected, _ := strconv.Atoi(strings.TrimSpace(infectedFiles))
|
||||
if itemInfected < 0 {
|
||||
return
|
||||
}
|
||||
pushAlert := dto.PushAlert{
|
||||
TaskName: clamName,
|
||||
AlertType: "clams",
|
||||
EntryID: clamId,
|
||||
Param: strconv.Itoa(itemInfected),
|
||||
}
|
||||
_ = alert_push.PushAlert(pushAlert)
|
||||
}
|
||||
|
||||
@@ -640,7 +640,6 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
|
||||
}
|
||||
|
||||
var data dto.ContainerOperate
|
||||
data.ContainerID = oldContainer.ID
|
||||
data.Name = strings.ReplaceAll(oldContainer.Name, "/", "")
|
||||
data.Image = oldContainer.Config.Image
|
||||
if oldContainer.NetworkSettings != nil {
|
||||
@@ -702,21 +701,11 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
|
||||
}
|
||||
defer client.Close()
|
||||
ctx := context.Background()
|
||||
newContainer, _ := client.ContainerInspect(ctx, req.Name)
|
||||
if newContainer.ContainerJSONBase != nil && newContainer.ID != req.ContainerID {
|
||||
return buserr.New("ErrContainerName")
|
||||
}
|
||||
oldContainer, err := client.ContainerInspect(ctx, req.ContainerID)
|
||||
oldContainer, err := client.ContainerInspect(ctx, req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(oldContainer.Config.Entrypoint) != 0 {
|
||||
if oldContainer.Config.Entrypoint[0] == "/docker-entrypoint.sh" {
|
||||
oldContainer.Config.Entrypoint = []string{}
|
||||
}
|
||||
}
|
||||
|
||||
taskItem, err := task.NewTaskWithOps(req.Name, task.TaskUpdate, task.TaskScopeContainer, req.TaskID, 1)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("new task for create container failed, err: %v", err)
|
||||
@@ -736,7 +725,7 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
|
||||
}, nil)
|
||||
|
||||
taskItem.AddSubTask(i18n.GetWithName("ContainerCreate", req.Name), func(t *task.Task) error {
|
||||
err := client.ContainerRemove(ctx, req.ContainerID, container.RemoveOptions{Force: true})
|
||||
err := client.ContainerRemove(ctx, req.Name, container.RemoveOptions{Force: true})
|
||||
taskItem.LogWithStatus(i18n.GetWithName("ContainerRemoveOld", req.Name), err)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -805,19 +794,10 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
inspected, err := client.ImageInspect(ctx, req.Image)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inspect image failed, err: %v", err)
|
||||
}
|
||||
if isDynamicImage(inspected) {
|
||||
oldContainer.Config.Entrypoint = nil
|
||||
oldContainer.Config.Cmd = nil
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
taskItem.AddSubTask(i18n.GetWithName("ContainerCreate", item), func(t *task.Task) error {
|
||||
oldContainer.Config.Cmd = nil
|
||||
config := oldContainer.Config
|
||||
config.Image = req.Image
|
||||
hostConf := oldContainer.HostConfig
|
||||
@@ -1518,17 +1498,19 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *conta
|
||||
hostConf.DNS = req.DNS
|
||||
config.Volumes = make(map[string]struct{})
|
||||
for _, volume := range req.Volumes {
|
||||
if volume.Type == "volume" {
|
||||
hostConf.Mounts = append(hostConf.Mounts, mount.Mount{
|
||||
Type: mount.Type(volume.Type),
|
||||
Source: volume.SourceDir,
|
||||
Target: volume.ContainerDir,
|
||||
ReadOnly: volume.Mode == "ro",
|
||||
})
|
||||
config.Volumes[volume.ContainerDir] = struct{}{}
|
||||
} else {
|
||||
hostConf.Binds = append(hostConf.Binds, fmt.Sprintf("%s:%s:%s", volume.SourceDir, volume.ContainerDir, volume.Mode))
|
||||
item := mount.Mount{
|
||||
Type: mount.Type(volume.Type),
|
||||
Source: volume.SourceDir,
|
||||
Target: volume.ContainerDir,
|
||||
ReadOnly: volume.Mode == "ro",
|
||||
}
|
||||
if volume.Type == "bind" {
|
||||
item.BindOptions = &mount.BindOptions{
|
||||
Propagation: mount.Propagation(volume.Shared),
|
||||
}
|
||||
}
|
||||
hostConf.Mounts = append(hostConf.Mounts, item)
|
||||
config.Volumes[volume.ContainerDir] = struct{}{}
|
||||
}
|
||||
return &config, &hostConf, &networkConf, nil
|
||||
}
|
||||
@@ -1570,6 +1552,7 @@ func loadVolumeBinds(binds []container.MountPoint) []dto.VolumeHelper {
|
||||
if bind.RW {
|
||||
volumeItem.Mode = "rw"
|
||||
}
|
||||
volumeItem.Shared = string(bind.Propagation)
|
||||
datas = append(datas, volumeItem)
|
||||
}
|
||||
return datas
|
||||
@@ -1743,23 +1726,3 @@ func loadContainerPortForInfo(itemPorts []container.Port) []dto.PortHelper {
|
||||
}
|
||||
return exposedPorts
|
||||
}
|
||||
|
||||
func isDynamicImage(inspected image.InspectResponse) bool {
|
||||
if len(inspected.Config.Entrypoint) > 0 {
|
||||
entrypointStr := strings.Join(inspected.Config.Entrypoint, " ")
|
||||
if strings.Contains(entrypointStr, "entrypoint") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
dirs := []string{"/docker-entrypoint.d", "/docker-entrypoint-initdb.d"}
|
||||
for _, dir := range dirs {
|
||||
for _, layer := range inspected.RootFS.Layers {
|
||||
if strings.Contains(layer, dir) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
@@ -53,25 +54,27 @@ func (u *ContainerService) PageVolume(req dto.SearchWithPage) (int64, interface{
|
||||
|
||||
nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
|
||||
for _, item := range records {
|
||||
tag := make([]string, 0)
|
||||
for _, val := range item.Labels {
|
||||
tag = append(tag, val)
|
||||
var volume dto.Volume
|
||||
volume.Driver = item.Driver
|
||||
volume.Mountpoint = item.Mountpoint
|
||||
volume.Name = simplifyVolumeName(item.Name)
|
||||
for key, val := range item.Labels {
|
||||
volume.Labels = append(volume.Labels, dto.VolumeOption{Key: key, Value: val})
|
||||
}
|
||||
var createTime time.Time
|
||||
if strings.Contains(item.CreatedAt, "Z") {
|
||||
createTime, _ = time.ParseInLocation("2006-01-02T15:04:05Z", item.CreatedAt, nyc)
|
||||
} else if strings.Contains(item.CreatedAt, "+") {
|
||||
createTime, _ = time.ParseInLocation("2006-01-02T15:04:05+08:00", item.CreatedAt, nyc)
|
||||
} else {
|
||||
createTime, _ = time.ParseInLocation("2006-01-02T15:04:05", item.CreatedAt, nyc)
|
||||
for key, val := range item.Options {
|
||||
volume.Options = append(volume.Options, dto.VolumeOption{Key: key, Value: val})
|
||||
}
|
||||
data = append(data, dto.Volume{
|
||||
CreatedAt: createTime,
|
||||
Name: item.Name,
|
||||
Driver: item.Driver,
|
||||
Mountpoint: item.Mountpoint,
|
||||
Labels: tag,
|
||||
sort.Slice(volume.Options, func(i, j int) bool {
|
||||
return volume.Options[i].Key < volume.Options[j].Key
|
||||
})
|
||||
if strings.Contains(item.CreatedAt, "Z") {
|
||||
volume.CreatedAt, _ = time.ParseInLocation("2006-01-02T15:04:05Z", item.CreatedAt, nyc)
|
||||
} else if strings.Contains(item.CreatedAt, "+") {
|
||||
volume.CreatedAt, _ = time.ParseInLocation("2006-01-02T15:04:05+08:00", item.CreatedAt, nyc)
|
||||
} else {
|
||||
volume.CreatedAt, _ = time.ParseInLocation("2006-01-02T15:04:05", item.CreatedAt, nyc)
|
||||
}
|
||||
data = append(data, volume)
|
||||
}
|
||||
|
||||
return int64(total), data, nil
|
||||
@@ -140,3 +143,16 @@ 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]
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ type ICronjobService interface {
|
||||
Delete(req dto.CronjobBatchDelete) error
|
||||
StartJob(cronjob *model.Cronjob, isUpdate bool) (string, error)
|
||||
CleanRecord(req dto.CronjobClean) error
|
||||
HandleStop(id uint) error
|
||||
|
||||
Export(req dto.OperateByIDs) (string, error)
|
||||
Import(req []dto.CronjobTrans) error
|
||||
@@ -499,8 +500,8 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
|
||||
if hasBackup(cronjob.Type) {
|
||||
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if !req.CleanRemoteData {
|
||||
for key := range accountMap {
|
||||
if key != constant.Local {
|
||||
for key, val := range accountMap {
|
||||
if val.accountType != constant.Local {
|
||||
delete(accountMap, key)
|
||||
}
|
||||
}
|
||||
@@ -536,6 +537,9 @@ func (u *CronjobService) HandleOnce(id uint) error {
|
||||
if cronjob.ID == 0 {
|
||||
return buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
if cronjob.IsExecuting {
|
||||
return buserr.New("InExecuting")
|
||||
}
|
||||
u.HandleJob(&cronjob)
|
||||
return nil
|
||||
}
|
||||
@@ -601,7 +605,7 @@ func (u *CronjobService) StartJob(cronjob *model.Cronjob, isUpdate bool) (string
|
||||
global.Cron.Remove(cron.EntryID(idItem))
|
||||
}
|
||||
}
|
||||
specs := strings.Split(cronjob.Spec, ",")
|
||||
specs := strings.Split(cronjob.Spec, "&&")
|
||||
var ids []string
|
||||
for _, spec := range specs {
|
||||
cronjob.Spec = spec
|
||||
@@ -614,6 +618,20 @@ func (u *CronjobService) StartJob(cronjob *model.Cronjob, isUpdate bool) (string
|
||||
return strings.Join(ids, ","), nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) HandleStop(id uint) error {
|
||||
record, _ := cronjobRepo.GetRecord(repo.WithByID(id))
|
||||
if record.ID == 0 {
|
||||
return buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
if len(record.TaskID) == 0 {
|
||||
return nil
|
||||
}
|
||||
if cancle, ok := global.TaskCtxMap[record.TaskID]; ok {
|
||||
cancle()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error {
|
||||
for _, id := range req.IDs {
|
||||
cronjob, _ := cronjobRepo.Get(repo.WithByID(id))
|
||||
|
||||
@@ -27,7 +27,8 @@ import (
|
||||
func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
|
||||
apps := loadAppsForJob(cronjob)
|
||||
if len(apps) == 0 {
|
||||
return errors.New("no such app in database!")
|
||||
addSkipTask("App", taskItem)
|
||||
return nil
|
||||
}
|
||||
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
|
||||
@@ -37,6 +38,7 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, t
|
||||
retry := 0
|
||||
taskItem.AddSubTaskWithOps(task.GetTaskName(app.Name, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
|
||||
var record model.BackupRecord
|
||||
record.Status = constant.StatusSuccess
|
||||
record.From = "cronjob"
|
||||
record.Type = "app"
|
||||
record.CronjobID = cronjob.ID
|
||||
@@ -83,7 +85,8 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, t
|
||||
func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
|
||||
webs := loadWebsForJob(cronjob)
|
||||
if len(webs) == 0 {
|
||||
return errors.New("no such website in database!")
|
||||
addSkipTask("Website", taskItem)
|
||||
return nil
|
||||
}
|
||||
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
|
||||
@@ -93,6 +96,7 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
|
||||
retry := 0
|
||||
taskItem.AddSubTaskWithOps(task.GetTaskName(web.Alias, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
|
||||
var record model.BackupRecord
|
||||
record.Status = constant.StatusSuccess
|
||||
record.From = "cronjob"
|
||||
record.Type = "website"
|
||||
record.CronjobID = cronjob.ID
|
||||
@@ -140,7 +144,8 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
|
||||
func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
|
||||
dbs := loadDbsForJob(cronjob)
|
||||
if len(dbs) == 0 {
|
||||
return errors.New("no such db in database!")
|
||||
addSkipTask("Database", taskItem)
|
||||
return nil
|
||||
}
|
||||
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
|
||||
@@ -151,6 +156,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
|
||||
itemName := fmt.Sprintf("%s[%s] - %s", dbInfo.Database, dbInfo.DBType, dbInfo.Name)
|
||||
taskItem.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
|
||||
var record model.BackupRecord
|
||||
record.Status = constant.StatusSuccess
|
||||
record.From = "cronjob"
|
||||
record.Type = dbInfo.DBType
|
||||
record.CronjobID = cronjob.ID
|
||||
@@ -235,6 +241,7 @@ func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.T
|
||||
}
|
||||
}
|
||||
var record model.BackupRecord
|
||||
record.Status = constant.StatusSuccess
|
||||
record.From = "cronjob"
|
||||
record.Type = "directory"
|
||||
record.CronjobID = cronjob.ID
|
||||
@@ -270,6 +277,7 @@ func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.T
|
||||
return err
|
||||
}
|
||||
var record model.BackupRecord
|
||||
record.Status = constant.StatusSuccess
|
||||
record.From = "cronjob"
|
||||
record.Type = "log"
|
||||
record.CronjobID = cronjob.ID
|
||||
@@ -298,6 +306,7 @@ func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.J
|
||||
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
}
|
||||
var record model.BackupRecord
|
||||
record.Status = constant.StatusSuccess
|
||||
record.From = "cronjob"
|
||||
record.Type = "snapshot"
|
||||
record.CronjobID = cronjob.ID
|
||||
@@ -372,6 +381,13 @@ type DatabaseHelper struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func addSkipTask(source string, taskItem *task.Task) {
|
||||
taskItem.AddSubTask(task.GetTaskName(i18n.GetMsgByKey(source), task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
|
||||
taskItem.Log(i18n.GetMsgByKey("NoSuchResource"))
|
||||
return nil
|
||||
}, nil)
|
||||
}
|
||||
|
||||
func loadDbsForJob(cronjob model.Cronjob) []DatabaseHelper {
|
||||
var dbs []DatabaseHelper
|
||||
if cronjob.DBName == "all" {
|
||||
|
||||
@@ -25,7 +25,12 @@ import (
|
||||
)
|
||||
|
||||
func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
record := cronjobRepo.StartRecords(cronjob.ID, "", cronjob.Type)
|
||||
cronjobItem, _ := cronjobRepo.Get(repo.WithByID(cronjob.ID))
|
||||
if cronjobItem.IsExecuting {
|
||||
cronjobRepo.AddFailedRecord(cronjob.ID, i18n.GetMsgByKey("InExecuting"))
|
||||
return
|
||||
}
|
||||
record := cronjobRepo.StartRecords(cronjob.ID)
|
||||
taskItem, err := task.NewTaskWithOps(fmt.Sprintf("cronjob-%s", cronjob.Name), task.TaskHandle, task.TaskScopeCronjob, record.TaskID, cronjob.ID)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("new task for exec shell failed, err: %v", err)
|
||||
@@ -127,7 +132,8 @@ func (u *CronjobService) loadTask(cronjob *model.Cronjob, record *model.JobRecor
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleShell(cronjob model.Cronjob, taskItem *task.Task) {
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem))
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem), cmd.WithContext(taskItem.TaskCtx))
|
||||
|
||||
taskItem.AddSubTaskWithOps(i18n.GetWithName("HandleShell", cronjob.Name), func(t *task.Task) error {
|
||||
if len(cronjob.ContainerName) != 0 {
|
||||
scriptItem := cronjob.Script
|
||||
@@ -142,17 +148,21 @@ func (u *CronjobService) handleShell(cronjob model.Cronjob, taskItem *task.Task)
|
||||
command = cronjob.Command
|
||||
}
|
||||
if len(cronjob.User) != 0 {
|
||||
return cmdMgr.Run("docker", "exec", "-u", cronjob.User, cronjob.ContainerName, command, "-c", strings.ReplaceAll(scriptItem, "\"", "\\\""))
|
||||
return cmdMgr.Run("docker", "exec", "-u", cronjob.User, cronjob.ContainerName, command, "-c", scriptItem)
|
||||
}
|
||||
return cmdMgr.Run("docker", "exec", cronjob.ContainerName, command, "-c", strings.ReplaceAll(scriptItem, "\"", "\\\""))
|
||||
return cmdMgr.Run("docker", "exec", cronjob.ContainerName, command, "-c", scriptItem)
|
||||
}
|
||||
if len(cronjob.Executor) == 0 {
|
||||
cronjob.Executor = "bash"
|
||||
}
|
||||
if cronjob.ScriptMode == "input" {
|
||||
fileItem := pathUtils.Join(global.Dir.DataDir, "task", "shell", cronjob.Name, cronjob.Name+".sh")
|
||||
suffix := ".sh"
|
||||
if strings.HasPrefix(cronjob.Executor, "python") {
|
||||
suffix = ".py"
|
||||
}
|
||||
fileItem := pathUtils.Join(global.Dir.DataDir, "task", "shell", cronjob.Name, cronjob.Name+suffix)
|
||||
_ = os.MkdirAll(pathUtils.Dir(fileItem), os.ModePerm)
|
||||
shellFile, err := os.OpenFile(fileItem, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, constant.FilePerm)
|
||||
shellFile, err := os.OpenFile(fileItem, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, constant.DirPerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -212,6 +222,7 @@ func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime t
|
||||
for _, website := range websites {
|
||||
taskItem.Log(website.Alias)
|
||||
var record model.BackupRecord
|
||||
record.Status = constant.StatusSuccess
|
||||
record.From = "cronjob"
|
||||
record.Type = "cut-website-log"
|
||||
record.CronjobID = cronjob.ID
|
||||
|
||||
@@ -14,6 +14,7 @@ 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/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu"
|
||||
@@ -38,6 +39,9 @@ type IDashboardService interface {
|
||||
LoadCurrentInfoForNode() *dto.NodeCurrent
|
||||
LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent
|
||||
|
||||
LoadQuickOptions() []dto.QuickJump
|
||||
ChangeQuick(req dto.ChangeQuicks) error
|
||||
|
||||
LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher, error)
|
||||
ChangeShow(req dto.SettingUpdate) error
|
||||
ListLauncherOption(filter string) ([]dto.LauncherOption, error)
|
||||
@@ -140,7 +144,7 @@ func (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto
|
||||
baseInfo.KernelVersion = hostInfo.KernelVersion
|
||||
ss, _ := json.Marshal(hostInfo)
|
||||
baseInfo.VirtualizationSystem = string(ss)
|
||||
baseInfo.IpV4Addr = GetOutboundIP()
|
||||
baseInfo.IpV4Addr = loadOutboundIP()
|
||||
httpProxy := os.Getenv("http_proxy")
|
||||
if httpProxy == "" {
|
||||
httpProxy = os.Getenv("HTTP_PROXY")
|
||||
@@ -150,30 +154,7 @@ func (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto
|
||||
}
|
||||
baseInfo.SystemProxy = "noProxy"
|
||||
|
||||
appInstall, err := appInstallRepo.ListBy(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
baseInfo.AppInstalledNumber = len(appInstall)
|
||||
postgresqlDbs, err := postgresqlRepo.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mysqlDbs, err := mysqlRepo.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
baseInfo.DatabaseNumber = len(mysqlDbs) + len(postgresqlDbs)
|
||||
website, err := websiteRepo.GetBy()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
baseInfo.WebsiteNumber = len(website)
|
||||
cronjobs, err := cronjobRepo.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
baseInfo.CronjobNumber = len(cronjobs)
|
||||
loadQuickJump(&baseInfo)
|
||||
|
||||
cpuInfo, err := cpu.Info()
|
||||
if err == nil {
|
||||
@@ -347,6 +328,39 @@ func (u *DashboardService) ChangeShow(req dto.SettingUpdate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *DashboardService) LoadQuickOptions() []dto.QuickJump {
|
||||
quicks := launcherRepo.ListQuickJump(true)
|
||||
var list []dto.QuickJump
|
||||
for _, quick := range quicks {
|
||||
var item dto.QuickJump
|
||||
_ = copier.Copy(&item, &quick)
|
||||
list = append(list, item)
|
||||
}
|
||||
return list
|
||||
}
|
||||
func (u *DashboardService) ChangeQuick(req dto.ChangeQuicks) error {
|
||||
showCount := 0
|
||||
var quicks []model.QuickJump
|
||||
for _, item := range req.Quicks {
|
||||
var quick model.QuickJump
|
||||
if item.IsShow {
|
||||
showCount++
|
||||
}
|
||||
if err := copier.Copy(&quick, &item); err != nil {
|
||||
return err
|
||||
}
|
||||
quicks = append(quicks, quick)
|
||||
}
|
||||
if showCount == 0 {
|
||||
return buserr.New("ErrMinQuickJump")
|
||||
}
|
||||
if showCount > 4 {
|
||||
return buserr.New("ErrMaxQuickJump")
|
||||
}
|
||||
|
||||
return launcherRepo.UpdateQuicks(quicks)
|
||||
}
|
||||
|
||||
func (u *DashboardService) ListLauncherOption(filter string) ([]dto.LauncherOption, error) {
|
||||
showList, _ := launcherRepo.ListName()
|
||||
var data []dto.LauncherOption
|
||||
@@ -545,7 +559,7 @@ func loadXpuInfo() []dto.XPUInfo {
|
||||
return data
|
||||
}
|
||||
|
||||
func GetOutboundIP() string {
|
||||
func loadOutboundIP() string {
|
||||
conn, err := network.Dial("udp", "8.8.8.8:80")
|
||||
|
||||
if err != nil {
|
||||
@@ -556,3 +570,38 @@ func GetOutboundIP() string {
|
||||
localAddr := conn.LocalAddr().(*network.UDPAddr)
|
||||
return localAddr.IP.String()
|
||||
}
|
||||
|
||||
func loadQuickJump(base *dto.DashboardBase) {
|
||||
website, _ := websiteRepo.GetBy()
|
||||
base.WebsiteNumber = len(website)
|
||||
|
||||
postgresqlDbs, _ := postgresqlRepo.List()
|
||||
mysqlDbs, _ := mysqlRepo.List()
|
||||
base.DatabaseNumber = len(mysqlDbs) + len(postgresqlDbs)
|
||||
|
||||
cronjobs, _ := cronjobRepo.List()
|
||||
base.CronjobNumber = len(cronjobs)
|
||||
|
||||
appInstall, _ := appInstallRepo.ListBy(context.Background())
|
||||
base.AppInstalledNumber = len(appInstall)
|
||||
|
||||
quicks := launcherRepo.ListQuickJump(false)
|
||||
for i := 0; i < len(quicks); i++ {
|
||||
switch quicks[i].Name {
|
||||
case "Website":
|
||||
quicks[i].Detail = fmt.Sprintf("%d", base.WebsiteNumber)
|
||||
case "Database":
|
||||
quicks[i].Detail = fmt.Sprintf("%d", base.DatabaseNumber)
|
||||
case "Cronjob":
|
||||
quicks[i].Detail = fmt.Sprintf("%d", base.CronjobNumber)
|
||||
case "AppInstalled":
|
||||
quicks[i].Detail = fmt.Sprintf("%d", base.AppInstalledNumber)
|
||||
}
|
||||
var item dto.QuickJump
|
||||
_ = copier.Copy(&item, quicks[i])
|
||||
base.QuickJumps = append(base.QuickJumps, item)
|
||||
}
|
||||
sort.Slice(quicks, func(i, j int) bool {
|
||||
return quicks[i].Recommend < quicks[j].Recommend
|
||||
})
|
||||
}
|
||||
|
||||
+51
-31
@@ -12,9 +12,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"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/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ntp"
|
||||
@@ -231,40 +233,58 @@ func (u *DeviceService) UpdatePasswd(req dto.ChangePasswd) error {
|
||||
}
|
||||
|
||||
func (u *DeviceService) UpdateSwap(req dto.SwapHelper) error {
|
||||
if cmd.CheckIllegal(req.Path) {
|
||||
return buserr.New("ErrCmdIllegal")
|
||||
}
|
||||
if !req.IsNew {
|
||||
std, err := cmd.RunDefaultWithStdoutBashCf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("handle swapoff %s failed, err: %s", req.Path, std)
|
||||
}
|
||||
}
|
||||
if req.Size == 0 {
|
||||
if req.Path == path.Join(global.Dir.BaseDir, ".1panel_swap") {
|
||||
_ = os.Remove(path.Join(global.Dir.BaseDir, ".1panel_swap"))
|
||||
}
|
||||
return operateSwapWithFile(true, req)
|
||||
}
|
||||
stdDD, err := cmd.RunDefaultWithStdoutBashCf("%s dd if=/dev/zero of=%s bs=1024 count=%d", cmd.SudoHandleCmd(), req.Path, req.Size)
|
||||
taskItem, err := task.NewTaskWithOps(req.Path, task.TaskSwapSet, task.TaskScopeSystem, req.TaskID, 1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("handle dd %s failed, std: %s, err: %s", req.Path, stdDD, err)
|
||||
}
|
||||
stdChmod, err := cmd.RunDefaultWithStdoutBashCf("%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)
|
||||
}
|
||||
stdMkswap, err := cmd.RunDefaultWithStdoutBashCf("%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)
|
||||
global.LOG.Errorf("new task for create container failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
stdSwapon, err := cmd.RunDefaultWithStdoutBashCf("%s swapon %s", cmd.SudoHandleCmd(), req.Path)
|
||||
if err != nil {
|
||||
_, _ = cmd.RunDefaultWithStdoutBashCf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
|
||||
return fmt.Errorf("handle swapoff %s failed,std: %s, err: %s", req.Path, stdSwapon, err)
|
||||
}
|
||||
return operateSwapWithFile(false, req)
|
||||
taskItem.AddSubTask(i18n.GetMsgWithMap("SetSwap", map[string]interface{}{"path": req.Path, "size": common.LoadSizeUnit2F(float64(req.Size * 1024))}), func(t *task.Task) error {
|
||||
if cmd.CheckIllegal(req.Path) {
|
||||
return buserr.New("ErrCmdIllegal")
|
||||
}
|
||||
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 req.Size == 0 {
|
||||
if req.Path == path.Join(global.Dir.BaseDir, ".1panel_swap") {
|
||||
_ = os.Remove(path.Join(global.Dir.BaseDir, ".1panel_swap"))
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
return operateSwapWithFile(false, req)
|
||||
}, nil)
|
||||
go func() {
|
||||
_ = taskItem.Execute()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *DeviceService) LoadConf(name string) (string, error) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -18,6 +19,7 @@ import (
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
|
||||
"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/global"
|
||||
@@ -74,8 +76,21 @@ func (u *DeviceService) Scan() dto.CleanData {
|
||||
sort.Slice(upgradeTree.Children, func(i, j int) bool {
|
||||
return common.CompareVersion(upgradeTree.Children[i].Label, upgradeTree.Children[j].Label)
|
||||
})
|
||||
upgradeTree.Children[0].IsCheck = false
|
||||
upgradeTree.Children[0].IsRecommend = false
|
||||
if global.IsMaster {
|
||||
var copiesSeeting model.Setting
|
||||
_ = global.CoreDB.Where("key = ?", "UpgradeBackupCopies").First(&copiesSeeting).Error
|
||||
copies, _ := strconv.Atoi(copiesSeeting.Value)
|
||||
if copies == 0 || copies > len(upgradeTree.Children) {
|
||||
copies = len(upgradeTree.Children)
|
||||
}
|
||||
for i := 0; i < copies; i++ {
|
||||
upgradeTree.Children[i].IsCheck = false
|
||||
upgradeTree.Children[i].IsRecommend = false
|
||||
}
|
||||
} else {
|
||||
upgradeTree.Children[0].IsCheck = false
|
||||
upgradeTree.Children[0].IsRecommend = false
|
||||
}
|
||||
}
|
||||
treeData = append(treeData, upgradeTree)
|
||||
|
||||
@@ -214,7 +229,7 @@ func (u *DeviceService) Clean(req []dto.Clean) {
|
||||
}
|
||||
case "task_log":
|
||||
if len(item.Name) == 0 {
|
||||
files, _ := os.ReadDir(path.Join(global.Dir.TaskDir))
|
||||
files, _ := os.ReadDir(global.Dir.TaskDir)
|
||||
if len(files) == 0 {
|
||||
continue
|
||||
}
|
||||
@@ -222,15 +237,10 @@ func (u *DeviceService) Clean(req []dto.Clean) {
|
||||
if file.Name() == "ssl" || !file.IsDir() {
|
||||
continue
|
||||
}
|
||||
dropFileOrDir(path.Join(global.Dir.TaskDir, file.Name()))
|
||||
dropTaskLog(path.Join(global.Dir.TaskDir, file.Name()))
|
||||
}
|
||||
_ = taskRepo.DeleteAll()
|
||||
} else {
|
||||
pathItem := path.Join(global.Dir.TaskDir, item.Name)
|
||||
dropFileOrDir(pathItem)
|
||||
if len(item.Name) != 0 {
|
||||
_ = taskRepo.Delete(repo.WithByType(item.Name))
|
||||
}
|
||||
dropTaskLog(path.Join(global.Dir.TaskDir, item.Name))
|
||||
}
|
||||
case "script":
|
||||
dropFileOrDir(path.Join(global.Dir.TmpDir, "script", item.Name))
|
||||
@@ -530,7 +540,6 @@ func loadTreeWithAllFile(isCheck bool, originalPath, treeType, pathItem string,
|
||||
}
|
||||
|
||||
func dropFileOrDir(itemPath string) {
|
||||
global.LOG.Debugf("drop file %s", itemPath)
|
||||
if err := os.RemoveAll(itemPath); err != nil {
|
||||
global.LOG.Errorf("drop file %s failed, err %v", itemPath, err)
|
||||
}
|
||||
@@ -606,6 +615,62 @@ func dropVolumes() (int, int) {
|
||||
return len(res.VolumesDeleted), int(res.SpaceReclaimed)
|
||||
}
|
||||
|
||||
func dropTaskLog(logDir string) {
|
||||
files, err := os.ReadDir(logDir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
taskType := path.Base(logDir)
|
||||
var usedTasks []string
|
||||
switch taskType {
|
||||
case "Cronjob":
|
||||
_ = global.DB.Model(&model.JobRecords{}).Where("task_id != ?", "").Select("task_id").Find(&usedTasks).Error
|
||||
case "Snapshot":
|
||||
var (
|
||||
snapIDs []string
|
||||
recoverIDs []string
|
||||
rollbackIDs []string
|
||||
)
|
||||
_ = global.DB.Model(&model.Snapshot{}).Where("task_id != ?", "").Select("task_id").Find(&snapIDs).Error
|
||||
_ = global.DB.Model(&model.Snapshot{}).Where("task_recover_id != ", "").Select("task_id").Find(&recoverIDs).Error
|
||||
_ = global.DB.Model(&model.Snapshot{}).Where("task_rollback_id != ?", "").Select("task_id").Find(&rollbackIDs).Error
|
||||
usedTasks = append(usedTasks, snapIDs...)
|
||||
usedTasks = append(usedTasks, recoverIDs...)
|
||||
usedTasks = append(usedTasks, rollbackIDs...)
|
||||
case "Backup":
|
||||
_ = global.DB.Model(&model.BackupRecord{}).Where("task_id != ?", "").Select("task_id").Find(&usedTasks).Error
|
||||
case "Clam":
|
||||
_ = global.DB.Model(&model.ClamRecord{}).Where("task_id != ?", "").Select("task_id").Find(&usedTasks).Error
|
||||
case "Tamper":
|
||||
xpackDB, err := common.LoadDBConnByPathWithErr(path.Join(global.CONF.Base.InstallDir, "1panel/db/xpack.db"), "xpack.db")
|
||||
if err == nil {
|
||||
_ = xpackDB.Table("tampers").Where("task_id != ?", "").Select("task_id").Find(&usedTasks).Error
|
||||
}
|
||||
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
|
||||
}
|
||||
default:
|
||||
dropFileOrDir(logDir)
|
||||
_ = taskRepo.Delete(repo.WithByType(taskType))
|
||||
return
|
||||
}
|
||||
usedMap := make(map[string]struct{})
|
||||
for _, item := range usedTasks {
|
||||
if _, ok := usedMap[item]; !ok {
|
||||
usedMap[item] = struct{}{}
|
||||
}
|
||||
}
|
||||
for _, item := range files {
|
||||
if _, ok := usedMap[strings.TrimSuffix(item.Name(), ".log")]; ok {
|
||||
continue
|
||||
}
|
||||
_ = os.Remove(logDir + "/" + item.Name())
|
||||
}
|
||||
_ = taskRepo.Delete(repo.WithByType(taskType), taskRepo.WithByIDNotIn(usedTasks))
|
||||
}
|
||||
|
||||
func dropWithExclude(pathToDelete string, excludeSubDirs []string, taskItem *task.Task, size *int64, count *int) {
|
||||
entries, err := os.ReadDir(pathToDelete)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
|
||||
)
|
||||
|
||||
type DiskService struct{}
|
||||
|
||||
type IDiskService interface {
|
||||
GetCompleteDiskInfo() (*response.CompleteDiskInfo, error)
|
||||
PartitionDisk(req request.DiskPartitionRequest) (string, error)
|
||||
MountDisk(req request.DiskMountRequest) error
|
||||
UnmountDisk(req request.DiskUnmountRequest) error
|
||||
}
|
||||
|
||||
func NewIDiskService() IDiskService {
|
||||
return &DiskService{}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
diskInfos, err := parseLsblkOutput(output)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse lsblk output: %v", err)
|
||||
}
|
||||
|
||||
result := organizeDiskInfo(diskInfos)
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (s *DiskService) PartitionDisk(req request.DiskPartitionRequest) (string, error) {
|
||||
if !strings.HasPrefix("/dev", req.Device) {
|
||||
req.Device = "/dev/" + req.Device
|
||||
}
|
||||
if !deviceExists(req.Device) {
|
||||
return "", buserr.WithName("DeviceNotFound", req.Device)
|
||||
}
|
||||
|
||||
if isDeviceMounted(req.Device) {
|
||||
return "", buserr.WithName("DeviceIsMounted", req.Device)
|
||||
}
|
||||
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10 * time.Second))
|
||||
if err := cmdMgr.RunBashC(fmt.Sprintf("partprobe %s", req.Device)); err != nil {
|
||||
return "", buserr.WithErr("PartitionDiskErr", err)
|
||||
}
|
||||
|
||||
if err := cmdMgr.RunBashC(fmt.Sprintf("parted -s %s mklabel gpt", req.Device)); err != nil {
|
||||
return "", buserr.WithErr("PartitionDiskErr", err)
|
||||
}
|
||||
|
||||
if err := cmdMgr.RunBashC(fmt.Sprintf("parted -s %s mkpart primary 1MiB 100%%", req.Device)); err != nil {
|
||||
return "", buserr.WithErr("PartitionDiskErr", err)
|
||||
}
|
||||
|
||||
if err := cmdMgr.RunBashC(fmt.Sprintf("partprobe %s", req.Device)); err != nil {
|
||||
return "", buserr.WithErr("PartitionDiskErr", err)
|
||||
}
|
||||
partition := req.Device + "1"
|
||||
|
||||
formatReq := dto.DiskFormatRequest{
|
||||
Device: partition,
|
||||
Filesystem: req.Filesystem,
|
||||
Label: req.Label,
|
||||
}
|
||||
if err := formatDisk(formatReq); err != nil {
|
||||
return "", buserr.WithErr("FormatDiskErr", err)
|
||||
}
|
||||
|
||||
if req.MountPoint != "" {
|
||||
mountReq := request.DiskMountRequest{
|
||||
Device: partition,
|
||||
MountPoint: req.MountPoint,
|
||||
Filesystem: req.Filesystem,
|
||||
AutoMount: req.AutoMount,
|
||||
}
|
||||
if err := s.MountDisk(mountReq); err != nil {
|
||||
return "", buserr.WithErr("MountDiskErr", err)
|
||||
}
|
||||
}
|
||||
|
||||
return partition, nil
|
||||
}
|
||||
|
||||
func (s *DiskService) MountDisk(req request.DiskMountRequest) error {
|
||||
if !deviceExists(req.Device) {
|
||||
return buserr.WithName("DeviceNotFound", req.Device)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(req.MountPoint, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileSystem, err := getFilesystemType(req.Device)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fileSystem == "" {
|
||||
formatReq := dto.DiskFormatRequest{
|
||||
Device: req.Device,
|
||||
Filesystem: req.Filesystem,
|
||||
}
|
||||
if err := formatDisk(formatReq); err != nil {
|
||||
return buserr.WithErr("FormatDiskErr", err)
|
||||
}
|
||||
}
|
||||
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(1 * time.Minute))
|
||||
if err := cmdMgr.RunBashC(fmt.Sprintf("mount -t %s %s %s", req.Filesystem, req.Device, req.MountPoint)); err != nil {
|
||||
return buserr.WithErr("MountDiskErr", err)
|
||||
}
|
||||
if req.AutoMount {
|
||||
if err := addToFstabWithOptions(req.Device, req.MountPoint, req.Filesystem, ""); err != nil {
|
||||
return buserr.WithErr("MountDiskErr", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *DiskService) UnmountDisk(req request.DiskUnmountRequest) error {
|
||||
if !isPointMounted(req.MountPoint) {
|
||||
return buserr.New("MountDiskErr")
|
||||
}
|
||||
if err := cmd.RunDefaultBashC(fmt.Sprintf("umount -f %s", req.MountPoint)); err != nil {
|
||||
return buserr.WithErr("MountDiskErr", err)
|
||||
}
|
||||
if err := removeFromFstab(req.MountPoint); err != nil {
|
||||
global.LOG.Errorf("remove %s mountPoint err: %v", req.MountPoint, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,440 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"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"
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
for _, info := range diskInfos {
|
||||
isPartition := isPartitionDevice(info.Device)
|
||||
|
||||
if isPartition {
|
||||
parentDevice := getParentDevice(info.Device)
|
||||
partitions[parentDevice] = append(partitions[parentDevice], info)
|
||||
} else {
|
||||
disk := &response.DiskInfo{
|
||||
DiskBasicInfo: info,
|
||||
Partitions: []response.DiskBasicInfo{},
|
||||
}
|
||||
diskMap[info.Device] = disk
|
||||
}
|
||||
}
|
||||
|
||||
for parentDevice, partList := range partitions {
|
||||
if disk, exists := diskMap[parentDevice]; exists {
|
||||
for index, part := range partList {
|
||||
part.Device = fmt.Sprintf("/dev/%s", part.Device)
|
||||
if part.IsSystem {
|
||||
disk.IsSystem = true
|
||||
}
|
||||
partList[index] = part
|
||||
}
|
||||
disk.Partitions = partList
|
||||
}
|
||||
}
|
||||
|
||||
var totalCapacity int64
|
||||
for _, disk := range diskMap {
|
||||
capacity := parseSizeToBytes(disk.Size)
|
||||
totalCapacity += capacity
|
||||
|
||||
if disk.IsSystem {
|
||||
systemDisk = disk
|
||||
} else if len(disk.Partitions) == 0 {
|
||||
result.UnpartitionedDisks = append(result.UnpartitionedDisks, disk.DiskBasicInfo)
|
||||
} else {
|
||||
result.Disks = append(result.Disks, *disk)
|
||||
}
|
||||
}
|
||||
|
||||
result.SystemDisk = systemDisk
|
||||
result.TotalDisks = len(diskMap)
|
||||
result.TotalCapacity = totalCapacity
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func parseLsblkOutput(output string) ([]response.DiskBasicInfo, error) {
|
||||
lines := strings.Split(strings.TrimSpace(output), "\n")
|
||||
if len(lines) == 0 {
|
||||
return nil, fmt.Errorf("invalid lsblk output")
|
||||
}
|
||||
|
||||
var diskInfos []response.DiskBasicInfo
|
||||
lvmMap := make(map[string]response.DiskBasicInfo)
|
||||
var pendingDevices []map[string]string
|
||||
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
fields := parseKeyValuePairs(line)
|
||||
|
||||
name, ok := fields["NAME"]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(name, "loop") ||
|
||||
strings.HasPrefix(name, "dm-") {
|
||||
continue
|
||||
}
|
||||
|
||||
diskType := fields["TYPE"]
|
||||
mountPoint := fields["MOUNTPOINT"]
|
||||
fsType := fields["FSTYPE"]
|
||||
size := fields["SIZE"]
|
||||
|
||||
if diskType == "lvm" {
|
||||
total, used, avail, usePercent, _ := getDiskUsageInfo("/dev/mapper/" + name)
|
||||
if total != "" && fsType != "" {
|
||||
size = total
|
||||
}
|
||||
|
||||
lvmInfo := response.DiskBasicInfo{
|
||||
Device: name,
|
||||
Size: size,
|
||||
Model: fields["MODEL"],
|
||||
DiskType: "LVM",
|
||||
IsRemovable: false,
|
||||
IsSystem: isSystemDisk(mountPoint),
|
||||
Filesystem: fsType,
|
||||
Used: used,
|
||||
Avail: avail,
|
||||
UsePercent: usePercent,
|
||||
MountPoint: mountPoint,
|
||||
IsMounted: mountPoint != "" && mountPoint != "-",
|
||||
Serial: fields["SERIAL"],
|
||||
}
|
||||
lvmMap[name] = lvmInfo
|
||||
} else if diskType == "disk" || diskType == "part" {
|
||||
pendingDevices = append(pendingDevices, fields)
|
||||
}
|
||||
}
|
||||
|
||||
for _, fields := range pendingDevices {
|
||||
name := fields["NAME"]
|
||||
size := fields["SIZE"]
|
||||
mountPoint := fields["MOUNTPOINT"]
|
||||
fsType := fields["FSTYPE"]
|
||||
model := fields["MODEL"]
|
||||
serial := fields["SERIAL"]
|
||||
tran := fields["TRAN"]
|
||||
rota := fields["ROTA"]
|
||||
|
||||
var (
|
||||
used, avail, totalSize string
|
||||
usePercent int
|
||||
)
|
||||
if mountPoint != "" {
|
||||
totalSize, used, avail, usePercent, _ = getDiskUsageInfo("/dev/" + name)
|
||||
if totalSize != "" {
|
||||
size = totalSize
|
||||
}
|
||||
}
|
||||
|
||||
actualMountPoint := mountPoint
|
||||
actualFsType := fsType
|
||||
actualUsed := used
|
||||
actualAvail := avail
|
||||
actualUsePercent := usePercent
|
||||
isMounted := mountPoint != "" && mountPoint != "-"
|
||||
isSystemPartition := isSystemDisk(mountPoint)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info := response.DiskBasicInfo{
|
||||
Device: name,
|
||||
Size: size,
|
||||
Model: model,
|
||||
DiskType: getDiskType(rota),
|
||||
IsRemovable: tran == "usb",
|
||||
IsSystem: isSystemPartition,
|
||||
Filesystem: actualFsType,
|
||||
Used: actualUsed,
|
||||
Avail: actualAvail,
|
||||
UsePercent: actualUsePercent,
|
||||
MountPoint: actualMountPoint,
|
||||
IsMounted: isMounted,
|
||||
Serial: serial,
|
||||
}
|
||||
|
||||
diskInfos = append(diskInfos, info)
|
||||
}
|
||||
|
||||
return diskInfos, nil
|
||||
}
|
||||
|
||||
func getDiskType(rota string) string {
|
||||
if rota == "0" {
|
||||
return "SSD"
|
||||
} else if rota == "1" {
|
||||
return "HDD"
|
||||
}
|
||||
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)
|
||||
for _, m := range matches {
|
||||
key := m[1]
|
||||
raw := m[2]
|
||||
|
||||
val := raw
|
||||
if len(val) >= 2 && val[0] == '"' && val[len(val)-1] == '"' {
|
||||
if unq, err := strconv.Unquote(val); err == nil {
|
||||
val = unq
|
||||
} else {
|
||||
val = val[1 : len(val)-1]
|
||||
}
|
||||
}
|
||||
fields[key] = val
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
func isPartitionDevice(device string) bool {
|
||||
if strings.Contains(device, "nvme") {
|
||||
return strings.Contains(device, "p") &&
|
||||
strings.ContainsAny(device[strings.LastIndex(device, "p")+1:], "0123456789")
|
||||
} else if strings.HasPrefix(device, "sd") || strings.HasPrefix(device, "hd") {
|
||||
return len(device) > 3 &&
|
||||
strings.ContainsAny(device[len(device)-1:], "0123456789")
|
||||
} else if strings.HasPrefix(device, "vd") {
|
||||
return len(device) > 3 &&
|
||||
strings.ContainsAny(device[len(device)-1:], "0123456789")
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func getParentDevice(device string) string {
|
||||
if strings.Contains(device, "nvme") {
|
||||
if idx := strings.LastIndex(device, "p"); idx != -1 {
|
||||
return device[:idx]
|
||||
}
|
||||
} else {
|
||||
return strings.TrimRight(device, "0123456789")
|
||||
}
|
||||
|
||||
return device
|
||||
}
|
||||
|
||||
func getDiskUsageInfo(device string) (size, used, avail string, usePercent int, err error) {
|
||||
output, err := cmd.RunDefaultWithStdoutBashC(fmt.Sprintf("df -h %s | tail -1", device))
|
||||
if err != nil {
|
||||
return "", "", "", 0, nil
|
||||
}
|
||||
|
||||
fields := strings.Fields(output)
|
||||
if len(fields) >= 5 {
|
||||
size = fields[1]
|
||||
used = fields[2]
|
||||
avail = fields[3]
|
||||
usePercentStr := strings.TrimSuffix(fields[4], "%")
|
||||
usePercent, _ = strconv.Atoi(usePercentStr)
|
||||
}
|
||||
|
||||
return size, used, avail, usePercent, nil
|
||||
}
|
||||
|
||||
func formatDisk(req dto.DiskFormatRequest) error {
|
||||
var mkfsCmd *exec.Cmd
|
||||
|
||||
switch req.Filesystem {
|
||||
case "ext4":
|
||||
mkfsCmd = exec.Command("mkfs.ext4", "-F", req.Device)
|
||||
case "xfs":
|
||||
if !cmd.Which("mkfs.xfs") {
|
||||
return buserr.New("XfsNotFound")
|
||||
}
|
||||
mkfsCmd = exec.Command("mkfs.xfs", "-f", req.Device)
|
||||
default:
|
||||
return fmt.Errorf("unsupport type: %s", req.Filesystem)
|
||||
}
|
||||
if err := mkfsCmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deviceExists(device string) bool {
|
||||
_, err := os.Stat(device)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func isDeviceMounted(device string) bool {
|
||||
file, err := os.Open("/proc/mounts")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
fields := strings.Fields(scanner.Text())
|
||||
if len(fields) >= 2 && fields[0] == device {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isPointMounted(mountPoint string) bool {
|
||||
file, err := os.Open("/proc/mounts")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
fields := strings.Fields(scanner.Text())
|
||||
if len(fields) >= 2 && fields[1] == mountPoint {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func addToFstabWithOptions(device, mountPoint, filesystem, options string) error {
|
||||
if filesystem == "" {
|
||||
fsType, err := getFilesystemType(device)
|
||||
if err != nil {
|
||||
filesystem = "auto"
|
||||
} else {
|
||||
filesystem = fsType
|
||||
}
|
||||
}
|
||||
|
||||
if options == "" {
|
||||
options = "defaults"
|
||||
}
|
||||
|
||||
entry := fmt.Sprintf("%s %s %s %s 0 2\n", device, mountPoint, filesystem, options)
|
||||
|
||||
file, err := os.OpenFile("/etc/fstab", os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.WriteString(entry)
|
||||
return err
|
||||
}
|
||||
|
||||
func removeFromFstab(mountPoint string) error {
|
||||
file, err := os.Open("/etc/fstab")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var lines []string
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) >= 2 && fields[1] == mountPoint {
|
||||
continue
|
||||
}
|
||||
lines = append(lines, line)
|
||||
}
|
||||
|
||||
return os.WriteFile("/etc/fstab", []byte(strings.Join(lines, "\n")+"\n"), 0644)
|
||||
}
|
||||
|
||||
func getFilesystemType(device string) (string, error) {
|
||||
output, err := cmd.RunDefaultWithStdoutBashC(fmt.Sprintf("blkid -o value -s TYPE %s", device))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(output), nil
|
||||
}
|
||||
|
||||
func isSystemDisk(mountPoint string) bool {
|
||||
systemMountPoints := []string{
|
||||
"/",
|
||||
"/boot",
|
||||
"/boot/efi",
|
||||
"/usr",
|
||||
"/var",
|
||||
"/home",
|
||||
}
|
||||
|
||||
for _, sysMount := range systemMountPoints {
|
||||
if mountPoint == sysMount {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func parseSizeToBytes(sizeStr string) int64 {
|
||||
if sizeStr == "" || sizeStr == "-" {
|
||||
return 0
|
||||
}
|
||||
|
||||
sizeStr = strings.TrimSpace(sizeStr)
|
||||
if len(sizeStr) < 2 {
|
||||
return 0
|
||||
}
|
||||
|
||||
unit := strings.ToUpper(sizeStr[len(sizeStr)-1:])
|
||||
valueStr := sizeStr[:len(sizeStr)-1]
|
||||
|
||||
value, err := strconv.ParseFloat(valueStr, 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
switch unit {
|
||||
case "K":
|
||||
return int64(value * 1024)
|
||||
case "M":
|
||||
return int64(value * 1024 * 1024)
|
||||
case "G":
|
||||
return int64(value * 1024 * 1024 * 1024)
|
||||
case "T":
|
||||
return int64(value * 1024 * 1024 * 1024 * 1024)
|
||||
default:
|
||||
val, _ := strconv.ParseInt(sizeStr, 10, 64)
|
||||
return val
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
@@ -60,7 +61,7 @@ func (u *DockerService) LoadDockerStatus() *dto.DockerStatus {
|
||||
return &data
|
||||
}
|
||||
data.IsExist = true
|
||||
data.IsActive, _ = systemctl.IsActive("docker")
|
||||
data.IsActive = true
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load docker client failed, err: %v", err)
|
||||
@@ -457,7 +458,7 @@ func restartDocker() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("%s restart docker", restartCmd)
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -12,8 +12,9 @@ var (
|
||||
appInstallResourceRepo = repo.NewIAppInstallResourceRpo()
|
||||
appIgnoreUpgradeRepo = repo.NewIAppIgnoreUpgradeRepo()
|
||||
|
||||
aiRepo = repo.NewIAiRepo()
|
||||
mcpServerRepo = repo.NewIMcpServerRepo()
|
||||
aiRepo = repo.NewIAiRepo()
|
||||
mcpServerRepo = repo.NewIMcpServerRepo()
|
||||
tensorrtLLMRepo = repo.NewITensorRTLLMRepo()
|
||||
|
||||
mysqlRepo = repo.NewIMysqlRepo()
|
||||
postgresqlRepo = repo.NewIPostgresqlRepo()
|
||||
|
||||
+120
-7
@@ -3,7 +3,12 @@ package service
|
||||
import (
|
||||
"bufio"
|
||||
"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"
|
||||
@@ -66,6 +71,8 @@ type IFileService interface {
|
||||
BatchCheckFiles(req request.FilePathsCheck) []response.ExistFileInfo
|
||||
GetHostMount() []dto.DiskInfo
|
||||
GetUsersAndGroups() (*response.UserGroupResponse, error)
|
||||
Convert(req request.FileConvertRequest)
|
||||
ConvertLog(req dto.PageInfo) (int64, []response.FileConvertLog, error)
|
||||
}
|
||||
|
||||
var filteredPaths = []string{
|
||||
@@ -554,6 +561,19 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
|
||||
logFilePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
|
||||
case "mariadb-slow-logs":
|
||||
logFilePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/mariadb/%s/db/data/1Panel-slow.log", req.Name))
|
||||
case "php-fpm-slow-logs":
|
||||
php, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logFilePath = php.GetSlowLogPath()
|
||||
case constant.Supervisord:
|
||||
configPath := "/etc/supervisord.conf"
|
||||
pathSet, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorConfigPath))
|
||||
if pathSet.ID != 0 || pathSet.Value != "" {
|
||||
configPath = pathSet.Value
|
||||
}
|
||||
logFilePath, _ = ini_conf.GetIniValue(configPath, "supervisord", "logfile")
|
||||
}
|
||||
|
||||
file, err := os.Open(logFilePath)
|
||||
@@ -580,13 +600,6 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.Latest && req.Page == 1 && len(logFileRes.Lines) < 1000 && logFileRes.TotalPages > 1 {
|
||||
res, err := files.ReadFileByLine(logFilePath, logFileRes.TotalPages-1, req.PageSize, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logFileRes.Lines = append(res.Lines, logFileRes.Lines...)
|
||||
}
|
||||
scope = "page"
|
||||
lines = logFileRes.Lines
|
||||
}
|
||||
@@ -730,3 +743,103 @@ func getValidUsers(validGroups map[string]bool) ([]response.UserInfo, map[string
|
||||
}
|
||||
return users, groupSet, nil
|
||||
}
|
||||
|
||||
func (f *FileService) Convert(req request.FileConvertRequest) {
|
||||
convertTask, err := task.NewTaskWithOps(i18n.GetMsgByKey("FileConvert"), task.TaskExec, task.TaskScopeFileConvert, req.TaskID, 1)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("Create convert task failed %v", err)
|
||||
return
|
||||
}
|
||||
convertTask.AddSubTask(task.GetTaskName(i18n.GetMsgByKey("FileConvert"), task.TaskExec, task.TaskScopeFileConvert), func(t *task.Task) (err error) {
|
||||
for _, file := range req.Files {
|
||||
input := filepath.Join(file.Path, file.InputFile)
|
||||
nameOnly := file.InputFile[0 : len(file.InputFile)-len(file.Extension)]
|
||||
output := filepath.Join(req.OutputPath, nameOnly+"."+file.OutputFormat)
|
||||
status, errMsg := convert.MediaFile(input, output, file.OutputFormat, req.DeleteSource)
|
||||
if status == "FAILED" {
|
||||
convertTask.Log(fmt.Sprintf("%s -> %s [%s]: %s\n",
|
||||
input, output, status, errMsg))
|
||||
} else {
|
||||
convertTask.Log(fmt.Sprintf("%s -> %s [%s]: %s\n",
|
||||
input, output, status, "SUCCESS"))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
go func() {
|
||||
_ = convertTask.Execute()
|
||||
}()
|
||||
}
|
||||
|
||||
func (f *FileService) ConvertLog(req dto.PageInfo) (total int64, data []response.FileConvertLog, err error) {
|
||||
logFilePath := filepath.Join(global.Dir.ConvertLogDir, "convert.log")
|
||||
file, err := os.Open(logFilePath)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
const chunkSize = 64 * 1024
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
fileSize := stat.Size()
|
||||
|
||||
var (
|
||||
buf []byte
|
||||
remainder []byte
|
||||
offset = fileSize
|
||||
lines []string
|
||||
)
|
||||
|
||||
pageStart := int64((req.Page - 1) * req.PageSize)
|
||||
pageEnd := pageStart + int64(req.PageSize)
|
||||
|
||||
for offset > 0 {
|
||||
readSize := chunkSize
|
||||
if offset < int64(readSize) {
|
||||
readSize = int(offset)
|
||||
}
|
||||
offset -= int64(readSize)
|
||||
|
||||
tmp := make([]byte, readSize)
|
||||
if _, err := file.ReadAt(tmp, offset); err != nil && err != io.EOF {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
buf = append(tmp, remainder...)
|
||||
linesSplit := strings.Split(string(buf), "\n")
|
||||
|
||||
if offset > 0 {
|
||||
remainder = []byte(linesSplit[0])
|
||||
linesSplit = linesSplit[1:]
|
||||
}
|
||||
|
||||
for i := len(linesSplit) - 1; i >= 0; i-- {
|
||||
line := strings.TrimSpace(linesSplit[i])
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
total++
|
||||
if total > pageStart && total <= pageEnd {
|
||||
lines = append(lines, line)
|
||||
}
|
||||
if total >= pageEnd {
|
||||
break
|
||||
}
|
||||
}
|
||||
if total >= pageEnd {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, line := range lines {
|
||||
var entry response.FileConvertLog
|
||||
if err := json.Unmarshal([]byte(line), &entry); err == nil {
|
||||
data = append(data, entry)
|
||||
}
|
||||
}
|
||||
|
||||
return total, data, nil
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ type FirewallService struct{}
|
||||
type IFirewallService interface {
|
||||
LoadBaseInfo() (dto.FirewallBaseInfo, error)
|
||||
SearchWithPage(search dto.RuleSearch) (int64, interface{}, error)
|
||||
OperateFirewall(operation string) error
|
||||
OperateFirewall(req dto.FirewallOperation) error
|
||||
OperatePortRule(req dto.PortRuleOperate, reload bool) error
|
||||
OperateForwardRule(req dto.ForwardRuleOperate) error
|
||||
OperateAddressRule(req dto.AddrRuleOperate, reload bool) error
|
||||
@@ -177,13 +177,13 @@ func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}
|
||||
return int64(total), backDatas, nil
|
||||
}
|
||||
|
||||
func (u *FirewallService) OperateFirewall(operation string) error {
|
||||
func (u *FirewallService) OperateFirewall(req dto.FirewallOperation) error {
|
||||
client, err := firewall.NewFirewallClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
needRestartDocker := false
|
||||
switch operation {
|
||||
switch req.Operation {
|
||||
case "start":
|
||||
if err := client.Start(); err != nil {
|
||||
return err
|
||||
@@ -208,9 +208,9 @@ func (u *FirewallService) OperateFirewall(operation string) error {
|
||||
case "enablePing":
|
||||
return u.updatePingStatus("1")
|
||||
default:
|
||||
return fmt.Errorf("not supported operation: %s", operation)
|
||||
return fmt.Errorf("not supported operation: %s", req.Operation)
|
||||
}
|
||||
if needRestartDocker {
|
||||
if needRestartDocker && req.WithDockerRestart {
|
||||
if err := restartDocker(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -329,7 +329,8 @@ func (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {
|
||||
if reqRule.Port == rule.Port &&
|
||||
reqRule.TargetPort == rule.TargetPort &&
|
||||
reqRule.TargetIP == rule.TargetIP &&
|
||||
proto == rule.Protocol {
|
||||
proto == rule.Protocol &&
|
||||
reqRule.Interface == rule.Interface {
|
||||
shouldKeep = false
|
||||
break
|
||||
}
|
||||
@@ -353,7 +354,8 @@ func (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {
|
||||
if reqRule.Port == rule.Port &&
|
||||
reqRule.TargetPort == rule.TargetPort &&
|
||||
reqRule.TargetIP == rule.TargetIP &&
|
||||
proto == rule.Protocol {
|
||||
proto == rule.Protocol &&
|
||||
reqRule.Interface == rule.Interface {
|
||||
return buserr.New("ErrRecordExist")
|
||||
}
|
||||
}
|
||||
@@ -383,6 +385,7 @@ func (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {
|
||||
Port: r.Port,
|
||||
TargetIP: r.TargetIP,
|
||||
TargetPort: r.TargetPort,
|
||||
Interface: r.Interface,
|
||||
}, r.Operation); err != nil {
|
||||
if req.ForceDelete {
|
||||
global.LOG.Error(err)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user