mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 16:00:29 +00:00
Compare commits
297
Commits
v2.0.4
...
release-2.0.10
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5d403f10a | ||
|
|
598098c2ed | ||
|
|
bff1a8ad60 | ||
|
|
592e315f21 | ||
|
|
76933bc6ef | ||
|
|
0dc312db64 | ||
|
|
4deb92e715 | ||
|
|
bc95e47b92 | ||
|
|
f2335d313c | ||
|
|
4c287f4a2d | ||
|
|
0a6e1c275b | ||
|
|
ff2dc05e7f | ||
|
|
f62ce085bf | ||
|
|
29306827ba | ||
|
|
222d07175a | ||
|
|
663196c3d9 | ||
|
|
df839714a3 | ||
|
|
dcb004e983 | ||
|
|
cee0604efc | ||
|
|
dfbd364f60 | ||
|
|
339d6732d7 | ||
|
|
b0dbfb4c7c | ||
|
|
021603d25d | ||
|
|
a875e1ec5c | ||
|
|
5c5904216b | ||
|
|
1dae294d76 | ||
|
|
acbb151b15 | ||
|
|
a1311a2a0d | ||
|
|
bc8d564ce8 | ||
|
|
97cdf7bc78 | ||
|
|
ce69b4de01 | ||
|
|
e880df8142 | ||
|
|
a35ca87778 | ||
|
|
5a34e2d380 | ||
|
|
abf3247c50 | ||
|
|
8663ed129d | ||
|
|
c0a0f4c801 | ||
|
|
e84aa49003 | ||
|
|
6f45b90520 | ||
|
|
0685984a80 | ||
|
|
5f33c083bf | ||
|
|
ab0e0f3f43 | ||
|
|
6b62b84157 | ||
|
|
fd9627576b | ||
|
|
39f334f3a2 | ||
|
|
ca84909716 | ||
|
|
283609cf2d | ||
|
|
9591409fe2 | ||
|
|
9cf52ccc97 | ||
|
|
261cb2061b | ||
|
|
f45af6e0cc | ||
|
|
c752ea4959 | ||
|
|
b2b578a5f0 | ||
|
|
47e60bfdcb | ||
|
|
780b6cdec9 | ||
|
|
0a5b13a418 | ||
|
|
ebdc3f05de | ||
|
|
aad79077bf | ||
|
|
00b25b39c5 | ||
|
|
dd6a709053 | ||
|
|
99b04e3635 | ||
|
|
2e06b78409 | ||
|
|
c7d603e142 | ||
|
|
4fcf845934 | ||
|
|
83cefb2a72 | ||
|
|
7ab40996e0 | ||
|
|
bbe4d49947 | ||
|
|
b6fbacc992 | ||
|
|
89491eac56 | ||
|
|
acf27693b2 | ||
|
|
5fd9d20f99 | ||
|
|
fd9c421969 | ||
|
|
f5bae6d75c | ||
|
|
4ac8bc7bd2 | ||
|
|
ec81d0d2c3 | ||
|
|
815b633fb8 | ||
|
|
c69c76f97d | ||
|
|
75197db3e6 | ||
|
|
d09d686378 | ||
|
|
d6f16cf700 | ||
|
|
536a5fc1a0 | ||
|
|
a07df9d7fe | ||
|
|
e9c195c778 | ||
|
|
0637ca308b | ||
|
|
fd1e0e72a0 | ||
|
|
34b80373c7 | ||
|
|
1ade1b8d8b | ||
|
|
19fa30fb96 | ||
|
|
efe7815a7e | ||
|
|
e9b4ff1932 | ||
|
|
0dca3fc997 | ||
|
|
4e6ebb53ac | ||
|
|
83ed835c9e | ||
|
|
7bcc897379 | ||
|
|
cd2109fefe | ||
|
|
d337c4726d | ||
|
|
356e6a4ef6 | ||
|
|
037da3a7fd | ||
|
|
18462296ae | ||
|
|
1d76e54b0d | ||
|
|
22c903f555 | ||
|
|
6d4d53e2c7 | ||
|
|
6b81ae1b7b | ||
|
|
5b62a0f2b3 | ||
|
|
137465683a | ||
|
|
6be7b4a75a | ||
|
|
58ca377418 | ||
|
|
b173e808c7 | ||
|
|
ec44ba9335 | ||
|
|
f54ac59ff7 | ||
|
|
db44204c1a | ||
|
|
ec01aef71a | ||
|
|
b706774b5b | ||
|
|
9eee2d6b6f | ||
|
|
c9d4e2a4b5 | ||
|
|
4c4df5cbb0 | ||
|
|
cb69128838 | ||
|
|
81af207dc5 | ||
|
|
5e9c43fc71 | ||
|
|
fb44e3c1e2 | ||
|
|
7e23ae126a | ||
|
|
7e229c05e7 | ||
|
|
764fc8fec4 | ||
|
|
ad17918c84 | ||
|
|
d7013727b9 | ||
|
|
6a8bd11fc9 | ||
|
|
4df0c3e461 | ||
|
|
f8f47b51e9 | ||
|
|
b735178fcd | ||
|
|
1759c2c81e | ||
|
|
2be7054b40 | ||
|
|
c77d2f7dd6 | ||
|
|
3256ac9f44 | ||
|
|
b156ef9476 | ||
|
|
c49c16cbb5 | ||
|
|
2b7ec9ffe1 | ||
|
|
c2ad006a19 | ||
|
|
c12bab477c | ||
|
|
fe39545cf6 | ||
|
|
39d3b819fb | ||
|
|
04a0e15899 | ||
|
|
3dc81d85e9 | ||
|
|
5a8ddde495 | ||
|
|
4e6f872c02 | ||
|
|
e76836f394 | ||
|
|
9fed5f4bb8 | ||
|
|
b3a55ea44d | ||
|
|
270411f7fd | ||
|
|
3c01198e61 | ||
|
|
483b47ad63 | ||
|
|
b10de66ed3 | ||
|
|
305eff048c | ||
|
|
da7968d364 | ||
|
|
d3e2c8e9d3 | ||
|
|
e3a9f2dfb7 | ||
|
|
3c967f8906 | ||
|
|
39bb7acdba | ||
|
|
a30066989b | ||
|
|
0c30dfa092 | ||
|
|
7a44493d13 | ||
|
|
9b726d5d22 | ||
|
|
78e3d9e5fb | ||
|
|
d74b41a5f2 | ||
|
|
f122afdbec | ||
|
|
37308b5c42 | ||
|
|
9c5a6e3925 | ||
|
|
58b95ef2ba | ||
|
|
f617c9d5dd | ||
|
|
c661cc37b3 | ||
|
|
7c49f6c79b | ||
|
|
12b9356c17 | ||
|
|
d3a34293f7 | ||
|
|
c131061f81 | ||
|
|
6a5e1ebaa5 | ||
|
|
a07d016951 | ||
|
|
9d343a90db | ||
|
|
4c813be09d | ||
|
|
d48166f546 | ||
|
|
0f138c1b7c | ||
|
|
8542323c0b | ||
|
|
00f103c66a | ||
|
|
430c75e7ed | ||
|
|
67ffb5e938 | ||
|
|
9c292bebf0 | ||
|
|
ba4307c1dc | ||
|
|
b8b2041a0c | ||
|
|
c45bb60b50 | ||
|
|
fba46be47a | ||
|
|
be0a460935 | ||
|
|
c0a041d024 | ||
|
|
2ce5ad82ce | ||
|
|
50dcb6c7e4 | ||
|
|
017424bf7b | ||
|
|
c7e7407f7c | ||
|
|
cab42b3c7d | ||
|
|
78f280c88f | ||
|
|
22f5a975c3 | ||
|
|
90ba036e39 | ||
|
|
d1e18e43ee | ||
|
|
a801140a82 | ||
|
|
951ab2502e | ||
|
|
0316cb0662 | ||
|
|
04b9cbd87a | ||
|
|
4565c5b300 | ||
|
|
bf9a9bf17a | ||
|
|
70df4dc6c4 | ||
|
|
ed3e738fff | ||
|
|
bc376ec495 | ||
|
|
40314e9079 | ||
|
|
82f978c683 | ||
|
|
ed288368ea | ||
|
|
db8fa144eb | ||
|
|
57b76d97ec | ||
|
|
28d8210ce6 | ||
|
|
80ce60c56c | ||
|
|
2758ab07dc | ||
|
|
0828804de2 | ||
|
|
134b17d2f0 | ||
|
|
3af254cdda | ||
|
|
f3d3d54722 | ||
|
|
fa419809f6 | ||
|
|
da606826c6 | ||
|
|
b3d9cc156a | ||
|
|
ba03d26b2c | ||
|
|
40f02889fe | ||
|
|
4f8fb26d89 | ||
|
|
0ca11e2b88 | ||
|
|
0daf0d690c | ||
|
|
0cfdf0f335 | ||
|
|
cca1863b63 | ||
|
|
033f08a6d2 | ||
|
|
a83a6690ff | ||
|
|
83df457379 | ||
|
|
657e55cde3 | ||
|
|
90e76e3a0f | ||
|
|
2af1993383 | ||
|
|
6e650d977e | ||
|
|
8a76171dbe | ||
|
|
949b6fe944 | ||
|
|
3f90333090 | ||
|
|
aeaca0c5cd | ||
|
|
fb7f638a54 | ||
|
|
7994193a39 | ||
|
|
3334bf594d | ||
|
|
71193cc912 | ||
|
|
9fde8c447d | ||
|
|
117faa952e | ||
|
|
6427337728 | ||
|
|
81f97f6f88 | ||
|
|
b3b472d711 | ||
|
|
1c22e1cf99 | ||
|
|
3110d91bf6 | ||
|
|
624a11407a | ||
|
|
ead6d1268f | ||
|
|
80c00c5a8c | ||
|
|
da0f6acd01 | ||
|
|
485aa99dea | ||
|
|
5f68d7ce90 | ||
|
|
685fca6c8c | ||
|
|
9c609da332 | ||
|
|
84608b7d42 | ||
|
|
02cf2a80cb | ||
|
|
a74c6dd522 | ||
|
|
5d4ab23d95 | ||
|
|
a8faba3206 | ||
|
|
c603495b12 | ||
|
|
adce6103bd | ||
|
|
6b673017c5 | ||
|
|
e66bd43fc6 | ||
|
|
ff5cdb9a02 | ||
|
|
1aec5e443d | ||
|
|
0b1f7e9322 | ||
|
|
3ec2dec49b | ||
|
|
7cbebc8236 | ||
|
|
700076f278 | ||
|
|
7bf1bf61a7 | ||
|
|
ef2cb1b315 | ||
|
|
75bfc3a114 | ||
|
|
70f63090fe | ||
|
|
ab2cf0d0c3 | ||
|
|
ea1a34f57a | ||
|
|
765add6184 | ||
|
|
e5263d6c05 | ||
|
|
c391177d34 | ||
|
|
7d5f0aa906 | ||
|
|
2694a725d6 | ||
|
|
54ca40bbcd | ||
|
|
b20411f8f1 | ||
|
|
355e656caa | ||
|
|
383bdd5f15 | ||
|
|
96208c786d | ||
|
|
4c7b04cf27 | ||
|
|
a067a97e21 | ||
|
|
4a07603a7c | ||
|
|
7196c5b656 | ||
|
|
b5de2df119 | ||
|
|
2f72eb37ee |
@@ -32,6 +32,7 @@ builds:
|
||||
- arm
|
||||
- ppc64le
|
||||
- s390x
|
||||
- riscv64
|
||||
hooks:
|
||||
post:
|
||||
- ./ci/script.sh compress_binary {{ .Path }} {{ .Arch }}
|
||||
@@ -57,6 +58,7 @@ builds:
|
||||
- arm
|
||||
- ppc64le
|
||||
- s390x
|
||||
- riscv64
|
||||
hooks:
|
||||
post:
|
||||
- ./ci/script.sh compress_binary {{ .Path }} {{ .Arch }}
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (b *BaseApi) PageAlert(c *gin.Context) {
|
||||
var req dto.AlertSearch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
total, alerts, err := alertService.PageAlert(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Total: total,
|
||||
Items: alerts,
|
||||
})
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetAlerts(c *gin.Context) {
|
||||
alerts, err := alertService.GetAlerts()
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, alerts)
|
||||
}
|
||||
|
||||
func (b *BaseApi) CreateAlert(c *gin.Context) {
|
||||
var req dto.AlertCreate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
err := alertService.CreateAlert(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
func (b *BaseApi) DeleteAlert(c *gin.Context) {
|
||||
var req dto.DeleteRequest
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
err := alertService.DeleteAlert(req.ID)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
func (b *BaseApi) UpdateAlert(c *gin.Context) {
|
||||
var req dto.AlertUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := alertService.UpdateAlert(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetAlert(c *gin.Context) {
|
||||
id, err := helper.GetParamID(c)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, errors.New("no such id in request param"))
|
||||
return
|
||||
}
|
||||
alert, err := alertService.GetAlert(id)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, alert)
|
||||
}
|
||||
|
||||
func (b *BaseApi) UpdateAlertStatus(c *gin.Context) {
|
||||
var req dto.AlertUpdateStatus
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := alertService.UpdateStatus(req.ID, req.Status); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetDisks(c *gin.Context) {
|
||||
alerts, err := alertService.GetDisks()
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, alerts)
|
||||
}
|
||||
|
||||
func (b *BaseApi) PageAlertLogs(c *gin.Context) {
|
||||
var req dto.AlertLogSearch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
total, alertLogs, err := alertService.PageAlertLogs(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Total: total,
|
||||
Items: alertLogs,
|
||||
})
|
||||
}
|
||||
|
||||
func (b *BaseApi) CleanAlertLogs(c *gin.Context) {
|
||||
if err := alertService.CleanAlertLogs(); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetClams(c *gin.Context) {
|
||||
clams, err := alertService.GetClams()
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, clams)
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetCronJobs(c *gin.Context) {
|
||||
var req dto.CronJobReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
cronJobs, err := alertService.GetCronJobs(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, cronJobs)
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetAlertConfig(c *gin.Context) {
|
||||
config, err := alertService.GetAlertConfig()
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, config)
|
||||
}
|
||||
|
||||
func (b *BaseApi) UpdateAlertConfig(c *gin.Context) {
|
||||
var req dto.AlertConfigUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := alertService.UpdateAlertConfig(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
func (b *BaseApi) DeleteAlertConfig(c *gin.Context) {
|
||||
var req dto.DeleteRequest
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
err := alertService.DeleteAlertConfig(req.ID)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
func (b *BaseApi) TestAlertConfig(c *gin.Context) {
|
||||
var req dto.AlertConfigTest
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
flag, err := alertService.TestAlertConfig(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, flag)
|
||||
}
|
||||
@@ -327,3 +327,25 @@ func (b *BaseApi) UpdateAppConfig(c *gin.Context) {
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags App
|
||||
// @Summary Get app install info
|
||||
// @Accept json
|
||||
// @Param appInstallId path integer true "App install id"
|
||||
// @Success 200 {object} dto.AppInstallInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /apps/installed/info/:appInstallId [get]
|
||||
func (b *BaseApi) GetAppInstallInfo(c *gin.Context) {
|
||||
appInstallId, err := helper.GetIntParamByKey(c, "appInstallId")
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
info, err := appInstallService.GetAppInstallInfo(appInstallId)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, info)
|
||||
}
|
||||
|
||||
@@ -371,12 +371,12 @@ func (b *BaseApi) Backup(c *gin.Context) {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
case "mysql", "mariadb":
|
||||
case "mysql", "mariadb", constant.AppMysqlCluster:
|
||||
if err := backupService.MysqlBackup(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
case constant.AppPostgresql:
|
||||
case constant.AppPostgresql, constant.AppPostgresqlCluster:
|
||||
if err := backupService.PostgresqlBackup(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
@@ -386,7 +386,7 @@ func (b *BaseApi) Backup(c *gin.Context) {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
case "redis":
|
||||
case "redis", constant.AppRedisCluster:
|
||||
if err := backupService.RedisBackup(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
@@ -421,12 +421,12 @@ func (b *BaseApi) Recover(c *gin.Context) {
|
||||
}
|
||||
req.File = downloadPath
|
||||
switch req.Type {
|
||||
case "mysql", "mariadb":
|
||||
case "mysql", "mariadb", constant.AppMysqlCluster:
|
||||
if err := backupService.MysqlRecover(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
case constant.AppPostgresql:
|
||||
case constant.AppPostgresql, constant.AppPostgresqlCluster:
|
||||
if err := backupService.PostgresqlRecover(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
@@ -436,7 +436,7 @@ func (b *BaseApi) Recover(c *gin.Context) {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
case "redis":
|
||||
case "redis", constant.AppRedisCluster:
|
||||
if err := backupService.RedisRecover(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
@@ -466,12 +466,12 @@ func (b *BaseApi) RecoverByUpload(c *gin.Context) {
|
||||
}
|
||||
|
||||
switch req.Type {
|
||||
case "mysql", "mariadb":
|
||||
case "mysql", "mariadb", constant.AppMysqlCluster:
|
||||
if err := backupService.MysqlRecoverByUpload(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
case constant.AppPostgresql:
|
||||
case constant.AppPostgresql, constant.AppPostgresqlCluster:
|
||||
if err := backupService.PostgresqlRecoverByUpload(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
|
||||
@@ -50,19 +50,14 @@ func (b *BaseApi) LoadContainerUsers(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
list, err := containerService.LoadUsers(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
helper.SuccessWithData(c, containerService.LoadUsers(req))
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary List containers
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {array} string
|
||||
// @Success 200 {array} dto.ContainerOptions
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/list [post]
|
||||
@@ -70,6 +65,23 @@ func (b *BaseApi) ListContainer(c *gin.Context) {
|
||||
helper.SuccessWithData(c, containerService.List())
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary List containers by image
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {array} dto.ContainerOptions
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/list/byimage [post]
|
||||
func (b *BaseApi) ListContainerByImage(c *gin.Context) {
|
||||
var req dto.OperationWithName
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, containerService.ListByImage(req.Name))
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Load containers status
|
||||
// @Accept json
|
||||
@@ -266,7 +278,7 @@ func (b *BaseApi) ContainerCreate(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := containerService.ContainerCreate(req); err != nil {
|
||||
if err := containerService.ContainerCreate(req, true); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
@@ -320,7 +332,7 @@ func (b *BaseApi) ContainerUpgrade(c *gin.Context) {
|
||||
// @Summary Clean container
|
||||
// @Accept json
|
||||
// @Param request body dto.ContainerPrune true "request"
|
||||
// @Success 200 {object} dto.ContainerPruneReport
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/prune [post]
|
||||
@@ -331,12 +343,11 @@ func (b *BaseApi) ContainerPrune(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
report, err := containerService.Prune(req)
|
||||
if err != nil {
|
||||
if err := containerService.Prune(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, report)
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
|
||||
+67
-24
@@ -1,6 +1,8 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
@@ -53,6 +55,49 @@ func (b *BaseApi) LoadCronjobInfo(c *gin.Context) {
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Cronjob
|
||||
// @Summary Export cronjob list
|
||||
// @Accept json
|
||||
// @Param request body dto.OperateByIDs true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /cronjobs/export [post]
|
||||
func (b *BaseApi) ExportCronjob(c *gin.Context) {
|
||||
var req dto.OperateByIDs
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
content, err := cronjobService.Export(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
http.ServeContent(c.Writer, c.Request, "", time.Now(), strings.NewReader(content))
|
||||
}
|
||||
|
||||
// @Tags Cronjob
|
||||
// @Summary Import cronjob list
|
||||
// @Accept json
|
||||
// @Param request body dto.CronjobImport true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /cronjobs/import [post]
|
||||
func (b *BaseApi) ImportCronjob(c *gin.Context) {
|
||||
var req dto.CronjobImport
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := cronjobService.Import(req.Cronjobs); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Cronjob
|
||||
// @Summary Load script options
|
||||
// @Success 200 {array} dto.ScriptOptions
|
||||
@@ -225,6 +270,28 @@ func (b *BaseApi) UpdateCronjob(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Cronjob
|
||||
// @Summary Update cronjob group
|
||||
// @Accept json
|
||||
// @Param request body dto.ChangeGroup true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /cronjobs/group/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"cronjobs","output_column":"name","output_value":"name"}],"formatZH":"更新计划任务分组 [name]","formatEN":"update cronjob group [name]"}
|
||||
func (b *BaseApi) UpdateCronjobGroup(c *gin.Context) {
|
||||
var req dto.ChangeGroup
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := cronjobService.UpdateGroup(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Cronjob
|
||||
// @Summary Update cronjob status
|
||||
// @Accept json
|
||||
@@ -247,30 +314,6 @@ func (b *BaseApi) UpdateCronjobStatus(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Cronjob
|
||||
// @Summary Download cronjob records
|
||||
// @Accept json
|
||||
// @Param request body dto.CronjobDownload true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /cronjobs/download [post]
|
||||
// @x-panel-log {"bodyKeys":["recordID"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"recordID","isList":false,"db":"job_records","output_column":"file","output_value":"file"}],"formatZH":"下载计划任务记录 [file]","formatEN":"download the cronjob record [file]"}
|
||||
func (b *BaseApi) TargetDownload(c *gin.Context) {
|
||||
var req dto.CronjobDownload
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
filePath, err := cronjobService.Download(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.File(filePath)
|
||||
}
|
||||
|
||||
// @Tags Cronjob
|
||||
// @Summary Handle cronjob once
|
||||
// @Accept json
|
||||
|
||||
@@ -11,13 +11,13 @@ import (
|
||||
// @Tags Database Redis
|
||||
// @Summary Load redis status info
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithName true "request"
|
||||
// @Param request body dto.LoadRedisStatus true "request"
|
||||
// @Success 200 {object} dto.RedisStatus
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /databases/redis/status [post]
|
||||
func (b *BaseApi) LoadRedisStatus(c *gin.Context) {
|
||||
var req dto.OperationWithName
|
||||
var req dto.LoadRedisStatus
|
||||
if err := helper.CheckBind(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -33,13 +33,13 @@ func (b *BaseApi) LoadRedisStatus(c *gin.Context) {
|
||||
// @Tags Database Redis
|
||||
// @Summary Load redis conf
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithName true "request"
|
||||
// @Param request body dto.LoadRedisStatus true "request"
|
||||
// @Success 200 {object} dto.RedisConf
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /databases/redis/conf [post]
|
||||
func (b *BaseApi) LoadRedisConf(c *gin.Context) {
|
||||
var req dto.OperationWithName
|
||||
var req dto.LoadRedisStatus
|
||||
if err := helper.CheckBind(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -55,13 +55,13 @@ func (b *BaseApi) LoadRedisConf(c *gin.Context) {
|
||||
// @Tags Database Redis
|
||||
// @Summary Load redis persistence conf
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithName true "request"
|
||||
// @Param request body dto.LoadRedisStatus true "request"
|
||||
// @Success 200 {object} dto.RedisPersistence
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /databases/redis/persistence/conf [post]
|
||||
func (b *BaseApi) LoadPersistenceConf(c *gin.Context) {
|
||||
var req dto.OperationWithName
|
||||
var req dto.LoadRedisStatus
|
||||
if err := helper.CheckBind(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -70,4 +70,5 @@ var (
|
||||
websiteCAService = service.NewIWebsiteCAService()
|
||||
taskService = service.NewITaskService()
|
||||
groupService = service.NewIGroupService()
|
||||
alertService = service.NewIAlertService()
|
||||
)
|
||||
|
||||
@@ -9,14 +9,14 @@ import (
|
||||
// @Tags Container Image
|
||||
// @Summary Page images
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchWithPage true "request"
|
||||
// @Param request body dto.PageImage true "request"
|
||||
// @Produce json
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/image/search [post]
|
||||
func (b *BaseApi) SearchImage(c *gin.Context) {
|
||||
var req dto.SearchWithPage
|
||||
var req dto.PageImage
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -138,7 +138,7 @@ func (b *BaseApi) ImagePush(c *gin.Context) {
|
||||
// @Summary Delete image
|
||||
// @Accept json
|
||||
// @Param request body dto.BatchDelete true "request"
|
||||
// @Success 200 {object} dto.ContainerPruneReport
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/image/remove [post]
|
||||
@@ -149,13 +149,12 @@ func (b *BaseApi) ImageRemove(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := imageService.ImageRemove(req)
|
||||
if err != nil {
|
||||
if err := imageService.ImageRemove(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, data)
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Container Image
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"github.com/shirou/gopsutil/v3/net"
|
||||
"github.com/shirou/gopsutil/v4/disk"
|
||||
"github.com/shirou/gopsutil/v4/net"
|
||||
)
|
||||
|
||||
// @Tags Monitor
|
||||
|
||||
@@ -157,3 +157,39 @@ func (b *BaseApi) GetNginxModules(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithData(c, modules)
|
||||
}
|
||||
|
||||
// @Tags OpenResty
|
||||
// @Summary Operate default HTTPs
|
||||
// @Accept json
|
||||
// @Param request body request.NginxOperateReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /openresty/https [post]
|
||||
func (b *BaseApi) OperateDefaultHTTPs(c *gin.Context) {
|
||||
var req request.NginxOperateReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := nginxService.OperateDefaultHTTPs(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags OpenResty
|
||||
// @Summary Get default HTTPs status
|
||||
// @Success 200 {object} response.NginxConfigRes
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /openresty/https [get]
|
||||
func (b *BaseApi) GetDefaultHTTPsStatus(c *gin.Context) {
|
||||
res, err := nginxService.GetDefaultHttpsStatus()
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
@@ -555,3 +555,25 @@ func (b *BaseApi) UpdateRuntimeRemark(c *gin.Context) {
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Runtime
|
||||
// @Summary Get PHP runtime status
|
||||
// @Accept json
|
||||
// @Param id path integer true "request"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /runtimes/php/fpm/status/:id [get]
|
||||
func (b *BaseApi) GetFPMStatus(c *gin.Context) {
|
||||
id, err := helper.GetParamID(c)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
data, err := runtimeService.GetFPMStatus(id)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ func loadLocalConn() (*ssh.SSHClient, error) {
|
||||
itemPath = path.Join(currentInfo.HomeDir, ".ssh/id_ed25519_1panel")
|
||||
}
|
||||
if _, err := os.Stat(itemPath); err != nil {
|
||||
_ = sshService.GenerateSSH(dto.GenerateSSH{EncryptionMode: "ed25519", Name: "_1panel"})
|
||||
_ = sshService.CreateRootCert(dto.CreateRootCert{EncryptionMode: "ed25519", Name: "id_ed25519_1panel", Description: "1Panel Terminal"})
|
||||
}
|
||||
|
||||
privateKey, _ := os.ReadFile(itemPath)
|
||||
|
||||
@@ -36,7 +36,7 @@ func (b *BaseApi) CreateSnapshot(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := snapshotService.SnapshotCreate(nil, req, 0, 0, 0); err != nil {
|
||||
if err := snapshotService.SnapshotCreate(nil, req, 0, 3); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
+106
-15
@@ -1,6 +1,8 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -90,19 +92,58 @@ func (b *BaseApi) UpdateSSHByfile(c *gin.Context) {
|
||||
// @Tags SSH
|
||||
// @Summary Generate host SSH secret
|
||||
// @Accept json
|
||||
// @Param request body dto.GenerateSSH true "request"
|
||||
// @Param request body dto.CreateRootCert true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/ssh/generate [post]
|
||||
// @Router /hosts/ssh/cert [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"生成 SSH 密钥 ","formatEN":"generate SSH secret"}
|
||||
func (b *BaseApi) GenerateSSH(c *gin.Context) {
|
||||
var req dto.GenerateSSH
|
||||
func (b *BaseApi) CreateRootCert(c *gin.Context) {
|
||||
var req dto.CreateRootCert
|
||||
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 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.GenerateSSH(req); err != nil {
|
||||
if err := sshService.CreateRootCert(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Sycn host SSH secret
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/ssh/cert/sync [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"同步 SSH 密钥 ","formatEN":"sync SSH secret"}
|
||||
func (b *BaseApi) SyncRootCert(c *gin.Context) {
|
||||
if err := sshService.SyncRootCert(); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
@@ -112,30 +153,55 @@ func (b *BaseApi) GenerateSSH(c *gin.Context) {
|
||||
// @Tags SSH
|
||||
// @Summary Load host SSH secret
|
||||
// @Accept json
|
||||
// @Param request body dto.GenerateLoad true "request"
|
||||
// @Success 200 {string} secret
|
||||
// @Param request body dto.SearchWithPage true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/ssh/secret [post]
|
||||
func (b *BaseApi) LoadSSHSecret(c *gin.Context) {
|
||||
var req dto.GenerateLoad
|
||||
// @Router /hosts/ssh/cert/search [post]
|
||||
func (b *BaseApi) SearchRootCert(c *gin.Context) {
|
||||
var req dto.SearchWithPage
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := sshService.LoadSSHSecret(req.EncryptionMode)
|
||||
total, data, err := sshService.SearchRootCerts(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Total: total,
|
||||
Items: data,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Delete host SSH secret
|
||||
// @Accept json
|
||||
// @Param request body dto.ForceDelete true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/ssh/cert/delete [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除 SSH 密钥 ","formatEN":"delete SSH secret"}
|
||||
func (b *BaseApi) DeleteRootCert(c *gin.Context) {
|
||||
var req dto.ForceDelete
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := sshService.DeleteRootCerts(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Load host SSH logs
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchSSHLog true "request"
|
||||
// @Success 200 {object} dto.SSHLog
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/ssh/log [post]
|
||||
@@ -145,12 +211,37 @@ func (b *BaseApi) LoadSSHLogs(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := sshService.LoadLog(c, req)
|
||||
total, data, err := sshService.LoadLog(c, req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Total: total,
|
||||
Items: data,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Export host SSH logs
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchSSHLog true "request"
|
||||
// @Success 200 {string} path
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/ssh/log/export [post]
|
||||
func (b *BaseApi) ExportSSHLogs(c *gin.Context) {
|
||||
var req dto.SearchSSHLog
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
tmpFile, err := sshService.ExportLog(c, req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, tmpFile)
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
|
||||
@@ -89,8 +89,8 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
|
||||
var containerID string
|
||||
var initCmd []string
|
||||
switch source {
|
||||
case "redis":
|
||||
containerID, initCmd, err = loadRedisInitCmd(c)
|
||||
case "redis", "redis-cluster":
|
||||
containerID, initCmd, err = loadRedisInitCmd(c, source)
|
||||
case "ollama":
|
||||
containerID, initCmd, err = loadOllamaInitCmd(c)
|
||||
case "container":
|
||||
@@ -127,7 +127,7 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
|
||||
_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)
|
||||
}
|
||||
|
||||
func loadRedisInitCmd(c *gin.Context) (string, []string, error) {
|
||||
func loadRedisInitCmd(c *gin.Context, redisType string) (string, []string, error) {
|
||||
name := c.Query("name")
|
||||
from := c.Query("from")
|
||||
commands := []string{"exec", "-it"}
|
||||
@@ -136,7 +136,7 @@ func loadRedisInitCmd(c *gin.Context) (string, []string, error) {
|
||||
return "", nil, fmt.Errorf("no such database in db, err: %v", err)
|
||||
}
|
||||
if from == "local" {
|
||||
redisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: name, Type: "redis"})
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1121,3 +1121,23 @@ func (b *BaseApi) OperateCrossSiteAccess(c *gin.Context) {
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Exec Composer
|
||||
// @Accept json
|
||||
// @Param request body request.ExecComposerReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/exec/composer [post]
|
||||
func (b *BaseApi) ExecComposer(c *gin.Context) {
|
||||
var req request.ExecComposerReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := websiteService.ExecComposer(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
@@ -245,3 +246,15 @@ func (b *BaseApi) DownloadWebsiteSSL(c *gin.Context) {
|
||||
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(info.Name()))
|
||||
http.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), file)
|
||||
}
|
||||
|
||||
func (b *BaseApi) ImportMasterSSL(c *gin.Context) {
|
||||
var req model.WebsiteSSL
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := websiteSSLService.ImportMasterSSL(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
+314
-4
@@ -1,10 +1,17 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CreateOrUpdateAlert struct {
|
||||
AlertTitle string `json:"alertTitle"`
|
||||
AlertType string `json:"alertType"`
|
||||
AlertCount uint `json:"alertCount"`
|
||||
EntryID uint `json:"entryID"`
|
||||
AlertTitle string `json:"alertTitle"`
|
||||
AlertType string `json:"alertType"`
|
||||
AlertMethod string `json:"alertMethod"`
|
||||
AlertCount uint `json:"alertCount"`
|
||||
EntryID uint `json:"entryID"`
|
||||
}
|
||||
|
||||
type AlertBase struct {
|
||||
@@ -18,3 +25,306 @@ type PushAlert struct {
|
||||
EntryID uint `json:"entryID"`
|
||||
Param string `json:"param"`
|
||||
}
|
||||
|
||||
type AlertSearch struct {
|
||||
PageInfo
|
||||
OrderBy string `json:"orderBy" validate:"required,oneof=created_at"`
|
||||
Order string `json:"order" validate:"required,oneof=null ascending descending"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
Method string `json:"method"`
|
||||
}
|
||||
|
||||
type AlertDTO struct {
|
||||
ID uint `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Cycle uint `json:"cycle"`
|
||||
Count uint `json:"count"`
|
||||
Method string `json:"method"`
|
||||
Title string `json:"title"`
|
||||
Project string `json:"project"`
|
||||
Status string `json:"status"`
|
||||
SendCount uint `json:"sendCount"`
|
||||
AdvancedParams string `json:"advancedParams"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type AlertCreate struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
Cycle uint `json:"cycle"`
|
||||
Count uint `json:"count"`
|
||||
Method string `json:"method" validate:"required"`
|
||||
Title string `json:"title"`
|
||||
Project string `json:"project"`
|
||||
Status string `json:"status"`
|
||||
SendCount uint `json:"sendCount"`
|
||||
AdvancedParams string `json:"advancedParams"`
|
||||
}
|
||||
|
||||
type AlertUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Type string `json:"type"`
|
||||
Cycle uint `json:"cycle"`
|
||||
Count uint `json:"count"`
|
||||
Method string `json:"method"`
|
||||
Title string `json:"title"`
|
||||
Project string `json:"project"`
|
||||
Status string `json:"status"`
|
||||
SendCount uint `json:"sendCount"`
|
||||
AdvancedParams string `json:"advancedParams"`
|
||||
}
|
||||
|
||||
type DeleteRequest struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
type AlertUpdateStatus struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Status string `json:"status" validate:"required"`
|
||||
}
|
||||
|
||||
type DiskDTO struct {
|
||||
Path string `json:"path"`
|
||||
Type string `json:"type"`
|
||||
Device string `json:"device"`
|
||||
Total uint64 `json:"total"`
|
||||
Free uint64 `json:"free"`
|
||||
Used uint64 `json:"used"`
|
||||
UsedPercent float64 `json:"usedPercent"`
|
||||
|
||||
InodesTotal uint64 `json:"inodesTotal"`
|
||||
InodesUsed uint64 `json:"inodesUsed"`
|
||||
InodesFree uint64 `json:"inodesFree"`
|
||||
InodesUsedPercent float64 `json:"inodesUsedPercent"`
|
||||
}
|
||||
|
||||
type AlertLogSearch struct {
|
||||
PageInfo
|
||||
Count uint `json:"count"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type AlertLogDTO struct {
|
||||
ID uint `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Count uint `json:"count"`
|
||||
AlertId uint `json:"alertId"`
|
||||
AlertDetail AlertDetail `json:"alertDetail"`
|
||||
AlertRule AlertRule `json:"alertRule"`
|
||||
Status string `json:"status"`
|
||||
Method string `json:"method"`
|
||||
Message string `json:"message"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type AlertLogCreate struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
Count uint `json:"count" validate:"required"`
|
||||
AlertId uint `json:"alertId" validate:"required"`
|
||||
AlertDetail string `json:"alertDetail" validate:"required"`
|
||||
AlertRule string `json:"alertRule" validate:"required"`
|
||||
Status string `json:"status" validate:"required"`
|
||||
Method string `json:"method" validate:"required"`
|
||||
Message string `json:"message"`
|
||||
RecordId uint `json:"recordId"`
|
||||
LicenseId string `json:"licenseId" validate:"required"`
|
||||
}
|
||||
|
||||
type AlertLog struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
type AlertDetail struct {
|
||||
LicenseId string `json:"licenseId"`
|
||||
Type string `json:"type"`
|
||||
SubType string `json:"subType"`
|
||||
Title string `json:"title"`
|
||||
Method string `json:"method"`
|
||||
LicenseCode string `json:"licenseCode"`
|
||||
DeviceId string `json:"deviceId"`
|
||||
Project string `json:"project"`
|
||||
Params []Param `json:"params"`
|
||||
Phone string `json:"phone"`
|
||||
}
|
||||
|
||||
type AlertRule struct {
|
||||
ID uint `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Cycle uint `json:"cycle"`
|
||||
Count uint `json:"count"`
|
||||
Method string `json:"method"`
|
||||
Title string `json:"title"`
|
||||
Project string `json:"project"`
|
||||
Status string `json:"status"`
|
||||
SendCount uint `json:"sendCount"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type Param struct {
|
||||
Index string `json:"index"`
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type ClamDTO struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
type CronJobDTO struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type CronJobReq struct {
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type UpgradeInfo struct {
|
||||
TestVersion string `json:"testVersion"`
|
||||
NewVersion string `json:"newVersion"`
|
||||
LatestVersion string `json:"latestVersion"`
|
||||
ReleaseNote string `json:"releaseNote"`
|
||||
}
|
||||
|
||||
type AlertDiskInfo struct {
|
||||
Type string
|
||||
Mount string
|
||||
Device string
|
||||
}
|
||||
|
||||
type SyncResult struct {
|
||||
ID uint `json:"id"`
|
||||
LicenseID string `json:"licenseId"`
|
||||
SendStatus string `json:"sendStatus"`
|
||||
CreateTime string `json:"createTime"`
|
||||
UpdateTime string `json:"updateTime"`
|
||||
Remarks string `json:"remarks"`
|
||||
MsgCount int `json:"msgCount"`
|
||||
MsgCountMax int `json:"msgCountMax"`
|
||||
}
|
||||
|
||||
type QueryRequest struct {
|
||||
QueryIds []uint `json:"queryIds"`
|
||||
LicenseId string `json:"licenseId"`
|
||||
}
|
||||
|
||||
type AlertResponse struct {
|
||||
Result bool `json:"result"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type PushResult struct {
|
||||
RecordId uint `json:"recordId"`
|
||||
LicenseId string `json:"licenseId"`
|
||||
}
|
||||
|
||||
type UpdateOfflineAlertLog struct {
|
||||
ID uint `json:"id"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
RecordId uint `json:"recordId"`
|
||||
LicenseId string `json:"licenseId"`
|
||||
}
|
||||
|
||||
type SyncOfflineAlertLogDTO struct {
|
||||
QueryRequest string `json:"queryRequest"`
|
||||
Ids []uint `json:"ids"`
|
||||
AlertLogs []model.AlertLog `json:"alertLogs"`
|
||||
LicenseId string `json:"licenseId"`
|
||||
}
|
||||
|
||||
type OfflineAlertResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Type string `json:"type"`
|
||||
RemoteErr error `json:"remoteErr"`
|
||||
ResponseStruct AlertResponse `json:"responseStruct"`
|
||||
AlertLogs []model.AlertLog `json:"alertLogs"`
|
||||
Ids []uint `json:"ids"`
|
||||
LicenseId string `json:"licenseId"`
|
||||
}
|
||||
|
||||
type OfflineAlertLogDTO struct {
|
||||
ID uint `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Count uint `json:"count"`
|
||||
AlertId uint `json:"alertId"`
|
||||
AlertDetail string `json:"alertDetail"`
|
||||
AlertRule string `json:"alertRule"`
|
||||
Status string `json:"status"`
|
||||
Method string `json:"method"`
|
||||
Message string `json:"message"`
|
||||
RecordId uint `json:"recordId"`
|
||||
LicenseId string `json:"licenseId"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type OfflineQueryRequest struct {
|
||||
Ids []uint `json:"ids"`
|
||||
QueryIds []uint `json:"queryIds"`
|
||||
LicenseId string `json:"licenseId"`
|
||||
}
|
||||
|
||||
type AlertConfigUpdate struct {
|
||||
ID uint `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Title string `json:"title"`
|
||||
Status string `json:"status"`
|
||||
Config string `json:"config"`
|
||||
}
|
||||
|
||||
type AlertConfigTest struct {
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
Sender string `json:"sender"`
|
||||
Password string `json:"password"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Encryption string `json:"encryption"` // "ssl" / "tls" / "none"
|
||||
Recipient string `json:"recipient"`
|
||||
}
|
||||
|
||||
type AlertSendTimeRange struct {
|
||||
NoticeAlert struct {
|
||||
SendTimeRange string `json:"sendTimeRange"`
|
||||
Type []string `json:"type"`
|
||||
} `json:"noticeAlert"`
|
||||
ResourceAlert struct {
|
||||
SendTimeRange string `json:"sendTimeRange"`
|
||||
Type []string `json:"type"`
|
||||
} `json:"resourceAlert"`
|
||||
}
|
||||
|
||||
type AlertCommonConfig struct {
|
||||
IsOffline string `json:"isOffline"`
|
||||
AlertSendTimeRange AlertSendTimeRange `json:"alertSendTimeRange"`
|
||||
}
|
||||
|
||||
type AlertSmsConfig struct {
|
||||
Phone string `json:"phone"`
|
||||
AlertDailyNum uint `json:"alertDailyNum"`
|
||||
}
|
||||
|
||||
type AlertEmailConfig struct {
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
Sender string `json:"sender"`
|
||||
Password string `json:"password"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Encryption string `json:"encryption"` // "ssl" / "tls" / "none"
|
||||
Recipient string `json:"recipient"`
|
||||
}
|
||||
|
||||
@@ -7,10 +7,11 @@ import (
|
||||
)
|
||||
|
||||
type AppDatabase struct {
|
||||
ServiceName string `json:"PANEL_DB_HOST"`
|
||||
DbName string `json:"PANEL_DB_NAME"`
|
||||
DbUser string `json:"PANEL_DB_USER"`
|
||||
Password string `json:"PANEL_DB_USER_PASSWORD"`
|
||||
ServiceName string `json:"PANEL_DB_HOST"`
|
||||
DbName string `json:"PANEL_DB_NAME"`
|
||||
DbUser string `json:"PANEL_DB_USER"`
|
||||
Password string `json:"PANEL_DB_USER_PASSWORD"`
|
||||
DatabaseName string `json:"DATABASE_NAME"`
|
||||
}
|
||||
|
||||
type AuthParam struct {
|
||||
@@ -126,6 +127,7 @@ type Locale struct {
|
||||
ZhHant string `json:"zh-hant" yaml:"zh-hant"`
|
||||
Zh string `json:"zh"`
|
||||
Ko string `json:"ko"`
|
||||
Tr string `json:"tr"`
|
||||
}
|
||||
|
||||
type AppForm struct {
|
||||
|
||||
@@ -54,7 +54,7 @@ type BackupOption struct {
|
||||
}
|
||||
|
||||
type CommonBackup struct {
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql"`
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql mysql-cluster postgresql-cluster redis-cluster"`
|
||||
Name string `json:"name"`
|
||||
DetailName string `json:"detailName"`
|
||||
Secret string `json:"secret"`
|
||||
@@ -65,7 +65,7 @@ type CommonBackup struct {
|
||||
}
|
||||
type CommonRecover struct {
|
||||
DownloadAccountID uint `json:"downloadAccountID" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql"`
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql mysql-cluster postgresql-cluster redis-cluster"`
|
||||
Name string `json:"name"`
|
||||
DetailName string `json:"detailName"`
|
||||
File string `json:"file"`
|
||||
|
||||
@@ -34,6 +34,7 @@ type ClamInfo struct {
|
||||
Spec string `json:"spec"`
|
||||
Description string `json:"description"`
|
||||
AlertCount uint `json:"alertCount"`
|
||||
AlertMethod string `json:"alertMethod"`
|
||||
}
|
||||
|
||||
type ClamLogSearch struct {
|
||||
@@ -74,6 +75,7 @@ type ClamCreate struct {
|
||||
Description string `json:"description"`
|
||||
AlertCount uint `json:"alertCount"`
|
||||
AlertTitle string `json:"alertTitle"`
|
||||
AlertMethod string `json:"alertMethod"`
|
||||
}
|
||||
|
||||
type ClamUpdate struct {
|
||||
@@ -87,6 +89,7 @@ type ClamUpdate struct {
|
||||
Description string `json:"description"`
|
||||
AlertCount uint `json:"alertCount"`
|
||||
AlertTitle string `json:"alertTitle"`
|
||||
AlertMethod string `json:"alertMethod"`
|
||||
}
|
||||
|
||||
type ClamUpdateStatus struct {
|
||||
|
||||
@@ -28,6 +28,9 @@ type OperationWithName struct {
|
||||
type OperateByID struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
}
|
||||
type OperateByIDs struct {
|
||||
IDs []uint `json:"ids"`
|
||||
}
|
||||
|
||||
type Operate struct {
|
||||
Operation string `json:"operation" validate:"required"`
|
||||
@@ -76,3 +79,8 @@ type ForceDelete struct {
|
||||
IDs []uint `json:"ids"`
|
||||
ForceDelete bool `json:"forceDelete"`
|
||||
}
|
||||
|
||||
type ChangeGroup struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
GroupID uint `json:"groupID" validate:"required"`
|
||||
}
|
||||
|
||||
+27
-21
@@ -39,6 +39,11 @@ type ContainerInfo struct {
|
||||
Websites []string `json:"websites"`
|
||||
}
|
||||
|
||||
type ContainerOptions struct {
|
||||
Name string `json:"name"`
|
||||
State string `json:"state"`
|
||||
}
|
||||
|
||||
type ContainerStatus struct {
|
||||
All uint `json:"all"`
|
||||
Created uint `json:"created"`
|
||||
@@ -102,10 +107,10 @@ type ContainerCreateByCommand struct {
|
||||
}
|
||||
|
||||
type ContainerUpgrade struct {
|
||||
TaskID string `json:"taskID"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Image string `json:"image" validate:"required"`
|
||||
ForcePull bool `json:"forcePull"`
|
||||
TaskID string `json:"taskID"`
|
||||
Names []string `json:"names" validate:"required"`
|
||||
Image string `json:"image" validate:"required"`
|
||||
ForcePull bool `json:"forcePull"`
|
||||
}
|
||||
|
||||
type ContainerListStats struct {
|
||||
@@ -164,18 +169,16 @@ type ContainerCommit struct {
|
||||
Comment string `json:"comment"`
|
||||
Author string `json:"author"`
|
||||
Pause bool `json:"pause"`
|
||||
|
||||
TaskID string `json:"taskID"`
|
||||
}
|
||||
|
||||
type ContainerPrune struct {
|
||||
TaskID string `json:"taskID"`
|
||||
PruneType string `json:"pruneType" validate:"required,oneof=container image volume network buildcache"`
|
||||
WithTagAll bool `json:"withTagAll"`
|
||||
}
|
||||
|
||||
type ContainerPruneReport struct {
|
||||
DeletedNumber int `json:"deletedNumber"`
|
||||
SpaceReclaimed int `json:"spaceReclaimed"`
|
||||
}
|
||||
|
||||
type Network struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
@@ -220,20 +223,22 @@ type VolumeCreate struct {
|
||||
}
|
||||
|
||||
type BatchDelete struct {
|
||||
Force bool `json:"force"`
|
||||
Names []string `json:"names" validate:"required"`
|
||||
TaskID string `json:"taskID"`
|
||||
Force bool `json:"force"`
|
||||
Names []string `json:"names" validate:"required"`
|
||||
}
|
||||
|
||||
type ComposeInfo struct {
|
||||
Name string `json:"name"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
ContainerNumber int `json:"containerNumber"`
|
||||
ConfigFile string `json:"configFile"`
|
||||
Workdir string `json:"workdir"`
|
||||
Path string `json:"path"`
|
||||
Containers []ComposeContainer `json:"containers"`
|
||||
Env []string `json:"env"`
|
||||
Name string `json:"name"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
ContainerCount int `json:"containerCount"`
|
||||
RunningCount int `json:"runningCount"`
|
||||
ConfigFile string `json:"configFile"`
|
||||
Workdir string `json:"workdir"`
|
||||
Path string `json:"path"`
|
||||
Containers []ComposeContainer `json:"containers"`
|
||||
Env []string `json:"env"`
|
||||
}
|
||||
type ComposeContainer struct {
|
||||
ContainerID string `json:"containerID"`
|
||||
@@ -253,8 +258,9 @@ type ComposeCreate struct {
|
||||
type ComposeOperation struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Path string `json:"path"`
|
||||
Operation string `json:"operation" validate:"required,oneof=up start stop down delete"`
|
||||
Operation string `json:"operation" validate:"required,oneof=up start restart stop down delete"`
|
||||
WithFile bool `json:"withFile"`
|
||||
Force bool `josn:"force"`
|
||||
}
|
||||
type ComposeUpdate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
|
||||
+61
-10
@@ -6,9 +6,10 @@ import (
|
||||
|
||||
type PageCronjob struct {
|
||||
PageInfo
|
||||
Info string `json:"info"`
|
||||
OrderBy string `json:"orderBy" validate:"required,oneof=name status createdAt"`
|
||||
Order string `json:"order" validate:"required,oneof=null ascending descending"`
|
||||
Info string `json:"info"`
|
||||
GroupIDs []uint `json:"groupIDs"`
|
||||
OrderBy string `json:"orderBy" validate:"required,oneof=name status createdAt"`
|
||||
Order string `json:"order" validate:"required,oneof=null ascending descending"`
|
||||
}
|
||||
|
||||
type CronjobSpec struct {
|
||||
@@ -19,6 +20,7 @@ type CronjobOperate struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
GroupID uint `json:"groupID"`
|
||||
SpecCustom bool `json:"specCustom"`
|
||||
Spec string `json:"spec" validate:"required"`
|
||||
|
||||
@@ -48,8 +50,9 @@ type CronjobOperate struct {
|
||||
IgnoreErr bool `json:"ignoreErr"`
|
||||
Secret string `json:"secret"`
|
||||
|
||||
AlertCount uint `json:"alertCount"`
|
||||
AlertTitle string `json:"alertTitle"`
|
||||
AlertCount uint `json:"alertCount"`
|
||||
AlertTitle string `json:"alertTitle"`
|
||||
AlertMethod string `json:"alertMethod"`
|
||||
}
|
||||
|
||||
type SnapshotRule struct {
|
||||
@@ -62,11 +65,6 @@ type CronjobUpdateStatus struct {
|
||||
Status string `json:"status" validate:"required"`
|
||||
}
|
||||
|
||||
type CronjobDownload struct {
|
||||
RecordID uint `json:"recordID" validate:"required"`
|
||||
BackupAccountID uint `json:"backupAccountID" validate:"required"`
|
||||
}
|
||||
|
||||
type CronjobClean struct {
|
||||
IsDelete bool `json:"isDelete"`
|
||||
CleanData bool `json:"cleanData"`
|
||||
@@ -84,6 +82,7 @@ type CronjobInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
GroupID uint `json:"groupID"`
|
||||
SpecCustom bool `json:"specCustom"`
|
||||
Spec string `json:"spec"`
|
||||
|
||||
@@ -122,6 +121,58 @@ type CronjobInfo struct {
|
||||
AlertCount uint `json:"alertCount"`
|
||||
}
|
||||
|
||||
type CronjobImport struct {
|
||||
Cronjobs []CronjobTrans `json:"cronjobs"`
|
||||
}
|
||||
type CronjobTrans struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
GroupID uint `json:"groupID"`
|
||||
SpecCustom bool `json:"specCustom"`
|
||||
Spec string `json:"spec"`
|
||||
|
||||
Executor string `json:"executor"`
|
||||
ScriptMode string `json:"scriptMode"`
|
||||
Script string `json:"script"`
|
||||
Command string `json:"command"`
|
||||
ContainerName string `json:"containerName"`
|
||||
User string `json:"user"`
|
||||
URL string `json:"url"`
|
||||
|
||||
ScriptName string `json:"scriptName"`
|
||||
Apps []TransHelper `json:"apps"`
|
||||
Websites []string `json:"websites"`
|
||||
DBType string `json:"dbType"`
|
||||
DBNames []TransHelper `json:"dbName"`
|
||||
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
|
||||
IsDir bool `json:"isDir"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
|
||||
RetainCopies uint64 `json:"retainCopies"`
|
||||
RetryTimes uint `json:"retryTimes"`
|
||||
Timeout uint `json:"timeout"`
|
||||
IgnoreErr bool `json:"ignoreErr"`
|
||||
SnapshotRule SnapshotTransHelper `json:"snapshotRule"`
|
||||
Secret string `json:"secret"`
|
||||
|
||||
SourceAccounts []string `json:"sourceAccounts"`
|
||||
DownloadAccount string `json:"downloadAccount"`
|
||||
|
||||
AlertCount uint `json:"alertCount"`
|
||||
AlertTitle string `json:"alertTitle"`
|
||||
AlertMethod string `json:"alertMethod"`
|
||||
}
|
||||
type TransHelper struct {
|
||||
Name string `json:"name"`
|
||||
DetailName string `json:"detailName"`
|
||||
}
|
||||
type SnapshotTransHelper struct {
|
||||
WithImage bool `json:"withImage"`
|
||||
IgnoreApps []TransHelper `json:"ignoreApps"`
|
||||
}
|
||||
|
||||
type ScriptOptions struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
||||
@@ -74,8 +74,11 @@ type DashboardCurrent struct {
|
||||
CPUTotal int `json:"cpuTotal"`
|
||||
|
||||
MemoryTotal uint64 `json:"memoryTotal"`
|
||||
MemoryAvailable uint64 `json:"memoryAvailable"`
|
||||
MemoryUsed uint64 `json:"memoryUsed"`
|
||||
MemoryFree uint64 `json:"memoryFree"`
|
||||
MemoryShard uint64 `json:"memoryShard"`
|
||||
MemoryCache uint64 `json:"memoryCache"`
|
||||
MemoryAvailable uint64 `json:"memoryAvailable"`
|
||||
MemoryUsedPercent float64 `json:"memoryUsedPercent"`
|
||||
|
||||
SwapMemoryTotal uint64 `json:"swapMemoryTotal"`
|
||||
|
||||
@@ -4,14 +4,14 @@ import "time"
|
||||
|
||||
// common
|
||||
type DBConfUpdateByFile struct {
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql redis"`
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql redis mysql-cluster postgresql-cluster redis-cluster"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
File string `json:"file"`
|
||||
}
|
||||
type ChangeDBInfo struct {
|
||||
ID uint `json:"id"`
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql"`
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql redis mysql-cluster postgresql-cluster redis-cluster"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
Value string `json:"value" validate:"required"`
|
||||
}
|
||||
@@ -74,19 +74,19 @@ type BindUser struct {
|
||||
|
||||
type MysqlLoadDB struct {
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb mysql-cluster"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
}
|
||||
|
||||
type MysqlDBDeleteCheck struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb mysql-cluster"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
}
|
||||
|
||||
type MysqlDBDelete struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb mysql-cluster"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
ForceDelete bool `json:"forceDelete"`
|
||||
DeleteBackup bool `json:"deleteBackup"`
|
||||
@@ -153,7 +153,7 @@ type MysqlVariables struct {
|
||||
}
|
||||
|
||||
type MysqlVariablesUpdate struct {
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb mysql-cluster"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
Variables []MysqlVariablesUpdateHelper `json:"variables"`
|
||||
}
|
||||
@@ -174,6 +174,7 @@ type RedisConfUpdate struct {
|
||||
Timeout string `json:"timeout"`
|
||||
Maxclients string `json:"maxclients"`
|
||||
Maxmemory string `json:"maxmemory"`
|
||||
DBType string `json:"dbType" validate:"required,oneof=redis redis-cluster"`
|
||||
}
|
||||
type RedisConfPersistenceUpdate struct {
|
||||
Database string `json:"database" validate:"required"`
|
||||
@@ -181,6 +182,7 @@ type RedisConfPersistenceUpdate struct {
|
||||
Appendonly string `json:"appendonly"`
|
||||
Appendfsync string `json:"appendfsync"`
|
||||
Save string `json:"save"`
|
||||
DBType string `json:"dbType" validate:"required,oneof=redis redis-cluster"`
|
||||
}
|
||||
|
||||
type RedisConf struct {
|
||||
@@ -258,6 +260,7 @@ type DatabaseInfo struct {
|
||||
ClientCert string `json:"clientCert"`
|
||||
SkipVerify bool `json:"skipVerify"`
|
||||
|
||||
Timeout uint `json:"timeout"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
@@ -293,6 +296,7 @@ type DatabaseCreate struct {
|
||||
ClientCert string `json:"clientCert"`
|
||||
SkipVerify bool `json:"skipVerify"`
|
||||
|
||||
Timeout uint `json:"timeout"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
@@ -311,6 +315,7 @@ type DatabaseUpdate struct {
|
||||
ClientCert string `json:"clientCert"`
|
||||
SkipVerify bool `json:"skipVerify"`
|
||||
|
||||
Timeout uint `json:"timeout"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
@@ -319,3 +324,13 @@ type DatabaseDelete struct {
|
||||
ForceDelete bool `json:"forceDelete"`
|
||||
DeleteBackup bool `json:"deleteBackup"`
|
||||
}
|
||||
|
||||
type LoadRedisStatus struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
}
|
||||
|
||||
type DBResource struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
@@ -60,26 +60,20 @@ type PostgresqlPrivileges struct {
|
||||
|
||||
type PostgresqlLoadDB struct {
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Type string `json:"type" validate:"required,oneof=postgresql"`
|
||||
Type string `json:"type" validate:"required,oneof=postgresql postgresql-cluster"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
}
|
||||
|
||||
type PostgresqlDBDeleteCheck struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=postgresql"`
|
||||
Type string `json:"type" validate:"required,oneof=postgresql postgresql-cluster"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
}
|
||||
|
||||
type PostgresqlDBDelete struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=postgresql"`
|
||||
Type string `json:"type" validate:"required,oneof=postgresql postgresql-cluster"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
ForceDelete bool `json:"forceDelete"`
|
||||
DeleteBackup bool `json:"deleteBackup"`
|
||||
}
|
||||
|
||||
type PostgresqlConfUpdateByFile struct {
|
||||
Type string `json:"type" validate:"required,oneof=postgresql mariadb"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
File string `json:"file"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package dto
|
||||
|
||||
type LogFileRes struct {
|
||||
Lines []string `json:"lines"`
|
||||
IsEndOfFile bool `json:"isEndOfFile"`
|
||||
TotalPages int `json:"totalPages"`
|
||||
TotalLines int `json:"totalLines"`
|
||||
}
|
||||
@@ -2,12 +2,19 @@ package dto
|
||||
|
||||
import "time"
|
||||
|
||||
type PageImage struct {
|
||||
PageInfo
|
||||
Name string `json:"name"`
|
||||
OrderBy string `json:"orderBy" validate:"required,oneof=size tags createdAt isUsed"`
|
||||
Order string `json:"order" validate:"required,oneof=null ascending descending"`
|
||||
}
|
||||
|
||||
type ImageInfo struct {
|
||||
ID string `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
IsUsed bool `json:"isUsed"`
|
||||
Tags []string `json:"tags"`
|
||||
Size string `json:"size"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
type ImageLoad struct {
|
||||
|
||||
@@ -40,6 +40,7 @@ type AppContainerConfig struct {
|
||||
WebUI string `json:"webUI"`
|
||||
Type string `json:"type"`
|
||||
SpecifyIP string `json:"specifyIP"`
|
||||
RestartPolicy string `json:"restartPolicy" validate:"omitempty,oneof=always unless-stopped no on-failure"`
|
||||
}
|
||||
|
||||
type AppInstalledSearch struct {
|
||||
|
||||
@@ -33,6 +33,7 @@ type SupervisorProcessConfig struct {
|
||||
Dir string `json:"dir"`
|
||||
Numprocs string `json:"numprocs"`
|
||||
AutoRestart string `json:"autoRestart"`
|
||||
AutoStart string `json:"autoStart"`
|
||||
}
|
||||
|
||||
type SupervisorProcessFileReq struct {
|
||||
|
||||
@@ -9,15 +9,17 @@ type McpServerSearch struct {
|
||||
}
|
||||
|
||||
type McpServerCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Command string `json:"command" validate:"required"`
|
||||
Environments []Environment `json:"environments"`
|
||||
Volumes []Volume `json:"volumes"`
|
||||
Port int `json:"port" validate:"required"`
|
||||
ContainerName string `json:"containerName"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
SsePath string `json:"ssePath"`
|
||||
HostIP string `json:"hostIP"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Command string `json:"command" validate:"required"`
|
||||
Environments []Environment `json:"environments"`
|
||||
Volumes []Volume `json:"volumes"`
|
||||
Port int `json:"port" validate:"required"`
|
||||
ContainerName string `json:"containerName"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
SsePath string `json:"ssePath"`
|
||||
HostIP string `json:"hostIP"`
|
||||
StreamableHttpPath string `json:"streamableHttpPath"`
|
||||
OutputTransport string `json:"outputTransport" validate:"required"`
|
||||
}
|
||||
|
||||
type McpServerUpdate struct {
|
||||
|
||||
@@ -126,3 +126,7 @@ type NginxModuleUpdate struct {
|
||||
Enable bool `json:"enable"`
|
||||
Params string `json:"params"`
|
||||
}
|
||||
|
||||
type NginxOperateReq struct {
|
||||
Operate string `json:"operate" validate:"required,oneof=enable disable"`
|
||||
}
|
||||
|
||||
@@ -152,21 +152,22 @@ type WebsiteDomainDelete struct {
|
||||
}
|
||||
|
||||
type WebsiteHTTPSOp struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Enable bool `json:"enable"`
|
||||
WebsiteSSLID uint `json:"websiteSSLId"`
|
||||
Type string `json:"type" validate:"oneof=existed auto manual"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
Certificate string `json:"certificate"`
|
||||
PrivateKeyPath string `json:"privateKeyPath"`
|
||||
CertificatePath string `json:"certificatePath"`
|
||||
ImportType string `json:"importType"`
|
||||
HttpConfig string `json:"httpConfig" validate:"oneof=HTTPSOnly HTTPAlso HTTPToHTTPS"`
|
||||
SSLProtocol []string `json:"SSLProtocol"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
Hsts bool `json:"hsts"`
|
||||
HttpsPorts []int `json:"httpsPorts"`
|
||||
Http3 bool `json:"http3"`
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Enable bool `json:"enable"`
|
||||
WebsiteSSLID uint `json:"websiteSSLId"`
|
||||
Type string `json:"type" validate:"oneof=existed auto manual"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
Certificate string `json:"certificate"`
|
||||
PrivateKeyPath string `json:"privateKeyPath"`
|
||||
CertificatePath string `json:"certificatePath"`
|
||||
ImportType string `json:"importType"`
|
||||
HttpConfig string `json:"httpConfig" validate:"oneof=HTTPSOnly HTTPAlso HTTPToHTTPS"`
|
||||
SSLProtocol []string `json:"SSLProtocol"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
Hsts bool `json:"hsts"`
|
||||
HstsIncludeSubDomains bool `json:"hstsIncludeSubDomains"`
|
||||
HttpsPorts []int `json:"httpsPorts"`
|
||||
Http3 bool `json:"http3"`
|
||||
}
|
||||
|
||||
type WebsiteNginxUpdate struct {
|
||||
@@ -203,22 +204,24 @@ type WebsiteUpdateDirPermission struct {
|
||||
}
|
||||
|
||||
type WebsiteProxyConfig struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Operate string `json:"operate" validate:"required"`
|
||||
Enable bool `json:"enable" `
|
||||
Cache bool `json:"cache" `
|
||||
CacheTime int `json:"cacheTime" `
|
||||
CacheUnit string `json:"cacheUnit"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Modifier string `json:"modifier"`
|
||||
Match string `json:"match" validate:"required"`
|
||||
ProxyPass string `json:"proxyPass" validate:"required"`
|
||||
ProxyHost string `json:"proxyHost" validate:"required"`
|
||||
Content string `json:"content"`
|
||||
FilePath string `json:"filePath"`
|
||||
Replaces map[string]string `json:"replaces"`
|
||||
SNI bool `json:"sni"`
|
||||
ProxySSLName string `json:"proxySSLName"`
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Operate string `json:"operate" validate:"required"`
|
||||
Enable bool `json:"enable" `
|
||||
Cache bool `json:"cache" `
|
||||
CacheTime int `json:"cacheTime"`
|
||||
CacheUnit string `json:"cacheUnit"`
|
||||
ServerCacheTime int `json:"serverCacheTime"`
|
||||
ServerCacheUnit string `json:"serverCacheUnit"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Modifier string `json:"modifier"`
|
||||
Match string `json:"match" validate:"required"`
|
||||
ProxyPass string `json:"proxyPass" validate:"required"`
|
||||
ProxyHost string `json:"proxyHost" validate:"required"`
|
||||
Content string `json:"content"`
|
||||
FilePath string `json:"filePath"`
|
||||
Replaces map[string]string `json:"replaces"`
|
||||
SNI bool `json:"sni"`
|
||||
ProxySSLName string `json:"proxySSLName"`
|
||||
}
|
||||
|
||||
type WebsiteProxyReq struct {
|
||||
@@ -283,8 +286,8 @@ type WebsiteRealIP struct {
|
||||
|
||||
type ChangeDatabase struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
DatabaseID uint `json:"databaseID" validate:"required"`
|
||||
DatabaseType string `json:"databaseType" validate:"required"`
|
||||
DatabaseID uint `json:"databaseID" `
|
||||
DatabaseType string `json:"databaseType" `
|
||||
}
|
||||
|
||||
type WebsiteProxyDel struct {
|
||||
@@ -296,3 +299,13 @@ type CrossSiteAccessOp struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
Operation string `json:"operation" validate:"required,oneof=Enable Disable"`
|
||||
}
|
||||
|
||||
type ExecComposerReq struct {
|
||||
Command string `json:"command" validate:"required"`
|
||||
ExtCommand string `json:"extCommand"`
|
||||
Mirror string `json:"mirror" validate:"required"`
|
||||
Dir string `json:"dir" validate:"required"`
|
||||
User string `json:"user" validate:"required"`
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
TaskID string `json:"taskID" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ type WebsiteSSLCreate struct {
|
||||
Nameserver2 string `json:"nameserver2"`
|
||||
ExecShell bool `json:"execShell"`
|
||||
Shell string `json:"shell"`
|
||||
PushNode bool `json:"pushNode"`
|
||||
Nodes string `json:"nodes"`
|
||||
}
|
||||
|
||||
type WebsiteDNSReq struct {
|
||||
@@ -105,6 +107,8 @@ type WebsiteSSLUpdate struct {
|
||||
Nameserver2 string `json:"nameserver2"`
|
||||
ExecShell bool `json:"execShell"`
|
||||
Shell string `json:"shell"`
|
||||
PushNode bool `json:"pushNode"`
|
||||
Nodes string `json:"nodes"`
|
||||
}
|
||||
|
||||
type WebsiteSSLUpload struct {
|
||||
|
||||
@@ -66,6 +66,7 @@ type AppInstalledCheck struct {
|
||||
InstallPath string `json:"installPath"`
|
||||
HttpPort int `json:"httpPort"`
|
||||
HttpsPort int `json:"httpsPort"`
|
||||
WebsiteDir string `json:"websiteDir"`
|
||||
}
|
||||
|
||||
type AppDetailDTO struct {
|
||||
@@ -129,6 +130,19 @@ type AppInstallDTO struct {
|
||||
Container string `json:"container"`
|
||||
}
|
||||
|
||||
type AppInstallInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
HttpPort int `json:"HttpPort"`
|
||||
Container string `json:"container"`
|
||||
ComposePath string `json:"composePath"`
|
||||
|
||||
Env map[string]interface{} `json:"env"`
|
||||
}
|
||||
|
||||
type DatabaseConn struct {
|
||||
Status string `json:"status"`
|
||||
Username string `json:"username"`
|
||||
@@ -158,6 +172,7 @@ type AppParam struct {
|
||||
ShowValue string `json:"showValue"`
|
||||
Required bool `json:"required"`
|
||||
Multiple bool `json:"multiple"`
|
||||
Label dto.Locale `json:"label"`
|
||||
}
|
||||
|
||||
type AppConfig struct {
|
||||
|
||||
@@ -6,5 +6,5 @@ type AppIgnoreUpgradeDTO struct {
|
||||
AppDetailID uint `json:"appDetailID"`
|
||||
Scope string `json:"scope"`
|
||||
Version string `json:"version"`
|
||||
Icon string `json:"icon"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
)
|
||||
|
||||
type FileInfo struct {
|
||||
@@ -37,11 +38,13 @@ type FileWgetRes struct {
|
||||
}
|
||||
|
||||
type FileLineContent struct {
|
||||
Content string `json:"content"`
|
||||
End bool `json:"end"`
|
||||
Path string `json:"path"`
|
||||
Total int `json:"total"`
|
||||
Lines []string `json:"lines"`
|
||||
End bool `json:"end"`
|
||||
Path string `json:"path"`
|
||||
Total int `json:"total"`
|
||||
TaskStatus string `json:"taskStatus"`
|
||||
Lines []string `json:"lines"`
|
||||
Scope string `json:"scope"`
|
||||
TotalLines int `json:"totalLines"`
|
||||
}
|
||||
|
||||
type FileExist struct {
|
||||
|
||||
@@ -31,6 +31,7 @@ type SupervisorProcessConfig struct {
|
||||
Msg string `json:"msg"`
|
||||
Status []ProcessStatus `json:"status"`
|
||||
AutoRestart string `json:"autoRestart"`
|
||||
AutoStart string `json:"autoStart"`
|
||||
}
|
||||
|
||||
type ProcessStatus struct {
|
||||
|
||||
@@ -80,3 +80,7 @@ type NginxBuildConfig struct {
|
||||
Mirror string `json:"mirror"`
|
||||
Modules []NginxModule `json:"modules"`
|
||||
}
|
||||
|
||||
type NginxConfigRes struct {
|
||||
Https bool `json:"https"`
|
||||
}
|
||||
|
||||
@@ -78,3 +78,8 @@ type PHPExtensionRes struct {
|
||||
Extensions []string `json:"extensions"`
|
||||
SupportExtensions []SupportExtension `json:"supportExtensions"`
|
||||
}
|
||||
|
||||
type FpmStatusItem struct {
|
||||
Key string `json:"key"`
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ type WebsiteRes struct {
|
||||
ChildSites []string `json:"childSites"`
|
||||
RuntimeType string `json:"runtimeType"`
|
||||
Favorite bool `json:"favorite"`
|
||||
IPV6 bool `json:"IPV6"`
|
||||
}
|
||||
|
||||
type WebsiteOption struct {
|
||||
@@ -64,6 +65,7 @@ type WebsiteHTTPS struct {
|
||||
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"`
|
||||
|
||||
@@ -18,6 +18,7 @@ type SnapshotCreate struct {
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
Secret string `json:"secret"`
|
||||
InterruptStep string `json:"interruptStep"`
|
||||
Timeout uint `json:"timeout"`
|
||||
|
||||
AppData []DataTree `json:"appData"`
|
||||
BackupData []DataTree `json:"backupData"`
|
||||
|
||||
+18
-9
@@ -19,12 +19,27 @@ type SSHInfo struct {
|
||||
PubkeyAuthentication string `json:"pubkeyAuthentication"`
|
||||
PermitRootLogin string `json:"permitRootLogin"`
|
||||
UseDNS string `json:"useDNS"`
|
||||
CurrentUser string `json:"currentUser"`
|
||||
}
|
||||
|
||||
type GenerateSSH struct {
|
||||
EncryptionMode string `json:"encryptionMode" validate:"required,oneof=rsa ed25519 ecdsa dsa"`
|
||||
Password string `json:"password"`
|
||||
type CreateRootCert struct {
|
||||
Name string `json:"name"`
|
||||
Mode string `json:"mode"`
|
||||
EncryptionMode string `json:"encryptionMode" validate:"required,oneof=rsa ed25519 ecdsa dsa"`
|
||||
PassPhrase string `json:"passPhrase"`
|
||||
PublicKey string `json:"publicKey"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
type RootCert struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Name string `json:"name"`
|
||||
EncryptionMode string `json:"encryptionMode"`
|
||||
PassPhrase string `json:"passPhrase"`
|
||||
PublicKey string `json:"publicKey"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type GenerateLoad struct {
|
||||
@@ -39,12 +54,6 @@ type SearchSSHLog struct {
|
||||
Info string `json:"info"`
|
||||
Status string `json:"Status" validate:"required,oneof=Success Failed All"`
|
||||
}
|
||||
type SSHLog struct {
|
||||
Logs []SSHHistory `json:"logs"`
|
||||
TotalCount int `json:"totalCount"`
|
||||
SuccessfulCount int `json:"successfulCount"`
|
||||
FailedCount int `json:"failedCount"`
|
||||
}
|
||||
|
||||
type SSHHistory struct {
|
||||
Date time.Time `json:"date"`
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package model
|
||||
|
||||
type Alert struct {
|
||||
BaseModel
|
||||
|
||||
Title string `gorm:"type:varchar(256);not null" json:"title"`
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
Cycle uint `gorm:"type:integer;not null" json:"cycle"`
|
||||
Count uint `gorm:"type:integer;not null" json:"count"`
|
||||
Project string `gorm:"type:varchar(64)" json:"project"`
|
||||
Status string `gorm:"type:varchar(64);not null" json:"status"`
|
||||
Method string `gorm:"type:varchar(64);not null" json:"method"`
|
||||
SendCount uint `gorm:"type:integer" json:"sendCount"`
|
||||
AdvancedParams string `gorm:"type:longText" json:"advancedParam"`
|
||||
}
|
||||
|
||||
type AlertTask struct {
|
||||
BaseModel
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
Quota string `gorm:"type:varchar(64)" json:"quota"`
|
||||
QuotaType string `gorm:"type:varchar(64)" json:"quotaType"`
|
||||
Method string `gorm:"type:varchar(64);not null;default:'sms'" json:"method"`
|
||||
}
|
||||
|
||||
type AlertLog struct {
|
||||
BaseModel
|
||||
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
Status string `gorm:"type:varchar(64);not null" json:"status"`
|
||||
AlertId uint `gorm:"type:integer;not null" json:"alertId"`
|
||||
AlertDetail string `gorm:"type:varchar(256);not null" json:"alertDetail"`
|
||||
AlertRule string `gorm:"type:varchar(256);not null" json:"alertRule"`
|
||||
Count uint `gorm:"type:integer;not null" json:"count"`
|
||||
Message string `gorm:"type:varchar(256);" json:"message"`
|
||||
RecordId uint `gorm:"type:integer;" json:"recordId"`
|
||||
LicenseId string `gorm:"type:varchar(256);not null;" json:"licenseId" `
|
||||
Method string `gorm:"type:varchar(64);not null;default:'sms'" json:"method"`
|
||||
}
|
||||
|
||||
type AlertConfig struct {
|
||||
BaseModel
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
Title string `gorm:"type:varchar(64);not null" json:"title"`
|
||||
Status string `gorm:"type:varchar(64);not null" json:"status"`
|
||||
Config string `gorm:"type:varchar(256);not null" json:"config"`
|
||||
}
|
||||
|
||||
type LoginLog struct {
|
||||
BaseModel
|
||||
IP string `json:"ip"`
|
||||
Address string `json:"address"`
|
||||
Agent string `json:"agent"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
@@ -11,6 +11,7 @@ type Cronjob struct {
|
||||
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Type string `gorm:"not null" json:"type"`
|
||||
GroupID uint `json:"groupID"`
|
||||
SpecCustom bool `json:"specCustom"`
|
||||
Spec string `gorm:"not null" json:"spec"`
|
||||
|
||||
|
||||
@@ -18,5 +18,6 @@ type Database struct {
|
||||
ClientCert string `json:"clientCert"`
|
||||
SkipVerify bool `json:"skipVerify"`
|
||||
|
||||
Timeout uint `json:"timeout"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
@@ -2,17 +2,19 @@ package model
|
||||
|
||||
type McpServer struct {
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
DockerCompose string `json:"dockerCompose"`
|
||||
Command string `json:"command"`
|
||||
ContainerName string `json:"containerName"`
|
||||
Message string `json:"message"`
|
||||
Port int `json:"port"`
|
||||
Status string `json:"status"`
|
||||
Env string `json:"env"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
SsePath string `json:"ssePath"`
|
||||
WebsiteID int `json:"websiteID"`
|
||||
Dir string `json:"dir"`
|
||||
HostIP string `json:"hostIP"`
|
||||
Name string `json:"name"`
|
||||
DockerCompose string `json:"dockerCompose"`
|
||||
Command string `json:"command"`
|
||||
ContainerName string `json:"containerName"`
|
||||
Message string `json:"message"`
|
||||
Port int `json:"port"`
|
||||
Status string `json:"status"`
|
||||
Env string `json:"env"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
SsePath string `json:"ssePath"`
|
||||
WebsiteID int `json:"websiteID"`
|
||||
Dir string `json:"dir"`
|
||||
HostIP string `json:"hostIP"`
|
||||
StreamableHttpPath string `json:"streamableHttpPath"`
|
||||
OutputTransport string `json:"outputTransport"`
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ type Snapshot struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Version string `json:"version"`
|
||||
Timeout uint `json:"timeout"`
|
||||
|
||||
TaskID string `json:"taskID"`
|
||||
TaskRecoverID string `json:"taskRecoverID"`
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package model
|
||||
|
||||
type RootCert struct {
|
||||
BaseModel
|
||||
Name string `json:"name" gorm:"not null;"`
|
||||
EncryptionMode string `json:"encryptionMode"`
|
||||
PassPhrase string `json:"passPhrase"`
|
||||
PublicKeyPath string `json:"publicKeyPath"`
|
||||
PrivateKeyPath string `json:"privateKeyPath"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
@@ -36,6 +36,9 @@ type WebsiteSSL struct {
|
||||
DisableCNAME bool `json:"disableCNAME"`
|
||||
ExecShell bool `json:"execShell"`
|
||||
Shell string `json:"shell"`
|
||||
MasterSSLID uint `json:"masterSslId"`
|
||||
Nodes string `json:"nodes"`
|
||||
PushNode bool `json:"pushNode"`
|
||||
|
||||
AcmeAccount WebsiteAcmeAccount `json:"acmeAccount" gorm:"-:migration"`
|
||||
DnsAccount WebsiteDnsAccount `json:"dnsAccount" gorm:"-:migration"`
|
||||
|
||||
@@ -0,0 +1,337 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"google.golang.org/genproto/googleapis/type/date"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AlertRepo struct{}
|
||||
|
||||
type IAlertRepo interface {
|
||||
WithByType(alertType string) DBOption
|
||||
WithByStatusIn(status []string) DBOption
|
||||
WithByProject(project string) DBOption
|
||||
WithByCount(count uint) DBOption
|
||||
WithByAlertId(alertId uint) DBOption
|
||||
WithByCreateAt(date *date.Date) DBOption
|
||||
WithByLicenseId(licenseId string) DBOption
|
||||
WithByRecordId(recordId uint) DBOption
|
||||
WithByMethod(method string) DBOption
|
||||
|
||||
Create(alert *model.Alert) error
|
||||
Get(opts ...DBOption) (model.Alert, error)
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.Alert, error)
|
||||
List(opts ...DBOption) ([]model.Alert, error)
|
||||
Delete(opts ...DBOption) error
|
||||
Save(alert *model.Alert) error
|
||||
Update(maps map[string]interface{}, opts ...DBOption) error
|
||||
|
||||
GetLog(opts ...DBOption) (model.AlertLog, error)
|
||||
CreateLog(alertLog *model.AlertLog) error
|
||||
PageLog(limit, offset int, opts ...DBOption) (int64, []model.AlertLog, error)
|
||||
ListLog(opts ...DBOption) ([]model.AlertLog, error)
|
||||
UpdateLog(id uint, maps map[string]interface{}) error
|
||||
BatchUpdateLogBy(maps map[string]interface{}, opts ...DBOption) error
|
||||
DeleteLog(opts ...DBOption) error
|
||||
CleanAlertLogs() error
|
||||
|
||||
CreateAlertTask(alertTaskBase *model.AlertTask) error
|
||||
DeleteAlertTask(opts ...DBOption) error
|
||||
GetAlertTask(opts ...DBOption) (model.AlertTask, error)
|
||||
LoadTaskCount(alertType string, project string, method string) (uint, uint, error)
|
||||
GetTaskLog(alertType string, alertId uint) (time.Time, error)
|
||||
GetLicensePushCount(method string) (uint, error)
|
||||
|
||||
GetConfig(opts ...DBOption) (model.AlertConfig, error)
|
||||
AlertConfigList(opts ...DBOption) ([]model.AlertConfig, error)
|
||||
UpdateAlertConfig(maps map[string]interface{}, opts ...DBOption) error
|
||||
CreateAlertConfig(config *model.AlertConfig) error
|
||||
DeleteAlertConfig(opts ...DBOption) error
|
||||
|
||||
SyncAll(data []model.AlertConfig) error
|
||||
}
|
||||
|
||||
func NewIAlertRepo() IAlertRepo {
|
||||
return &AlertRepo{}
|
||||
}
|
||||
|
||||
func (a *AlertRepo) WithByType(alertType string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("`type` = ?", alertType)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AlertRepo) WithByStatusIn(status []string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("status in (?)", status)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AlertRepo) WithByCount(count uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("count = ?", count)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AlertRepo) WithByProject(project string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("project = ?", project)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AlertRepo) WithByAlertId(alertId uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("alert_id = ?", alertId)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AlertRepo) WithByLicenseId(licenseId string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("license_id = ?", licenseId)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AlertRepo) WithByRecordId(recordId uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("record_id = ?", recordId)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AlertRepo) WithByMethod(method string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("method = ?", method)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AlertRepo) WithByCreateAt(createAt *date.Date) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("DATE(created_at) = DATE(?)", createAt)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AlertRepo) Create(alert *model.Alert) error {
|
||||
return global.AlertDB.Model(&model.Alert{}).Create(alert).Error
|
||||
}
|
||||
|
||||
func (a *AlertRepo) Save(alert *model.Alert) error {
|
||||
return global.AlertDB.Save(alert).Error
|
||||
}
|
||||
|
||||
func (a *AlertRepo) Get(opts ...DBOption) (model.Alert, error) {
|
||||
var alert model.Alert
|
||||
db, _ := getAlertDB(opts...)
|
||||
err := db.First(&alert).Error
|
||||
return alert, err
|
||||
}
|
||||
|
||||
func (a *AlertRepo) Page(page, size int, opts ...DBOption) (int64, []model.Alert, error) {
|
||||
var alerts []model.Alert
|
||||
alertDb, _ := getAlertDB(opts...)
|
||||
db := alertDb.Model(&model.Alert{})
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&alerts).Error
|
||||
return count, alerts, err
|
||||
}
|
||||
|
||||
func (a *AlertRepo) List(opts ...DBOption) ([]model.Alert, error) {
|
||||
var alert []model.Alert
|
||||
db, _ := getAlertDB(opts...)
|
||||
err := db.Find(&alert).Error
|
||||
return alert, err
|
||||
}
|
||||
|
||||
func (a *AlertRepo) Update(maps map[string]interface{}, opts ...DBOption) error {
|
||||
db, _ := getAlertDB(opts...)
|
||||
return db.Model(&model.Alert{}).Updates(maps).Error
|
||||
}
|
||||
|
||||
func (a *AlertRepo) Delete(opts ...DBOption) error {
|
||||
db, _ := getAlertDB(opts...)
|
||||
return db.Delete(&model.Alert{}).Error
|
||||
}
|
||||
|
||||
func (a *AlertRepo) GetLog(opts ...DBOption) (model.AlertLog, error) {
|
||||
var alertLog model.AlertLog
|
||||
db, _ := getAlertDB(opts...)
|
||||
err := db.First(&alertLog).Error
|
||||
return alertLog, err
|
||||
}
|
||||
|
||||
func (a *AlertRepo) CreateLog(log *model.AlertLog) error {
|
||||
return global.AlertDB.Model(&model.AlertLog{}).Create(&log).Error
|
||||
}
|
||||
|
||||
func (a *AlertRepo) UpdateLog(id uint, maps map[string]interface{}) error {
|
||||
return global.AlertDB.Model(&model.AlertLog{}).Where("id = ?", id).Updates(maps).Error
|
||||
}
|
||||
|
||||
func (a *AlertRepo) BatchUpdateLogBy(maps map[string]interface{}, opts ...DBOption) error {
|
||||
db, _ := getAlertDB(opts...)
|
||||
if len(opts) == 0 {
|
||||
db = db.Where("1=1")
|
||||
}
|
||||
return db.Model(&model.AlertLog{}).Updates(&maps).Error
|
||||
}
|
||||
|
||||
func (a *AlertRepo) PageLog(page, size int, opts ...DBOption) (int64, []model.AlertLog, error) {
|
||||
var alerts []model.AlertLog
|
||||
db := global.AlertDB.Model(&model.AlertLog{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
count := int64(0)
|
||||
db = db.Order("created_at desc").Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&alerts).Error
|
||||
return count, alerts, err
|
||||
}
|
||||
|
||||
func (a *AlertRepo) ListLog(opts ...DBOption) ([]model.AlertLog, error) {
|
||||
var alertLog []model.AlertLog
|
||||
db, _ := getAlertDB(opts...)
|
||||
err := db.Find(&alertLog).Error
|
||||
return alertLog, err
|
||||
}
|
||||
|
||||
func (a *AlertRepo) DeleteLog(opts ...DBOption) error {
|
||||
db, _ := getAlertDB(opts...)
|
||||
return db.Delete(&model.AlertLog{}).Error
|
||||
}
|
||||
|
||||
func (a *AlertRepo) CleanAlertLogs() error {
|
||||
return global.AlertDB.Where("1 = 1").Delete(&model.AlertLog{}).Error
|
||||
}
|
||||
|
||||
func (a *AlertRepo) CreateAlertTask(alertTaskBase *model.AlertTask) error {
|
||||
return global.AlertDB.Model(&model.AlertTask{}).Create(&alertTaskBase).Error
|
||||
}
|
||||
|
||||
func (a *AlertRepo) DeleteAlertTask(opts ...DBOption) error {
|
||||
db, _ := getAlertDB(opts...)
|
||||
return db.Delete(&model.AlertTask{}).Error
|
||||
}
|
||||
|
||||
func (a *AlertRepo) GetAlertTask(opts ...DBOption) (model.AlertTask, error) {
|
||||
var data model.AlertTask
|
||||
db, _ := getAlertDB(opts...)
|
||||
err := db.First(&data).Error
|
||||
return data, err
|
||||
}
|
||||
|
||||
func (a *AlertRepo) LoadTaskCount(alertType string, project string, method string) (uint, uint, error) {
|
||||
var (
|
||||
todayCount int64
|
||||
totalCount int64
|
||||
)
|
||||
_ = global.AlertDB.Model(&model.AlertTask{}).Where("type = ? AND quota_type = ? AND method = ?", alertType, project, method).Count(&totalCount).Error
|
||||
|
||||
now := time.Now()
|
||||
todayMidnight := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||
tomorrowMidnight := todayMidnight.Add(24 * time.Hour)
|
||||
err := global.AlertDB.Model(&model.AlertTask{}).Where("type = ? AND quota_type = ? AND method = ? AND created_at > ? AND created_at < ?", alertType, project, method, todayMidnight, tomorrowMidnight).Count(&todayCount).Error
|
||||
return uint(todayCount), uint(totalCount), err
|
||||
}
|
||||
|
||||
func (a *AlertRepo) GetTaskLog(alertType string, alertId uint) (time.Time, error) {
|
||||
var newDate time.Time
|
||||
status := []string{constant.AlertSuccess, constant.AlertPushSuccess, constant.AlertSyncError, constant.AlertPushing}
|
||||
now := time.Now()
|
||||
todayMidnight := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||
tomorrowMidnight := todayMidnight.Add(24 * time.Hour)
|
||||
err := global.AlertDB.Model(&model.AlertLog{}).
|
||||
Where("type = ? AND alert_id = ? AND status in ? AND created_at > ? AND created_at < ?", alertType, alertId, status, todayMidnight, tomorrowMidnight).
|
||||
Order("created_at DESC").
|
||||
Limit(1).
|
||||
Pluck("created_at", &newDate).Error
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
if newDate.IsZero() {
|
||||
return time.Time{}, nil
|
||||
}
|
||||
|
||||
return newDate, nil
|
||||
}
|
||||
|
||||
func getAlertDB(opts ...DBOption) (*gorm.DB, error) {
|
||||
var db *gorm.DB
|
||||
db = global.AlertDB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func (a *AlertRepo) GetLicensePushCount(method string) (uint, error) {
|
||||
var (
|
||||
todayCount int64
|
||||
)
|
||||
now := time.Now()
|
||||
todayMidnight := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||
tomorrowMidnight := todayMidnight.Add(24 * time.Hour)
|
||||
err := global.AlertDB.Model(&model.AlertTask{}).Where("created_at > ? AND created_at < ? AND method = ?", todayMidnight, tomorrowMidnight, method).Count(&todayCount).Error
|
||||
return uint(todayCount), err
|
||||
}
|
||||
|
||||
func (a *AlertRepo) AlertConfigList(opts ...DBOption) ([]model.AlertConfig, error) {
|
||||
var config []model.AlertConfig
|
||||
db, _ := getAlertDB(opts...)
|
||||
err := db.Find(&config).Error
|
||||
return config, err
|
||||
}
|
||||
|
||||
func (a *AlertRepo) UpdateAlertConfig(maps map[string]interface{}, opts ...DBOption) error {
|
||||
db, _ := getAlertDB(opts...)
|
||||
return db.Model(&model.AlertConfig{}).Updates(maps).Error
|
||||
}
|
||||
|
||||
func (a *AlertRepo) CreateAlertConfig(config *model.AlertConfig) error {
|
||||
return global.AlertDB.Model(&model.AlertConfig{}).Create(config).Error
|
||||
}
|
||||
|
||||
func (a *AlertRepo) DeleteAlertConfig(opts ...DBOption) error {
|
||||
db, _ := getAlertDB(opts...)
|
||||
return db.Delete(&model.AlertConfig{}).Error
|
||||
}
|
||||
|
||||
func (a *AlertRepo) GetConfig(opts ...DBOption) (model.AlertConfig, error) {
|
||||
var alertConfig model.AlertConfig
|
||||
db, _ := getAlertDB(opts...)
|
||||
err := db.First(&alertConfig).Error
|
||||
return alertConfig, err
|
||||
}
|
||||
|
||||
func (a *AlertRepo) SyncAll(data []model.AlertConfig) error {
|
||||
tx := global.AlertDB.Begin()
|
||||
var oldConfigs []model.AlertConfig
|
||||
_ = tx.Find(&oldConfigs).Error
|
||||
oldConfigMap := make(map[string]uint)
|
||||
for _, item := range oldConfigs {
|
||||
oldConfigMap[item.Type] = item.ID
|
||||
}
|
||||
for _, item := range data {
|
||||
if val, ok := oldConfigMap[item.Type]; ok {
|
||||
item.ID = val
|
||||
delete(oldConfigMap, item.Type)
|
||||
} else {
|
||||
item.ID = 0
|
||||
}
|
||||
if err := tx.Model(model.AlertConfig{}).Where("id = ?", item.ID).Save(&item).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, val := range oldConfigMap {
|
||||
if err := tx.Where("id = ?", val).Delete(&model.AlertConfig{}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
return nil
|
||||
}
|
||||
+18
-1
@@ -33,6 +33,8 @@ type IAppRepo interface {
|
||||
BatchDelete(ctx context.Context, apps []model.App) error
|
||||
DeleteByIDs(ctx context.Context, ids []uint) error
|
||||
DeleteBy(opts ...DBOption) error
|
||||
|
||||
GetTopRecomment() ([]string, error)
|
||||
}
|
||||
|
||||
func NewIAppRepo() IAppRepo {
|
||||
@@ -44,7 +46,7 @@ func (a AppRepo) WithByLikeName(name string) DBOption {
|
||||
if len(name) == 0 {
|
||||
return g
|
||||
}
|
||||
return g.Where("name like ? or short_desc_zh like ? or short_desc_en like ?", "%"+name+"%", "%"+name+"%", "%"+name+"%")
|
||||
return g.Where("name like ? or description like ? or short_desc_zh like ? or short_desc_en like ?", "%"+name+"%", "%"+name+"%", "%"+name+"%", "%"+name+"%")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +125,21 @@ func (a AppRepo) GetBy(opts ...DBOption) ([]model.App, error) {
|
||||
return apps, nil
|
||||
}
|
||||
|
||||
func (a AppRepo) GetTopRecomment() ([]string, error) {
|
||||
var (
|
||||
apps []model.App
|
||||
names []string
|
||||
)
|
||||
db := getDb().Model(&model.App{})
|
||||
if err := db.Order("recommend asc").Limit(6).Find(&apps).Error; err != nil {
|
||||
return names, err
|
||||
}
|
||||
for _, item := range apps {
|
||||
names = append(names, item.Key)
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func (a AppRepo) BatchCreate(ctx context.Context, apps []model.App) error {
|
||||
return getTx(ctx).Omit(clause.Associations).Create(&apps).Error
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ type AppIgnoreUpgradeRepo struct {
|
||||
|
||||
type IAppIgnoreUpgradeRepo interface {
|
||||
WithScope(scope string) DBOption
|
||||
WithAppID(appID uint) DBOption
|
||||
List(opts ...DBOption) ([]model.AppIgnoreUpgrade, error)
|
||||
Create(appIgnoreUpgrade *model.AppIgnoreUpgrade) error
|
||||
Delete(opts ...DBOption) error
|
||||
@@ -26,6 +27,12 @@ func (a AppIgnoreUpgradeRepo) WithScope(scope string) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (a AppIgnoreUpgradeRepo) WithAppID(appID uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("app_id = ?", appID)
|
||||
}
|
||||
}
|
||||
|
||||
func (a AppIgnoreUpgradeRepo) List(opts ...DBOption) ([]model.AppIgnoreUpgrade, error) {
|
||||
var appIgnoreUpgradeList []model.AppIgnoreUpgrade
|
||||
err := getDb(opts...).Find(&appIgnoreUpgradeList).Error
|
||||
|
||||
@@ -36,6 +36,7 @@ type IAppInstallRepo interface {
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.AppInstall, error)
|
||||
BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error
|
||||
LoadBaseInfo(key string, name string) (*RootInfo, error)
|
||||
LoadInstallAppByKeyAndName(key string, name string) (*model.AppInstall, error)
|
||||
GetFirstByCtx(ctx context.Context, opts ...DBOption) (model.AppInstall, error)
|
||||
}
|
||||
|
||||
@@ -206,17 +207,17 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
|
||||
return nil, err
|
||||
}
|
||||
switch app.Key {
|
||||
case "mysql", "mariadb":
|
||||
case "mysql", "mariadb", constant.AppMysqlCluster:
|
||||
password, ok := envMap["PANEL_DB_ROOT_PASSWORD"].(string)
|
||||
if ok {
|
||||
info.Password = password
|
||||
}
|
||||
case "redis":
|
||||
case "redis", constant.AppRedisCluster:
|
||||
password, ok := envMap["PANEL_REDIS_ROOT_PASSWORD"].(string)
|
||||
if ok {
|
||||
info.Password = password
|
||||
}
|
||||
case "mongodb", constant.AppPostgresql:
|
||||
case "mongodb", constant.AppPostgresql, constant.AppPostgresqlCluster:
|
||||
user, ok := envMap["PANEL_DB_ROOT_USER"].(string)
|
||||
if ok {
|
||||
info.UserName = user
|
||||
@@ -246,3 +247,23 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
|
||||
info.Status = appInstall.Status
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func (a *AppInstallRepo) LoadInstallAppByKeyAndName(key string, name string) (*model.AppInstall, error) {
|
||||
var (
|
||||
app model.App
|
||||
appInstall model.AppInstall
|
||||
)
|
||||
if err := global.DB.Where("key = ?", key).First(&app).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(name) == 0 {
|
||||
if err := global.DB.Where("app_id = ?", app.ID).First(&appInstall).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := global.DB.Where("app_id = ? AND name = ?", app.ID, name).First(&appInstall).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &appInstall, nil
|
||||
}
|
||||
|
||||
@@ -17,8 +17,10 @@ type IAppTagRepo interface {
|
||||
GetByAppId(appId uint) ([]model.AppTag, error)
|
||||
GetByTagIds(tagIds []uint) ([]model.AppTag, error)
|
||||
DeleteBy(ctx context.Context, opts ...DBOption) error
|
||||
GetFirst(ctx context.Context, opts ...DBOption) (*model.AppTag, error)
|
||||
|
||||
WithByTagID(tagID uint) DBOption
|
||||
WithByAppID(appId uint) DBOption
|
||||
}
|
||||
|
||||
func NewIAppTagRepo() IAppTagRepo {
|
||||
@@ -31,6 +33,12 @@ func (a AppTagRepo) WithByTagID(tagID uint) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (a AppTagRepo) WithByAppID(appId uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("app_id = ?", appId)
|
||||
}
|
||||
}
|
||||
|
||||
func (a AppTagRepo) BatchCreate(ctx context.Context, tags []*model.AppTag) error {
|
||||
return getTx(ctx).Create(&tags).Error
|
||||
}
|
||||
@@ -62,3 +70,12 @@ func (a AppTagRepo) GetByTagIds(tagIds []uint) ([]model.AppTag, error) {
|
||||
func (a AppTagRepo) DeleteBy(ctx context.Context, opts ...DBOption) error {
|
||||
return getTx(ctx, opts...).Delete(&model.AppTag{}).Error
|
||||
}
|
||||
|
||||
func (a AppTagRepo) GetFirst(ctx context.Context, opts ...DBOption) (*model.AppTag, error) {
|
||||
var appTag model.AppTag
|
||||
if err := getTx(ctx, opts...).First(&appTag).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &appTag, nil
|
||||
|
||||
}
|
||||
|
||||
@@ -104,6 +104,15 @@ func WithByDate(startTime, endTime time.Time) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func WithByGroups(groupIDs []uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(groupIDs) == 0 {
|
||||
return g
|
||||
}
|
||||
return g.Where("group_id in (?)", groupIDs)
|
||||
}
|
||||
}
|
||||
|
||||
func WithByCreatedAt(startTime, endTime time.Time) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("created_at > ? AND created_at < ?", startTime, endTime)
|
||||
|
||||
@@ -3,6 +3,7 @@ package repo
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
|
||||
)
|
||||
|
||||
type HostRepo struct{}
|
||||
@@ -13,6 +14,14 @@ type IHostRepo interface {
|
||||
SaveFirewallRecord(firewall *model.Firewall) error
|
||||
DeleteFirewallRecordByID(id uint) error
|
||||
DeleteFirewallRecord(fType, port, protocol, address, strategy string) error
|
||||
|
||||
SyncCert(data []model.RootCert) error
|
||||
GetCert(opts ...DBOption) (model.RootCert, error)
|
||||
PageCert(limit, offset int, opts ...DBOption) (int64, []model.RootCert, error)
|
||||
ListCert(opts ...DBOption) ([]model.RootCert, error)
|
||||
CreateCert(cert *model.RootCert) error
|
||||
UpdateCert(id uint, vars map[string]interface{}) error
|
||||
DeleteCert(opts ...DBOption) error
|
||||
}
|
||||
|
||||
func NewIHostRepo() IHostRepo {
|
||||
@@ -63,3 +72,82 @@ func (h *HostRepo) DeleteFirewallRecordByID(id uint) error {
|
||||
func (h *HostRepo) DeleteFirewallRecord(fType, port, protocol, address, strategy string) error {
|
||||
return global.DB.Where("type = ? AND port = ? AND protocol = ? AND address = ? AND strategy = ?", fType, port, protocol, address, strategy).Delete(&model.Firewall{}).Error
|
||||
}
|
||||
|
||||
func (u *HostRepo) GetCert(opts ...DBOption) (model.RootCert, error) {
|
||||
var cert model.RootCert
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.First(&cert).Error
|
||||
return cert, err
|
||||
}
|
||||
|
||||
func (u *HostRepo) PageCert(page, size int, opts ...DBOption) (int64, []model.RootCert, error) {
|
||||
var ops []model.RootCert
|
||||
db := global.DB.Model(&model.RootCert{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&ops).Error
|
||||
return count, ops, err
|
||||
}
|
||||
|
||||
func (u *HostRepo) ListCert(opts ...DBOption) ([]model.RootCert, error) {
|
||||
var ops []model.RootCert
|
||||
db := global.DB.Model(&model.RootCert{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Find(&ops).Error
|
||||
return ops, err
|
||||
}
|
||||
|
||||
func (u *HostRepo) CreateCert(cert *model.RootCert) error {
|
||||
return global.DB.Create(cert).Error
|
||||
}
|
||||
|
||||
func (u *HostRepo) UpdateCert(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.RootCert{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
|
||||
func (u *HostRepo) DeleteCert(opts ...DBOption) error {
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
return db.Delete(&model.RootCert{}).Error
|
||||
}
|
||||
|
||||
func (u *HostRepo) SyncCert(data []model.RootCert) error {
|
||||
tx := global.DB.Begin()
|
||||
var oldCerts []model.RootCert
|
||||
_ = tx.Where("1 = ?", 1).Find(&oldCerts).Error
|
||||
oldCertsMap := make(map[string]uint)
|
||||
for _, item := range oldCerts {
|
||||
oldCertsMap[item.Name] = item.ID
|
||||
}
|
||||
for _, item := range data {
|
||||
if _, ok := oldCertsMap[item.Name]; ok {
|
||||
delete(oldCertsMap, item.Name)
|
||||
continue
|
||||
}
|
||||
item.PassPhrase, _ = encrypt.StringEncrypt("<UN-SET>")
|
||||
if err := tx.Model(model.RootCert{}).Create(&item).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, val := range oldCertsMap {
|
||||
if err := tx.Where("id = ?", val).Delete(&model.RootCert{}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ type IRuntimeRepo interface {
|
||||
WithNotId(id uint) DBOption
|
||||
WithStatus(status string) DBOption
|
||||
WithDetailId(id uint) DBOption
|
||||
WithDetailIdsIn(ids []uint) DBOption
|
||||
WithPort(port int) DBOption
|
||||
WithNormalStatus(status string) DBOption
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error)
|
||||
@@ -55,6 +56,12 @@ func (r *RuntimeRepo) WithDetailId(id uint) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) WithDetailIdsIn(ids []uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("app_detail_id in(?) ", ids)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) WithNotId(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("id != ?", id)
|
||||
|
||||
@@ -19,6 +19,7 @@ type ITagRepo interface {
|
||||
DeleteByID(ctx context.Context, id uint) error
|
||||
Create(ctx context.Context, tag *model.Tag) error
|
||||
Save(ctx context.Context, tag *model.Tag) error
|
||||
GetByKey(key string) (*model.Tag, error)
|
||||
}
|
||||
|
||||
func NewITagRepo() ITagRepo {
|
||||
@@ -41,6 +42,14 @@ func (t TagRepo) All() ([]model.Tag, error) {
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
func (t TagRepo) GetByKey(key string) (*model.Tag, error) {
|
||||
var tag model.Tag
|
||||
if err := getDb().Where("key = ?", key).First(&tag).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tag, nil
|
||||
}
|
||||
|
||||
func (t TagRepo) GetByIds(ids []uint) ([]model.Tag, error) {
|
||||
var tags []model.Tag
|
||||
if err := getDb().Where("id in (?)", ids).Find(&tags).Error; err != nil {
|
||||
|
||||
@@ -19,6 +19,8 @@ type IWebsiteRepo interface {
|
||||
WithRuntimeID(runtimeID uint) DBOption
|
||||
WithParentID(websiteID uint) DBOption
|
||||
WithType(websiteType string) DBOption
|
||||
WithDBType(dbType string) DBOption
|
||||
WithDBID(dbID uint) DBOption
|
||||
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.Website, error)
|
||||
List(opts ...DBOption) ([]model.Website, error)
|
||||
@@ -100,6 +102,18 @@ func (w *WebsiteRepo) WithType(websiteType string) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WebsiteRepo) WithDBType(dbType string) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("db_type = ?", dbType)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WebsiteRepo) WithDBID(dbID uint) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("db_id = ?", dbID)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WebsiteRepo) Page(page, size int, opts ...DBOption) (int64, []model.Website, error) {
|
||||
var websites []model.Website
|
||||
db := getDb(opts...).Model(&model.Website{})
|
||||
|
||||
@@ -17,6 +17,7 @@ type ISSLRepo interface {
|
||||
WithByDnsAccountId(dnsAccountId uint) DBOption
|
||||
WithByCAID(caID uint) DBOption
|
||||
WithByDomain(domain string) DBOption
|
||||
WithByMasterSSLID(sslID uint) DBOption
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error)
|
||||
GetFirst(opts ...DBOption) (*model.WebsiteSSL, error)
|
||||
List(opts ...DBOption) ([]model.WebsiteSSL, error)
|
||||
@@ -52,12 +53,19 @@ func (w WebsiteSSLRepo) WithByCAID(caID uint) DBOption {
|
||||
return db.Where("ca_id = ?", caID)
|
||||
}
|
||||
}
|
||||
|
||||
func (w WebsiteSSLRepo) WithByDomain(domain string) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("primary_domain Like ? or domains Like ?", "%"+domain+"%", "%"+domain+"%")
|
||||
}
|
||||
}
|
||||
|
||||
func (w WebsiteSSLRepo) WithByMasterSSLID(sslID uint) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("master_ssl_id = ?", sslID)
|
||||
}
|
||||
}
|
||||
|
||||
func (w WebsiteSSLRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error) {
|
||||
var sslList []model.WebsiteSSL
|
||||
db := getDb(opts...).Model(&model.WebsiteSSL{})
|
||||
|
||||
@@ -0,0 +1,538 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/copier"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/email"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||
"github.com/shirou/gopsutil/v4/disk"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AlertService struct{}
|
||||
|
||||
type IAlertService interface {
|
||||
PageAlert(req dto.AlertSearch) (int64, []dto.AlertDTO, error)
|
||||
GetAlerts() ([]dto.AlertDTO, error)
|
||||
CreateAlert(create dto.AlertCreate) error
|
||||
UpdateAlert(req dto.AlertUpdate) error
|
||||
DeleteAlert(id uint) error
|
||||
GetAlert(id uint) (dto.AlertDTO, error)
|
||||
UpdateStatus(id uint, status string) error
|
||||
ExternalUpdateAlert(req dto.AlertCreate) error
|
||||
|
||||
GetDisks() ([]dto.DiskDTO, error)
|
||||
PageAlertLogs(req dto.AlertLogSearch) (int64, []dto.AlertLogDTO, error)
|
||||
CleanAlertLogs() error
|
||||
GetClams() ([]dto.ClamDTO, error)
|
||||
GetCronJobs(req dto.CronJobReq) ([]dto.CronJobDTO, error)
|
||||
|
||||
GetAlertConfig() ([]model.AlertConfig, error)
|
||||
UpdateAlertConfig(req dto.AlertConfigUpdate) error
|
||||
DeleteAlertConfig(id uint) error
|
||||
TestAlertConfig(req dto.AlertConfigTest) (bool, error)
|
||||
}
|
||||
|
||||
func NewIAlertService() IAlertService {
|
||||
return &AlertService{}
|
||||
}
|
||||
|
||||
func (a AlertService) PageAlert(search dto.AlertSearch) (int64, []dto.AlertDTO, error) {
|
||||
var (
|
||||
opts []repo.DBOption
|
||||
result []dto.AlertDTO
|
||||
)
|
||||
if search.Status != "" {
|
||||
opts = append(opts, repo.WithByStatus(search.Status))
|
||||
}
|
||||
if search.Type != "" {
|
||||
opts = append(opts, alertRepo.WithByType(search.Type))
|
||||
}
|
||||
opts = append(opts, repo.WithOrderBy("created_at desc"))
|
||||
|
||||
total, alerts, err := alertRepo.Page(search.Page, search.PageSize, opts...)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
for _, item := range alerts {
|
||||
|
||||
result = append(result, dto.AlertDTO{
|
||||
ID: item.ID,
|
||||
Type: item.Type,
|
||||
Cycle: item.Cycle,
|
||||
Count: item.Count,
|
||||
Method: item.Method,
|
||||
Title: item.Title,
|
||||
Project: item.Project,
|
||||
Status: item.Status,
|
||||
SendCount: item.SendCount,
|
||||
AdvancedParams: item.AdvancedParams,
|
||||
CreatedAt: item.CreatedAt,
|
||||
UpdatedAt: item.UpdatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
return total, result, err
|
||||
}
|
||||
|
||||
func (a AlertService) GetAlerts() ([]dto.AlertDTO, error) {
|
||||
var (
|
||||
opts []repo.DBOption
|
||||
result []dto.AlertDTO
|
||||
)
|
||||
opts = append(opts, repo.WithByStatus(constant.AlertEnable))
|
||||
alerts, err := alertRepo.List(opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, item := range alerts {
|
||||
|
||||
result = append(result, dto.AlertDTO{
|
||||
ID: item.ID,
|
||||
Type: item.Type,
|
||||
Cycle: item.Cycle,
|
||||
Count: item.Count,
|
||||
Method: item.Method,
|
||||
Title: item.Title,
|
||||
Project: item.Project,
|
||||
Status: item.Status,
|
||||
SendCount: item.SendCount,
|
||||
AdvancedParams: item.AdvancedParams,
|
||||
CreatedAt: item.CreatedAt,
|
||||
UpdatedAt: item.UpdatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (a AlertService) CreateAlert(create dto.AlertCreate) error {
|
||||
var alertID uint
|
||||
var alertInfo model.Alert
|
||||
if create.Project != "" {
|
||||
alertInfo, _ := alertRepo.Get(alertRepo.WithByType(create.Type), alertRepo.WithByProject(create.Project))
|
||||
alertID = alertInfo.ID
|
||||
} else {
|
||||
alertInfo, _ := alertRepo.Get(alertRepo.WithByType(create.Type))
|
||||
alertID = alertInfo.ID
|
||||
}
|
||||
|
||||
if alertID != 0 {
|
||||
var upAlert dto.AlertUpdate
|
||||
if err := copier.Copy(&upAlert, &create); err != nil {
|
||||
return buserr.WithErr("ErrStructTransform", err)
|
||||
}
|
||||
upAlert.ID = alertID
|
||||
err := a.UpdateAlert(upAlert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
alertInfo.Status = constant.AlertEnable
|
||||
if err := copier.Copy(&alertInfo, &create); err != nil {
|
||||
return buserr.WithErr("ErrStructTransform", err)
|
||||
}
|
||||
|
||||
if err := alertRepo.Create(&alertInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
NewIAlertTaskHelper().InitTask(alertInfo.Type)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AlertService) UpdateAlert(req dto.AlertUpdate) error {
|
||||
|
||||
upMap := make(map[string]interface{})
|
||||
upMap["id"] = req.ID
|
||||
upMap["type"] = req.Type
|
||||
upMap["cycle"] = req.Cycle
|
||||
upMap["count"] = req.Count
|
||||
upMap["method"] = req.Method
|
||||
upMap["title"] = req.Title
|
||||
upMap["project"] = req.Project
|
||||
upMap["status"] = req.Status
|
||||
upMap["send_count"] = req.SendCount
|
||||
upMap["advanced_params"] = req.AdvancedParams
|
||||
|
||||
if err := alertRepo.Update(upMap, repo.WithByID(req.ID)); err != nil {
|
||||
return err
|
||||
}
|
||||
NewIAlertTaskHelper().InitTask(req.Type)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AlertService) DeleteAlert(id uint) error {
|
||||
return alertRepo.Delete(repo.WithByID(id))
|
||||
}
|
||||
|
||||
func (a AlertService) GetAlert(id uint) (dto.AlertDTO, error) {
|
||||
var res dto.AlertDTO
|
||||
alertInfo, err := alertRepo.Get(repo.WithByID(id))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
_ = copier.Copy(&res, &alertInfo)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (a AlertService) UpdateStatus(id uint, status string) error {
|
||||
alertInfo, _ := alertRepo.Get(repo.WithByID(id))
|
||||
if alertInfo.ID == 0 {
|
||||
return buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
err := alertRepo.Update(map[string]interface{}{"status": status}, repo.WithByID(alertInfo.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) GetDisks() ([]dto.DiskDTO, error) {
|
||||
var disks []dto.DiskDTO
|
||||
excludes := map[string]struct{}{
|
||||
"/mnt/cdrom": {}, "/boot": {}, "/boot/efi": {}, "/dev": {}, "/dev/shm": {},
|
||||
"/run/lock": {}, "/run": {}, "/run/shm": {}, "/run/user": {},
|
||||
}
|
||||
stdout, err := executeDiskCommand()
|
||||
if err != nil {
|
||||
return disks, nil
|
||||
}
|
||||
|
||||
lines := strings.Split(stdout, "\n")
|
||||
var mounts []dto.AlertDiskInfo
|
||||
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 7 {
|
||||
continue
|
||||
}
|
||||
mountPoint := strings.Join(fields[6:], " ")
|
||||
if shouldExclude(fields, mountPoint, excludes) {
|
||||
continue
|
||||
}
|
||||
mounts = append(mounts, dto.AlertDiskInfo{Type: fields[1], Device: fields[0], Mount: mountPoint})
|
||||
|
||||
}
|
||||
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
mu sync.Mutex
|
||||
)
|
||||
wg.Add(len(mounts))
|
||||
for i := 0; i < len(mounts); i++ {
|
||||
go func(timeoutCh <-chan time.Time, mount dto.AlertDiskInfo) {
|
||||
defer wg.Done()
|
||||
|
||||
var itemData dto.DiskDTO
|
||||
itemData.Path = mount.Mount
|
||||
itemData.Type = mount.Type
|
||||
itemData.Device = mount.Device
|
||||
select {
|
||||
case <-timeoutCh:
|
||||
mu.Lock()
|
||||
disks = append(disks, itemData)
|
||||
mu.Unlock()
|
||||
global.LOG.Errorf("load disk info from %s failed, err: timeout", mount.Mount)
|
||||
default:
|
||||
state, err := disk.Usage(mount.Mount)
|
||||
if err != nil {
|
||||
mu.Lock()
|
||||
disks = append(disks, itemData)
|
||||
mu.Unlock()
|
||||
global.LOG.Errorf("load disk info from %s failed, err: %v", mount.Mount, err)
|
||||
return
|
||||
}
|
||||
itemData.Total = state.Total
|
||||
itemData.Free = state.Free
|
||||
itemData.Used = state.Used
|
||||
itemData.UsedPercent = state.UsedPercent
|
||||
itemData.InodesTotal = state.InodesTotal
|
||||
itemData.InodesUsed = state.InodesUsed
|
||||
itemData.InodesFree = state.InodesFree
|
||||
itemData.InodesUsedPercent = state.InodesUsedPercent
|
||||
mu.Lock()
|
||||
disks = append(disks, itemData)
|
||||
mu.Unlock()
|
||||
}
|
||||
}(time.After(5*time.Second), mounts[i])
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
sort.Slice(disks, func(i, j int) bool {
|
||||
return disks[i].Path < disks[j].Path
|
||||
})
|
||||
return disks, nil
|
||||
}
|
||||
|
||||
func executeDiskCommand() (string, error) {
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(2 * time.Second))
|
||||
stdout, err := cmdMgr.RunWithStdoutBashC("df -hT -P | grep '/' | grep -v tmpfs | grep -v 'snap/core' | grep -v udev")
|
||||
if err != nil {
|
||||
cmdMgr2 := cmd.NewCommandMgr(cmd.WithTimeout(1 * time.Second))
|
||||
stdout, err = cmdMgr2.RunWithStdoutBashC("df -lhT -P | grep '/' | grep -v tmpfs | grep -v 'snap/core' | grep -v udev")
|
||||
}
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func shouldExclude(fields []string, mountPoint string, excludes map[string]struct{}) bool {
|
||||
if strings.HasPrefix(mountPoint, "/snap") || len(strings.Split(mountPoint, "/")) > 10 {
|
||||
return true
|
||||
}
|
||||
if strings.TrimSpace(fields[1]) == "tmpfs" {
|
||||
return true
|
||||
}
|
||||
if strings.Contains(fields[2], "K") {
|
||||
return true
|
||||
}
|
||||
if strings.Contains(mountPoint, "docker") {
|
||||
return true
|
||||
}
|
||||
_, excluded := excludes[mountPoint]
|
||||
return excluded
|
||||
}
|
||||
|
||||
func (a AlertService) PageAlertLogs(search dto.AlertLogSearch) (int64, []dto.AlertLogDTO, error) {
|
||||
var (
|
||||
opts []repo.DBOption
|
||||
result []dto.AlertLogDTO
|
||||
)
|
||||
if search.Status != "" {
|
||||
opts = append(opts, repo.WithByStatus(search.Status))
|
||||
}
|
||||
if search.Count != 0 {
|
||||
opts = append(opts, alertRepo.WithByCount(search.Count))
|
||||
}
|
||||
opts = append(opts, repo.WithOrderBy("created_at desc"))
|
||||
|
||||
total, alerts, err := alertRepo.PageLog(search.Page, search.PageSize, opts...)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
for _, item := range alerts {
|
||||
alertLogDTO, err := a.parseAlertLog(item)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
result = append(result, alertLogDTO)
|
||||
}
|
||||
|
||||
return total, result, err
|
||||
}
|
||||
|
||||
func (a AlertService) parseAlertLog(item model.AlertLog) (dto.AlertLogDTO, error) {
|
||||
var alertDetail dto.AlertDetail
|
||||
var alertRule dto.AlertRule
|
||||
|
||||
if err := unmarshalAlertInfo(item.AlertDetail, &alertDetail); err != nil {
|
||||
return dto.AlertLogDTO{}, err
|
||||
}
|
||||
if err := unmarshalAlertInfo(item.AlertRule, &alertRule); err != nil {
|
||||
return dto.AlertLogDTO{}, err
|
||||
}
|
||||
return dto.AlertLogDTO{
|
||||
ID: item.ID,
|
||||
Count: item.Count,
|
||||
Type: item.Type,
|
||||
Status: item.Status,
|
||||
Method: item.Method,
|
||||
Message: item.Message,
|
||||
AlertId: item.AlertId,
|
||||
AlertDetail: alertDetail,
|
||||
AlertRule: alertRule,
|
||||
CreatedAt: item.CreatedAt,
|
||||
UpdatedAt: item.UpdatedAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func unmarshalAlertInfo(data string, v interface{}) error {
|
||||
if err := json.Unmarshal([]byte(data), v); err != nil {
|
||||
return fmt.Errorf("unmarshal alert info vars failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AlertService) CleanAlertLogs() error {
|
||||
return alertRepo.CleanAlertLogs()
|
||||
}
|
||||
|
||||
func (a AlertService) GetClams() ([]dto.ClamDTO, error) {
|
||||
var clams []dto.ClamDTO
|
||||
clamList, err := clamRepo.List()
|
||||
|
||||
for _, clam := range clamList {
|
||||
var clamDTO dto.ClamDTO
|
||||
clamDTO.ID = clam.ID
|
||||
clamDTO.Name = clam.Name
|
||||
clamDTO.Path = clam.Path
|
||||
clamDTO.Status = clam.Status
|
||||
clamDTO.UpdatedAt = clam.UpdatedAt
|
||||
clamDTO.CreatedAt = clam.CreatedAt
|
||||
clams = append(clams, clamDTO)
|
||||
}
|
||||
return clams, err
|
||||
}
|
||||
|
||||
func (a AlertService) GetCronJobs(req dto.CronJobReq) ([]dto.CronJobDTO, error) {
|
||||
var cronJobs []dto.CronJobDTO
|
||||
var (
|
||||
opts []repo.DBOption
|
||||
)
|
||||
if req.Status != "" {
|
||||
opts = append(opts, repo.WithByStatus(req.Status))
|
||||
}
|
||||
if req.Type != "" {
|
||||
opts = append(opts, repo.WithByType(req.Type))
|
||||
}
|
||||
cronjobList, err := cronjobRepo.List(opts...)
|
||||
|
||||
for _, cronJob := range cronjobList {
|
||||
var cronJobDTO dto.CronJobDTO
|
||||
cronJobDTO.ID = cronJob.ID
|
||||
cronJobDTO.Name = cronJob.Name
|
||||
cronJobDTO.Status = cronJob.Status
|
||||
cronJobDTO.Type = cronJob.Type
|
||||
cronJobDTO.UpdatedAt = cronJob.UpdatedAt
|
||||
cronJobDTO.CreatedAt = cronJob.CreatedAt
|
||||
cronJobs = append(cronJobs, cronJobDTO)
|
||||
}
|
||||
return cronJobs, err
|
||||
}
|
||||
|
||||
func (a AlertService) GetAlertConfig() ([]model.AlertConfig, error) {
|
||||
var (
|
||||
opts []repo.DBOption
|
||||
configs []model.AlertConfig
|
||||
)
|
||||
opts = append(opts, repo.WithByStatus(constant.AlertEnable))
|
||||
configs, err := alertRepo.AlertConfigList(opts...)
|
||||
return configs, err
|
||||
}
|
||||
|
||||
func (a AlertService) UpdateAlertConfig(req dto.AlertConfigUpdate) error {
|
||||
if req.ID != 0 {
|
||||
upMap := make(map[string]interface{})
|
||||
upMap["id"] = req.ID
|
||||
upMap["type"] = req.Type
|
||||
upMap["title"] = req.Title
|
||||
upMap["status"] = req.Status
|
||||
upMap["config"] = req.Config
|
||||
if err := alertRepo.UpdateAlertConfig(upMap, repo.WithByID(req.ID)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
var alertConfig model.AlertConfig
|
||||
if err := copier.Copy(&alertConfig, &req); err != nil {
|
||||
return buserr.WithErr("ErrStructTransform", err)
|
||||
}
|
||||
if err := alertRepo.CreateAlertConfig(&alertConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AlertService) DeleteAlertConfig(id uint) error {
|
||||
return alertRepo.DeleteAlertConfig(repo.WithByID(id))
|
||||
}
|
||||
|
||||
func (a AlertService) TestAlertConfig(req dto.AlertConfigTest) (bool, error) {
|
||||
cfg := email.SMTPConfig{
|
||||
Host: req.Host,
|
||||
Port: req.Port,
|
||||
Username: req.Sender,
|
||||
Password: req.Password,
|
||||
From: fmt.Sprintf("%s <%s>", req.DisplayName, req.Sender),
|
||||
Encryption: req.Encryption,
|
||||
Recipient: req.Recipient,
|
||||
}
|
||||
|
||||
msg := email.EmailMessage{
|
||||
Subject: i18n.GetMsgByKey("TestAlertTitle"),
|
||||
Body: i18n.GetMsgByKey("TestAlert"),
|
||||
IsHTML: false,
|
||||
}
|
||||
transport := xpack.LoadRequestTransport()
|
||||
if err := email.SendMail(cfg, msg, transport); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (a AlertService) ExternalUpdateAlert(updateAlert dto.AlertCreate) error {
|
||||
upMap := make(map[string]interface{})
|
||||
var newStatus string
|
||||
if updateAlert.SendCount == 0 {
|
||||
newStatus = constant.AlertDisable
|
||||
} else {
|
||||
newStatus = constant.AlertEnable
|
||||
upMap["send_count"] = updateAlert.SendCount
|
||||
if updateAlert.Method != "" {
|
||||
upMap["method"] = updateAlert.Method
|
||||
}
|
||||
}
|
||||
upMap["status"] = newStatus
|
||||
|
||||
alertInfo, _ := alertRepo.Get(
|
||||
alertRepo.WithByType(updateAlert.Type),
|
||||
alertRepo.WithByProject(updateAlert.Project),
|
||||
)
|
||||
|
||||
if alertInfo.ID > 0 {
|
||||
shouldUpdate := false
|
||||
|
||||
if alertInfo.Status != newStatus {
|
||||
shouldUpdate = true
|
||||
}
|
||||
if val, ok := upMap["send_count"]; ok && val != alertInfo.SendCount {
|
||||
shouldUpdate = true
|
||||
}
|
||||
if val, ok := upMap["method"]; ok && val != "" && val != alertInfo.Method {
|
||||
shouldUpdate = true
|
||||
}
|
||||
|
||||
if shouldUpdate {
|
||||
if err := alertRepo.Update(
|
||||
upMap,
|
||||
alertRepo.WithByProject(updateAlert.Project),
|
||||
alertRepo.WithByType(updateAlert.Type),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if updateAlert.Method != "" && updateAlert.Title != "" {
|
||||
updateAlert.Status = newStatus
|
||||
if err := a.CreateAlert(updateAlert); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,889 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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"
|
||||
alertUtil "github.com/1Panel-dev/1Panel/agent/utils/alert"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
versionUtil "github.com/1Panel-dev/1Panel/agent/utils/version"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||
"github.com/shirou/gopsutil/v4/cpu"
|
||||
"github.com/shirou/gopsutil/v4/disk"
|
||||
"github.com/shirou/gopsutil/v4/load"
|
||||
"github.com/shirou/gopsutil/v4/mem"
|
||||
"github.com/shirou/gopsutil/v4/net"
|
||||
"math"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
ResourceAlertInterval = 30
|
||||
CheckIntervalSec = 3
|
||||
LoadCheckIntervalMin = 5
|
||||
)
|
||||
|
||||
type AlertTaskHelper struct {
|
||||
DiskIO chan []disk.IOCountersStat
|
||||
NetIO chan []net.IOCountersStat
|
||||
}
|
||||
|
||||
type IAlertTaskHelper interface {
|
||||
StopTask()
|
||||
StartTask()
|
||||
ResetTask()
|
||||
InitTask(alertType string)
|
||||
}
|
||||
|
||||
var cpuLoad1, cpuLoad5, cpuLoad15 []float64
|
||||
var memoryLoad1, memoryLoad5, memoryLoad15 []float64
|
||||
|
||||
var baseTypes = map[string]bool{"ssl": true, "siteEndTime": true, "panelPwdEndTime": true, "panelUpdate": true}
|
||||
var resourceTypes = map[string]bool{"cpu": true, "memory": true, "disk": true, "load": true, "panelLogin": true, "sshLogin": true, "nodeException": true, "licenseException": true}
|
||||
|
||||
func NewIAlertTaskHelper() IAlertTaskHelper {
|
||||
return &AlertTaskHelper{
|
||||
DiskIO: make(chan []disk.IOCountersStat, 1),
|
||||
NetIO: make(chan []net.IOCountersStat, 1),
|
||||
}
|
||||
}
|
||||
func (m *AlertTaskHelper) StartTask() {
|
||||
baseAlert, resourceAlert := m.getClassifiedAlerts()
|
||||
if len(baseAlert) == 0 && len(resourceAlert) == 0 {
|
||||
return
|
||||
}
|
||||
handleBaseAlerts(baseAlert)
|
||||
handleResourceAlerts(resourceAlert)
|
||||
}
|
||||
|
||||
func (m *AlertTaskHelper) StopTask() {
|
||||
stopBaseJob()
|
||||
stopResourceJob()
|
||||
}
|
||||
|
||||
func (m *AlertTaskHelper) ResetTask() {
|
||||
m.StopTask()
|
||||
m.StartTask()
|
||||
}
|
||||
|
||||
func (m *AlertTaskHelper) InitTask(alertType string) {
|
||||
resetAlertState(alertType)
|
||||
if baseTypes[alertType] {
|
||||
stopBaseJob()
|
||||
} else if resourceTypes[alertType] {
|
||||
stopResourceJob()
|
||||
}
|
||||
m.StartTask()
|
||||
}
|
||||
|
||||
func resetAlertState(alertType string) {
|
||||
switch alertType {
|
||||
case "cpu":
|
||||
cpuLoad1 = []float64{}
|
||||
cpuLoad5 = []float64{}
|
||||
cpuLoad15 = []float64{}
|
||||
case "memory":
|
||||
memoryLoad1 = []float64{}
|
||||
memoryLoad5 = []float64{}
|
||||
memoryLoad15 = []float64{}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *AlertTaskHelper) getClassifiedAlerts() (baseAlerts, resourceAlerts []dto.AlertDTO) {
|
||||
alertList, _ := NewIAlertService().GetAlerts()
|
||||
for _, alert := range alertList {
|
||||
if baseTypes[alert.Type] {
|
||||
baseAlerts = append(baseAlerts, alert)
|
||||
} else if resourceTypes[alert.Type] {
|
||||
resourceAlerts = append(resourceAlerts, alert)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func handleBaseAlerts(baseAlerts []dto.AlertDTO) {
|
||||
if len(baseAlerts) == 0 {
|
||||
stopResourceJob()
|
||||
return
|
||||
}
|
||||
if global.AlertBaseJobID == 0 {
|
||||
baseTask(baseAlerts)
|
||||
jobID, err := global.Cron.AddFunc("*/30 * * * *", func() {
|
||||
baseTask(baseAlerts)
|
||||
})
|
||||
if err != nil {
|
||||
global.LOG.Errorf("alert base job start failed: %v", err)
|
||||
return
|
||||
}
|
||||
global.AlertBaseJobID = jobID
|
||||
global.LOG.Info("start alert base job")
|
||||
}
|
||||
}
|
||||
|
||||
func handleResourceAlerts(resourceAlerts []dto.AlertDTO) {
|
||||
if len(resourceAlerts) == 0 {
|
||||
stopResourceJob()
|
||||
return
|
||||
}
|
||||
if global.AlertResourceJobID == 0 {
|
||||
jobID, err := global.Cron.AddFunc("*/1 * * * *", func() {
|
||||
resourceTask(resourceAlerts)
|
||||
})
|
||||
if err != nil {
|
||||
global.LOG.Errorf("alert resource job start failed: %v", err)
|
||||
return
|
||||
}
|
||||
global.AlertResourceJobID = jobID
|
||||
global.LOG.Info("start alert resource job")
|
||||
}
|
||||
}
|
||||
|
||||
func stopBaseJob() {
|
||||
if global.AlertBaseJobID != 0 {
|
||||
global.Cron.Remove(global.AlertBaseJobID)
|
||||
global.AlertBaseJobID = 0
|
||||
global.LOG.Info("stop alert base job")
|
||||
}
|
||||
}
|
||||
|
||||
func stopResourceJob() {
|
||||
if global.AlertResourceJobID != 0 {
|
||||
global.Cron.Remove(global.AlertResourceJobID)
|
||||
global.AlertResourceJobID = 0
|
||||
global.LOG.Info("stop alert resource job")
|
||||
}
|
||||
}
|
||||
|
||||
func baseTask(baseAlert []dto.AlertDTO) {
|
||||
for _, alert := range baseAlert {
|
||||
if !alertUtil.CheckSendTimeRange(alert.Type) {
|
||||
continue
|
||||
}
|
||||
switch alert.Type {
|
||||
case "ssl":
|
||||
loadSSLInfo(alert)
|
||||
case "siteEndTime":
|
||||
loadWebsiteInfo(alert)
|
||||
case "panelPwdEndTime":
|
||||
if global.IsMaster {
|
||||
loadPanelPwd(alert)
|
||||
}
|
||||
case "panelUpdate":
|
||||
if global.IsMaster {
|
||||
loadPanelUpdate(alert)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func resourceTask(resourceAlert []dto.AlertDTO) {
|
||||
minute := time.Now().Minute()
|
||||
for _, alert := range resourceAlert {
|
||||
if !alertUtil.CheckSendTimeRange(alert.Type) {
|
||||
continue
|
||||
}
|
||||
execute := minute%LoadCheckIntervalMin == 0
|
||||
switch alert.Type {
|
||||
case "cpu":
|
||||
loadCPUUsage(alert)
|
||||
case "memory":
|
||||
loadMemUsage(alert)
|
||||
case "load":
|
||||
loadLoadInfo(alert)
|
||||
case "disk":
|
||||
loadDiskUsage(alert)
|
||||
case "panelLogin":
|
||||
loadPanelLogin(alert)
|
||||
case "sshLogin":
|
||||
loadSSHLogin(alert)
|
||||
case "nodeException":
|
||||
if execute && global.IsMaster {
|
||||
loadNodeException(alert)
|
||||
}
|
||||
case "licenseException":
|
||||
if execute && global.IsMaster {
|
||||
loadLicenseException(alert)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadSSLInfo(alert dto.AlertDTO) {
|
||||
opts := getRepoOptionsByProject(alert.Project)
|
||||
sslList, _ := repo.NewISSLRepo().List(opts...)
|
||||
if len(sslList) == 0 {
|
||||
return
|
||||
}
|
||||
daysDiffMap, projectMap := calculateSSLExpiryDays(sslList, alert.Cycle)
|
||||
projectJSON := serializeAndSortProjects(projectMap)
|
||||
if projectJSON == "" || len(daysDiffMap) == 0 {
|
||||
return
|
||||
}
|
||||
sender := NewAlertSender(alert, projectJSON)
|
||||
for daysDiff, domains := range daysDiffMap {
|
||||
domainStr := strings.Join(domains, ",")
|
||||
params := createAlertBaseParams(strconv.Itoa(len(domains)), strconv.Itoa(daysDiff))
|
||||
sender.Send(domainStr, params)
|
||||
}
|
||||
}
|
||||
|
||||
func loadWebsiteInfo(alert dto.AlertDTO) {
|
||||
opts := getRepoOptionsByProject(alert.Project)
|
||||
websiteList, _ := websiteRepo.List(opts...)
|
||||
if len(websiteList) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
daysDiffMap, projectMap := calculateWebsiteExpiryDays(websiteList, alert.Cycle)
|
||||
projectJSON := serializeAndSortProjects(projectMap)
|
||||
if projectJSON == "" || len(daysDiffMap) == 0 {
|
||||
return
|
||||
}
|
||||
sender := NewAlertSender(alert, projectJSON)
|
||||
for daysDiff, domains := range daysDiffMap {
|
||||
domainStr := strings.Join(domains, ",")
|
||||
params := createAlertBaseParams(strconv.Itoa(len(domains)), strconv.Itoa(daysDiff))
|
||||
sender.Send(domainStr, params)
|
||||
}
|
||||
}
|
||||
|
||||
func loadPanelPwd(alert dto.AlertDTO) {
|
||||
// only master alert
|
||||
expDays, err := getSettingValue("ExpirationDays")
|
||||
if err != nil || expDays == "0" {
|
||||
global.LOG.Info("panel password expiration setting not enabled, skip")
|
||||
return
|
||||
}
|
||||
|
||||
expTimeStr, err := getSettingValue("ExpirationTime")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
expTime, _ := time.Parse(constant.DateTimeLayout, expTimeStr)
|
||||
daysDiff := calculateDaysDifference(expTime)
|
||||
if daysDiff >= 0 && int(alert.Cycle) >= daysDiff {
|
||||
params := createAlertPwdParams(strconv.Itoa(daysDiff))
|
||||
sender := NewAlertSender(alert, expTimeStr)
|
||||
sender.Send(strconv.Itoa(daysDiff), params)
|
||||
}
|
||||
}
|
||||
|
||||
func loadPanelUpdate(alert dto.AlertDTO) {
|
||||
// only master alert
|
||||
info, err := versionUtil.GetUpgradeVersionInfo()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("error getting version info: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
version := getValidVersion(info)
|
||||
if version == "" {
|
||||
return
|
||||
}
|
||||
|
||||
sender := NewAlertSender(alert, version)
|
||||
sender.Send(version, []dto.Param{})
|
||||
}
|
||||
|
||||
// 获取 CPU 使用率数据并发送到通道
|
||||
func loadCPUUsage(alert dto.AlertDTO) {
|
||||
percent, err := cpu.Percent(time.Duration(CheckIntervalSec)*time.Second, false)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("error getting cpu usage, err: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(percent) > 0 {
|
||||
var usageLoad *[]float64
|
||||
var threshold int
|
||||
|
||||
switch alert.Cycle {
|
||||
case 1:
|
||||
usageLoad = &cpuLoad1
|
||||
threshold = 1
|
||||
case 5:
|
||||
usageLoad = &cpuLoad5
|
||||
threshold = 5
|
||||
case 15:
|
||||
usageLoad = &cpuLoad15
|
||||
threshold = 15
|
||||
}
|
||||
shouldSendResourceAlert(alert, percent[0], usageLoad, threshold)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取内存使用情况数据并发送到通道
|
||||
func loadMemUsage(alert dto.AlertDTO) {
|
||||
memStat, err := mem.VirtualMemory()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("error getting memory usage, err: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
percent := memStat.UsedPercent
|
||||
var memoryLoad *[]float64
|
||||
var threshold int
|
||||
|
||||
switch alert.Cycle {
|
||||
case 1:
|
||||
memoryLoad = &memoryLoad1
|
||||
threshold = 1
|
||||
case 5:
|
||||
memoryLoad = &memoryLoad5
|
||||
threshold = 5
|
||||
case 15:
|
||||
memoryLoad = &memoryLoad15
|
||||
threshold = 15
|
||||
}
|
||||
shouldSendResourceAlert(alert, percent, memoryLoad, threshold)
|
||||
}
|
||||
|
||||
// 获取系统负载数据并发送到通道
|
||||
func loadLoadInfo(alert dto.AlertDTO) {
|
||||
avgStat, err := load.Avg()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("error getting load usage, err: %v", err)
|
||||
return
|
||||
}
|
||||
var loadValue float64
|
||||
CPUTotal, _ := cpu.Counts(true)
|
||||
switch alert.Cycle {
|
||||
case 1:
|
||||
loadValue = avgStat.Load1 / (float64(CPUTotal*2) * 0.75) * 100
|
||||
case 5:
|
||||
loadValue = avgStat.Load5 / (float64(CPUTotal*2) * 0.75) * 100
|
||||
case 15:
|
||||
loadValue = avgStat.Load15 / (float64(CPUTotal*2) * 0.75) * 100
|
||||
default:
|
||||
return
|
||||
}
|
||||
if loadValue < float64(alert.Count) {
|
||||
return
|
||||
}
|
||||
newDate, err := alertRepo.GetTaskLog(alert.Type, alert.ID)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("task log record not found, err: %v", err)
|
||||
}
|
||||
if isAlertDue(newDate) {
|
||||
sendResourceAlert(alert, loadValue)
|
||||
}
|
||||
}
|
||||
|
||||
func loadDiskUsage(alert dto.AlertDTO) {
|
||||
newDate, err := alertRepo.GetTaskLog(alert.Type, alert.ID)
|
||||
if err != nil || !isAlertDue(newDate) {
|
||||
global.LOG.Errorf("record not found, err: %v", err)
|
||||
return
|
||||
}
|
||||
if strings.Contains(alert.Project, "all") {
|
||||
err = processAllDisks(alert)
|
||||
} else {
|
||||
err = processSingleDisk(alert)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func loadPanelLogin(alert dto.AlertDTO) {
|
||||
count, isAlert, err := alertUtil.CountRecentFailedLoginLogs(alert.Cycle, alert.Count)
|
||||
alertType := alert.Type
|
||||
quota := strconv.Itoa(count)
|
||||
quotaType := strconv.Itoa(int(alert.Cycle))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("Failed to count recent failed login logs: %v", err)
|
||||
}
|
||||
if isAlert {
|
||||
alertType = "panelLogin"
|
||||
quota = strconv.Itoa(count)
|
||||
quotaType = "panelLogin"
|
||||
params := []dto.Param{
|
||||
{
|
||||
Index: "1",
|
||||
Key: "cycle",
|
||||
Value: "",
|
||||
},
|
||||
{
|
||||
Index: "2",
|
||||
Key: "project",
|
||||
Value: "",
|
||||
},
|
||||
}
|
||||
sendAlerts(alert, alertType, quota, quotaType, params)
|
||||
}
|
||||
|
||||
whitelist := strings.Split(strings.TrimSpace(alert.AdvancedParams), "\n")
|
||||
records, err := alertUtil.FindRecentSuccessLoginsNotInWhitelist(30, whitelist)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("Failed to check recent failed ip login logs: %v", err)
|
||||
}
|
||||
if len(records) > 0 {
|
||||
quota = strings.Join(func() []string {
|
||||
var ips []string
|
||||
for _, r := range records {
|
||||
ips = append(ips, r.IP)
|
||||
}
|
||||
return ips
|
||||
}(), "\n")
|
||||
alertType = "panelIpLogin"
|
||||
quotaType = "panelIpLogin"
|
||||
params := []dto.Param{
|
||||
{
|
||||
Index: "1",
|
||||
Key: "cycle",
|
||||
Value: "",
|
||||
},
|
||||
{
|
||||
Index: "2",
|
||||
Key: "project",
|
||||
Value: " IP ",
|
||||
},
|
||||
}
|
||||
sendAlerts(alert, alertType, quota, quotaType, params)
|
||||
}
|
||||
}
|
||||
|
||||
func loadSSHLogin(alert dto.AlertDTO) {
|
||||
count, isAlert, err := alertUtil.CountRecentFailedSSHLog(alert.Cycle, alert.Count)
|
||||
alertType := alert.Type
|
||||
quota := strconv.Itoa(count)
|
||||
quotaType := strconv.Itoa(int(alert.Cycle))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("Failed to count recent failed ssh login logs: %v", err)
|
||||
}
|
||||
if isAlert {
|
||||
alertType = "sshLogin"
|
||||
quota = strconv.Itoa(count)
|
||||
quotaType = "sshLogin"
|
||||
params := []dto.Param{
|
||||
{
|
||||
Index: "1",
|
||||
Key: "cycle",
|
||||
Value: " SSH ",
|
||||
},
|
||||
{
|
||||
Index: "2",
|
||||
Key: "project",
|
||||
Value: "",
|
||||
},
|
||||
}
|
||||
sendAlerts(alert, alertType, quota, quotaType, params)
|
||||
}
|
||||
whitelist := strings.Split(strings.TrimSpace(alert.AdvancedParams), "\n")
|
||||
records, err := alertUtil.FindRecentSuccessLoginNotInWhitelist(30, whitelist)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("Failed to check recent failed ip ssh login logs: %v", err)
|
||||
}
|
||||
if len(records) > 0 {
|
||||
quota = strings.Join(records, "\n")
|
||||
alertType = "sshIpLogin"
|
||||
quotaType = "sshIpLogin"
|
||||
params := []dto.Param{
|
||||
{
|
||||
Index: "1",
|
||||
Key: "cycle",
|
||||
Value: " SSH ",
|
||||
},
|
||||
{
|
||||
Index: "2",
|
||||
Key: "project",
|
||||
Value: " IP ",
|
||||
},
|
||||
}
|
||||
sendAlerts(alert, alertType, quota, quotaType, params)
|
||||
}
|
||||
}
|
||||
|
||||
func loadNodeException(alert dto.AlertDTO) {
|
||||
// only master alert
|
||||
failCount, err := xpack.GetNodeErrorAlert()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("error getting node, err: %s", err)
|
||||
return
|
||||
}
|
||||
if failCount > 0 {
|
||||
quotaType := "node-error"
|
||||
params := []dto.Param{
|
||||
{
|
||||
Index: "1",
|
||||
Key: "cycle",
|
||||
Value: strconv.Itoa(int(failCount)),
|
||||
},
|
||||
}
|
||||
newDate, err := alertRepo.GetTaskLog(alert.Type, alert.ID)
|
||||
if err != nil || !isAlertDue(newDate) {
|
||||
global.LOG.Errorf("record not found, err: %v", err)
|
||||
return
|
||||
}
|
||||
sender := NewAlertSender(alert, quotaType)
|
||||
sender.ResourceSend(strconv.Itoa(int(failCount)), params)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func loadLicenseException(alert dto.AlertDTO) {
|
||||
// only master alert
|
||||
failCount, err := xpack.GetLicenseErrorAlert()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("error getting license, err: %s", err)
|
||||
return
|
||||
}
|
||||
if failCount > 0 {
|
||||
quotaType := "license-error"
|
||||
params := []dto.Param{
|
||||
{
|
||||
Index: "1",
|
||||
Key: "cycle",
|
||||
Value: strconv.Itoa(int(failCount)),
|
||||
},
|
||||
}
|
||||
newDate, err := alertRepo.GetTaskLog(alert.Type, alert.ID)
|
||||
if err != nil || !isAlertDue(newDate) {
|
||||
global.LOG.Errorf("record not found, err: %v", err)
|
||||
return
|
||||
}
|
||||
sender := NewAlertSender(alert, quotaType)
|
||||
sender.ResourceSend(strconv.Itoa(int(failCount)), params)
|
||||
}
|
||||
}
|
||||
|
||||
func sendAlerts(alert dto.AlertDTO, alertType, quota, quotaType string, params []dto.Param) {
|
||||
methods := strings.Split(alert.Method, ",")
|
||||
newDate, err := alertRepo.GetTaskLog(alertType, alert.ID)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("task log record not found, err: %v", err)
|
||||
}
|
||||
if newDate.IsZero() || calculateMinutesDifference(newDate) > ResourceAlertInterval {
|
||||
for _, m := range methods {
|
||||
m = strings.TrimSpace(m)
|
||||
switch m {
|
||||
case constant.SMS:
|
||||
if !alertUtil.CheckSMSSendLimit(constant.SMS) {
|
||||
continue
|
||||
}
|
||||
todayCount, isValid := canSendAlertToday(alertType, quotaType, alert.SendCount, constant.SMS)
|
||||
if !isValid {
|
||||
continue
|
||||
}
|
||||
create := dto.AlertLogCreate{
|
||||
Type: alertType,
|
||||
AlertId: alert.ID,
|
||||
Count: todayCount + 1,
|
||||
}
|
||||
_ = xpack.CreateSMSAlertLog(alertType, alert, create, quotaType, params, constant.SMS)
|
||||
alertUtil.CreateNewAlertTask(quota, alertType, quotaType, constant.SMS)
|
||||
global.LOG.Infof("%s alert sms push successful", alertType)
|
||||
|
||||
case constant.Email:
|
||||
todayCount, isValid := canSendAlertToday(alertType, quotaType, alert.SendCount, constant.Email)
|
||||
if !isValid {
|
||||
continue
|
||||
}
|
||||
create := dto.AlertLogCreate{
|
||||
Type: alertType,
|
||||
AlertId: alert.ID,
|
||||
Count: todayCount + 1,
|
||||
}
|
||||
alertInfo := alert
|
||||
alertInfo.Type = alertType
|
||||
create.AlertRule = alertUtil.ProcessAlertRule(alert)
|
||||
create.AlertDetail = alertUtil.ProcessAlertDetail(alertInfo, quotaType, params, constant.Email)
|
||||
transport := xpack.LoadRequestTransport()
|
||||
_ = alertUtil.CreateEmailAlertLog(create, alertInfo, params, transport)
|
||||
alertUtil.CreateNewAlertTask(quota, alertType, quotaType, constant.Email)
|
||||
global.LOG.Infof("%s alert email push successful", alertType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
func getRepoOptionsByProject(project string) []repo.DBOption {
|
||||
var opts []repo.DBOption
|
||||
if project != "all" {
|
||||
itemID, _ := strconv.Atoi(project)
|
||||
opts = append(opts, repo.WithByID(uint(itemID)))
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
func serializeAndSortProjects(projectMap map[uint][]time.Time) string {
|
||||
if len(projectMap) == 0 {
|
||||
return ""
|
||||
}
|
||||
keys := make([]int, 0, len(projectMap))
|
||||
for k := range projectMap {
|
||||
keys = append(keys, int(k))
|
||||
}
|
||||
sort.Ints(keys)
|
||||
projectJSON, err := json.Marshal(projectMap)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("Failed to serialize projectMap: %v", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return string(projectJSON)
|
||||
}
|
||||
|
||||
func calculateSSLExpiryDays(sslList []model.WebsiteSSL, cycle uint) (map[int][]string, map[uint][]time.Time) {
|
||||
currentDate := time.Now()
|
||||
daysDiffMap := make(map[int][]string)
|
||||
projectMap := make(map[uint][]time.Time)
|
||||
|
||||
for _, ssl := range sslList {
|
||||
daysDiff := int(ssl.ExpireDate.Sub(currentDate).Hours() / 24)
|
||||
if daysDiff > 0 && int(cycle) >= daysDiff {
|
||||
daysDiffMap[daysDiff] = append(daysDiffMap[daysDiff], ssl.PrimaryDomain)
|
||||
projectMap[ssl.ID] = append(projectMap[ssl.ID], ssl.ExpireDate)
|
||||
}
|
||||
}
|
||||
return daysDiffMap, projectMap
|
||||
}
|
||||
|
||||
func calculateWebsiteExpiryDays(websites []model.Website, cycle uint) (map[int][]string, map[uint][]time.Time) {
|
||||
currentDate := time.Now()
|
||||
daysDiffMap := make(map[int][]string)
|
||||
projectMap := make(map[uint][]time.Time)
|
||||
|
||||
for _, website := range websites {
|
||||
daysDiff := int(website.ExpireDate.Sub(currentDate).Hours() / 24)
|
||||
if daysDiff > 0 && int(cycle) >= daysDiff {
|
||||
daysDiffMap[daysDiff] = append(daysDiffMap[daysDiff], website.PrimaryDomain)
|
||||
projectMap[website.ID] = append(projectMap[website.ID], website.ExpireDate)
|
||||
}
|
||||
}
|
||||
return daysDiffMap, projectMap
|
||||
}
|
||||
|
||||
func getSettingValue(key string) (string, error) {
|
||||
var setting model.Setting
|
||||
if err := global.CoreDB.Model(&model.Setting{}).Where("key = ?", key).First(&setting).Error; err != nil {
|
||||
global.LOG.Errorf("load %s from db setting failed: %v", key, err)
|
||||
return "", err
|
||||
}
|
||||
return setting.Value, nil
|
||||
}
|
||||
|
||||
func getValidVersion(info *dto.UpgradeInfo) string {
|
||||
if info.NewVersion != "" {
|
||||
return info.NewVersion
|
||||
} else if info.TestVersion != "" {
|
||||
return info.TestVersion
|
||||
} else if info.LatestVersion != "" {
|
||||
return info.LatestVersion
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func shouldSendResourceAlert(alert dto.AlertDTO, currentUsage float64, usageLoad *[]float64, threshold int) {
|
||||
newDate, err := alertRepo.GetTaskLog(alert.Type, alert.ID)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("record not found, err: %v", err)
|
||||
}
|
||||
if isAlertDue(newDate) {
|
||||
*usageLoad = append(*usageLoad, currentUsage)
|
||||
if len(*usageLoad) > threshold {
|
||||
*usageLoad = (*usageLoad)[1:]
|
||||
}
|
||||
if len(*usageLoad) == threshold {
|
||||
avgUsage := average(*usageLoad)
|
||||
if avgUsage >= float64(alert.Count) {
|
||||
sendResourceAlert(alert, avgUsage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isAlertDue(lastAlertTime time.Time) bool {
|
||||
if lastAlertTime.IsZero() {
|
||||
return true
|
||||
}
|
||||
return calculateMinutesDifference(lastAlertTime) > ResourceAlertInterval
|
||||
}
|
||||
|
||||
func sendResourceAlert(alert dto.AlertDTO, value float64) {
|
||||
valueStr := common.FormatPercent(value)
|
||||
module := getModuleName(alert.Type)
|
||||
params := createAlertAvgParams(strconv.Itoa(int(alert.Cycle)), module, valueStr)
|
||||
sender := NewAlertSender(alert, strconv.Itoa(int(alert.Cycle)))
|
||||
sender.ResourceSend(valueStr, params)
|
||||
}
|
||||
|
||||
func getModuleName(alertType string) string {
|
||||
var module string
|
||||
switch alertType {
|
||||
case "cpu":
|
||||
module = " CPU "
|
||||
case "memory":
|
||||
module = "内存"
|
||||
case "load":
|
||||
module = "负载"
|
||||
default:
|
||||
}
|
||||
return module
|
||||
}
|
||||
|
||||
func canSendAlertToday(alertType, quotaType string, sendCount uint, method string) (uint, bool) {
|
||||
todayCount, _, err := alertRepo.LoadTaskCount(alertType, quotaType, method)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("error getting task info, err: %v", err)
|
||||
return todayCount, false
|
||||
}
|
||||
if todayCount >= sendCount {
|
||||
return todayCount, false
|
||||
}
|
||||
|
||||
return todayCount, true
|
||||
}
|
||||
|
||||
func average(arr []float64) float64 {
|
||||
total := 0.0
|
||||
for _, v := range arr {
|
||||
total += v
|
||||
}
|
||||
return total / float64(len(arr))
|
||||
}
|
||||
|
||||
func createAlertBaseParams(project, cycle string) []dto.Param {
|
||||
return []dto.Param{
|
||||
{
|
||||
Index: "1",
|
||||
Key: "project",
|
||||
Value: project,
|
||||
},
|
||||
{
|
||||
Index: "2",
|
||||
Key: "cycle",
|
||||
Value: cycle,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createAlertPwdParams(cycle string) []dto.Param {
|
||||
return []dto.Param{
|
||||
{
|
||||
Index: "1",
|
||||
Key: "cycle",
|
||||
Value: cycle,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createAlertAvgParams(cycle, module, count string) []dto.Param {
|
||||
return []dto.Param{
|
||||
{
|
||||
Index: "1",
|
||||
Key: "cycle",
|
||||
Value: cycle,
|
||||
},
|
||||
{
|
||||
Index: "2",
|
||||
Key: "module",
|
||||
Value: module,
|
||||
},
|
||||
{
|
||||
Index: "3",
|
||||
Key: "count",
|
||||
Value: count,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createAlertDiskParams(project, count string) []dto.Param {
|
||||
return []dto.Param{
|
||||
{
|
||||
Index: "1",
|
||||
Key: "project",
|
||||
Value: project,
|
||||
},
|
||||
{
|
||||
Index: "2",
|
||||
Key: "count",
|
||||
Value: count,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func processAllDisks(alert dto.AlertDTO) error {
|
||||
diskList, err := NewIAlertService().GetDisks()
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
}
|
||||
if flag {
|
||||
global.LOG.Info("all disk alert push successful")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func processSingleDisk(alert dto.AlertDTO) error {
|
||||
success, err := checkAndCreateDiskAlert(alert, alert.Project)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if success {
|
||||
global.LOG.Info("disk alert push successful")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkAndCreateDiskAlert(alert dto.AlertDTO, path string) (bool, error) {
|
||||
usageStat, err := disk.Usage(path)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("error getting disk usage for %s, err: %v", path, err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
usedTotal, usedStr := calculateUsedTotal(alert.Cycle, usageStat)
|
||||
commonTotal := float64(alert.Count)
|
||||
if alert.Cycle == 1 {
|
||||
commonTotal *= 1024 * 1024 * 1024
|
||||
}
|
||||
if usedTotal < commonTotal {
|
||||
return false, nil
|
||||
}
|
||||
global.LOG.Infof("disk「 %s 」usage: %s", path, usedStr)
|
||||
params := createAlertDiskParams(path, usedStr)
|
||||
sender := NewAlertSender(alert, alert.Project)
|
||||
sender.ResourceSend(path, params)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func calculateUsedTotal(cycle uint, usageStat *disk.UsageStat) (float64, string) {
|
||||
if cycle == 1 {
|
||||
return float64(usageStat.Used), common.FormatBytes(usageStat.Used)
|
||||
}
|
||||
return usageStat.UsedPercent, common.FormatPercent(usageStat.UsedPercent)
|
||||
}
|
||||
|
||||
func calculateDaysDifference(expirationTime time.Time) int {
|
||||
currentDate := time.Now()
|
||||
formattedTime := currentDate.Format(constant.DateTimeLayout)
|
||||
parsedTime, _ := time.Parse(constant.DateTimeLayout, formattedTime)
|
||||
timeGap := expirationTime.Sub(parsedTime).Milliseconds()
|
||||
if timeGap < 0 {
|
||||
return -1
|
||||
}
|
||||
daysDifference := int(math.Floor(float64(timeGap) / (3600 * 1000 * 24)))
|
||||
return daysDifference
|
||||
}
|
||||
|
||||
func calculateMinutesDifference(newDate time.Time) int {
|
||||
now := time.Now()
|
||||
if newDate.After(now) {
|
||||
return -1
|
||||
}
|
||||
minutesDifference := int(now.Sub(newDate).Minutes())
|
||||
return minutesDifference
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
alertUtil "github.com/1Panel-dev/1Panel/agent/utils/alert"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type AlertSender struct {
|
||||
alert dto.AlertDTO
|
||||
quotaType string
|
||||
}
|
||||
|
||||
func NewAlertSender(alert dto.AlertDTO, quotaType string) *AlertSender {
|
||||
return &AlertSender{
|
||||
alert: alert,
|
||||
quotaType: quotaType,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *AlertSender) Send(quota string, params []dto.Param) {
|
||||
methods := strings.Split(s.alert.Method, ",")
|
||||
for _, method := range methods {
|
||||
method = strings.TrimSpace(method)
|
||||
switch method {
|
||||
case constant.SMS:
|
||||
s.sendSMS(quota, params)
|
||||
case constant.Email:
|
||||
s.sendEmail(quota, params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *AlertSender) ResourceSend(quota string, params []dto.Param) {
|
||||
methods := strings.Split(s.alert.Method, ",")
|
||||
for _, method := range methods {
|
||||
method = strings.TrimSpace(method)
|
||||
switch method {
|
||||
case constant.SMS:
|
||||
s.sendResourceSMS(quota, params)
|
||||
case constant.Email:
|
||||
s.sendResourceEmail(quota, params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *AlertSender) sendSMS(quota string, params []dto.Param) {
|
||||
if !alertUtil.CheckSMSSendLimit(constant.SMS) {
|
||||
return
|
||||
}
|
||||
|
||||
totalCount, isValid := s.canSendAlert(constant.SMS)
|
||||
if !isValid {
|
||||
return
|
||||
}
|
||||
|
||||
create := dto.AlertLogCreate{
|
||||
Status: constant.AlertSuccess,
|
||||
Count: totalCount + 1,
|
||||
AlertId: s.alert.ID,
|
||||
Type: s.alert.Type,
|
||||
}
|
||||
|
||||
_ = xpack.CreateSMSAlertLog(s.alert.Type, s.alert, create, quota, params, constant.SMS)
|
||||
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.SMS)
|
||||
global.LOG.Infof("%s alert sms push successful", s.alert.Type)
|
||||
}
|
||||
|
||||
func (s *AlertSender) sendEmail(quota string, params []dto.Param) {
|
||||
totalCount, isValid := s.canSendAlert(constant.Email)
|
||||
if !isValid {
|
||||
return
|
||||
}
|
||||
|
||||
create := dto.AlertLogCreate{
|
||||
Status: constant.AlertSuccess,
|
||||
Count: totalCount + 1,
|
||||
AlertId: s.alert.ID,
|
||||
Type: s.alert.Type,
|
||||
AlertRule: alertUtil.ProcessAlertRule(s.alert),
|
||||
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Email),
|
||||
}
|
||||
|
||||
transport := xpack.LoadRequestTransport()
|
||||
_ = alertUtil.CreateEmailAlertLog(create, s.alert, params, transport)
|
||||
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Email)
|
||||
global.LOG.Infof("%s alert email push successful", s.alert.Type)
|
||||
}
|
||||
|
||||
func (s *AlertSender) sendResourceSMS(quota string, params []dto.Param) {
|
||||
if !alertUtil.CheckSMSSendLimit(constant.SMS) {
|
||||
return
|
||||
}
|
||||
|
||||
todayCount, isValid := s.canResourceSendAlert(constant.SMS)
|
||||
if !isValid {
|
||||
return
|
||||
}
|
||||
|
||||
create := dto.AlertLogCreate{
|
||||
Status: constant.AlertSuccess,
|
||||
Count: todayCount + 1,
|
||||
AlertId: s.alert.ID,
|
||||
Type: s.alert.Type,
|
||||
}
|
||||
|
||||
if err := xpack.CreateSMSAlertLog(s.alert.Type, s.alert, create, quota, params, constant.SMS); err != nil {
|
||||
global.LOG.Errorf("failed to send SMS alert: %v", err)
|
||||
return
|
||||
}
|
||||
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.SMS)
|
||||
global.LOG.Infof("%s alert sms push successful", s.alert.Type)
|
||||
}
|
||||
|
||||
func (s *AlertSender) sendResourceEmail(quota string, params []dto.Param) {
|
||||
todayCount, isValid := s.canResourceSendAlert(constant.Email)
|
||||
if !isValid {
|
||||
return
|
||||
}
|
||||
|
||||
create := dto.AlertLogCreate{
|
||||
Status: constant.AlertSuccess,
|
||||
Count: todayCount + 1,
|
||||
AlertId: s.alert.ID,
|
||||
Type: s.alert.Type,
|
||||
AlertRule: alertUtil.ProcessAlertRule(s.alert),
|
||||
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Email),
|
||||
}
|
||||
|
||||
transport := xpack.LoadRequestTransport()
|
||||
if err := alertUtil.CreateEmailAlertLog(create, s.alert, params, transport); err != nil {
|
||||
global.LOG.Errorf("failed to send Email alert: %v", err)
|
||||
return
|
||||
}
|
||||
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Email)
|
||||
global.LOG.Infof("%s alert email push successful", s.alert.Type)
|
||||
}
|
||||
|
||||
func (s *AlertSender) canSendAlert(method string) (uint, bool) {
|
||||
todayCount, totalCount, err := alertRepo.LoadTaskCount(s.alert.Type, s.quotaType, method)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("error getting task count: %v", err)
|
||||
return totalCount, false
|
||||
}
|
||||
|
||||
if todayCount >= 1 || s.alert.SendCount <= totalCount {
|
||||
return totalCount, false
|
||||
}
|
||||
return totalCount, true
|
||||
}
|
||||
|
||||
func (s *AlertSender) canResourceSendAlert(method string) (uint, bool) {
|
||||
todayCount, _, err := alertRepo.LoadTaskCount(s.alert.Type, s.quotaType, method)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("error getting task count: %v", err)
|
||||
return todayCount, false
|
||||
}
|
||||
if s.alert.SendCount <= todayCount {
|
||||
return todayCount, false
|
||||
}
|
||||
return todayCount, true
|
||||
}
|
||||
+39
-52
@@ -7,7 +7,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
@@ -56,7 +55,7 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
|
||||
var opts []repo.DBOption
|
||||
opts = append(opts, appRepo.OrderByRecommend())
|
||||
if req.Name != "" {
|
||||
opts = append(opts, appRepo.WithByLikeName(req.Name))
|
||||
opts = append(opts, appRepo.WithByLikeName(strings.TrimSpace(req.Name)))
|
||||
}
|
||||
if req.Type != "" {
|
||||
opts = append(opts, appRepo.WithType(req.Type))
|
||||
@@ -100,7 +99,7 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var appDTOs []*response.AppItem
|
||||
appDTOs := make([]*response.AppItem, 0)
|
||||
info := &dto.SettingInfo{}
|
||||
if req.Type == "php" {
|
||||
info, _ = NewISettingService().GetSettingInfo()
|
||||
@@ -132,8 +131,21 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
|
||||
continue
|
||||
}
|
||||
appDTO.Tags = tags
|
||||
installs, _ := appInstallRepo.ListBy(context.Background(), appInstallRepo.WithAppId(ap.ID))
|
||||
appDTO.Installed = len(installs) > 0
|
||||
if ap.Type == constant.RuntimePHP || ap.Type == constant.RuntimeGo || ap.Type == constant.RuntimeNode || ap.Type == constant.RuntimePython || ap.Type == constant.RuntimeJava || ap.Type == constant.RuntimeDotNet {
|
||||
details, _ := appDetailRepo.GetBy(appDetailRepo.WithAppId(ap.ID))
|
||||
var ids []uint
|
||||
if len(details) == 0 {
|
||||
continue
|
||||
}
|
||||
for _, d := range details {
|
||||
ids = append(ids, d.ID)
|
||||
}
|
||||
runtimes, _ := runtimeRepo.List(runtimeRepo.WithDetailIdsIn(ids))
|
||||
appDTO.Installed = len(runtimes) > 0
|
||||
} else {
|
||||
installs, _ := appInstallRepo.ListBy(context.Background(), appInstallRepo.WithAppId(ap.ID))
|
||||
appDTO.Installed = len(installs) > 0
|
||||
}
|
||||
}
|
||||
res.Items = appDTOs
|
||||
res.Total = total
|
||||
@@ -355,29 +367,6 @@ func (a AppService) Install(req request.AppInstallCreate) (appInstall *model.App
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
oldWebStePath, _ := settingRepo.GetValueByKey("WEBSITE_DIR")
|
||||
fileOp := files.NewFileOp()
|
||||
if oldWebStePath != "" && oldWebStePath != siteDir && fileOp.Stat(oldWebStePath) {
|
||||
_ = fileOp.Rename(oldWebStePath, siteDir)
|
||||
}
|
||||
if !fileOp.Stat(siteDir) {
|
||||
_ = fileOp.CreateDir(siteDir, constant.DirPerm)
|
||||
_ = fileOp.CreateDir(path.Join(siteDir, "conf.d"), constant.DirPerm)
|
||||
}
|
||||
err = settingRepo.UpdateOrCreate("WEBSITE_DIR", siteDir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go RestartPHPRuntime()
|
||||
}
|
||||
}
|
||||
for key := range req.Params {
|
||||
if !strings.Contains(key, "PANEL_APP_PORT") {
|
||||
continue
|
||||
@@ -483,8 +472,14 @@ func (a AppService) Install(req request.AppInstallCreate) (appInstall *model.App
|
||||
req.Params["PANEL_DB_HOST"] = database.Address
|
||||
req.Params["PANEL_DB_PORT"] = database.Port
|
||||
req.Params["PANEL_DB_HOST_NAME"] = hostName
|
||||
req.Params["DATABASE_NAME"] = database.Name
|
||||
}
|
||||
}
|
||||
if app.Key == "openresty" {
|
||||
req.Params["CONTAINER_PACKAGE_URL"] = "http://archive.ubuntu.com/ubuntu/"
|
||||
req.Params["RESTY_ADD_PACKAGE_BUILDDEPS"] = ""
|
||||
req.Params["RESTY_CONFIG_OPTIONS_MORE"] = ""
|
||||
}
|
||||
paramByte, err = json.Marshal(req.Params)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -512,6 +507,9 @@ func (a AppService) Install(req request.AppInstallCreate) (appInstall *model.App
|
||||
return err
|
||||
}
|
||||
if app.Key == "openresty" {
|
||||
if err = handleSiteDir(app, appDetail, req, t); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = handleOpenrestyFile(appInstall); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -927,18 +925,11 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
tags []*model.Tag
|
||||
appTags []*model.AppTag
|
||||
oldAppIds []uint
|
||||
)
|
||||
for _, tag := range list.Extra.Tags {
|
||||
translations, _ := json.Marshal(tag.Locales)
|
||||
tags = append(tags, &model.Tag{
|
||||
Name: tag.Name,
|
||||
Translations: string(translations),
|
||||
Sort: tag.Sort,
|
||||
Key: tag.Key,
|
||||
})
|
||||
if err = SyncTags(list.Extra); err != nil {
|
||||
return err
|
||||
}
|
||||
deleteCustomApp()
|
||||
oldApps, err := appRepo.GetBy(appRepo.WithNotLocal())
|
||||
@@ -1014,6 +1005,7 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
|
||||
}
|
||||
t.LogSuccess(i18n.GetMsgByKey("SyncAppDetail"))
|
||||
|
||||
tags, _ := tagRepo.All()
|
||||
var (
|
||||
addAppArray []model.App
|
||||
updateAppArray []model.App
|
||||
@@ -1060,16 +1052,8 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if err = tagRepo.DeleteAll(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
if len(tags) > 0 {
|
||||
if err = tagRepo.BatchCreate(ctx, tags); err != nil {
|
||||
return
|
||||
}
|
||||
for _, tag := range tags {
|
||||
tagMap[tag.Key] = tag.ID
|
||||
}
|
||||
for _, tag := range tags {
|
||||
tagMap[tag.Key] = tag.ID
|
||||
}
|
||||
for _, update := range updateAppArray {
|
||||
if err = appRepo.Save(ctx, &update); err != nil {
|
||||
@@ -1087,10 +1071,13 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
|
||||
for _, tag := range app.TagsKey {
|
||||
tagId, ok := tagMap[tag]
|
||||
if ok {
|
||||
appTags = append(appTags, &model.AppTag{
|
||||
AppId: app.ID,
|
||||
TagId: tagId,
|
||||
})
|
||||
exist, _ := appTagRepo.GetFirst(ctx, appTagRepo.WithByTagID(tagId), appTagRepo.WithByAppID(app.ID))
|
||||
if exist == nil {
|
||||
appTags = append(appTags, &model.AppTag{
|
||||
AppId: app.ID,
|
||||
TagId: tagId,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, d := range app.Details {
|
||||
@@ -1133,7 +1120,7 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
|
||||
}
|
||||
|
||||
if len(oldAppIds) > 0 {
|
||||
if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {
|
||||
if err = appTagRepo.DeleteByAppIds(ctx, deleteIds); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func (a AppIgnoreUpgradeService) List() ([]response.AppIgnoreUpgradeDTO, error)
|
||||
_ = appIgnoreUpgradeRepo.Delete(repo.WithByID(ignore.ID))
|
||||
continue
|
||||
}
|
||||
dto.Icon = app.Icon
|
||||
dto.Name = app.Name
|
||||
if ignore.Scope == "version" {
|
||||
appDetail, err := appDetailRepo.GetFirst(repo.WithByID(ignore.AppDetailID))
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
|
||||
@@ -56,6 +56,7 @@ type IAppInstallService interface {
|
||||
|
||||
UpdateAppConfig(req request.AppConfigUpdate) error
|
||||
GetInstallList() ([]dto.AppInstallInfo, error)
|
||||
GetAppInstallInfo(appInstallID uint) (*response.AppInstallInfo, error)
|
||||
}
|
||||
|
||||
func NewIAppInstalledService() IAppInstallService {
|
||||
@@ -163,6 +164,11 @@ func (a *AppInstallService) CheckExist(req request.AppInstalledInfo) (*response.
|
||||
res.HttpPort = appInstall.HttpPort
|
||||
res.HttpsPort = appInstall.HttpsPort
|
||||
|
||||
if appInstall.App.Key == "openresty" {
|
||||
websiteDir, _ := settingRepo.GetValueByKey("WEBSITE_DIR")
|
||||
res.WebsiteDir = websiteDir
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -472,10 +478,20 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
|
||||
func (a *AppInstallService) GetServices(key string) ([]response.AppService, error) {
|
||||
var res []response.AppService
|
||||
if DatabaseKeys[key] > 0 {
|
||||
types := []string{key}
|
||||
if key == constant.AppPostgres {
|
||||
key = constant.AppPostgresql
|
||||
}
|
||||
dbs, _ := databaseRepo.GetList(repo.WithByType(key))
|
||||
switch key {
|
||||
case constant.AppMysql:
|
||||
types = []string{constant.AppMysql, constant.AppMysqlCluster}
|
||||
case constant.AppPostgresql:
|
||||
types = []string{constant.AppPostgresql, constant.AppPostgresqlCluster}
|
||||
case constant.AppRedis:
|
||||
types = []string{constant.AppRedis, constant.AppRedisCluster}
|
||||
}
|
||||
|
||||
dbs, _ := databaseRepo.GetList(repo.WithTypes(types))
|
||||
if len(dbs) == 0 {
|
||||
return res, nil
|
||||
}
|
||||
@@ -658,10 +674,10 @@ func (a *AppInstallService) GetDefaultConfigByKey(key, name string) (string, err
|
||||
return "", buserr.New("ErrPathNotFound")
|
||||
}
|
||||
|
||||
if key == constant.AppMysql || key == constant.AppMariaDB {
|
||||
if key == constant.AppMysql || key == constant.AppMariaDB || key == constant.AppMysqlCluster {
|
||||
filePath = path.Join(filePath, "my.cnf")
|
||||
}
|
||||
if key == constant.AppRedis {
|
||||
if key == constant.AppRedis || key == constant.AppRedisCluster {
|
||||
filePath = path.Join(filePath, "redis.conf")
|
||||
}
|
||||
if key == constant.AppOpenresty {
|
||||
@@ -710,6 +726,7 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
||||
}
|
||||
appParam.LabelZh = form.LabelZh
|
||||
appParam.LabelEn = form.LabelEn
|
||||
appParam.Label = form.Label
|
||||
appParam.Value = v
|
||||
if form.Type == "service" {
|
||||
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithServiceName(v.(string)))
|
||||
@@ -763,6 +780,7 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
||||
Values: form.Values,
|
||||
Multiple: form.Multiple,
|
||||
Required: form.Required,
|
||||
Label: form.Label,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -775,6 +793,7 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
||||
}
|
||||
res.AppContainerConfig = config
|
||||
res.HostMode = isHostModel(install.DockerCompose)
|
||||
res.RestartPolicy = getRestartPolicy(install.DockerCompose)
|
||||
res.WebUI = install.WebUI
|
||||
res.Type = install.App.Type
|
||||
return &res, nil
|
||||
@@ -898,3 +917,31 @@ func updateInstallInfoInDB(appKey, appName, param string, value interface{}) err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AppInstallService) GetAppInstallInfo(installID uint) (*response.AppInstallInfo, error) {
|
||||
appInstall, _ := appInstallRepo.GetFirst(repo.WithByID(installID))
|
||||
if appInstall.ID == 0 {
|
||||
return &response.AppInstallInfo{
|
||||
Status: constant.StatusDeleted,
|
||||
}, nil
|
||||
}
|
||||
_ = syncAppInstallStatus(&appInstall, false)
|
||||
appInstall, _ = appInstallRepo.GetFirst(repo.WithByID(installID))
|
||||
var envMap map[string]interface{}
|
||||
err := json.Unmarshal([]byte(appInstall.Env), &envMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := &response.AppInstallInfo{
|
||||
ID: appInstall.ID,
|
||||
Name: appInstall.Name,
|
||||
Version: appInstall.Version,
|
||||
Container: appInstall.ContainerName,
|
||||
HttpPort: appInstall.HttpPort,
|
||||
Status: appInstall.Status,
|
||||
Message: appInstall.Message,
|
||||
Env: envMap,
|
||||
ComposePath: appInstall.GetComposePath(),
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
+278
-51
@@ -1,11 +1,13 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"maps"
|
||||
"math"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -116,13 +118,16 @@ func checkPortExist(port int) error {
|
||||
}
|
||||
|
||||
var DatabaseKeys = map[string]uint{
|
||||
constant.AppMysql: 3306,
|
||||
constant.AppMariaDB: 3306,
|
||||
constant.AppPostgresql: 5432,
|
||||
constant.AppPostgres: 5432,
|
||||
constant.AppMongodb: 27017,
|
||||
constant.AppRedis: 6379,
|
||||
constant.AppMemcached: 11211,
|
||||
constant.AppMysql: 3306,
|
||||
constant.AppMariaDB: 3306,
|
||||
constant.AppPostgresql: 5432,
|
||||
constant.AppPostgres: 5432,
|
||||
constant.AppMongodb: 27017,
|
||||
constant.AppRedis: 6379,
|
||||
constant.AppMemcached: 11211,
|
||||
constant.AppMysqlCluster: 3306,
|
||||
constant.AppPostgresqlCluster: 5432,
|
||||
constant.AppRedisCluster: 6379,
|
||||
}
|
||||
|
||||
var ToolKeys = map[string]uint{
|
||||
@@ -138,7 +143,6 @@ func createLink(ctx context.Context, installTask *task.Task, app model.App, appI
|
||||
}
|
||||
_ = deleteLink(del)
|
||||
}
|
||||
var dbConfig dto.AppDatabase
|
||||
if DatabaseKeys[app.Key] > 0 {
|
||||
handleDataBaseApp := func(task *task.Task) error {
|
||||
database := &model.Database{
|
||||
@@ -170,11 +174,11 @@ func createLink(ctx context.Context, installTask *task.Task, app model.App, appI
|
||||
}
|
||||
|
||||
switch app.Key {
|
||||
case constant.AppMysql, constant.AppMariaDB, constant.AppPostgresql, constant.AppMongodb:
|
||||
case constant.AppMysql, constant.AppMariaDB, constant.AppPostgresql, constant.AppMongodb, constant.AppMysqlCluster, constant.AppPostgresqlCluster:
|
||||
if password, ok := params["PANEL_DB_ROOT_PASSWORD"]; ok {
|
||||
if password != "" {
|
||||
database.Password = password.(string)
|
||||
if app.Key == "mysql" || app.Key == "mariadb" {
|
||||
if app.Key == "mysql" || app.Key == "mariadb" || app.Key == constant.AppMysqlCluster {
|
||||
database.Username = "root"
|
||||
}
|
||||
if rootUser, ok := params["PANEL_DB_ROOT_USER"]; ok {
|
||||
@@ -192,7 +196,7 @@ func createLink(ctx context.Context, installTask *task.Task, app model.App, appI
|
||||
|
||||
}
|
||||
}
|
||||
case constant.AppRedis:
|
||||
case constant.AppRedis, constant.AppRedisCluster:
|
||||
if password, ok := params["PANEL_REDIS_ROOT_PASSWORD"]; ok {
|
||||
authParam := dto.RedisAuthParam{
|
||||
RootPassword: "",
|
||||
@@ -229,6 +233,7 @@ func createLink(ctx context.Context, installTask *task.Task, app model.App, appI
|
||||
}
|
||||
}
|
||||
|
||||
var dbConfig dto.AppDatabase
|
||||
if app.Type == "website" || app.Type == "tool" {
|
||||
paramByte, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
@@ -252,8 +257,11 @@ func createLink(ctx context.Context, installTask *task.Task, app model.App, appI
|
||||
var resourceId uint
|
||||
if dbConfig.DbName != "" && dbConfig.DbUser != "" && dbConfig.Password != "" {
|
||||
switch database.Type {
|
||||
case constant.AppPostgresql, constant.AppPostgres:
|
||||
case constant.AppPostgresql, constant.AppPostgres, constant.AppPostgresqlCluster:
|
||||
oldPostgresqlDb, _ := postgresqlRepo.Get(repo.WithByName(dbConfig.DbName), repo.WithByFrom(constant.ResourceLocal))
|
||||
if oldPostgresqlDb.ID == 0 {
|
||||
oldPostgresqlDb, _ = postgresqlRepo.Get(repo.WithByName(dbConfig.DbName), postgresqlRepo.WithByPostgresqlName(dbConfig.DatabaseName), repo.WithByFrom(constant.AppResourceRemote))
|
||||
}
|
||||
resourceId = oldPostgresqlDb.ID
|
||||
if oldPostgresqlDb.ID > 0 {
|
||||
if oldPostgresqlDb.Username != dbConfig.DbUser || oldPostgresqlDb.Password != dbConfig.Password {
|
||||
@@ -274,8 +282,11 @@ func createLink(ctx context.Context, installTask *task.Task, app model.App, appI
|
||||
}
|
||||
resourceId = pgdb.ID
|
||||
}
|
||||
case constant.AppMysql, constant.AppMariaDB:
|
||||
case constant.AppMysql, constant.AppMariaDB, constant.AppMysqlCluster:
|
||||
oldMysqlDb, _ := mysqlRepo.Get(repo.WithByName(dbConfig.DbName), repo.WithByFrom(constant.ResourceLocal))
|
||||
if oldMysqlDb.ID == 0 {
|
||||
oldMysqlDb, _ = mysqlRepo.Get(repo.WithByName(dbConfig.DbName), mysqlRepo.WithByMysqlName(dbConfig.DatabaseName), repo.WithByFrom(constant.AppResourceRemote))
|
||||
}
|
||||
resourceId = oldMysqlDb.ID
|
||||
if oldMysqlDb.ID > 0 {
|
||||
if oldMysqlDb.Username != dbConfig.DbUser || oldMysqlDb.Password != dbConfig.Password {
|
||||
@@ -348,7 +359,7 @@ func deleteAppInstall(deleteReq request.AppInstallDelete) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
images, err := composeV2.GetDockerComposeImagesV2(content, []byte(install.DockerCompose))
|
||||
images, err := composeV2.GetImagesFromDockerCompose(content, []byte(install.DockerCompose))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -405,9 +416,9 @@ func deleteAppInstall(deleteReq request.AppInstallDelete) error {
|
||||
}
|
||||
|
||||
switch install.App.Key {
|
||||
case constant.AppMysql, constant.AppMariaDB:
|
||||
case constant.AppMysql, constant.AppMariaDB, constant.AppMysqlCluster:
|
||||
_ = mysqlRepo.Delete(ctx, mysqlRepo.WithByMysqlName(install.Name))
|
||||
case constant.AppPostgresql:
|
||||
case constant.AppPostgresql, constant.AppPostgresqlCluster:
|
||||
_ = postgresqlRepo.Delete(ctx, postgresqlRepo.WithByPostgresqlName(install.Name))
|
||||
}
|
||||
|
||||
@@ -431,6 +442,11 @@ func deleteAppInstall(deleteReq request.AppInstallDelete) error {
|
||||
_ = op.DeleteDir(parentDir)
|
||||
}
|
||||
tx.Commit()
|
||||
|
||||
existApps, _ := appInstallRepo.ListBy(context.Background(), appInstallRepo.WithAppId(install.AppId))
|
||||
if len(existApps) == 0 {
|
||||
_ = appIgnoreUpgradeRepo.Delete(appIgnoreUpgradeRepo.WithAppID(install.AppId))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
uninstallTask.AddSubTask(task.GetTaskName(install.Name, task.TaskUninstall, task.TaskScopeApp), uninstall, nil)
|
||||
@@ -526,6 +542,9 @@ func handleUpgradeCompose(install model.AppInstall, detail model.AppDetail) (map
|
||||
if oldServiceValue["deploy"] != nil {
|
||||
serviceValue["deploy"] = oldServiceValue["deploy"]
|
||||
}
|
||||
if oldServiceValue["restart"] != nil {
|
||||
serviceValue["restart"] = oldServiceValue["restart"]
|
||||
}
|
||||
servicesMap[install.ServiceName] = serviceValue
|
||||
composeMap["services"] = servicesMap
|
||||
return composeMap, nil
|
||||
@@ -564,6 +583,76 @@ func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string
|
||||
return string(composeByte), nil
|
||||
}
|
||||
|
||||
func buildNginx(parentTask *task.Task) error {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
buildPath := path.Join(nginxInstall.GetPath(), "build")
|
||||
if !fileOp.Stat(buildPath) {
|
||||
return buserr.New("ErrBuildDirNotFound")
|
||||
}
|
||||
moduleConfigPath := path.Join(buildPath, "module.json")
|
||||
moduleContent, err := fileOp.GetContent(moduleConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
modules []dto.NginxModule
|
||||
addModuleParams []string
|
||||
addPackages []string
|
||||
)
|
||||
if len(moduleContent) > 0 {
|
||||
_ = json.Unmarshal(moduleContent, &modules)
|
||||
bashFile, err := os.OpenFile(path.Join(buildPath, "tmp", "pre.sh"), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, constant.DirPerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer bashFile.Close()
|
||||
bashFileWriter := bufio.NewWriter(bashFile)
|
||||
for _, module := range modules {
|
||||
if !module.Enable {
|
||||
continue
|
||||
}
|
||||
_, err = bashFileWriter.WriteString(module.Script + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
addModuleParams = append(addModuleParams, module.Params)
|
||||
addPackages = append(addPackages, module.Packages...)
|
||||
}
|
||||
err = bashFileWriter.Flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
envs, err := gotenv.Read(nginxInstall.GetEnvPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
envs["RESTY_CONFIG_OPTIONS_MORE"] = ""
|
||||
envs["RESTY_ADD_PACKAGE_BUILDDEPS"] = ""
|
||||
if len(addModuleParams) > 0 {
|
||||
envs["RESTY_CONFIG_OPTIONS_MORE"] = strings.Join(addModuleParams, " ")
|
||||
}
|
||||
if len(addPackages) > 0 {
|
||||
envs["RESTY_ADD_PACKAGE_BUILDDEPS"] = strings.Join(addPackages, " ")
|
||||
}
|
||||
_ = gotenv.Write(envs, nginxInstall.GetEnvPath())
|
||||
if len(addModuleParams) == 0 && len(addPackages) == 0 {
|
||||
return nil
|
||||
}
|
||||
logStr := fmt.Sprintf("%s %s", i18n.GetMsgByKey("TaskBuild"), i18n.GetMsgByKey("Image"))
|
||||
parentTask.LogStart(logStr)
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*parentTask), cmd.WithTimeout(60*time.Minute))
|
||||
if err = cmdMgr.RunBashCf("docker compose -f %s build", nginxInstall.GetComposePath()); err != nil {
|
||||
return err
|
||||
}
|
||||
parentTask.LogSuccess(logStr)
|
||||
return nil
|
||||
}
|
||||
|
||||
func upgradeInstall(req request.AppInstallUpgrade) error {
|
||||
install, err := appInstallRepo.GetFirst(repo.WithByID(req.InstallID))
|
||||
if err != nil {
|
||||
@@ -641,7 +730,11 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
||||
}
|
||||
dockerCLi, _ := docker.NewClient()
|
||||
if req.PullImage {
|
||||
images, err := composeV2.GetDockerComposeImagesV2(content, []byte(detail.DockerCompose))
|
||||
composeContent := []byte(detail.DockerCompose)
|
||||
if req.DockerCompose != "" {
|
||||
composeContent = []byte(req.DockerCompose)
|
||||
}
|
||||
images, err := composeV2.GetImagesFromDockerCompose(content, composeContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -656,10 +749,7 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
||||
}
|
||||
|
||||
command := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rn %s/* %s || true", detailDir, install.GetPath()))
|
||||
stdout, _ := command.CombinedOutput()
|
||||
if stdout != nil {
|
||||
t.Logger.Printf("upgrade app [%s] [%s] cp file log : %s ", install.App.Key, install.Name, string(stdout))
|
||||
}
|
||||
_, _ = command.CombinedOutput()
|
||||
sourceScripts := path.Join(detailDir, "scripts")
|
||||
if fileOp.Stat(sourceScripts) {
|
||||
dstScripts := path.Join(install.GetPath(), "scripts")
|
||||
@@ -695,6 +785,14 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
||||
return err
|
||||
}
|
||||
envParams := make(map[string]string, len(envs))
|
||||
if install.App.Key == constant.AppOpenresty {
|
||||
packageUrl, _ := env.GetEnvValueByKey(install.GetEnvPath(), "CONTAINER_PACKAGE_URL")
|
||||
addPackage, _ := env.GetEnvValueByKey(install.GetEnvPath(), "RESTY_ADD_PACKAGE_BUILDDEPS")
|
||||
options, _ := env.GetEnvValueByKey(install.GetEnvPath(), "RESTY_CONFIG_OPTIONS_MORE")
|
||||
envParams["CONTAINER_PACKAGE_URL"] = packageUrl
|
||||
envParams["RESTY_ADD_PACKAGE_BUILDDEPS"] = addPackage
|
||||
envParams["RESTY_CONFIG_OPTIONS_MORE"] = options
|
||||
}
|
||||
handleMap(envs, envParams)
|
||||
if err = env.Write(envParams, install.GetEnvPath()); err != nil {
|
||||
return err
|
||||
@@ -708,6 +806,13 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if install.App.Key == constant.AppOpenresty {
|
||||
if err = buildNginx(t); err != nil {
|
||||
t.Log(err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logStr := fmt.Sprintf("%s %s", i18n.GetMsgByKey("Run"), i18n.GetMsgByKey("App"))
|
||||
t.Log(logStr)
|
||||
if out, err := compose.Up(install.GetComposePath()); err != nil {
|
||||
@@ -733,7 +838,7 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
||||
}
|
||||
}
|
||||
|
||||
upgradeTask.AddSubTask(task.GetTaskName(install.Name, task.TaskScopeApp, task.TaskUpgrade), upgradeApp, rollBackApp)
|
||||
upgradeTask.AddSubTaskWithOps(task.GetTaskName(install.Name, task.TaskScopeApp, task.TaskUpgrade), upgradeApp, rollBackApp, 0, 1*time.Hour)
|
||||
|
||||
go func() {
|
||||
err = upgradeTask.Execute()
|
||||
@@ -858,9 +963,9 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App
|
||||
_ = fileOp.CreateDir(appVersionDir, constant.DirPerm)
|
||||
}
|
||||
if logger == nil {
|
||||
global.LOG.Infof("download app[%s] from %s", app.Name, appDetail.DownloadUrl)
|
||||
global.LOG.Infof("download app [%s] from %s", app.Name, appDetail.DownloadUrl)
|
||||
} else {
|
||||
logger.Printf("download app[%s] from %s", app.Name, appDetail.DownloadUrl)
|
||||
logger.Printf("download app [%s] from %s", app.Name, appDetail.DownloadUrl)
|
||||
}
|
||||
|
||||
filePath := path.Join(appVersionDir, app.Key+"-"+appDetail.Version+".tar.gz")
|
||||
@@ -876,9 +981,9 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App
|
||||
|
||||
if err = files.DownloadFileWithProxy(appDetail.DownloadUrl, filePath); err != nil {
|
||||
if logger == nil {
|
||||
global.LOG.Errorf("download app[%s] error %v", app.Name, err)
|
||||
global.LOG.Errorf("download app [%s] error %v", app.Name, err)
|
||||
} else {
|
||||
logger.Printf("download app[%s] error %v", app.Name, err)
|
||||
logger.Printf("download app [%s] error %v", app.Name, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -940,11 +1045,11 @@ func copyData(task *task.Task, app model.App, appDetail model.AppDetail, appInst
|
||||
return
|
||||
}
|
||||
envPath := path.Join(appDir, ".env")
|
||||
envParams := make(map[string]string, len(req.Params))
|
||||
envParams := make(map[string]string)
|
||||
if fileOp.Stat(envPath) {
|
||||
envs, _ := gotenv.Read(envPath)
|
||||
for k, v := range envs {
|
||||
envParams[k] = v
|
||||
if envParams = maps.Clone(envs); envParams == nil {
|
||||
envParams = make(map[string]string)
|
||||
}
|
||||
}
|
||||
handleMap(req.Params, envParams)
|
||||
@@ -968,14 +1073,16 @@ func runScript(task *task.Task, appInstall *model.AppInstall, operate string) er
|
||||
case "uninstall":
|
||||
scriptPath = path.Join(workDir, "scripts", "uninstall.sh")
|
||||
}
|
||||
if !files.NewFileOp().Stat(scriptPath) {
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(scriptPath) {
|
||||
return nil
|
||||
}
|
||||
_ = fileOp.ChmodR(scriptPath, constant.DirPerm, false)
|
||||
logStr := i18n.GetWithName("ExecShell", operate)
|
||||
task.LogStart(logStr)
|
||||
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute), cmd.WithScriptPath(scriptPath), cmd.WithWorkDir(workDir))
|
||||
out, err := cmdMgr.RunWithStdout("bash")
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute), cmd.WithWorkDir(workDir))
|
||||
out, err := cmdMgr.RunWithStdoutBashC(scriptPath)
|
||||
if err != nil {
|
||||
if out != "" {
|
||||
err = errors.New(out)
|
||||
@@ -1020,12 +1127,11 @@ func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) error
|
||||
errMsg string
|
||||
)
|
||||
if pullImages && appInstall.App.Type != "php" {
|
||||
projectName := strings.ToLower(appInstall.Name)
|
||||
envByte, err := files.NewFileOp().GetContent(appInstall.GetEnvPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
images, err := composeV2.GetDockerComposeImages(projectName, envByte, []byte(appInstall.DockerCompose))
|
||||
images, err := composeV2.GetImagesFromDockerCompose(envByte, []byte(appInstall.DockerCompose))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1050,8 +1156,8 @@ func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) error
|
||||
if strings.Contains(errOur, "no such host") {
|
||||
errMsg = i18n.GetMsgByKey("ErrNoSuchHost") + ":"
|
||||
}
|
||||
if strings.Contains(errOur, "timeout") {
|
||||
errMsg = i18n.GetMsgByKey("ErrImagePullTimeOut") + ":"
|
||||
if strings.Contains(errOur, "Error response from daemon") {
|
||||
errMsg = i18n.GetMsgByKey("PullImageTimeout") + ":"
|
||||
}
|
||||
}
|
||||
appInstall.Message = errMsg + errOur
|
||||
@@ -1490,7 +1596,7 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool)
|
||||
continue
|
||||
}
|
||||
lastVersion := versions[0]
|
||||
if app.Key == constant.AppMysql {
|
||||
if app.Key == constant.AppMysql || app.Key == constant.AppMysqlCluster {
|
||||
for _, version := range versions {
|
||||
majorVersion := getMajorVersion(installed.Version)
|
||||
if !strings.HasPrefix(version, majorVersion) {
|
||||
@@ -1642,6 +1748,14 @@ func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName
|
||||
deploy["resources"] = resource
|
||||
serviceValue["deploy"] = deploy
|
||||
|
||||
if req.RestartPolicy != "" {
|
||||
if req.RestartPolicy == "on-failure" {
|
||||
serviceValue["restart"] = "on-failure:5"
|
||||
} else {
|
||||
serviceValue["restart"] = req.RestartPolicy
|
||||
}
|
||||
}
|
||||
|
||||
ports, ok := serviceValue["ports"].([]interface{})
|
||||
if ok {
|
||||
for i, port := range ports {
|
||||
@@ -1744,6 +1858,17 @@ func isHostModel(dockerCompose string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func getRestartPolicy(yml string) string {
|
||||
var project docker.ComposeProject
|
||||
if err := yaml.Unmarshal([]byte(yml), &project); err != nil {
|
||||
return ""
|
||||
}
|
||||
for _, service := range project.Services {
|
||||
return service.Restart
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getMajorVersion(version string) string {
|
||||
parts := strings.Split(version, ".")
|
||||
if len(parts) >= 2 {
|
||||
@@ -1766,8 +1891,16 @@ func ignoreUpdate(installed model.AppInstall) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
if installed.App.Key == "sqlbot" {
|
||||
if common.CompareVersion("1.1.0", installed.Version) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
ignores, _ := appIgnoreUpgradeRepo.List(appDetailRepo.WithAppId(installed.AppId), appIgnoreUpgradeRepo.WithScope("all"))
|
||||
return len(ignores) > 0
|
||||
if len(ignores) > 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func RequestDownloadCallBack(downloadCallBackUrl string) {
|
||||
@@ -1792,29 +1925,67 @@ func getAppTags(appID uint, lang string) ([]response.TagDTO, error) {
|
||||
}
|
||||
var res []response.TagDTO
|
||||
for _, t := range tags {
|
||||
if t.Name != "" {
|
||||
var translations = make(map[string]string)
|
||||
_ = json.Unmarshal([]byte(t.Translations), &translations)
|
||||
if name, ok := translations[lang]; ok {
|
||||
tagDTO := response.TagDTO{
|
||||
ID: t.ID,
|
||||
Key: t.Key,
|
||||
Name: t.Name,
|
||||
Name: name,
|
||||
}
|
||||
res = append(res, tagDTO)
|
||||
} else {
|
||||
var translations = make(map[string]string)
|
||||
_ = json.Unmarshal([]byte(t.Translations), &translations)
|
||||
if name, ok := translations[lang]; ok {
|
||||
tagDTO := response.TagDTO{
|
||||
ID: t.ID,
|
||||
Key: t.Key,
|
||||
Name: name,
|
||||
}
|
||||
res = append(res, tagDTO)
|
||||
}
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func handleSiteDir(app model.App, appDetail model.AppDetail, req request.AppInstallCreate, t *task.Task) error {
|
||||
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
|
||||
oldWebStePath, _ := settingRepo.GetValueByKey("WEBSITE_DIR")
|
||||
fileOp := files.NewFileOp()
|
||||
if oldWebStePath != "" && oldWebStePath != siteDir && fileOp.Stat(oldWebStePath) {
|
||||
t.Log(i18n.GetWithName("MoveSiteDir", siteDir))
|
||||
if fileOp.Stat(siteDir) {
|
||||
if fileOp.Stat(path.Join(siteDir, "conf.d")) {
|
||||
_ = fileOp.Rename(path.Join(siteDir, "conf.d"), path.Join(siteDir, "conf.d.bak"))
|
||||
}
|
||||
if fileOp.Stat(path.Join(siteDir, "sites")) {
|
||||
_ = fileOp.Rename(path.Join(siteDir, "sites"), path.Join(siteDir, "sites.bak"))
|
||||
}
|
||||
if err := fileOp.Rename(path.Join(oldWebStePath, "sites"), path.Join(siteDir, "sites")); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.Rename(path.Join(oldWebStePath, "conf.d"), path.Join(siteDir, "conf.d")); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := fileOp.Rename(oldWebStePath, siteDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
t.Log(i18n.GetMsgByKey("MoveSiteDirSuccess"))
|
||||
}
|
||||
if !fileOp.Stat(siteDir) {
|
||||
_ = fileOp.CreateDir(siteDir, constant.DirPerm)
|
||||
_ = fileOp.CreateDir(path.Join(siteDir, "conf.d"), constant.DirPerm)
|
||||
}
|
||||
err := settingRepo.UpdateOrCreate("WEBSITE_DIR", siteDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go RestartPHPRuntime()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleOpenrestyFile(appInstall *model.AppInstall) error {
|
||||
websites, _ := websiteRepo.List()
|
||||
hasDefaultWebsite := false
|
||||
@@ -1900,3 +2071,59 @@ func handleSSLConfig(appInstall *model.AppInstall, hasDefaultWebsite bool) error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SyncTags(remoteProperties dto.ExtraProperties) error {
|
||||
tx, ctx := getTxAndContext()
|
||||
defer tx.Rollback()
|
||||
localTags, _ := tagRepo.All()
|
||||
localTagsMap := make(map[string]*model.Tag)
|
||||
for i := range localTags {
|
||||
localTagsMap[localTags[i].Key] = &localTags[i]
|
||||
}
|
||||
var err error
|
||||
remoteTagsMap := make(map[string]*dto.Tag)
|
||||
for i := range remoteProperties.Tags {
|
||||
remoteTagsMap[remoteProperties.Tags[i].Key] = &remoteProperties.Tags[i]
|
||||
}
|
||||
|
||||
for key, localTag := range localTagsMap {
|
||||
if _, exists := remoteTagsMap[key]; !exists {
|
||||
_ = tagRepo.DeleteByID(ctx, localTag.ID)
|
||||
}
|
||||
}
|
||||
|
||||
for _, remoteTag := range remoteProperties.Tags {
|
||||
translations, _ := json.Marshal(remoteTag.Locales)
|
||||
|
||||
if existTag, exists := localTagsMap[remoteTag.Key]; exists {
|
||||
if needsUpdate(existTag, remoteTag, string(translations)) {
|
||||
existTag.Name = remoteTag.Name
|
||||
existTag.Sort = remoteTag.Sort
|
||||
existTag.Translations = string(translations)
|
||||
|
||||
if err = tagRepo.Save(ctx, existTag); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
newTag := &model.Tag{
|
||||
Key: remoteTag.Key,
|
||||
Name: remoteTag.Name,
|
||||
Sort: remoteTag.Sort,
|
||||
Translations: string(translations),
|
||||
}
|
||||
if err = tagRepo.Create(ctx, newTag); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
return nil
|
||||
}
|
||||
|
||||
func needsUpdate(localTag *model.Tag, remoteTag dto.Tag, translations string) bool {
|
||||
return localTag.Name != remoteTag.Name ||
|
||||
localTag.Sort != remoteTag.Sort ||
|
||||
localTag.Translations != translations
|
||||
}
|
||||
|
||||
@@ -14,9 +14,11 @@ 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/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cloud_storage"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cloud_storage/client"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
|
||||
@@ -393,9 +395,13 @@ type backupClientHelper struct {
|
||||
name string
|
||||
backupPath string
|
||||
client cloud_storage.CloudStorageClient
|
||||
|
||||
isOk bool
|
||||
hasBackup bool
|
||||
message string
|
||||
}
|
||||
|
||||
func NewBackupClientMap(ids []string) (map[string]backupClientHelper, error) {
|
||||
func NewBackupClientMap(ids []string) map[string]backupClientHelper {
|
||||
var accounts []model.BackupAccount
|
||||
var idItems []uint
|
||||
for i := 0; i < len(ids); i++ {
|
||||
@@ -406,18 +412,59 @@ func NewBackupClientMap(ids []string) (map[string]backupClientHelper, error) {
|
||||
clientMap := make(map[string]backupClientHelper)
|
||||
for _, item := range accounts {
|
||||
backClient, err := newClient(&item, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientMap[fmt.Sprintf("%v", item.ID)] = backupClientHelper{
|
||||
itemHelper := backupClientHelper{
|
||||
client: backClient,
|
||||
name: item.Name,
|
||||
backupPath: item.BackupPath,
|
||||
accountType: item.Type,
|
||||
id: item.ID,
|
||||
isOk: err == nil,
|
||||
}
|
||||
if err != nil {
|
||||
itemHelper.message = err.Error()
|
||||
}
|
||||
clientMap[fmt.Sprintf("%v", item.ID)] = itemHelper
|
||||
}
|
||||
return clientMap, nil
|
||||
return clientMap
|
||||
}
|
||||
|
||||
func uploadWithMap(taskItem task.Task, accountMap map[string]backupClientHelper, src, dst, accountIDs string, downloadAccountID, retry uint) error {
|
||||
accounts := strings.Split(accountIDs, ",")
|
||||
for _, account := range accounts {
|
||||
if len(account) == 0 {
|
||||
continue
|
||||
}
|
||||
itemBackup, ok := accountMap[account]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if itemBackup.hasBackup {
|
||||
continue
|
||||
}
|
||||
if !itemBackup.isOk {
|
||||
taskItem.LogFailed(i18n.GetMsgWithDetail("LoadBackupFailed", itemBackup.message))
|
||||
continue
|
||||
}
|
||||
taskItem.LogStart(i18n.GetMsgWithMap("UploadFile", map[string]interface{}{
|
||||
"file": path.Join(itemBackup.backupPath, dst),
|
||||
"backup": itemBackup.name,
|
||||
}))
|
||||
for i := 0; i < int(retry); i++ {
|
||||
_, err := itemBackup.client.Upload(src, path.Join(itemBackup.backupPath, dst))
|
||||
taskItem.LogWithStatus(i18n.GetMsgByKey("Upload"), err)
|
||||
if err != nil {
|
||||
if account == fmt.Sprintf("%d", downloadAccountID) {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
itemBackup.hasBackup = true
|
||||
accountMap[account] = itemBackup
|
||||
}
|
||||
os.RemoveAll(src)
|
||||
return nil
|
||||
}
|
||||
|
||||
func newClient(account *model.BackupAccount, isEncrypt bool) (cloud_storage.CloudStorageClient, error) {
|
||||
@@ -541,11 +588,17 @@ func changeLocalBackup(oldPath, newPath string) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if fileOp.Stat(path.Join(oldPath, "master")) {
|
||||
if err := fileOp.CopyDir(path.Join(oldPath, "master"), newPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_ = fileOp.RmRf(path.Join(oldPath, "app"))
|
||||
_ = fileOp.RmRf(path.Join(oldPath, "database"))
|
||||
_ = fileOp.RmRf(path.Join(oldPath, "directory"))
|
||||
_ = fileOp.RmRf(path.Join(oldPath, "system_snapshot"))
|
||||
_ = fileOp.RmRf(path.Join(oldPath, "website"))
|
||||
_ = fileOp.RmRf(path.Join(oldPath, "log"))
|
||||
_ = fileOp.RmRf(path.Join(oldPath, "master"))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ func handlePostgresqlBackup(db DatabaseHelper, parentTask *task.Task, targetDir,
|
||||
}
|
||||
|
||||
itemHandler := func() error { return doPostgresqlgBackup(db, targetDir, fileName) }
|
||||
backupTask.AddSubTask(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeWebsite), func(task *task.Task) error { return itemHandler() }, nil)
|
||||
backupTask.AddSubTask(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeDatabase), func(task *task.Task) error { return itemHandler() }, nil)
|
||||
if parentTask != nil {
|
||||
return itemHandler()
|
||||
}
|
||||
@@ -137,7 +137,7 @@ func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRol
|
||||
}
|
||||
itemTask = parentTask
|
||||
if parentTask == nil {
|
||||
itemTask, err = task.NewTaskWithOps("Redis", task.TaskRecover, task.TaskScopeDatabase, req.TaskID, dbInfo.ID)
|
||||
itemTask, err = task.NewTaskWithOps(req.Name, task.TaskRecover, task.TaskScopeDatabase, req.TaskID, dbInfo.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
@@ -249,20 +250,32 @@ func (u *BackupRecordService) LoadRecordSize(req dto.SearchForSize) ([]dto.Recor
|
||||
recordIds = append(recordIds, fmt.Sprintf("%v", record.DownloadID))
|
||||
}
|
||||
}
|
||||
clientMap, err := NewBackupClientMap(recordIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientMap := NewBackupClientMap(recordIds)
|
||||
var datas []dto.RecordFileSize
|
||||
var wg sync.WaitGroup
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
for i := 0; i < len(list); i++ {
|
||||
datas = append(datas, dto.RecordFileSize{ID: list[i].ID})
|
||||
if val, ok := clientMap[fmt.Sprintf("%v", list[i].DownloadID)]; ok {
|
||||
if !val.isOk {
|
||||
continue
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(index int) {
|
||||
datas[index].Size, _ = val.client.Size(path.Join(val.backupPath, list[i].FilePath))
|
||||
wg.Done()
|
||||
defer wg.Done()
|
||||
done := make(chan struct{}, 1)
|
||||
go func() {
|
||||
datas[index].Size, _ = val.client.Size(path.Join(val.backupPath, list[i].FilePath))
|
||||
defer close(done)
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
)
|
||||
|
||||
func (u *BackupService) RedisBackup(req dto.CommonBackup) error {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name)
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -64,7 +64,7 @@ func (u *BackupService) RedisBackup(req dto.CommonBackup) error {
|
||||
}
|
||||
|
||||
func (u *BackupService) RedisRecover(req dto.CommonRecover) error {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name)
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -76,6 +76,7 @@ func (u *BackupService) RedisRecover(req dto.CommonRecover) error {
|
||||
}
|
||||
|
||||
func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, backupDir, fileName, secret, taskID string) error {
|
||||
|
||||
var (
|
||||
err error
|
||||
itemTask *task.Task
|
||||
@@ -102,7 +103,7 @@ func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, backupDi
|
||||
}
|
||||
|
||||
if strings.HasSuffix(fileName, ".tar.gz") {
|
||||
redisDataDir := fmt.Sprintf("%s/%s/%s/data/appendonlydir", global.Dir.AppInstallDir, "redis", redisInfo.Name)
|
||||
redisDataDir := fmt.Sprintf("%s/%s/%s/data/appendonlydir", global.Dir.AppInstallDir, redisInfo.Key, redisInfo.Name)
|
||||
if err := fileOp.TarGzCompressPro(true, redisDataDir, path.Join(backupDir, fileName), secret, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -111,14 +112,14 @@ func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, backupDi
|
||||
if strings.HasSuffix(fileName, ".aof") {
|
||||
stdout1, err := cmd.RunDefaultWithStdoutBashCf("docker cp %s:/data/appendonly.aof %s/%s", redisInfo.ContainerName, backupDir, fileName)
|
||||
if err != nil {
|
||||
return errors.New(string(stdout1))
|
||||
return errors.New(stdout1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
stdout1, err1 := cmd.RunDefaultWithStdoutBashCf("docker cp %s:/data/dump.rdb %s/%s", redisInfo.ContainerName, backupDir, fileName)
|
||||
if err1 != nil {
|
||||
return errors.New(string(stdout1))
|
||||
return errors.New(stdout1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -196,12 +197,12 @@ func handleRedisRecover(redisInfo *repo.RootInfo, parentTask *task.Task, recover
|
||||
}
|
||||
}()
|
||||
}
|
||||
composeDir := fmt.Sprintf("%s/redis/%s", global.Dir.AppInstallDir, redisInfo.Name)
|
||||
composeDir := fmt.Sprintf("%s/%s/%s", global.Dir.AppInstallDir, redisInfo.Key, redisInfo.Name)
|
||||
if _, err := compose.Down(composeDir + "/docker-compose.yml"); err != nil {
|
||||
return err
|
||||
}
|
||||
if appendonly == "yes" && strings.HasPrefix(redisInfo.Version, "7.") {
|
||||
redisDataDir := fmt.Sprintf("%s/%s/%s/data", global.Dir.AppInstallDir, "redis", redisInfo.Name)
|
||||
redisDataDir := fmt.Sprintf("%s/%s/%s/data", global.Dir.AppInstallDir, redisInfo.Key, redisInfo.Name)
|
||||
if err := fileOp.TarGzExtractPro(recoverFile, redisDataDir, secret); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+25
-25
@@ -11,13 +11,13 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
|
||||
"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/alert_push"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/systemctl"
|
||||
@@ -158,9 +158,10 @@ func (c *ClamService) SearchWithPage(req dto.SearchClamWithPage) (int64, interfa
|
||||
AlertType: "clams",
|
||||
EntryID: datas[i].ID,
|
||||
}
|
||||
alertCount := xpack.GetAlert(alertBase)
|
||||
if alertCount != 0 {
|
||||
datas[i].AlertCount = alertCount
|
||||
alertInfo, _ := alertRepo.Get(alertRepo.WithByType(alertBase.AlertType), alertRepo.WithByProject(strconv.Itoa(int(alertBase.EntryID))), repo.WithByStatus(constant.AlertEnable))
|
||||
datas[i].AlertMethod = alertInfo.Method
|
||||
if alertInfo.SendCount != 0 {
|
||||
datas[i].AlertCount = alertInfo.SendCount
|
||||
} else {
|
||||
datas[i].AlertCount = 0
|
||||
}
|
||||
@@ -190,14 +191,16 @@ func (c *ClamService) Create(req dto.ClamCreate) error {
|
||||
if err := clamRepo.Create(&clam); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.AlertCount != 0 {
|
||||
createAlert := dto.CreateOrUpdateAlert{
|
||||
AlertTitle: req.AlertTitle,
|
||||
AlertCount: req.AlertCount,
|
||||
AlertType: "clams",
|
||||
EntryID: clam.ID,
|
||||
if req.AlertCount != 0 && req.AlertTitle != "" && req.AlertMethod != "" {
|
||||
createAlert := dto.AlertCreate{
|
||||
Title: req.AlertTitle,
|
||||
SendCount: req.AlertCount,
|
||||
Method: req.AlertMethod,
|
||||
Type: "clams",
|
||||
Project: strconv.Itoa(int(clam.ID)),
|
||||
Status: constant.AlertEnable,
|
||||
}
|
||||
err := xpack.CreateAlert(createAlert)
|
||||
err := NewIAlertService().CreateAlert(createAlert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -247,13 +250,14 @@ func (c *ClamService) Update(req dto.ClamUpdate) error {
|
||||
if err := clamRepo.Update(req.ID, upMap); err != nil {
|
||||
return err
|
||||
}
|
||||
updateAlert := dto.CreateOrUpdateAlert{
|
||||
AlertTitle: req.AlertTitle,
|
||||
AlertType: "clams",
|
||||
AlertCount: req.AlertCount,
|
||||
EntryID: clam.ID,
|
||||
updateAlert := dto.AlertCreate{
|
||||
Title: req.AlertTitle,
|
||||
SendCount: req.AlertCount,
|
||||
Method: req.AlertMethod,
|
||||
Type: "clams",
|
||||
Project: strconv.Itoa(int(clam.ID)),
|
||||
}
|
||||
err := xpack.UpdateAlert(updateAlert)
|
||||
err := NewIAlertService().ExternalUpdateAlert(updateAlert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -297,11 +301,7 @@ func (c *ClamService) Delete(req dto.ClamDelete) error {
|
||||
if err := clamRepo.Delete(repo.WithByID(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
alertBase := dto.AlertBase{
|
||||
AlertType: "clams",
|
||||
EntryID: clam.ID,
|
||||
}
|
||||
err := xpack.DeleteAlert(alertBase)
|
||||
err := alertRepo.Delete(alertRepo.WithByProject(strconv.Itoa(int(clam.ID))), alertRepo.WithByType("clams"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -344,7 +344,7 @@ func (c *ClamService) HandleOnce(req dto.OperateByID) error {
|
||||
}
|
||||
}
|
||||
global.LOG.Debugf("clamdscan --fdpass %s %s -l %s", strategy, clam.Path, logFile)
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("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)
|
||||
}
|
||||
@@ -639,7 +639,7 @@ func handleAlert(stdout, clamName string, clamId uint) {
|
||||
EntryID: clamId,
|
||||
Param: strconv.Itoa(infectedFiles),
|
||||
}
|
||||
_ = xpack.PushAlert(pushAlert)
|
||||
_ = alert_push.PushAlert(pushAlert)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
+317
-168
@@ -12,6 +12,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -44,15 +45,16 @@ import (
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/go-connections/nat"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/mem"
|
||||
"github.com/shirou/gopsutil/v4/cpu"
|
||||
"github.com/shirou/gopsutil/v4/mem"
|
||||
)
|
||||
|
||||
type ContainerService struct{}
|
||||
|
||||
type IContainerService interface {
|
||||
Page(req dto.PageContainer) (int64, interface{}, error)
|
||||
List() []string
|
||||
List() []dto.ContainerOptions
|
||||
ListByImage(imageName string) []dto.ContainerOptions
|
||||
LoadStatus() (dto.ContainerStatus, error)
|
||||
PageNetwork(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
ListNetwork() ([]dto.Options, error)
|
||||
@@ -61,7 +63,7 @@ type IContainerService interface {
|
||||
PageCompose(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
CreateCompose(req dto.ComposeCreate) error
|
||||
ComposeOperation(req dto.ComposeOperation) error
|
||||
ContainerCreate(req dto.ContainerOperate) error
|
||||
ContainerCreate(req dto.ContainerOperate, inThread bool) error
|
||||
ContainerCreateByCommand(req dto.ContainerCreateByCommand) error
|
||||
ContainerUpdate(req dto.ContainerOperate) error
|
||||
ContainerUpgrade(req dto.ContainerUpgrade) error
|
||||
@@ -81,9 +83,9 @@ type IContainerService interface {
|
||||
CreateVolume(req dto.VolumeCreate) error
|
||||
TestCompose(req dto.ComposeCreate) (bool, error)
|
||||
ComposeUpdate(req dto.ComposeUpdate) error
|
||||
Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error)
|
||||
Prune(req dto.ContainerPrune) error
|
||||
|
||||
LoadUsers(req dto.OperationWithName) ([]string, error)
|
||||
LoadUsers(req dto.OperationWithName) []string
|
||||
|
||||
StreamLogs(ctx *gin.Context, params dto.StreamLog)
|
||||
}
|
||||
@@ -221,7 +223,8 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
return int64(total), backDatas, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) List() []string {
|
||||
func (u *ContainerService) List() []dto.ContainerOptions {
|
||||
var options []dto.ContainerOptions
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load docker client for contianer list failed, err: %v", err)
|
||||
@@ -230,19 +233,45 @@ func (u *ContainerService) List() []string {
|
||||
defer client.Close()
|
||||
containers, err := client.ContainerList(context.Background(), container.ListOptions{All: true})
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load contianer list failed, err: %v", err)
|
||||
global.LOG.Errorf("load container list failed, err: %v", err)
|
||||
return nil
|
||||
}
|
||||
var datas []string
|
||||
for _, container := range containers {
|
||||
for _, name := range container.Names {
|
||||
if len(name) != 0 {
|
||||
datas = append(datas, strings.TrimPrefix(name, "/"))
|
||||
options = append(options, dto.ContainerOptions{Name: strings.TrimPrefix(name, "/"), State: container.State})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return datas
|
||||
return options
|
||||
}
|
||||
|
||||
func (u *ContainerService) ListByImage(imageName string) []dto.ContainerOptions {
|
||||
var options []dto.ContainerOptions
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load docker client for contianer list failed, err: %v", err)
|
||||
return nil
|
||||
}
|
||||
defer client.Close()
|
||||
containers, err := client.ContainerList(context.Background(), container.ListOptions{All: true})
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load container list failed, err: %v", err)
|
||||
return nil
|
||||
}
|
||||
for _, container := range containers {
|
||||
if container.Image != imageName {
|
||||
continue
|
||||
}
|
||||
for _, name := range container.Names {
|
||||
if len(name) != 0 {
|
||||
options = append(options, dto.ContainerOptions{Name: strings.TrimPrefix(name, "/"), State: container.State})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
func (u *ContainerService) LoadStatus() (dto.ContainerStatus, error) {
|
||||
@@ -416,66 +445,93 @@ func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error) {
|
||||
report := dto.ContainerPruneReport{}
|
||||
func (u *ContainerService) Prune(req dto.ContainerPrune) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return report, err
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
pruneFilters := filters.NewArgs()
|
||||
if req.WithTagAll {
|
||||
pruneFilters.Add("dangling", "false")
|
||||
if req.PruneType != "image" {
|
||||
pruneFilters.Add("until", "24h")
|
||||
}
|
||||
}
|
||||
name := ""
|
||||
switch req.PruneType {
|
||||
case "container":
|
||||
rep, err := client.ContainersPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
report.DeletedNumber = len(rep.ContainersDeleted)
|
||||
report.SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||
name = "Container"
|
||||
case "image":
|
||||
rep, err := client.ImagesPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
report.DeletedNumber = len(rep.ImagesDeleted)
|
||||
report.SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||
case "network":
|
||||
rep, err := client.NetworksPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
report.DeletedNumber = len(rep.NetworksDeleted)
|
||||
name = "Image"
|
||||
case "volume":
|
||||
versions, err := client.ServerVersion(context.Background())
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
if common.ComparePanelVersion(versions.APIVersion, "1.42") {
|
||||
pruneFilters.Add("all", "true")
|
||||
}
|
||||
rep, err := client.VolumesPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
report.DeletedNumber = len(rep.VolumesDeleted)
|
||||
report.SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||
name = "Volume"
|
||||
case "buildcache":
|
||||
opts := build.CachePruneOptions{}
|
||||
opts.All = true
|
||||
rep, err := client.BuildCachePrune(context.Background(), opts)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
report.DeletedNumber = len(rep.CachesDeleted)
|
||||
report.SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||
name = "BuildCache"
|
||||
case "network":
|
||||
name = "Network"
|
||||
}
|
||||
return report, nil
|
||||
taskItem, err := task.NewTaskWithOps(i18n.GetMsgByKey(name), task.TaskClean, task.TaskScopeContainer, req.TaskID, 1)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("new task for create container failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
taskItem.AddSubTask(i18n.GetMsgByKey("TaskClean"), func(t *task.Task) error {
|
||||
pruneFilters := filters.NewArgs()
|
||||
if req.WithTagAll {
|
||||
pruneFilters.Add("dangling", "false")
|
||||
if req.PruneType != "image" {
|
||||
pruneFilters.Add("until", "24h")
|
||||
}
|
||||
}
|
||||
DeletedNumber := 0
|
||||
SpaceReclaimed := 0
|
||||
switch req.PruneType {
|
||||
case "container":
|
||||
rep, err := client.ContainersPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
DeletedNumber = len(rep.ContainersDeleted)
|
||||
SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||
case "image":
|
||||
rep, err := client.ImagesPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
DeletedNumber = len(rep.ImagesDeleted)
|
||||
SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||
case "network":
|
||||
rep, err := client.NetworksPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
DeletedNumber = len(rep.NetworksDeleted)
|
||||
case "volume":
|
||||
versions, err := client.ServerVersion(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if common.ComparePanelVersion(versions.APIVersion, "1.42") {
|
||||
pruneFilters.Add("all", "true")
|
||||
}
|
||||
rep, err := client.VolumesPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
DeletedNumber = len(rep.VolumesDeleted)
|
||||
SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||
case "buildcache":
|
||||
opts := build.CachePruneOptions{}
|
||||
opts.All = true
|
||||
rep, err := client.BuildCachePrune(context.Background(), opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
DeletedNumber = len(rep.CachesDeleted)
|
||||
SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||
}
|
||||
taskItem.Log(i18n.GetMsgWithMap("PruneHelper", map[string]interface{}{"name": i18n.GetMsgByKey(name), "count": DeletedNumber, "size": common.LoadSizeUnit2F(float64(SpaceReclaimed))}))
|
||||
return nil
|
||||
}, nil)
|
||||
go func() {
|
||||
_ = taskItem.Execute()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) LoadResourceLimit() (*dto.ResourceLimit, error) {
|
||||
@@ -495,7 +551,7 @@ func (u *ContainerService) LoadResourceLimit() (*dto.ResourceLimit, error) {
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerCreate(req dto.ContainerOperate) error {
|
||||
func (u *ContainerService) ContainerCreate(req dto.ContainerOperate, inThread bool) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -513,63 +569,62 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerOperate) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
taskItem.AddSubTask(i18n.GetWithName("ContainerImagePull", req.Image), func(t *task.Task) error {
|
||||
if !checkImageExist(client, req.Image) || req.ForcePull {
|
||||
if err := pullImages(taskItem, client, req.Image); err != nil {
|
||||
if !req.ForcePull {
|
||||
return err
|
||||
}
|
||||
taskItem.AddSubTask(i18n.GetWithName("ContainerImagePull", req.Image), func(t *task.Task) error {
|
||||
if !checkImageExist(client, req.Image) || req.ForcePull {
|
||||
if err := pullImages(taskItem, client, req.Image); err != nil {
|
||||
if !req.ForcePull {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
taskItem.AddSubTask(i18n.GetMsgByKey("ContainerImageCheck"), func(t *task.Task) error {
|
||||
imageInfo, _, err := client.ImageInspectWithRaw(ctx, req.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(req.Entrypoint) == 0 {
|
||||
req.Entrypoint = imageInfo.Config.Entrypoint
|
||||
}
|
||||
if len(req.Cmd) == 0 {
|
||||
req.Cmd = imageInfo.Config.Cmd
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
taskItem.AddSubTask(i18n.GetWithName("ContainerCreate", req.Name), func(t *task.Task) error {
|
||||
config, hostConf, networkConf, err := loadConfigInfo(true, req, nil)
|
||||
taskItem.LogWithStatus(i18n.GetMsgByKey("ContainerLoadInfo"), err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
con, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)
|
||||
if err != nil {
|
||||
taskItem.Log(i18n.GetMsgByKey("ContainerCreateFailed"))
|
||||
_ = client.ContainerRemove(ctx, req.Name, container.RemoveOptions{RemoveVolumes: true, Force: true})
|
||||
return err
|
||||
}
|
||||
err = client.ContainerStart(ctx, con.ID, container.StartOptions{})
|
||||
taskItem.LogWithStatus(i18n.GetMsgByKey("ContainerStartCheck"), err)
|
||||
if err != nil {
|
||||
taskItem.Log(i18n.GetMsgByKey("ContainerCreateFailed"))
|
||||
_ = client.ContainerRemove(ctx, req.Name, container.RemoveOptions{RemoveVolumes: true, Force: true})
|
||||
return fmt.Errorf("create successful but start failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
if err := taskItem.Execute(); err != nil {
|
||||
global.LOG.Error(err.Error())
|
||||
}
|
||||
}()
|
||||
wg.Wait()
|
||||
return nil
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
taskItem.AddSubTask(i18n.GetMsgByKey("ContainerImageCheck"), func(t *task.Task) error {
|
||||
imageInfo, _, err := client.ImageInspectWithRaw(ctx, req.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(req.Entrypoint) == 0 {
|
||||
req.Entrypoint = imageInfo.Config.Entrypoint
|
||||
}
|
||||
if len(req.Cmd) == 0 {
|
||||
req.Cmd = imageInfo.Config.Cmd
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
taskItem.AddSubTask(i18n.GetWithName("ContainerCreate", req.Name), func(t *task.Task) error {
|
||||
config, hostConf, networkConf, err := loadConfigInfo(true, req, nil)
|
||||
taskItem.LogWithStatus(i18n.GetMsgByKey("ContainerLoadInfo"), err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
con, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)
|
||||
if err != nil {
|
||||
taskItem.Log(i18n.GetMsgByKey("ContainerCreateFailed"))
|
||||
_ = client.ContainerRemove(ctx, req.Name, container.RemoveOptions{RemoveVolumes: true, Force: true})
|
||||
return err
|
||||
}
|
||||
err = client.ContainerStart(ctx, con.ID, container.StartOptions{})
|
||||
taskItem.LogWithStatus(i18n.GetMsgByKey("ContainerStartCheck"), err)
|
||||
if err != nil {
|
||||
taskItem.Log(i18n.GetMsgByKey("ContainerCreateFailed"))
|
||||
_ = client.ContainerRemove(ctx, req.Name, container.RemoveOptions{RemoveVolumes: true, Force: true})
|
||||
return fmt.Errorf("create successful but start failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
if inThread {
|
||||
go func() {
|
||||
if err := taskItem.Execute(); err != nil {
|
||||
global.LOG.Error(err.Error())
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
return taskItem.Execute()
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.ContainerOperate, error) {
|
||||
@@ -656,6 +711,12 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
|
||||
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)
|
||||
@@ -676,7 +737,7 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
|
||||
|
||||
taskItem.AddSubTask(i18n.GetWithName("ContainerCreate", req.Name), func(t *task.Task) error {
|
||||
err := client.ContainerRemove(ctx, req.ContainerID, container.RemoveOptions{Force: true})
|
||||
taskItem.LogWithStatus(i18n.GetMsgByKey("ContainerRemoveOld"), err)
|
||||
taskItem.LogWithStatus(i18n.GetWithName("ContainerRemoveOld", req.Name), err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -718,17 +779,14 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
|
||||
}
|
||||
defer client.Close()
|
||||
ctx := context.Background()
|
||||
oldContainer, err := client.ContainerInspect(ctx, req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
taskItem, err := task.NewTaskWithOps(req.Name, task.TaskUpgrade, task.TaskScopeContainer, req.TaskID, 1)
|
||||
taskItem, err := task.NewTaskWithOps(req.Image, task.TaskUpgrade, task.TaskScopeImage, req.TaskID, 1)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("new task for create container failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
taskItem.AddSubTask(i18n.GetWithName("ContainerImagePull", req.Image), func(t *task.Task) error {
|
||||
taskItem.LogStart(i18n.GetWithName("ContainerImagePull", req.Image))
|
||||
if !checkImageExist(client, req.Image) || req.ForcePull {
|
||||
if err := pullImages(taskItem, client, req.Image); err != nil {
|
||||
if !req.ForcePull {
|
||||
@@ -739,38 +797,58 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
taskItem.AddSubTask(i18n.GetWithName("ContainerCreate", req.Name), func(t *task.Task) error {
|
||||
config := oldContainer.Config
|
||||
config.Image = req.Image
|
||||
hostConf := oldContainer.HostConfig
|
||||
var networkConf network.NetworkingConfig
|
||||
if oldContainer.NetworkSettings != nil {
|
||||
for networkKey := range oldContainer.NetworkSettings.Networks {
|
||||
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{networkKey: {}}
|
||||
break
|
||||
for _, item := range req.Names {
|
||||
var oldContainer container.InspectResponse
|
||||
taskItem.AddSubTask(i18n.GetWithName("ContainerLoadInfo", item), func(t *task.Task) error {
|
||||
taskItem.Logf("----------------- %s -----------------", item)
|
||||
oldContainer, err = client.ContainerInspect(ctx, item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err := client.ContainerRemove(ctx, req.Name, container.RemoveOptions{Force: true})
|
||||
taskItem.LogWithStatus(i18n.GetMsgByKey("ContainerRemoveOld"), err)
|
||||
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)
|
||||
|
||||
con, err := client.ContainerCreate(ctx, config, hostConf, &networkConf, &v1.Platform{}, req.Name)
|
||||
if err != nil {
|
||||
taskItem.Log(i18n.GetMsgByKey("ContainerRecreate"))
|
||||
reCreateAfterUpdate(req.Name, client, oldContainer.Config, oldContainer.HostConfig, oldContainer.NetworkSettings)
|
||||
return fmt.Errorf("upgrade container failed, err: %v", err)
|
||||
}
|
||||
err = client.ContainerStart(ctx, con.ID, container.StartOptions{})
|
||||
taskItem.LogWithStatus(i18n.GetMsgByKey("ContainerStartCheck"), err)
|
||||
if err != nil {
|
||||
return fmt.Errorf("upgrade successful but start failed, err: %v", err)
|
||||
}
|
||||
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
|
||||
var networkConf network.NetworkingConfig
|
||||
if oldContainer.NetworkSettings != nil {
|
||||
for networkKey := range oldContainer.NetworkSettings.Networks {
|
||||
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{networkKey: {}}
|
||||
break
|
||||
}
|
||||
}
|
||||
err := client.ContainerRemove(ctx, item, container.RemoveOptions{Force: true})
|
||||
taskItem.LogWithStatus(i18n.GetWithName("ContainerRemoveOld", item), err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
con, err := client.ContainerCreate(ctx, config, hostConf, &networkConf, &v1.Platform{}, item)
|
||||
if err != nil {
|
||||
taskItem.Log(i18n.GetMsgByKey("ContainerRecreate"))
|
||||
reCreateAfterUpdate(item, client, oldContainer.Config, oldContainer.HostConfig, oldContainer.NetworkSettings)
|
||||
return fmt.Errorf("upgrade container failed, err: %v", err)
|
||||
}
|
||||
err = client.ContainerStart(ctx, con.ID, container.StartOptions{})
|
||||
taskItem.LogWithStatus(i18n.GetMsgByKey("ContainerStartCheck"), err)
|
||||
if err != nil {
|
||||
return fmt.Errorf("upgrade successful but start failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
}
|
||||
if err := taskItem.Execute(); err != nil {
|
||||
global.LOG.Error(err.Error())
|
||||
}
|
||||
@@ -809,10 +887,23 @@ func (u *ContainerService) ContainerCommit(req dto.ContainerCommit) error {
|
||||
Pause: req.Pause,
|
||||
Config: nil,
|
||||
}
|
||||
_, err = client.ContainerCommit(ctx, req.ContainerId, options)
|
||||
|
||||
taskItem, err := task.NewTaskWithOps(req.NewImageName, task.TaskCommit, task.TaskScopeContainer, req.TaskID, 1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to commit container, err: %v", err)
|
||||
return fmt.Errorf("new task for container commit failed, err: %v", err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
taskItem.AddSubTask(i18n.GetWithName("TaskCommit", req.NewImageName), func(t *task.Task) error {
|
||||
res, err := client.ContainerCommit(ctx, req.ContainerId, options)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to commit container, err: %v", err)
|
||||
}
|
||||
taskItem.Log(res.ID)
|
||||
return nil
|
||||
}, nil)
|
||||
_ = taskItem.Execute()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1014,9 +1105,15 @@ func (u *ContainerService) DownloadContainerLogs(containerType, container, since
|
||||
return buserr.New("ErrCmdIllegal")
|
||||
}
|
||||
commandArg := []string{"logs", container}
|
||||
dockerCommand := global.CONF.DockerConfig.Command
|
||||
if containerType == "compose" {
|
||||
commandArg = []string{"compose", "-f", container, "logs"}
|
||||
if dockerCommand == "docker-compose" {
|
||||
commandArg = []string{"-f", container, "logs"}
|
||||
} else {
|
||||
commandArg = []string{"compose", "-f", container, "logs"}
|
||||
}
|
||||
}
|
||||
|
||||
if tail != "0" {
|
||||
commandArg = append(commandArg, "--tail")
|
||||
commandArg = append(commandArg, tail)
|
||||
@@ -1025,16 +1122,20 @@ func (u *ContainerService) DownloadContainerLogs(containerType, container, since
|
||||
commandArg = append(commandArg, "--since")
|
||||
commandArg = append(commandArg, since)
|
||||
}
|
||||
|
||||
cmd := exec.Command("docker", commandArg...)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
var dockerCmd *exec.Cmd
|
||||
if containerType == "compose" && dockerCommand == "docker-compose" {
|
||||
dockerCmd = exec.Command("docker-compose", commandArg...)
|
||||
} else {
|
||||
dockerCmd = exec.Command("docker", commandArg...)
|
||||
}
|
||||
stdout, err := dockerCmd.StdoutPipe()
|
||||
if err != nil {
|
||||
_ = cmd.Process.Signal(syscall.SIGTERM)
|
||||
_ = dockerCmd.Process.Signal(syscall.SIGTERM)
|
||||
return err
|
||||
}
|
||||
cmd.Stderr = cmd.Stdout
|
||||
if err := cmd.Start(); err != nil {
|
||||
_ = cmd.Process.Signal(syscall.SIGTERM)
|
||||
dockerCmd.Stderr = dockerCmd.Stdout
|
||||
if err := dockerCmd.Start(); err != nil {
|
||||
_ = dockerCmd.Process.Signal(syscall.SIGTERM)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1051,9 +1152,11 @@ func (u *ContainerService) DownloadContainerLogs(containerType, container, since
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
var ansiRegex = regexp.MustCompile(`\x1b\[[0-9;?]*[A-Za-z]|\x1b=|\x1b>`)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if _, err := tempFile.WriteString(line + "\n"); err != nil {
|
||||
cleanLine := ansiRegex.ReplaceAllString(line, "")
|
||||
if _, err := tempFile.WriteString(cleanLine + "\n"); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
@@ -1114,19 +1217,19 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) LoadUsers(req dto.OperationWithName) ([]string, error) {
|
||||
func (u *ContainerService) LoadUsers(req dto.OperationWithName) []string {
|
||||
var users []string
|
||||
std, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s cat /etc/passwd", req.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return users
|
||||
}
|
||||
var users []string
|
||||
lines := strings.Split(string(std), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, ":") {
|
||||
users = append(users, strings.Split(line, ":")[0])
|
||||
}
|
||||
}
|
||||
return users, nil
|
||||
return users
|
||||
}
|
||||
|
||||
func stringsToMap(list []string) map[string]string {
|
||||
@@ -1215,6 +1318,32 @@ func checkImageExist(client *client.Client, imageItem string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func checkImageLike(client *client.Client, imageName string) bool {
|
||||
if client == nil {
|
||||
var err error
|
||||
client, err = docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
images, err := client.ImageList(context.Background(), image.ListOptions{})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, img := range images {
|
||||
for _, tag := range img.RepoTags {
|
||||
parts := strings.Split(tag, "/")
|
||||
imageNameWithTag := parts[len(parts)-1]
|
||||
|
||||
if imageNameWithTag == imageName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func pullImages(task *task.Task, client *client.Client, imageName string) error {
|
||||
dockerCli := docker.NewClientWithExist(client)
|
||||
options := image.PullOptions{}
|
||||
@@ -1300,7 +1429,7 @@ func checkPortStats(ports []dto.PortHelper) (nat.PortMap, error) {
|
||||
portMap[nat.Port(fmt.Sprintf("%d/%s", containerStart+i, port.Protocol))] = []nat.PortBinding{bindItem}
|
||||
}
|
||||
for i := hostStart; i <= hostEnd; i++ {
|
||||
if common.ScanPort(i) {
|
||||
if common.ScanPortWithIP(port.HostIP, i) {
|
||||
return portMap, buserr.WithDetail("ErrPortInUsed", i, nil)
|
||||
}
|
||||
}
|
||||
@@ -1311,7 +1440,7 @@ func checkPortStats(ports []dto.PortHelper) (nat.PortMap, error) {
|
||||
} else {
|
||||
portItem, _ = strconv.Atoi(port.HostPort)
|
||||
}
|
||||
if common.ScanPort(portItem) {
|
||||
if common.ScanPortWithIP(port.HostIP, portItem) {
|
||||
return portMap, buserr.WithDetail("ErrPortInUsed", portItem, nil)
|
||||
}
|
||||
bindItem := nat.PortBinding{HostPort: strconv.Itoa(portItem), HostIP: port.HostIP}
|
||||
@@ -1614,3 +1743,23 @@ 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,18 +5,14 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
|
||||
"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"
|
||||
@@ -25,6 +21,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
@@ -55,14 +52,13 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
||||
}
|
||||
|
||||
composeCreatedByLocal, _ := composeRepo.ListRecord()
|
||||
|
||||
composeLocalMap := make(map[string]dto.ComposeInfo)
|
||||
for _, localItem := range composeCreatedByLocal {
|
||||
composeItemLocal := dto.ComposeInfo{
|
||||
ContainerNumber: 0,
|
||||
CreatedAt: localItem.CreatedAt.Format(constant.DateTimeLayout),
|
||||
ConfigFile: localItem.Path,
|
||||
Workdir: strings.TrimSuffix(localItem.Path, "/docker-compose.yml"),
|
||||
ContainerCount: 0,
|
||||
CreatedAt: localItem.CreatedAt.Format(constant.DateTimeLayout),
|
||||
ConfigFile: localItem.Path,
|
||||
Workdir: strings.TrimSuffix(localItem.Path, "/docker-compose.yml"),
|
||||
}
|
||||
composeItemLocal.CreatedBy = "1Panel"
|
||||
composeItemLocal.Path = localItem.Path
|
||||
@@ -79,18 +75,24 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
||||
CreateTime: time.Unix(container.Created, 0).Format(constant.DateTimeLayout),
|
||||
}
|
||||
if compose, has := composeMap[name]; has {
|
||||
compose.ContainerNumber++
|
||||
compose.ContainerCount++
|
||||
if strings.ToLower(containerItem.State) == "running" {
|
||||
compose.RunningCount++
|
||||
}
|
||||
compose.Containers = append(compose.Containers, containerItem)
|
||||
composeMap[name] = compose
|
||||
} else {
|
||||
config := container.Labels[composeConfigLabel]
|
||||
workdir := container.Labels[composeWorkdirLabel]
|
||||
composeItem := dto.ComposeInfo{
|
||||
ContainerNumber: 1,
|
||||
CreatedAt: time.Unix(container.Created, 0).Format(constant.DateTimeLayout),
|
||||
ConfigFile: config,
|
||||
Workdir: workdir,
|
||||
Containers: []dto.ComposeContainer{containerItem},
|
||||
ContainerCount: 1,
|
||||
CreatedAt: time.Unix(container.Created, 0).Format(constant.DateTimeLayout),
|
||||
ConfigFile: config,
|
||||
Workdir: workdir,
|
||||
Containers: []dto.ComposeContainer{containerItem},
|
||||
}
|
||||
if strings.ToLower(containerItem.State) == "running" {
|
||||
composeItem.RunningCount = 1
|
||||
}
|
||||
createdBy, ok := container.Labels[composeCreatedBy]
|
||||
if ok {
|
||||
@@ -119,8 +121,8 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
||||
}
|
||||
for key, item := range composeMap {
|
||||
if existingItem, exists := mergedMap[key]; exists {
|
||||
if item.ContainerNumber > 0 {
|
||||
if existingItem.ContainerNumber <= 0 {
|
||||
if item.ContainerCount > 0 {
|
||||
if existingItem.ContainerCount <= 0 {
|
||||
mergedMap[key] = item
|
||||
}
|
||||
}
|
||||
@@ -174,7 +176,7 @@ func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) {
|
||||
if err := newComposeEnv(req.Path, req.Env); err != nil {
|
||||
return false, err
|
||||
}
|
||||
cmd := exec.Command("docker", "compose", "-f", req.Path, "config")
|
||||
cmd := getComposeCmd(req.Path, "config")
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return false, errors.New(string(stdout))
|
||||
@@ -201,7 +203,7 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error {
|
||||
}
|
||||
go func() {
|
||||
taskItem.AddSubTask(i18n.GetMsgByKey("ComposeCreate"), func(t *task.Task) error {
|
||||
cmd := exec.Command("docker", "compose", "-f", req.Path, "up", "-d")
|
||||
cmd := getComposeCmd(req.Path, "up")
|
||||
out, err := cmd.CombinedOutput()
|
||||
taskItem.Log(i18n.GetWithName("ComposeCreateRes", string(out)))
|
||||
if err != nil {
|
||||
@@ -225,12 +227,9 @@ func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
|
||||
if cmd.CheckIllegal(req.Path, req.Operation) {
|
||||
return buserr.New("ErrCmdIllegal")
|
||||
}
|
||||
if _, err := os.Stat(req.Path); err != nil {
|
||||
return fmt.Errorf("load file with path %s failed, %v", req.Path, err)
|
||||
}
|
||||
if req.Operation == "delete" {
|
||||
if stdout, err := compose.Operate(req.Path, "down"); err != nil {
|
||||
return errors.New(string(stdout))
|
||||
if err := removeContainerForCompose(req.Name, req.Path); err != nil && !req.Force {
|
||||
return err
|
||||
}
|
||||
if req.WithFile {
|
||||
_ = os.RemoveAll(path.Dir(req.Path))
|
||||
@@ -238,6 +237,9 @@ func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
|
||||
_ = composeRepo.DeleteRecord(repo.WithByName(req.Name))
|
||||
return nil
|
||||
}
|
||||
if _, err := os.Stat(req.Path); err != nil {
|
||||
return fmt.Errorf("load file with path %s failed, %v", req.Path, err)
|
||||
}
|
||||
if req.Operation == "up" {
|
||||
if stdout, err := compose.Up(req.Path); err != nil {
|
||||
return errors.New(string(stdout))
|
||||
@@ -306,6 +308,33 @@ func (u *ContainerService) loadPath(req *dto.ComposeCreate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeContainerForCompose(composeName, composePath string) error {
|
||||
if _, err := os.Stat(composePath); err == nil {
|
||||
if stdout, err := compose.Operate(composePath, "down"); err != nil {
|
||||
return errors.New(stdout)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
var options container.ListOptions
|
||||
options.All = true
|
||||
options.Filters = filters.NewArgs()
|
||||
options.Filters.Add("label", "com.docker.compose.project="+composeName)
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
ctx := context.Background()
|
||||
containers, err := client.ContainerList(ctx, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, c := range containers {
|
||||
_ = client.ContainerRemove(ctx, c.ID, container.RemoveOptions{RemoveVolumes: true, Force: true})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func recreateCompose(content, path string) error {
|
||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||
if err != nil {
|
||||
|
||||
+336
-60
@@ -1,6 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -9,14 +10,13 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||
|
||||
"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/docker"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/robfig/cron/v3"
|
||||
@@ -32,11 +32,13 @@ type ICronjobService interface {
|
||||
HandleOnce(id uint) error
|
||||
Update(id uint, req dto.CronjobOperate) error
|
||||
UpdateStatus(id uint, status string) error
|
||||
UpdateGroup(req dto.ChangeGroup) error
|
||||
Delete(req dto.CronjobBatchDelete) error
|
||||
Download(down dto.CronjobDownload) (string, error)
|
||||
StartJob(cronjob *model.Cronjob, isUpdate bool) (string, error)
|
||||
CleanRecord(req dto.CronjobClean) error
|
||||
|
||||
Export(req dto.OperateByIDs) (string, error)
|
||||
Import(req []dto.CronjobTrans) error
|
||||
LoadScriptOptions() []dto.ScriptOptions
|
||||
|
||||
LoadInfo(req dto.OperateByID) (*dto.CronjobOperate, error)
|
||||
@@ -48,7 +50,11 @@ func NewICronjobService() ICronjobService {
|
||||
}
|
||||
|
||||
func (u *CronjobService) SearchWithPage(search dto.PageCronjob) (int64, interface{}, error) {
|
||||
total, cronjobs, err := cronjobRepo.Page(search.Page, search.PageSize, repo.WithByLikeName(search.Info), repo.WithOrderRuleBy(search.OrderBy, search.Order))
|
||||
total, cronjobs, err := cronjobRepo.Page(search.Page,
|
||||
search.PageSize,
|
||||
repo.WithByGroups(search.GroupIDs),
|
||||
repo.WithByLikeName(search.Info),
|
||||
repo.WithOrderRuleBy(search.OrderBy, search.Order))
|
||||
var dtoCronjobs []dto.CronjobInfo
|
||||
for _, cronjob := range cronjobs {
|
||||
var item dto.CronjobInfo
|
||||
@@ -67,9 +73,9 @@ func (u *CronjobService) SearchWithPage(search dto.PageCronjob) (int64, interfac
|
||||
AlertType: cronjob.Type,
|
||||
EntryID: cronjob.ID,
|
||||
}
|
||||
alertCount := xpack.GetAlert(alertBase)
|
||||
if alertCount != 0 {
|
||||
item.AlertCount = alertCount
|
||||
alertInfo, _ := alertRepo.Get(alertRepo.WithByType(alertBase.AlertType), alertRepo.WithByProject(strconv.Itoa(int(alertBase.EntryID))), repo.WithByStatus(constant.AlertEnable))
|
||||
if alertInfo.SendCount != 0 {
|
||||
item.AlertCount = alertInfo.SendCount
|
||||
} else {
|
||||
item.AlertCount = 0
|
||||
}
|
||||
@@ -91,9 +97,10 @@ func (u *CronjobService) LoadInfo(req dto.OperateByID) (*dto.CronjobOperate, err
|
||||
AlertType: cronjob.Type,
|
||||
EntryID: cronjob.ID,
|
||||
}
|
||||
alertCount := xpack.GetAlert(alertBase)
|
||||
if alertCount != 0 {
|
||||
item.AlertCount = alertCount
|
||||
alertInfo, _ := alertRepo.Get(alertRepo.WithByType(alertBase.AlertType), alertRepo.WithByProject(strconv.Itoa(int(alertBase.EntryID))), repo.WithByStatus(constant.AlertEnable))
|
||||
item.AlertMethod = alertInfo.Method
|
||||
if alertInfo.SendCount != 0 {
|
||||
item.AlertCount = alertInfo.SendCount
|
||||
} else {
|
||||
item.AlertCount = 0
|
||||
}
|
||||
@@ -103,6 +110,292 @@ func (u *CronjobService) LoadInfo(req dto.OperateByID) (*dto.CronjobOperate, err
|
||||
return &item, err
|
||||
}
|
||||
|
||||
func (u *CronjobService) Export(req dto.OperateByIDs) (string, error) {
|
||||
cronjobs, err := cronjobRepo.List(repo.WithByIDs(req.IDs))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var data []dto.CronjobTrans
|
||||
for _, cronjob := range cronjobs {
|
||||
item := dto.CronjobTrans{
|
||||
Name: cronjob.Name,
|
||||
Type: cronjob.Type,
|
||||
GroupID: cronjob.GroupID,
|
||||
SpecCustom: cronjob.SpecCustom,
|
||||
Spec: cronjob.Spec,
|
||||
Executor: cronjob.Executor,
|
||||
ScriptMode: cronjob.ScriptMode,
|
||||
Script: cronjob.Script,
|
||||
Command: cronjob.Command,
|
||||
ContainerName: cronjob.ContainerName,
|
||||
User: cronjob.User,
|
||||
URL: cronjob.URL,
|
||||
DBType: cronjob.DBType,
|
||||
ExclusionRules: cronjob.ExclusionRules,
|
||||
IsDir: cronjob.IsDir,
|
||||
SourceDir: cronjob.SourceDir,
|
||||
RetainCopies: cronjob.RetainCopies,
|
||||
RetryTimes: cronjob.RetryTimes,
|
||||
Timeout: cronjob.Timeout,
|
||||
IgnoreErr: cronjob.IgnoreErr,
|
||||
Secret: cronjob.Secret,
|
||||
}
|
||||
switch cronjob.Type {
|
||||
case "app":
|
||||
if cronjob.AppID == "all" {
|
||||
break
|
||||
}
|
||||
apps := loadAppsForJob(cronjob)
|
||||
for _, app := range apps {
|
||||
item.Apps = append(item.Apps, dto.TransHelper{Name: app.App.Key, DetailName: app.Name})
|
||||
}
|
||||
case "website", "cutWebsiteLog":
|
||||
if cronjob.Website == "all" {
|
||||
break
|
||||
}
|
||||
websites := loadWebsForJob(cronjob)
|
||||
for _, website := range websites {
|
||||
item.Websites = append(item.Websites, website.Alias)
|
||||
}
|
||||
case "database":
|
||||
if cronjob.DBName == "all" {
|
||||
break
|
||||
}
|
||||
databases := loadDbsForJob(cronjob)
|
||||
for _, db := range databases {
|
||||
item.DBNames = append(item.DBNames, dto.TransHelper{Name: db.Database, DetailName: db.Name})
|
||||
}
|
||||
case "shell":
|
||||
if cronjob.ScriptMode == "library" {
|
||||
script, err := scriptRepo.Get(repo.WithByID(cronjob.ScriptID))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
item.ScriptName = script.Name
|
||||
}
|
||||
case "snapshot":
|
||||
if len(cronjob.SnapshotRule) == 0 {
|
||||
break
|
||||
}
|
||||
var snapRule dto.SnapshotRule
|
||||
if err := json.Unmarshal([]byte(cronjob.SnapshotRule), &snapRule); err != nil {
|
||||
return "", err
|
||||
}
|
||||
item.SnapshotRule.WithImage = snapRule.WithImage
|
||||
if len(snapRule.IgnoreAppIDs) != 0 {
|
||||
ignoreApps, _ := appInstallRepo.ListBy(context.Background(), repo.WithByIDs(snapRule.IgnoreAppIDs))
|
||||
for _, app := range ignoreApps {
|
||||
item.SnapshotRule.IgnoreApps = append(item.SnapshotRule.IgnoreApps, dto.TransHelper{Name: app.App.Key, DetailName: app.Name})
|
||||
}
|
||||
}
|
||||
}
|
||||
item.SourceAccounts, item.DownloadAccount, _ = loadBackupNamesByID(cronjob.SourceAccountIDs, cronjob.DownloadAccountID)
|
||||
alertInfo, _ := alertRepo.Get(alertRepo.WithByType(cronjob.Type), alertRepo.WithByProject(strconv.Itoa(int(cronjob.ID))), repo.WithByStatus(constant.AlertEnable))
|
||||
if alertInfo.SendCount != 0 {
|
||||
item.AlertCount = alertInfo.SendCount
|
||||
item.AlertTitle = alertInfo.Title
|
||||
item.AlertMethod = alertInfo.Method
|
||||
} else {
|
||||
item.AlertCount = 0
|
||||
}
|
||||
data = append(data, item)
|
||||
}
|
||||
itemJson, err := json.Marshal(&data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(itemJson), nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) Import(req []dto.CronjobTrans) error {
|
||||
for _, item := range req {
|
||||
cronjobItem, _ := cronjobRepo.Get(repo.WithByName(item.Name))
|
||||
if cronjobItem.ID != 0 {
|
||||
continue
|
||||
}
|
||||
cronjob := model.Cronjob{
|
||||
Name: item.Name,
|
||||
Type: item.Type,
|
||||
GroupID: item.GroupID,
|
||||
SpecCustom: item.SpecCustom,
|
||||
Spec: item.Spec,
|
||||
Executor: item.Executor,
|
||||
ScriptMode: item.ScriptMode,
|
||||
Command: item.Command,
|
||||
ContainerName: item.ContainerName,
|
||||
User: item.User,
|
||||
URL: item.URL,
|
||||
DBType: item.DBType,
|
||||
ExclusionRules: item.ExclusionRules,
|
||||
IsDir: item.IsDir,
|
||||
RetainCopies: item.RetainCopies,
|
||||
RetryTimes: item.RetryTimes,
|
||||
Timeout: item.Timeout,
|
||||
IgnoreErr: item.IgnoreErr,
|
||||
Secret: item.Secret,
|
||||
}
|
||||
hasNotFound := false
|
||||
switch item.Type {
|
||||
case "app":
|
||||
if len(item.Apps) == 0 {
|
||||
cronjob.AppID = "all"
|
||||
break
|
||||
}
|
||||
var appIDs []string
|
||||
for _, app := range item.Apps {
|
||||
appItem, err := appInstallRepo.LoadInstallAppByKeyAndName(app.Name, app.DetailName)
|
||||
if err != nil {
|
||||
hasNotFound = true
|
||||
continue
|
||||
}
|
||||
appIDs = append(appIDs, fmt.Sprintf("%v", appItem.ID))
|
||||
}
|
||||
cronjob.AppID = strings.Join(appIDs, ",")
|
||||
case "website", "cutWebsiteLog":
|
||||
if len(item.Websites) == 0 {
|
||||
cronjob.Website = "all"
|
||||
break
|
||||
}
|
||||
var webIDs []string
|
||||
for _, web := range item.Websites {
|
||||
webItem, err := websiteRepo.GetFirst(websiteRepo.WithAlias(web))
|
||||
if err != nil {
|
||||
hasNotFound = true
|
||||
continue
|
||||
}
|
||||
webIDs = append(webIDs, fmt.Sprintf("%v", webItem.ID))
|
||||
}
|
||||
cronjob.Website = strings.Join(webIDs, ",")
|
||||
case "database":
|
||||
if len(item.DBNames) == 0 {
|
||||
cronjob.DBName = "all"
|
||||
break
|
||||
}
|
||||
var dbIDs []string
|
||||
if strings.Contains(cronjob.DBType, "postgresql") {
|
||||
for _, db := range item.DBNames {
|
||||
dbItem, err := postgresqlRepo.Get(postgresqlRepo.WithByPostgresqlName(db.Name), repo.WithByName(db.DetailName))
|
||||
if err != nil {
|
||||
hasNotFound = true
|
||||
continue
|
||||
}
|
||||
dbIDs = append(dbIDs, fmt.Sprintf("%v", dbItem.ID))
|
||||
}
|
||||
} else {
|
||||
for _, db := range item.DBNames {
|
||||
dbItem, err := mysqlRepo.Get(mysqlRepo.WithByMysqlName(db.Name), repo.WithByName(db.DetailName))
|
||||
if err != nil {
|
||||
hasNotFound = true
|
||||
continue
|
||||
}
|
||||
dbIDs = append(dbIDs, fmt.Sprintf("%v", dbItem.ID))
|
||||
}
|
||||
}
|
||||
cronjob.DBName = strings.Join(dbIDs, ",")
|
||||
case "shell":
|
||||
if len(item.ContainerName) != 0 {
|
||||
cronjob.Script = item.Script
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
hasNotFound = true
|
||||
break
|
||||
}
|
||||
defer client.Close()
|
||||
if _, err := client.ContainerStats(context.Background(), item.ContainerName, false); err != nil {
|
||||
hasNotFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
switch item.ScriptMode {
|
||||
case "library":
|
||||
library, _ := scriptRepo.Get(repo.WithByName(item.ScriptName))
|
||||
if library.ID == 0 {
|
||||
hasNotFound = true
|
||||
break
|
||||
}
|
||||
cronjob.ScriptID = library.ID
|
||||
case "select":
|
||||
if _, err := os.Stat(item.Script); err != nil {
|
||||
hasNotFound = true
|
||||
break
|
||||
}
|
||||
cronjob.Script = item.Script
|
||||
case "input":
|
||||
cronjob.Script = item.Script
|
||||
}
|
||||
case "directory":
|
||||
if item.IsDir {
|
||||
if _, err := os.Stat(item.SourceDir); err != nil {
|
||||
hasNotFound = true
|
||||
break
|
||||
}
|
||||
cronjob.SourceDir = item.SourceDir
|
||||
} else {
|
||||
fileList := strings.Split(item.SourceDir, ",")
|
||||
var newFiles []string
|
||||
for _, item := range fileList {
|
||||
if len(item) == 0 {
|
||||
continue
|
||||
}
|
||||
if _, err := os.Stat(item); err != nil {
|
||||
hasNotFound = true
|
||||
continue
|
||||
}
|
||||
newFiles = append(newFiles, item)
|
||||
}
|
||||
cronjob.SourceDir = strings.Join(newFiles, ",")
|
||||
}
|
||||
case "snapshot":
|
||||
if len(item.SnapshotRule.IgnoreApps) == 0 && !item.SnapshotRule.WithImage {
|
||||
break
|
||||
}
|
||||
var itemRules dto.SnapshotRule
|
||||
itemRules.WithImage = item.SnapshotRule.WithImage
|
||||
for _, app := range item.SnapshotRule.IgnoreApps {
|
||||
appItem, err := appInstallRepo.LoadInstallAppByKeyAndName(app.Name, app.DetailName)
|
||||
if err != nil {
|
||||
hasNotFound = true
|
||||
continue
|
||||
}
|
||||
itemRules.IgnoreAppIDs = append(itemRules.IgnoreAppIDs, appItem.ID)
|
||||
}
|
||||
itemRulesStr, _ := json.Marshal(itemRules)
|
||||
cronjob.SnapshotRule = string(itemRulesStr)
|
||||
}
|
||||
var acIDs []string
|
||||
for _, ac := range item.SourceAccounts {
|
||||
backup, err := backupRepo.Get(repo.WithByName(ac))
|
||||
if err != nil {
|
||||
hasNotFound = true
|
||||
continue
|
||||
}
|
||||
if ac == item.DownloadAccount {
|
||||
cronjob.DownloadAccountID = backup.ID
|
||||
}
|
||||
acIDs = append(acIDs, fmt.Sprintf("%v", backup.ID))
|
||||
}
|
||||
cronjob.SourceAccountIDs = strings.Join(acIDs, ",")
|
||||
if hasNotFound {
|
||||
cronjob.Status = constant.StatusPending
|
||||
} else {
|
||||
cronjob.Status = constant.StatusDisable
|
||||
}
|
||||
_ = cronjobRepo.Create(&cronjob)
|
||||
if item.AlertCount != 0 && item.AlertTitle != "" && item.AlertMethod != "" {
|
||||
createAlert := dto.AlertCreate{
|
||||
Title: item.AlertTitle,
|
||||
SendCount: item.AlertCount,
|
||||
Method: item.AlertMethod,
|
||||
Type: cronjob.Type,
|
||||
Project: strconv.Itoa(int(cronjob.ID)),
|
||||
Status: constant.AlertEnable,
|
||||
}
|
||||
_ = NewIAlertService().CreateAlert(createAlert)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) LoadScriptOptions() []dto.ScriptOptions {
|
||||
scripts, err := scriptRepo.List()
|
||||
if err != nil {
|
||||
@@ -204,10 +497,7 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
|
||||
}
|
||||
if req.CleanData {
|
||||
if hasBackup(cronjob.Type) {
|
||||
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if !req.CleanRemoteData {
|
||||
for key := range accountMap {
|
||||
if key != constant.Local {
|
||||
@@ -241,32 +531,6 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) Download(req dto.CronjobDownload) (string, error) {
|
||||
record, _ := cronjobRepo.GetRecord(repo.WithByID(req.RecordID))
|
||||
if record.ID == 0 {
|
||||
return "", buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
account, client, err := NewBackupClientWithID(req.BackupAccountID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if account.Type == "LOCAL" || record.FromLocal {
|
||||
if _, err := os.Stat(record.File); err != nil && os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
return record.File, nil
|
||||
}
|
||||
tempPath := fmt.Sprintf("%s/download/%s", global.Dir.DataDir, record.File)
|
||||
if _, err := os.Stat(tempPath); err != nil && os.IsNotExist(err) {
|
||||
_ = os.MkdirAll(path.Dir(tempPath), os.ModePerm)
|
||||
isOK, err := client.Download(record.File, tempPath)
|
||||
if !isOK || err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return tempPath, nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) HandleOnce(id uint) error {
|
||||
cronjob, _ := cronjobRepo.Get(repo.WithByID(id))
|
||||
if cronjob.ID == 0 {
|
||||
@@ -312,14 +576,16 @@ func (u *CronjobService) Create(req dto.CronjobOperate) error {
|
||||
if err := cronjobRepo.Create(&cronjob); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.AlertCount != 0 {
|
||||
createAlert := dto.CreateOrUpdateAlert{
|
||||
AlertTitle: req.AlertTitle,
|
||||
AlertCount: req.AlertCount,
|
||||
AlertType: cronjob.Type,
|
||||
EntryID: cronjob.ID,
|
||||
if req.AlertCount != 0 && req.AlertTitle != "" && req.AlertMethod != "" {
|
||||
createAlert := dto.AlertCreate{
|
||||
Title: req.AlertTitle,
|
||||
SendCount: req.AlertCount,
|
||||
Method: req.AlertMethod,
|
||||
Type: cronjob.Type,
|
||||
Project: strconv.Itoa(int(cronjob.ID)),
|
||||
Status: constant.AlertEnable,
|
||||
}
|
||||
err := xpack.CreateAlert(createAlert)
|
||||
err := NewIAlertService().CreateAlert(createAlert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -367,11 +633,7 @@ func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error {
|
||||
if err := cronjobRepo.Delete(repo.WithByID(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
alertBase := dto.AlertBase{
|
||||
AlertType: cronjob.Type,
|
||||
EntryID: cronjob.ID,
|
||||
}
|
||||
err := xpack.DeleteAlert(alertBase)
|
||||
err := alertRepo.Delete(alertRepo.WithByProject(strconv.Itoa(int(cronjob.ID))), alertRepo.WithByType(cronjob.Type))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -414,7 +676,11 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate) error {
|
||||
}
|
||||
}
|
||||
|
||||
if cronModel.Status == constant.StatusPending {
|
||||
upMap["status"] = constant.StatusEnable
|
||||
}
|
||||
upMap["name"] = req.Name
|
||||
upMap["group_id"] = req.GroupID
|
||||
upMap["spec_custom"] = req.SpecCustom
|
||||
upMap["spec"] = spec
|
||||
upMap["script"] = req.Script
|
||||
@@ -431,6 +697,7 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate) error {
|
||||
upMap["db_type"] = req.DBType
|
||||
upMap["db_name"] = req.DBName
|
||||
upMap["url"] = req.URL
|
||||
upMap["is_dir"] = req.IsDir
|
||||
upMap["source_dir"] = req.SourceDir
|
||||
upMap["snapshot_rule"] = cronjob.SnapshotRule
|
||||
|
||||
@@ -445,13 +712,14 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updateAlert := dto.CreateOrUpdateAlert{
|
||||
AlertTitle: req.AlertTitle,
|
||||
AlertType: cronModel.Type,
|
||||
AlertCount: req.AlertCount,
|
||||
EntryID: cronModel.ID,
|
||||
updateAlert := dto.AlertCreate{
|
||||
Title: req.AlertTitle,
|
||||
SendCount: req.AlertCount,
|
||||
Method: req.AlertMethod,
|
||||
Type: cronjob.Type,
|
||||
Project: strconv.Itoa(int(cronModel.ID)),
|
||||
}
|
||||
err = xpack.UpdateAlert(updateAlert)
|
||||
err = NewIAlertService().ExternalUpdateAlert(updateAlert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -484,6 +752,14 @@ func (u *CronjobService) UpdateStatus(id uint, status string) error {
|
||||
return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"status": status, "entry_ids": entryIDs})
|
||||
}
|
||||
|
||||
func (u *CronjobService) UpdateGroup(req dto.ChangeGroup) error {
|
||||
cronjob, _ := cronjobRepo.Get(repo.WithByID(req.ID))
|
||||
if cronjob.ID == 0 {
|
||||
return buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"group_id": req.GroupID})
|
||||
}
|
||||
|
||||
func (u *CronjobService) AddCronJob(cronjob *model.Cronjob) (int, error) {
|
||||
addFunc := func() {
|
||||
u.HandleJob(cronjob)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -24,28 +25,13 @@ import (
|
||||
)
|
||||
|
||||
func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
|
||||
var apps []model.AppInstall
|
||||
if cronjob.AppID == "all" {
|
||||
apps, _ = appInstallRepo.ListBy(context.Background())
|
||||
} else {
|
||||
appIds := strings.Split(cronjob.AppID, ",")
|
||||
var idItems []uint
|
||||
for i := 0; i < len(appIds); i++ {
|
||||
itemID, _ := strconv.Atoi(appIds[i])
|
||||
idItems = append(idItems, uint(itemID))
|
||||
}
|
||||
appItems, err := appInstallRepo.ListBy(context.Background(), repo.WithByIDs(idItems))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
apps = appItems
|
||||
}
|
||||
apps := loadAppsForJob(cronjob)
|
||||
if len(apps) == 0 {
|
||||
return errors.New("no such app in database!")
|
||||
}
|
||||
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if err != nil {
|
||||
return err
|
||||
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
|
||||
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
}
|
||||
for _, app := range apps {
|
||||
retry := 0
|
||||
@@ -57,32 +43,37 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, t
|
||||
record.Name = app.App.Key
|
||||
record.DetailName = app.Name
|
||||
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
|
||||
backupDir := path.Join(global.Dir.TmpDir, fmt.Sprintf("app/%s/%s", app.App.Key, app.Name))
|
||||
record.FileName = fmt.Sprintf("app_%s_%s.tar.gz", app.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||
backupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf("tmp/app/%s/%s", app.App.Key, app.Name))
|
||||
record.FileName = simplifiedFileName(fmt.Sprintf("app_%s_%s.tar.gz", app.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)))
|
||||
if err := doAppBackup(&app, task, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
} else {
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
|
||||
cleanAccountMap(accountMap)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, record.FileName))
|
||||
if err != nil {
|
||||
|
||||
src := path.Join(backupDir, record.FileName)
|
||||
dst := strings.TrimPrefix(src, global.Dir.LocalBackupDir+"/tmp/")
|
||||
if err := uploadWithMap(*task, accountMap, src, dst, cronjob.SourceAccountIDs, cronjob.DownloadAccountID, cronjob.RetryTimes); err != nil {
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
}
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreUploadErr", err.Error()))
|
||||
cleanAccountMap(accountMap)
|
||||
return nil
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
record.FileDir = path.Dir(dst)
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
u.removeExpiredBackup(cronjob, accountMap, record)
|
||||
cleanAccountMap(accountMap)
|
||||
return nil
|
||||
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
|
||||
}
|
||||
@@ -94,9 +85,9 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
|
||||
if len(webs) == 0 {
|
||||
return errors.New("no such website in database!")
|
||||
}
|
||||
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if err != nil {
|
||||
return err
|
||||
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
|
||||
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
}
|
||||
for _, web := range webs {
|
||||
retry := 0
|
||||
@@ -108,8 +99,8 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
|
||||
record.Name = web.Alias
|
||||
record.DetailName = web.Alias
|
||||
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
|
||||
backupDir := path.Join(global.Dir.TmpDir, fmt.Sprintf("website/%s", web.Alias))
|
||||
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", web.Alias, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||
backupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf("tmp/website/%s", web.Alias))
|
||||
record.FileName = simplifiedFileName(fmt.Sprintf("website_%s_%s.tar.gz", web.Alias, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)))
|
||||
|
||||
if err := doWebsiteBackup(&web, taskItem, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
@@ -117,25 +108,29 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
|
||||
return err
|
||||
} else {
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
|
||||
cleanAccountMap(accountMap)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, record.FileName))
|
||||
if err != nil {
|
||||
src := path.Join(backupDir, record.FileName)
|
||||
dst := strings.TrimPrefix(src, global.Dir.LocalBackupDir+"/tmp/")
|
||||
if err := uploadWithMap(*task, accountMap, src, dst, cronjob.SourceAccountIDs, cronjob.DownloadAccountID, cronjob.RetryTimes); err != nil {
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
}
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreUploadErr", err.Error()))
|
||||
cleanAccountMap(accountMap)
|
||||
return nil
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
record.FileDir = path.Dir(dst)
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
u.removeExpiredBackup(cronjob, accountMap, record)
|
||||
cleanAccountMap(accountMap)
|
||||
return nil
|
||||
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
|
||||
}
|
||||
@@ -147,9 +142,9 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
|
||||
if len(dbs) == 0 {
|
||||
return errors.New("no such db in database!")
|
||||
}
|
||||
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if err != nil {
|
||||
return err
|
||||
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
|
||||
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
}
|
||||
for _, dbInfo := range dbs {
|
||||
retry := 0
|
||||
@@ -163,15 +158,16 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
|
||||
record.DetailName = dbInfo.Name
|
||||
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
|
||||
|
||||
backupDir := path.Join(global.Dir.TmpDir, fmt.Sprintf("database/%s/%s/%s", dbInfo.DBType, record.Name, dbInfo.Name))
|
||||
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
|
||||
backupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf("tmp/database/%s/%s/%s", dbInfo.DBType, record.Name, dbInfo.Name))
|
||||
record.FileName = simplifiedFileName(fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)))
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" || cronjob.DBType == "mysql-cluster" {
|
||||
if err := doMysqlBackup(dbInfo, backupDir, record.FileName); err != nil {
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
} else {
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
|
||||
cleanAccountMap(accountMap)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -182,26 +178,30 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
|
||||
return err
|
||||
} else {
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
|
||||
cleanAccountMap(accountMap)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, record.FileName))
|
||||
if err != nil {
|
||||
src := path.Join(backupDir, record.FileName)
|
||||
dst := strings.TrimPrefix(src, global.Dir.LocalBackupDir+"/tmp/")
|
||||
if err := uploadWithMap(*task, accountMap, src, dst, cronjob.SourceAccountIDs, cronjob.DownloadAccountID, cronjob.RetryTimes); err != nil {
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
}
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreUploadErr", err.Error()))
|
||||
cleanAccountMap(accountMap)
|
||||
return nil
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
record.FileDir = path.Dir(dst)
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
u.removeExpiredBackup(cronjob, accountMap, record)
|
||||
cleanAccountMap(accountMap)
|
||||
return nil
|
||||
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
|
||||
}
|
||||
@@ -209,13 +209,17 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
|
||||
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
|
||||
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
}
|
||||
taskItem.AddSubTaskWithOps(task.GetTaskName(cronjob.SourceDir, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
|
||||
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if err != nil {
|
||||
return err
|
||||
fileName := fmt.Sprintf("%s.tar.gz", startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(2))
|
||||
if cronjob.IsDir || len(strings.Split(cronjob.SourceDir, ",")) == 1 {
|
||||
fileName = loadFileName(cronjob.SourceDir)
|
||||
}
|
||||
fileName := fmt.Sprintf("%s.tar.gz", startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||
backupDir := path.Join(global.Dir.TmpDir, fmt.Sprintf("%s/%s", cronjob.Type, cronjob.Name))
|
||||
fileName = simplifiedFileName(fileName)
|
||||
backupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf("tmp/%s/%s", cronjob.Type, cronjob.Name))
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
if cronjob.IsDir {
|
||||
@@ -236,15 +240,15 @@ func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.T
|
||||
record.CronjobID = cronjob.ID
|
||||
record.Name = cronjob.Name
|
||||
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, fileName))
|
||||
if err != nil {
|
||||
taskItem.LogFailedWithErr("Upload backup file", err)
|
||||
|
||||
src := path.Join(backupDir, fileName)
|
||||
dst := strings.TrimPrefix(src, global.Dir.LocalBackupDir+"/tmp/")
|
||||
if err := uploadWithMap(*task, accountMap, src, dst, cronjob.SourceAccountIDs, cronjob.DownloadAccountID, cronjob.RetryTimes); err != nil {
|
||||
return err
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
record.FileDir = path.Dir(dst)
|
||||
record.FileName = fileName
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
taskItem.LogFailedWithErr("Save record", err)
|
||||
return err
|
||||
}
|
||||
u.removeExpiredBackup(cronjob, accountMap, record)
|
||||
@@ -254,14 +258,14 @@ func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.T
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
|
||||
taskItem.AddSubTaskWithOps(task.GetTaskName(i18n.GetMsgByKey("BackupSystemLog"), task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
|
||||
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
|
||||
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
}
|
||||
taskItem.AddSubTaskWithOps(task.GetTaskName(i18n.GetMsgByKey("SystemLog"), task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
|
||||
nameItem := startTime.Format(constant.DateTimeSlimLayout) + common.RandStrAndNum(5)
|
||||
fileName := fmt.Sprintf("system_log_%s.tar.gz", nameItem)
|
||||
backupDir := path.Join(global.Dir.TmpDir, "log", nameItem)
|
||||
backupDir := path.Join(global.Dir.LocalBackupDir, "tmp/log", nameItem)
|
||||
if err := handleBackupLogs(taskItem, backupDir, fileName, cronjob.Secret); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -271,15 +275,15 @@ func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.T
|
||||
record.CronjobID = cronjob.ID
|
||||
record.Name = cronjob.Name
|
||||
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(path.Dir(backupDir), fileName))
|
||||
if err != nil {
|
||||
taskItem.LogFailedWithErr("Upload backup file", err)
|
||||
|
||||
src := path.Join(path.Dir(backupDir), fileName)
|
||||
dst := strings.TrimPrefix(src, global.Dir.LocalBackupDir+"/tmp/")
|
||||
if err := uploadWithMap(*task, accountMap, src, dst, cronjob.SourceAccountIDs, cronjob.DownloadAccountID, cronjob.RetryTimes); err != nil {
|
||||
return err
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
record.FileDir = path.Dir(dst)
|
||||
record.FileName = fileName
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
taskItem.LogFailedWithErr("Save record", err)
|
||||
return err
|
||||
}
|
||||
u.removeExpiredBackup(cronjob, accountMap, record)
|
||||
@@ -289,11 +293,10 @@ func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.T
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.JobRecords, taskItem *task.Task) error {
|
||||
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if err != nil {
|
||||
return err
|
||||
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
|
||||
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
|
||||
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
}
|
||||
|
||||
var record model.BackupRecord
|
||||
record.From = "cronjob"
|
||||
record.Type = "snapshot"
|
||||
@@ -313,9 +316,10 @@ func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.J
|
||||
return err
|
||||
}
|
||||
req := dto.SnapshotCreate{
|
||||
Name: fmt.Sprintf("snapshot-1panel-%s-%s-linux-%s-%s", scope, versionItem.Value, loadOs(), jobRecord.StartTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)),
|
||||
Secret: cronjob.Secret,
|
||||
TaskID: jobRecord.TaskID,
|
||||
Name: fmt.Sprintf("snapshot-1panel-%s-%s-linux-%s-%s", scope, versionItem.Value, loadOs(), jobRecord.StartTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)),
|
||||
Secret: cronjob.Secret,
|
||||
TaskID: jobRecord.TaskID,
|
||||
Timeout: cronjob.Timeout,
|
||||
|
||||
SourceAccountIDs: record.SourceAccountIDs,
|
||||
DownloadAccountID: cronjob.DownloadAccountID,
|
||||
@@ -331,7 +335,7 @@ func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.J
|
||||
IgnoreFiles: strings.Split(cronjob.ExclusionRules, ","),
|
||||
}
|
||||
|
||||
if err := NewISnapshotService().SnapshotCreate(taskItem, req, jobRecord.ID, cronjob.RetryTimes, cronjob.Timeout); err != nil {
|
||||
if err := NewISnapshotService().SnapshotCreate(taskItem, req, jobRecord.ID, cronjob.RetryTimes); err != nil {
|
||||
return err
|
||||
}
|
||||
record.FileName = req.Name + ".tar.gz"
|
||||
@@ -344,6 +348,23 @@ func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.J
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadAppsForJob(cronjob model.Cronjob) []model.AppInstall {
|
||||
var apps []model.AppInstall
|
||||
if cronjob.AppID == "all" {
|
||||
apps, _ = appInstallRepo.ListBy(context.Background())
|
||||
} else {
|
||||
appIds := strings.Split(cronjob.AppID, ",")
|
||||
var idItems []uint
|
||||
for i := 0; i < len(appIds); i++ {
|
||||
itemID, _ := strconv.Atoi(appIds[i])
|
||||
idItems = append(idItems, uint(itemID))
|
||||
}
|
||||
appItems, _ := appInstallRepo.ListBy(context.Background(), repo.WithByIDs(idItems))
|
||||
apps = appItems
|
||||
}
|
||||
return apps
|
||||
}
|
||||
|
||||
type DatabaseHelper struct {
|
||||
ID uint
|
||||
DBType string
|
||||
@@ -354,7 +375,7 @@ type DatabaseHelper struct {
|
||||
func loadDbsForJob(cronjob model.Cronjob) []DatabaseHelper {
|
||||
var dbs []DatabaseHelper
|
||||
if cronjob.DBName == "all" {
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" || cronjob.DBType == "mysql-cluster" {
|
||||
databaseService := NewIDatabaseService()
|
||||
mysqlItems, _ := databaseService.LoadItems(cronjob.DBType)
|
||||
for _, mysql := range mysqlItems {
|
||||
@@ -381,7 +402,7 @@ func loadDbsForJob(cronjob model.Cronjob) []DatabaseHelper {
|
||||
dbNames := strings.Split(cronjob.DBName, ",")
|
||||
for _, name := range dbNames {
|
||||
itemID, _ := strconv.Atoi(name)
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" || cronjob.DBType == "mysql-cluster" {
|
||||
mysqlItem, _ := mysqlRepo.Get(repo.WithByID(uint(itemID)))
|
||||
dbs = append(dbs, DatabaseHelper{
|
||||
ID: mysqlItem.ID,
|
||||
@@ -443,7 +464,7 @@ func handleBackupLogs(taskItem *task.Task, targetDir, fileName string, secret st
|
||||
}
|
||||
}
|
||||
}
|
||||
itemDir2 := path.Join(global.Dir.LocalBackupDir, "log/website", website.Alias)
|
||||
itemDir2 := path.Join(global.Dir.LocalBackupDir, "tmp/log/website", website.Alias)
|
||||
logFiles2, _ := os.ReadDir(itemDir2)
|
||||
if len(logFiles2) != 0 {
|
||||
for i := 0; i < len(logFiles2); i++ {
|
||||
@@ -454,6 +475,7 @@ func handleBackupLogs(taskItem *task.Task, targetDir, fileName string, secret st
|
||||
}
|
||||
}
|
||||
}
|
||||
taskItem.Logf("%s Website logs...", i18n.GetMsgByKey("TaskBackup"))
|
||||
|
||||
systemDir := path.Join(targetDir, "system")
|
||||
if _, err := os.Stat(systemDir); err != nil && os.IsNotExist(err) {
|
||||
@@ -462,7 +484,6 @@ func handleBackupLogs(taskItem *task.Task, targetDir, fileName string, secret st
|
||||
}
|
||||
}
|
||||
|
||||
taskItem.Logf("%s System logs...", i18n.GetMsgByKey("TaskBackup"))
|
||||
systemLogFiles, _ := os.ReadDir(global.Dir.LogDir)
|
||||
if len(systemLogFiles) != 0 {
|
||||
for i := 0; i < len(systemLogFiles); i++ {
|
||||
@@ -471,8 +492,8 @@ func handleBackupLogs(taskItem *task.Task, targetDir, fileName string, secret st
|
||||
}
|
||||
}
|
||||
}
|
||||
taskItem.Logf("%s System logs...", i18n.GetMsgByKey("TaskBackup"))
|
||||
|
||||
taskItem.Logf("%s SSH logs...", i18n.GetMsgByKey("TaskBackup"))
|
||||
loginLogFiles, _ := os.ReadDir("/var/log")
|
||||
loginDir := path.Join(targetDir, "login")
|
||||
if _, err := os.Stat(loginDir); err != nil && os.IsNotExist(err) {
|
||||
@@ -487,7 +508,7 @@ func handleBackupLogs(taskItem *task.Task, targetDir, fileName string, secret st
|
||||
}
|
||||
}
|
||||
}
|
||||
taskItem.Log("backup ssh log successful!")
|
||||
taskItem.Logf("%s SSH logs...", i18n.GetMsgByKey("TaskBackup"))
|
||||
|
||||
if err := fileOp.TarGzCompressPro(true, targetDir, path.Join(path.Dir(targetDir), fileName), secret, ""); err != nil {
|
||||
return err
|
||||
@@ -546,3 +567,33 @@ func loadSnapWithRule(cronjob model.Cronjob) (dto.SnapshotData, error) {
|
||||
}
|
||||
return itemData, nil
|
||||
}
|
||||
|
||||
func loadFileName(src string) string {
|
||||
dirs := strings.Split(filepath.ToSlash(src), "/")
|
||||
var keyPart string
|
||||
if len(dirs) >= 3 {
|
||||
keyPart = filepath.Join(dirs[len(dirs)-3], dirs[len(dirs)-2], dirs[len(dirs)-1])
|
||||
}
|
||||
cleanName := strings.ReplaceAll(keyPart, string(filepath.Separator), "_")
|
||||
timestamp := time.Now().Format(constant.DateTimeSlimLayout)
|
||||
return fmt.Sprintf("%s_%s_%s.tar.gz", cleanName, timestamp, common.RandStrAndNum(2))
|
||||
}
|
||||
|
||||
func simplifiedFileName(name string) string {
|
||||
name = strings.ReplaceAll(name, "/", "_")
|
||||
name = strings.ReplaceAll(name, ":", "_")
|
||||
name = strings.ReplaceAll(name, "*", "_")
|
||||
name = strings.ReplaceAll(name, "?", "_")
|
||||
name = strings.ReplaceAll(name, "\"", "_")
|
||||
name = strings.ReplaceAll(name, "<", "_")
|
||||
name = strings.ReplaceAll(name, ">", "_")
|
||||
name = strings.ReplaceAll(name, "|", "_")
|
||||
return name
|
||||
}
|
||||
|
||||
func cleanAccountMap(accountMap map[string]backupClientHelper) {
|
||||
for key, val := range accountMap {
|
||||
val.hasBackup = false
|
||||
accountMap[key] = val
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/alert_push"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
@@ -19,37 +22,49 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ntp"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||
)
|
||||
|
||||
func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
record := cronjobRepo.StartRecords(cronjob.ID, "", cronjob.Type)
|
||||
go func() {
|
||||
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)
|
||||
return
|
||||
}
|
||||
err = u.loadTask(cronjob, &record, taskItem)
|
||||
if cronjob.Type == "snapshot" {
|
||||
if err != nil {
|
||||
taskItem, _ := taskRepo.GetFirst(taskRepo.WithByID(record.TaskID))
|
||||
if len(taskItem.ID) == 0 {
|
||||
record.TaskID = ""
|
||||
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)
|
||||
return
|
||||
}
|
||||
if cronjob.Type == "snapshot" {
|
||||
go func() {
|
||||
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
|
||||
if err := taskRepo.Save(context.Background(), taskItem.Task); err != nil {
|
||||
global.LOG.Errorf("save task for snapshot cronjob failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
if err = u.handleSnapshot(*cronjob, record, taskItem); err != nil {
|
||||
if len(taskItem.Task.CurrentStep) == 0 {
|
||||
taskItem.Log(err.Error())
|
||||
taskItem.Task.Status = constant.StatusFailed
|
||||
taskItem.Task.ErrorMsg = err.Error()
|
||||
taskItem.Task.EndAt = time.Now()
|
||||
_ = taskRepo.Save(context.Background(), taskItem.Task)
|
||||
}
|
||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
|
||||
handleCronJobAlert(cronjob)
|
||||
return
|
||||
}
|
||||
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
global.LOG.Debugf("preper to handle cron job [%s] %s failed, err: %v", cronjob.Type, cronjob.Name, err)
|
||||
}()
|
||||
return
|
||||
}
|
||||
if err = u.loadTask(cronjob, &record, taskItem); err != nil {
|
||||
global.LOG.Debugf("preper to handle cron job [%s] %s failed, err: %v", cronjob.Type, cronjob.Name, err)
|
||||
item, _ := taskRepo.GetFirst(taskRepo.WithByID(record.TaskID))
|
||||
if len(item.ID) == 0 {
|
||||
record.TaskID = ""
|
||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
|
||||
return
|
||||
}
|
||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
|
||||
handleCronJobAlert(cronjob)
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
if err := taskItem.Execute(); err != nil {
|
||||
taskItem, _ := taskRepo.GetFirst(taskRepo.WithByID(record.TaskID))
|
||||
if len(taskItem.ID) == 0 {
|
||||
@@ -107,9 +122,6 @@ func (u *CronjobService) loadTask(cronjob *model.Cronjob, record *model.JobRecor
|
||||
err = u.handleDirectory(*cronjob, record.StartTime, taskItem)
|
||||
case "log":
|
||||
err = u.handleSystemLog(*cronjob, record.StartTime, taskItem)
|
||||
case "snapshot":
|
||||
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
|
||||
err = u.handleSnapshot(*cronjob, *record, taskItem)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -189,14 +201,14 @@ func (u *CronjobService) handleNtpSync(cronjob model.Cronjob, taskItem *task.Tas
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime time.Time, taskItem *task.Task) error {
|
||||
clientMap := NewBackupClientMap([]string{fmt.Sprintf("%v", cronjob.DownloadAccountID)})
|
||||
if !clientMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
|
||||
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", clientMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
|
||||
}
|
||||
taskItem.AddSubTaskWithOps(i18n.GetWithName("CutWebsiteLog", cronjob.Name), func(t *task.Task) error {
|
||||
websites := loadWebsForJob(*cronjob)
|
||||
fileOp := files.NewFileOp()
|
||||
baseDir := GetOpenrestyDir(SitesRootDir)
|
||||
clientMap, err := NewBackupClientMap([]string{fmt.Sprintf("%v", cronjob.DownloadAccountID)})
|
||||
if err != nil {
|
||||
return fmt.Errorf("load local backup client failed, err: %v", err)
|
||||
}
|
||||
for _, website := range websites {
|
||||
taskItem.Log(website.Alias)
|
||||
var record model.BackupRecord
|
||||
@@ -262,34 +274,6 @@ func (u *CronjobService) handleSystemClean(cronjob model.Cronjob, taskItem *task
|
||||
taskItem.AddSubTaskWithOps(i18n.GetMsgByKey("HandleSystemClean"), cleanTask, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
|
||||
}
|
||||
|
||||
func (u *CronjobService) uploadCronjobBackFile(cronjob model.Cronjob, task *task.Task, accountMap map[string]backupClientHelper, file string) (string, error) {
|
||||
defer func() {
|
||||
_ = os.Remove(file)
|
||||
}()
|
||||
var errItem error
|
||||
accounts := strings.Split(cronjob.SourceAccountIDs, ",")
|
||||
cloudSrc := strings.TrimPrefix(file, global.Dir.TmpDir+"/")
|
||||
for _, account := range accounts {
|
||||
if len(account) != 0 {
|
||||
task.LogStart(i18n.GetMsgWithMap("UploadFile", map[string]interface{}{
|
||||
"file": pathUtils.Join(accountMap[account].backupPath, cloudSrc),
|
||||
"backup": accountMap[account].name,
|
||||
}))
|
||||
_, err := accountMap[account].client.Upload(file, pathUtils.Join(accountMap[account].backupPath, cloudSrc))
|
||||
task.LogWithStatus(
|
||||
i18n.GetMsgWithMap("UploadFile", map[string]interface{}{
|
||||
"file": pathUtils.Join(accountMap[account].backupPath, cloudSrc),
|
||||
"backup": accountMap[account].name,
|
||||
}), err)
|
||||
if err != nil {
|
||||
errItem = err
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return cloudSrc, errItem
|
||||
}
|
||||
|
||||
func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap map[string]backupClientHelper, record model.BackupRecord) {
|
||||
var opts []repo.DBOption
|
||||
opts = append(opts, repo.WithByFrom("cronjob"))
|
||||
@@ -312,6 +296,9 @@ func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap m
|
||||
if _, ok := accountMap[account]; !ok {
|
||||
continue
|
||||
}
|
||||
if !accountMap[account].isOk {
|
||||
continue
|
||||
}
|
||||
_, _ = accountMap[account].client.Delete(pathUtils.Join(accountMap[account].backupPath, "system_snapshot", records[i].FileName))
|
||||
}
|
||||
}
|
||||
@@ -322,6 +309,9 @@ func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap m
|
||||
if _, ok := accountMap[account]; !ok {
|
||||
continue
|
||||
}
|
||||
if !accountMap[account].isOk {
|
||||
continue
|
||||
}
|
||||
_, _ = accountMap[account].client.Delete(pathUtils.Join(accountMap[account].backupPath, records[i].FileDir, records[i].FileName))
|
||||
}
|
||||
}
|
||||
@@ -359,5 +349,5 @@ func handleCronJobAlert(cronjob *model.Cronjob) {
|
||||
EntryID: cronjob.ID,
|
||||
Param: cronjob.Type,
|
||||
}
|
||||
_ = xpack.PushAlert(pushAlert)
|
||||
_ = alert_push.PushAlert(pushAlert)
|
||||
}
|
||||
|
||||
@@ -22,12 +22,12 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/copier"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"github.com/shirou/gopsutil/v3/host"
|
||||
"github.com/shirou/gopsutil/v3/load"
|
||||
"github.com/shirou/gopsutil/v3/mem"
|
||||
"github.com/shirou/gopsutil/v3/net"
|
||||
"github.com/shirou/gopsutil/v4/cpu"
|
||||
"github.com/shirou/gopsutil/v4/disk"
|
||||
"github.com/shirou/gopsutil/v4/host"
|
||||
"github.com/shirou/gopsutil/v4/load"
|
||||
"github.com/shirou/gopsutil/v4/mem"
|
||||
"github.com/shirou/gopsutil/v4/net"
|
||||
)
|
||||
|
||||
type DashboardService struct{}
|
||||
@@ -113,7 +113,7 @@ func (u *DashboardService) LoadCurrentInfoForNode() *dto.NodeCurrent {
|
||||
memoryInfo, _ := mem.VirtualMemory()
|
||||
currentInfo.MemoryTotal = memoryInfo.Total
|
||||
currentInfo.MemoryAvailable = memoryInfo.Available
|
||||
currentInfo.MemoryUsed = memoryInfo.Used
|
||||
currentInfo.MemoryUsed = memoryInfo.Used + memoryInfo.Shared
|
||||
currentInfo.MemoryUsedPercent = memoryInfo.UsedPercent
|
||||
|
||||
swapInfo, _ := mem.SwapMemory()
|
||||
@@ -210,8 +210,11 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
|
||||
|
||||
memoryInfo, _ := mem.VirtualMemory()
|
||||
currentInfo.MemoryTotal = memoryInfo.Total
|
||||
currentInfo.MemoryUsed = memoryInfo.Used + memoryInfo.Shared
|
||||
currentInfo.MemoryFree = memoryInfo.Free
|
||||
currentInfo.MemoryCache = memoryInfo.Cached + memoryInfo.Buffers
|
||||
currentInfo.MemoryShard = memoryInfo.Shared
|
||||
currentInfo.MemoryAvailable = memoryInfo.Available
|
||||
currentInfo.MemoryUsed = memoryInfo.Used
|
||||
currentInfo.MemoryUsedPercent = memoryInfo.UsedPercent
|
||||
|
||||
swapInfo, _ := mem.SwapMemory()
|
||||
@@ -265,10 +268,7 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
|
||||
}
|
||||
|
||||
func (u *DashboardService) LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher, error) {
|
||||
var (
|
||||
data []dto.AppLauncher
|
||||
recommendList []dto.AppLauncher
|
||||
)
|
||||
var data []dto.AppLauncher
|
||||
appInstalls, err := appInstallRepo.ListBy(context.Background())
|
||||
if err != nil {
|
||||
return data, err
|
||||
@@ -278,8 +278,11 @@ func (u *DashboardService) LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher,
|
||||
return data, err
|
||||
}
|
||||
|
||||
showList, _ := launcherRepo.ListName()
|
||||
defaultList := []string{"openresty", "mysql", "halo", "redis", "maxkb", "wordpress"}
|
||||
showList, err := launcherRepo.ListName()
|
||||
defaultList, err := appRepo.GetTopRecomment()
|
||||
if err != nil {
|
||||
return data, nil
|
||||
}
|
||||
allList := common.RemoveRepeatStr(append(defaultList, showList...))
|
||||
for _, showItem := range allList {
|
||||
var itemData dto.AppLauncher
|
||||
@@ -314,24 +317,18 @@ func (u *DashboardService) LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher,
|
||||
})
|
||||
}
|
||||
}
|
||||
if ArryContains(defaultList, showItem) && len(itemData.Detail) == 0 {
|
||||
itemData.IsRecommend = true
|
||||
recommendList = append(recommendList, itemData)
|
||||
continue
|
||||
if (ArryContains(showList, showItem) && len(itemData.Detail) != 0) ||
|
||||
(ArryContains(defaultList, showItem) && len(itemData.Detail) == 0) {
|
||||
data = append(data, itemData)
|
||||
}
|
||||
if !ArryContains(showList, showItem) && len(itemData.Detail) != 0 {
|
||||
continue
|
||||
}
|
||||
data = append(data, itemData)
|
||||
}
|
||||
|
||||
sort.Slice(recommendList, func(i, j int) bool {
|
||||
return recommendList[i].Recommend < recommendList[j].Recommend
|
||||
})
|
||||
sort.Slice(data, func(i, j int) bool {
|
||||
return data[i].Name < data[j].Name
|
||||
if data[i].IsInstall != data[j].IsInstall {
|
||||
return data[i].IsInstall
|
||||
}
|
||||
return data[i].Recommend < data[j].Recommend
|
||||
})
|
||||
data = append(data, recommendList...)
|
||||
return data, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -6,19 +6,17 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/postgresql"
|
||||
pgclient "github.com/1Panel-dev/1Panel/agent/utils/postgresql/client"
|
||||
redisclient "github.com/1Panel-dev/1Panel/agent/utils/redis"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/mysql"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/mysql/client"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/postgresql"
|
||||
pgclient "github.com/1Panel-dev/1Panel/agent/utils/postgresql/client"
|
||||
redisclient "github.com/1Panel-dev/1Panel/agent/utils/redis"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -92,7 +90,7 @@ func (u *DatabaseService) LoadItems(dbType string) ([]dto.DatabaseItem, error) {
|
||||
dbs, err := databaseRepo.GetList(databaseRepo.WithTypeList(dbType))
|
||||
var datas []dto.DatabaseItem
|
||||
for _, db := range dbs {
|
||||
if dbType == "postgresql" {
|
||||
if dbType == constant.AppPostgresql || dbType == constant.AppPostgresqlCluster {
|
||||
items, _ := postgresqlRepo.List(postgresqlRepo.WithByPostgresqlName(db.Name))
|
||||
for _, item := range items {
|
||||
var dItem dto.DatabaseItem
|
||||
@@ -118,6 +116,9 @@ func (u *DatabaseService) LoadItems(dbType string) ([]dto.DatabaseItem, error) {
|
||||
}
|
||||
|
||||
func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
||||
if req.Timeout == 0 {
|
||||
req.Timeout = 30
|
||||
}
|
||||
switch req.Type {
|
||||
case constant.AppPostgresql:
|
||||
_, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{
|
||||
@@ -126,7 +127,7 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: 6,
|
||||
Timeout: req.Timeout,
|
||||
})
|
||||
return err == nil
|
||||
case constant.AppRedis:
|
||||
@@ -134,6 +135,7 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Password: req.Password,
|
||||
Timeout: req.Timeout,
|
||||
})
|
||||
return err == nil
|
||||
case "mysql", "mariadb":
|
||||
@@ -149,7 +151,7 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
Timeout: 6,
|
||||
Timeout: req.Timeout,
|
||||
})
|
||||
return err == nil
|
||||
}
|
||||
@@ -158,6 +160,9 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
||||
}
|
||||
|
||||
func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
|
||||
if req.Timeout == 0 {
|
||||
req.Timeout = 30
|
||||
}
|
||||
db, _ := databaseRepo.Get(repo.WithByName(req.Name))
|
||||
if db.ID != 0 {
|
||||
if db.From == "local" {
|
||||
@@ -173,7 +178,7 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: 6,
|
||||
Timeout: req.Timeout,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -182,6 +187,7 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Password: req.Password,
|
||||
Timeout: req.Timeout,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -198,7 +204,7 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
Timeout: 6,
|
||||
Timeout: req.Timeout,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -273,7 +279,7 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: 300,
|
||||
Timeout: req.Timeout,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -282,6 +288,7 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Password: req.Password,
|
||||
Timeout: req.Timeout,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -298,7 +305,7 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
Timeout: 300,
|
||||
Timeout: req.Timeout,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -318,6 +325,7 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
||||
upMap["port"] = req.Port
|
||||
upMap["username"] = req.Username
|
||||
upMap["password"] = pass
|
||||
upMap["timeout"] = req.Timeout
|
||||
upMap["description"] = req.Description
|
||||
upMap["ssl"] = req.SSL
|
||||
upMap["client_key"] = req.ClientKey
|
||||
|
||||
@@ -34,7 +34,7 @@ func (u *DBCommonService) LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.D
|
||||
}
|
||||
data.ContainerName = app.ContainerName
|
||||
data.Name = app.Name
|
||||
data.Port = int64(app.Port)
|
||||
data.Port = app.Port
|
||||
|
||||
return &data, nil
|
||||
}
|
||||
@@ -42,6 +42,10 @@ func (u *DBCommonService) LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.D
|
||||
func (u *DBCommonService) LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error) {
|
||||
filePath := ""
|
||||
switch req.Type {
|
||||
case "mysql-cluster-conf":
|
||||
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/mysql-cluster/%s/conf/my.cnf", req.Name))
|
||||
case "postgresql-cluster-conf":
|
||||
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/postgresql-cluster/%s/data/postgresql.conf", req.Name))
|
||||
case "mysql-conf":
|
||||
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/mysql/%s/conf/my.cnf", req.Name))
|
||||
case "mariadb-conf":
|
||||
@@ -50,6 +54,8 @@ func (u *DBCommonService) LoadDatabaseFile(req dto.OperationWithNameAndType) (st
|
||||
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/postgresql/%s/data/postgresql.conf", req.Name))
|
||||
case "redis-conf":
|
||||
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/redis/%s/conf/redis.conf", req.Name))
|
||||
case "redis-cluster-conf":
|
||||
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/redis-cluster/%s/conf/redis.conf", req.Name))
|
||||
}
|
||||
if _, err := os.Stat(filePath); err != nil {
|
||||
return "", buserr.New("ErrHttpReqNotFound")
|
||||
@@ -68,11 +74,11 @@ func (u *DBCommonService) UpdateConfByFile(req dto.DBConfUpdateByFile) error {
|
||||
}
|
||||
path := ""
|
||||
switch req.Type {
|
||||
case constant.AppMariaDB, constant.AppMysql:
|
||||
case constant.AppMariaDB, constant.AppMysql, constant.AppMysqlCluster:
|
||||
path = fmt.Sprintf("%s/%s/%s/conf/my.cnf", global.Dir.AppInstallDir, req.Type, app.Name)
|
||||
case constant.AppPostgresql:
|
||||
case constant.AppPostgresql, constant.AppPostgresqlCluster:
|
||||
path = fmt.Sprintf("%s/%s/%s/data/postgresql.conf", global.Dir.AppInstallDir, req.Type, app.Name)
|
||||
case constant.AppRedis:
|
||||
case constant.AppRedis, constant.AppRedisCluster:
|
||||
path = fmt.Sprintf("%s/%s/%s/conf/redis.conf", global.Dir.AppInstallDir, req.Type, app.Name)
|
||||
}
|
||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -42,7 +42,7 @@ type IMysqlService interface {
|
||||
ChangePassword(info dto.ChangeDBInfo) error
|
||||
UpdateVariables(req dto.MysqlVariablesUpdate) error
|
||||
UpdateDescription(req dto.UpdateDescription) error
|
||||
DeleteCheck(req dto.MysqlDBDeleteCheck) ([]string, error)
|
||||
DeleteCheck(req dto.MysqlDBDeleteCheck) ([]dto.DBResource, error)
|
||||
Delete(ctx context.Context, req dto.MysqlDBDelete) error
|
||||
|
||||
LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlStatus, error)
|
||||
@@ -229,23 +229,41 @@ func (u *MysqlService) UpdateDescription(req dto.UpdateDescription) error {
|
||||
return mysqlRepo.Update(req.ID, map[string]interface{}{"description": req.Description})
|
||||
}
|
||||
|
||||
func (u *MysqlService) DeleteCheck(req dto.MysqlDBDeleteCheck) ([]string, error) {
|
||||
var appInUsed []string
|
||||
func (u *MysqlService) DeleteCheck(req dto.MysqlDBDeleteCheck) ([]dto.DBResource, error) {
|
||||
var res []dto.DBResource
|
||||
db, err := mysqlRepo.Get(repo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return appInUsed, err
|
||||
return res, err
|
||||
}
|
||||
|
||||
website, _ := websiteRepo.GetFirst(websiteRepo.WithDBType(constant.AppMysql), websiteRepo.WithDBID(req.ID))
|
||||
if website.ID != 0 {
|
||||
res = append(res, dto.DBResource{
|
||||
Type: constant.TypeWebsite,
|
||||
Name: website.PrimaryDomain,
|
||||
})
|
||||
}
|
||||
website, _ = websiteRepo.GetFirst(websiteRepo.WithDBType(constant.AppMysqlCluster), websiteRepo.WithDBID(req.ID))
|
||||
if website.ID != 0 {
|
||||
res = append(res, dto.DBResource{
|
||||
Type: constant.TypeWebsite,
|
||||
Name: website.PrimaryDomain,
|
||||
})
|
||||
}
|
||||
|
||||
if db.From == "local" {
|
||||
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
|
||||
if err != nil {
|
||||
return appInUsed, err
|
||||
return res, err
|
||||
}
|
||||
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(db.ID))
|
||||
for _, app := range apps {
|
||||
appInstall, _ := appInstallRepo.GetFirst(repo.WithByID(app.AppInstallId))
|
||||
if appInstall.ID != 0 {
|
||||
appInUsed = append(appInUsed, appInstall.Name)
|
||||
res = append(res, dto.DBResource{
|
||||
Type: constant.TypeApp,
|
||||
Name: appInstall.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -253,12 +271,15 @@ func (u *MysqlService) DeleteCheck(req dto.MysqlDBDeleteCheck) ([]string, error)
|
||||
for _, app := range apps {
|
||||
appInstall, _ := appInstallRepo.GetFirst(repo.WithByID(app.AppInstallId))
|
||||
if appInstall.ID != 0 {
|
||||
appInUsed = append(appInUsed, appInstall.Name)
|
||||
res = append(res, dto.DBResource{
|
||||
Type: constant.TypeApp,
|
||||
Name: appInstall.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return appInUsed, nil
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) Delete(ctx context.Context, req dto.MysqlDBDelete) error {
|
||||
@@ -282,11 +303,11 @@ func (u *MysqlService) Delete(ctx context.Context, req dto.MysqlDBDelete) error
|
||||
}
|
||||
|
||||
if req.DeleteBackup {
|
||||
uploadDir := path.Join(global.Dir.DataDir, fmt.Sprintf("uploads/database/%s/%s/%s", req.Type, req.Database, db.Name))
|
||||
uploadDir := filepath.Join(global.Dir.DataDir, fmt.Sprintf("uploads/database/%s/%s/%s", req.Type, req.Database, db.Name))
|
||||
if _, err := os.Stat(uploadDir); err == nil {
|
||||
_ = os.RemoveAll(uploadDir)
|
||||
}
|
||||
backupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf("database/%s/%s/%s", req.Type, db.MysqlName, db.Name))
|
||||
backupDir := filepath.Join(global.Dir.LocalBackupDir, fmt.Sprintf("database/%s/%s/%s", req.Type, db.MysqlName, db.Name))
|
||||
if _, err := os.Stat(backupDir); err == nil {
|
||||
_ = os.RemoveAll(backupDir)
|
||||
}
|
||||
@@ -438,6 +459,17 @@ func (u *MysqlService) UpdateVariables(req dto.MysqlVariablesUpdate) error {
|
||||
|
||||
group := "[mysqld]"
|
||||
for _, info := range req.Variables {
|
||||
if info.Param == "slow_query_log" && info.Value == "ON" {
|
||||
logFilePath := filepath.Join(global.Dir.DataDir, fmt.Sprintf("apps/%s/%s/data/1Panel-slow.log", app.Key, app.Name))
|
||||
if req.Type == "mariadb" {
|
||||
logFilePath = filepath.Join(global.Dir.DataDir, fmt.Sprintf("apps/%s/%s/db/data/1Panel-slow.log", app.Key, app.Name))
|
||||
}
|
||||
file, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
}
|
||||
if !strings.HasPrefix(app.Version, "5.7") && !strings.HasPrefix(app.Version, "5.6") {
|
||||
if info.Param == "query_cache_size" {
|
||||
continue
|
||||
@@ -534,7 +566,7 @@ func (u *MysqlService) LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlS
|
||||
info.File = "OFF"
|
||||
info.Position = "OFF"
|
||||
masterStatus := "show master status;"
|
||||
if common.CompareAppVersion(app.Version, "8.4.0") && req.Type == constant.AppMysql {
|
||||
if common.CompareAppVersion(app.Version, "8.4.0") && (req.Type == constant.AppMysql || req.Type == constant.AppMysqlCluster) {
|
||||
masterStatus = "show binary log status;"
|
||||
}
|
||||
rows, err := executeSqlForRows(app.ContainerName, app.Key, app.Password, masterStatus)
|
||||
@@ -553,10 +585,13 @@ func (u *MysqlService) LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlS
|
||||
}
|
||||
|
||||
func executeSqlForMaps(containerName, dbType, password, command string) (map[string]string, error) {
|
||||
if dbType == "mysql-cluster" {
|
||||
dbType = "mysql"
|
||||
}
|
||||
cmd := exec.Command("docker", "exec", containerName, dbType, "-uroot", "-p"+password, "-e", command)
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
stdStr := strings.ReplaceAll(string(stdout), "mysql: [Warning] Using a password on the command line interface can be insecure.\n", "")
|
||||
if err != nil || strings.HasPrefix(string(stdStr), "ERROR ") {
|
||||
if err != nil || strings.HasPrefix(stdStr, "ERROR ") {
|
||||
return nil, errors.New(stdStr)
|
||||
}
|
||||
|
||||
@@ -572,10 +607,13 @@ func executeSqlForMaps(containerName, dbType, password, command string) (map[str
|
||||
}
|
||||
|
||||
func executeSqlForRows(containerName, dbType, password, command string) ([]string, error) {
|
||||
if dbType == "mysql-cluster" {
|
||||
dbType = "mysql"
|
||||
}
|
||||
cmd := exec.Command("docker", "exec", containerName, dbType, "-uroot", "-p"+password, "-e", command)
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
stdStr := strings.ReplaceAll(string(stdout), "mysql: [Warning] Using a password on the command line interface can be insecure.\n", "")
|
||||
if err != nil || strings.HasPrefix(string(stdStr), "ERROR ") {
|
||||
if err != nil || strings.HasPrefix(stdStr, "ERROR ") {
|
||||
return nil, errors.New(stdStr)
|
||||
}
|
||||
return strings.Split(stdStr, "\n"), nil
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
@@ -33,7 +35,7 @@ type IPostgresqlService interface {
|
||||
ChangePrivileges(req dto.PostgresqlPrivileges) error
|
||||
ChangePassword(info dto.ChangeDBInfo) error
|
||||
UpdateDescription(req dto.UpdateDescription) error
|
||||
DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]string, error)
|
||||
DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]dto.DBResource, error)
|
||||
Delete(ctx context.Context, req dto.PostgresqlDBDelete) error
|
||||
}
|
||||
|
||||
@@ -250,23 +252,41 @@ func (u *PostgresqlService) UpdateDescription(req dto.UpdateDescription) error {
|
||||
return postgresqlRepo.Update(req.ID, map[string]interface{}{"description": req.Description})
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]string, error) {
|
||||
var appInUsed []string
|
||||
func (u *PostgresqlService) DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]dto.DBResource, error) {
|
||||
var res []dto.DBResource
|
||||
db, err := postgresqlRepo.Get(repo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return appInUsed, err
|
||||
return res, err
|
||||
}
|
||||
|
||||
website, _ := websiteRepo.GetFirst(websiteRepo.WithDBType(constant.AppPostgresql), websiteRepo.WithDBID(req.ID))
|
||||
if website.ID != 0 {
|
||||
res = append(res, dto.DBResource{
|
||||
Type: constant.TypeWebsite,
|
||||
Name: website.PrimaryDomain,
|
||||
})
|
||||
}
|
||||
website, _ = websiteRepo.GetFirst(websiteRepo.WithDBType(constant.AppPostgresqlCluster), websiteRepo.WithDBID(req.ID))
|
||||
if website.ID != 0 {
|
||||
res = append(res, dto.DBResource{
|
||||
Type: constant.TypeWebsite,
|
||||
Name: website.PrimaryDomain,
|
||||
})
|
||||
}
|
||||
|
||||
if db.From == "local" {
|
||||
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
|
||||
if err != nil {
|
||||
return appInUsed, err
|
||||
return res, err
|
||||
}
|
||||
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(db.ID))
|
||||
for _, app := range apps {
|
||||
appInstall, _ := appInstallRepo.GetFirst(repo.WithByID(app.AppInstallId))
|
||||
if appInstall.ID != 0 {
|
||||
appInUsed = append(appInUsed, appInstall.Name)
|
||||
res = append(res, dto.DBResource{
|
||||
Type: constant.TypeApp,
|
||||
Name: appInstall.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -274,12 +294,15 @@ func (u *PostgresqlService) DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]stri
|
||||
for _, app := range apps {
|
||||
appInstall, _ := appInstallRepo.GetFirst(repo.WithByID(app.AppInstallId))
|
||||
if appInstall.ID != 0 {
|
||||
appInUsed = append(appInUsed, appInstall.Name)
|
||||
res = append(res, dto.DBResource{
|
||||
Type: constant.TypeApp,
|
||||
Name: appInstall.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return appInUsed, nil
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) Delete(ctx context.Context, req dto.PostgresqlDBDelete) error {
|
||||
@@ -298,6 +321,9 @@ func (u *PostgresqlService) Delete(ctx context.Context, req dto.PostgresqlDBDele
|
||||
ForceDelete: req.ForceDelete,
|
||||
Timeout: 300,
|
||||
}); err != nil && !req.ForceDelete {
|
||||
if strings.HasPrefix(err.Error(), "drop user") {
|
||||
_ = postgresqlRepo.Update(db.ID, map[string]interface{}{"is_delete": true})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ type IRedisService interface {
|
||||
UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error
|
||||
ChangePassword(info dto.ChangeRedisPass) error
|
||||
|
||||
LoadStatus(req dto.OperationWithName) (*dto.RedisStatus, error)
|
||||
LoadConf(req dto.OperationWithName) (*dto.RedisConf, error)
|
||||
LoadPersistenceConf(req dto.OperationWithName) (*dto.RedisPersistence, error)
|
||||
LoadStatus(req dto.LoadRedisStatus) (*dto.RedisStatus, error)
|
||||
LoadConf(req dto.LoadRedisStatus) (*dto.RedisConf, error)
|
||||
LoadPersistenceConf(req dto.LoadRedisStatus) (*dto.RedisPersistence, error)
|
||||
|
||||
CheckHasCli() bool
|
||||
InstallCli() error
|
||||
@@ -41,7 +41,7 @@ func NewIRedisService() IRedisService {
|
||||
}
|
||||
|
||||
func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Database)
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo(req.DBType, req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -50,10 +50,10 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
|
||||
confs = append(confs, redisConfig{key: "timeout", value: req.Timeout})
|
||||
confs = append(confs, redisConfig{key: "maxclients", value: req.Maxclients})
|
||||
confs = append(confs, redisConfig{key: "maxmemory", value: req.Maxmemory})
|
||||
if err := confSet(redisInfo.Name, "", confs); err != nil {
|
||||
if err := confSet(redisInfo.Name, req.DBType, "", confs); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := compose.Restart(fmt.Sprintf("%s/redis/%s/docker-compose.yml", global.Dir.AppInstallDir, redisInfo.Name)); err != nil {
|
||||
if _, err := compose.Restart(fmt.Sprintf("%s/%s/%s/docker-compose.yml", global.Dir.AppInstallDir, req.DBType, redisInfo.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ func (u *RedisService) InstallCli() error {
|
||||
Image: "redis:7.4.4",
|
||||
Network: "1panel-network",
|
||||
}
|
||||
return NewIContainerService().ContainerCreate(item)
|
||||
return NewIContainerService().ContainerCreate(item, false)
|
||||
}
|
||||
|
||||
func (u *RedisService) ChangePassword(req dto.ChangeRedisPass) error {
|
||||
@@ -107,7 +107,7 @@ func (u *RedisService) ChangePassword(req dto.ChangeRedisPass) error {
|
||||
}
|
||||
|
||||
func (u *RedisService) UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Database)
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo(req.DBType, req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -119,18 +119,18 @@ func (u *RedisService) UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate)
|
||||
confs = append(confs, redisConfig{key: "appendonly", value: req.Appendonly})
|
||||
confs = append(confs, redisConfig{key: "appendfsync", value: req.Appendfsync})
|
||||
}
|
||||
if err := confSet(redisInfo.Name, req.Type, confs); err != nil {
|
||||
if err := confSet(redisInfo.Name, req.DBType, req.Type, confs); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := compose.Restart(fmt.Sprintf("%s/redis/%s/docker-compose.yml", global.Dir.AppInstallDir, redisInfo.Name)); err != nil {
|
||||
if _, err := compose.Restart(fmt.Sprintf("%s/%s/%s/docker-compose.yml", global.Dir.AppInstallDir, req.DBType, redisInfo.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *RedisService) LoadStatus(req dto.OperationWithName) (*dto.RedisStatus, error) {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name)
|
||||
func (u *RedisService) LoadStatus(req dto.LoadRedisStatus) (*dto.RedisStatus, error) {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -157,8 +157,8 @@ func (u *RedisService) LoadStatus(req dto.OperationWithName) (*dto.RedisStatus,
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func (u *RedisService) LoadConf(req dto.OperationWithName) (*dto.RedisConf, error) {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name)
|
||||
func (u *RedisService) LoadConf(req dto.LoadRedisStatus) (*dto.RedisConf, error) {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -174,8 +174,8 @@ func (u *RedisService) LoadConf(req dto.OperationWithName) (*dto.RedisConf, erro
|
||||
return &item, nil
|
||||
}
|
||||
|
||||
func (u *RedisService) LoadPersistenceConf(req dto.OperationWithName) (*dto.RedisPersistence, error) {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name)
|
||||
func (u *RedisService) LoadPersistenceConf(req dto.LoadRedisStatus) (*dto.RedisPersistence, error) {
|
||||
redisInfo, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -214,8 +214,8 @@ type redisConfig struct {
|
||||
value string
|
||||
}
|
||||
|
||||
func confSet(redisName string, updateType string, changeConf []redisConfig) error {
|
||||
path := fmt.Sprintf("%s/redis/%s/conf/redis.conf", global.Dir.AppInstallDir, redisName)
|
||||
func confSet(redisName string, redisType string, updateType string, changeConf []redisConfig) error {
|
||||
path := fmt.Sprintf("%s/%s/%s/conf/redis.conf", global.Dir.AppInstallDir, redisType, redisName)
|
||||
lineBytes, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -244,6 +244,10 @@ func confSet(redisName string, updateType string, changeConf []redisConfig) erro
|
||||
}
|
||||
newFiles = append(newFiles, files[i])
|
||||
}
|
||||
if startIndex == 0 {
|
||||
newFiles = append(newFiles, "# Redis configuration rewrite by 1Panel")
|
||||
startIndex = len(newFiles) - 1
|
||||
}
|
||||
endIndex = endIndex - emptyLine
|
||||
for _, item := range changeConf {
|
||||
if item.key == "save" {
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ntp"
|
||||
"github.com/shirou/gopsutil/v3/mem"
|
||||
"github.com/shirou/gopsutil/v4/mem"
|
||||
)
|
||||
|
||||
const defaultDNSPath = "/etc/resolv.conf"
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/build"
|
||||
@@ -78,19 +79,19 @@ func (u *DeviceService) Scan() dto.CleanData {
|
||||
}
|
||||
treeData = append(treeData, upgradeTree)
|
||||
|
||||
snapTree := loadSnapshotTree(fileOp)
|
||||
snapSize := uint64(0)
|
||||
for _, snap := range snapTree {
|
||||
snapSize += snap.Size
|
||||
tmpBackupTree := loadTmpBackupTree(fileOp)
|
||||
tmpBackupSize := uint64(0)
|
||||
for _, tmp := range tmpBackupTree {
|
||||
tmpBackupSize += tmp.Size
|
||||
}
|
||||
treeData = append(treeData, dto.CleanTree{
|
||||
ID: uuid.NewString(),
|
||||
Label: "snapshot",
|
||||
Size: snapSize,
|
||||
IsCheck: snapSize > 0,
|
||||
Label: "backup",
|
||||
Size: tmpBackupSize,
|
||||
IsCheck: tmpBackupSize > 0,
|
||||
IsRecommend: true,
|
||||
Type: "snapshot",
|
||||
Children: snapTree,
|
||||
Type: "backup",
|
||||
Children: tmpBackupTree,
|
||||
})
|
||||
|
||||
rollBackTree := loadRollBackTree(fileOp)
|
||||
@@ -118,21 +119,6 @@ func (u *DeviceService) Scan() dto.CleanData {
|
||||
IsRecommend: false,
|
||||
Type: "cache",
|
||||
})
|
||||
|
||||
unusedTree := loadUnusedFile(fileOp)
|
||||
unusedSize := uint64(0)
|
||||
for _, unused := range unusedTree {
|
||||
unusedSize += unused.Size
|
||||
}
|
||||
treeData = append(treeData, dto.CleanTree{
|
||||
ID: uuid.NewString(),
|
||||
Label: "unused",
|
||||
Size: unusedSize,
|
||||
IsCheck: unusedSize > 0,
|
||||
IsRecommend: true,
|
||||
Type: "unused",
|
||||
Children: unusedTree,
|
||||
})
|
||||
SystemClean.SystemClean = treeData
|
||||
|
||||
uploadTreeData := loadUploadTree(fileOp)
|
||||
@@ -162,13 +148,13 @@ func (u *DeviceService) Clean(req []dto.Clean) {
|
||||
case "upgrade":
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, upgradePath, item.Name))
|
||||
|
||||
case "snapshot":
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, snapshotTmpPath, item.Name))
|
||||
dropFileOrDir(path.Join(global.Dir.LocalBackupDir, "system", item.Name))
|
||||
case "snapshot_tmp":
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, snapshotTmpPath, item.Name))
|
||||
case "snapshot_local":
|
||||
dropFileOrDir(path.Join(global.Dir.LocalBackupDir, "system", item.Name))
|
||||
case "backup":
|
||||
dropFileOrDir(path.Join(global.Dir.LocalBackupDir, "tmp/app"))
|
||||
dropFileOrDir(path.Join(global.Dir.LocalBackupDir, "tmp/database"))
|
||||
dropFileOrDir(path.Join(global.Dir.LocalBackupDir, "tmp/website"))
|
||||
dropFileOrDir(path.Join(global.Dir.LocalBackupDir, "tmp/directory"))
|
||||
dropFileOrDir(path.Join(global.Dir.LocalBackupDir, "tmp/log"))
|
||||
dropFileOrDir(path.Join(global.Dir.LocalBackupDir, "tmp/system"))
|
||||
|
||||
case "rollback":
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, rollbackPath, "app"))
|
||||
@@ -185,40 +171,6 @@ func (u *DeviceService) Clean(req []dto.Clean) {
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, cachePath, item.Name))
|
||||
restart = true
|
||||
|
||||
case "unused":
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, oldOriginalPath))
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, oldAppBackupPath))
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, oldDownloadPath))
|
||||
files, _ := os.ReadDir(path.Join(global.Dir.BaseDir, oldUpgradePath))
|
||||
if len(files) == 0 {
|
||||
continue
|
||||
}
|
||||
for _, file := range files {
|
||||
if strings.HasPrefix(file.Name(), "upgrade_") {
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, oldUpgradePath, file.Name()))
|
||||
}
|
||||
}
|
||||
case "old_original":
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, oldOriginalPath, item.Name))
|
||||
case "old_apps_bak":
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, oldAppBackupPath, item.Name))
|
||||
case "old_download":
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, oldDownloadPath, item.Name))
|
||||
case "old_upgrade":
|
||||
if len(item.Name) == 0 {
|
||||
files, _ := os.ReadDir(path.Join(global.Dir.BaseDir, oldUpgradePath))
|
||||
if len(files) == 0 {
|
||||
continue
|
||||
}
|
||||
for _, file := range files {
|
||||
if strings.HasPrefix(file.Name(), "upgrade_") {
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, oldUpgradePath, file.Name()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, oldUpgradePath, item.Name))
|
||||
}
|
||||
|
||||
case "upload":
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, uploadPath, item.Name))
|
||||
if len(item.Name) == 0 {
|
||||
@@ -283,13 +235,13 @@ func (u *DeviceService) Clean(req []dto.Clean) {
|
||||
case "script":
|
||||
dropFileOrDir(path.Join(global.Dir.TmpDir, "script", item.Name))
|
||||
case "images":
|
||||
dropImages()
|
||||
_, _ = dropImages()
|
||||
case "containers":
|
||||
dropContainers()
|
||||
_, _ = dropContainers()
|
||||
case "volumes":
|
||||
dropVolumes()
|
||||
_, _ = dropVolumes()
|
||||
case "build_cache":
|
||||
dropBuildCache()
|
||||
_, _ = dropBuildCache()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,7 +274,7 @@ func doSystemClean(taskItem *task.Task) func(t *task.Task) error {
|
||||
}
|
||||
|
||||
dropWithTask(path.Join(global.Dir.BaseDir, snapshotTmpPath), taskItem, &size, &fileCount)
|
||||
dropWithTask(path.Join(global.Dir.LocalBackupDir, "system"), taskItem, &size, &fileCount)
|
||||
dropWithTask(path.Join(global.Dir.LocalBackupDir, "tmp/system"), taskItem, &size, &fileCount)
|
||||
|
||||
dropWithTask(path.Join(global.Dir.BaseDir, rollbackPath, "app"), taskItem, &size, &fileCount)
|
||||
dropWithTask(path.Join(global.Dir.BaseDir, rollbackPath, "website"), taskItem, &size, &fileCount)
|
||||
@@ -354,9 +306,17 @@ func doSystemClean(taskItem *task.Task) func(t *task.Task) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
count1, size1 := dropVolumes()
|
||||
size += int64(size1)
|
||||
fileCount += count1
|
||||
count2, size2 := dropBuildCache()
|
||||
size += int64(size2)
|
||||
fileCount += count2
|
||||
|
||||
timeNow := time.Now().Format(constant.DateTimeLayout)
|
||||
if fileCount != 0 {
|
||||
taskItem.LogSuccessF("%s: total clean: %s, total count: %d", timeNow, common.LoadSizeUnit2F(float64(size)), fileCount)
|
||||
taskItem.Log(i18n.GetMsgWithMap("FileDropSum", map[string]interface{}{"size": common.LoadSizeUnit2F(float64(size)), "count": fileCount}))
|
||||
}
|
||||
|
||||
_ = settingRepo.Update("LastCleanTime", timeNow)
|
||||
@@ -367,106 +327,32 @@ func doSystemClean(taskItem *task.Task) func(t *task.Task) error {
|
||||
}
|
||||
}
|
||||
|
||||
func loadSnapshotTree(fileOp fileUtils.FileOp) []dto.CleanTree {
|
||||
func loadTmpBackupTree(fileOp fileUtils.FileOp) []dto.CleanTree {
|
||||
var treeData []dto.CleanTree
|
||||
path1 := path.Join(global.Dir.BaseDir, snapshotTmpPath)
|
||||
list1 := loadTreeWithAllFile(true, path1, "snapshot_tmp", path1, fileOp)
|
||||
if len(list1) != 0 {
|
||||
size, _ := fileOp.GetDirSize(path1)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "snapshot_tmp", Size: uint64(size), Children: list1, Type: "snapshot_tmp", IsRecommend: true})
|
||||
}
|
||||
|
||||
path2 := path.Join(global.Dir.LocalBackupDir, "system")
|
||||
list2 := loadTreeWithAllFile(true, path2, "snapshot_local", path2, fileOp)
|
||||
if len(list2) != 0 {
|
||||
size, _ := fileOp.GetDirSize(path2)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "snapshot_local", Size: uint64(size), Children: list2, Type: "snapshot_local", IsRecommend: true})
|
||||
}
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.LocalBackupDir, "tmp/app"), "tmp_backup_app", fileOp)
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.LocalBackupDir, "tmp/website"), "tmp_backup_website", fileOp)
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.LocalBackupDir, "tmp/database"), "tmp_backup_database", fileOp)
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.LocalBackupDir, "tmp/system"), "tmp_backup_snapshot", fileOp)
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.LocalBackupDir, "tmp/directory"), "tmp_backup_directory", fileOp)
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.LocalBackupDir, "tmp/log"), "tmp_backup_log", fileOp)
|
||||
return treeData
|
||||
}
|
||||
|
||||
func loadRollBackTree(fileOp fileUtils.FileOp) []dto.CleanTree {
|
||||
var treeData []dto.CleanTree
|
||||
path1 := path.Join(global.Dir.BaseDir, rollbackPath, "app")
|
||||
list1 := loadTreeWithAllFile(true, path1, "rollback_app", path1, fileOp)
|
||||
size1, _ := fileOp.GetDirSize(path1)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "rollback_app", Size: uint64(size1), Children: list1, Type: "rollback_app", IsRecommend: true})
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, rollbackPath, "app"), "rollback_app", fileOp)
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, rollbackPath, "website"), "rollback_website", fileOp)
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, rollbackPath, "database"), "rollback_database", fileOp)
|
||||
|
||||
path2 := path.Join(global.Dir.BaseDir, rollbackPath, "website")
|
||||
list2 := loadTreeWithAllFile(true, path2, "rollback_website", path2, fileOp)
|
||||
size2, _ := fileOp.GetDirSize(path2)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "rollback_website", Size: uint64(size2), Children: list2, Type: "rollback_website", IsRecommend: true})
|
||||
|
||||
path3 := path.Join(global.Dir.BaseDir, rollbackPath, "database")
|
||||
list3 := loadTreeWithAllFile(true, path3, "rollback_database", path3, fileOp)
|
||||
size3, _ := fileOp.GetDirSize(path3)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "rollback_database", Size: uint64(size3), Children: list3, Type: "rollback_database", IsRecommend: true})
|
||||
|
||||
return treeData
|
||||
}
|
||||
|
||||
func loadUnusedFile(fileOp fileUtils.FileOp) []dto.CleanTree {
|
||||
var treeData []dto.CleanTree
|
||||
path1 := path.Join(global.Dir.BaseDir, oldOriginalPath)
|
||||
list1 := loadTreeWithAllFile(true, path1, "old_original", path1, fileOp)
|
||||
if len(list1) != 0 {
|
||||
size, _ := fileOp.GetDirSize(path1)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "old_original", Size: uint64(size), Children: list1, Type: "old_original"})
|
||||
}
|
||||
|
||||
path2 := path.Join(global.Dir.BaseDir, oldAppBackupPath)
|
||||
list2 := loadTreeWithAllFile(true, path2, "old_apps_bak", path2, fileOp)
|
||||
if len(list2) != 0 {
|
||||
size, _ := fileOp.GetDirSize(path2)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "old_apps_bak", Size: uint64(size), Children: list2, Type: "old_apps_bak"})
|
||||
}
|
||||
|
||||
path3 := path.Join(global.Dir.BaseDir, oldDownloadPath)
|
||||
list3 := loadTreeWithAllFile(true, path3, "old_download", path3, fileOp)
|
||||
if len(list3) != 0 {
|
||||
size, _ := fileOp.GetDirSize(path3)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "old_download", Size: uint64(size), Children: list3, Type: "old_download"})
|
||||
}
|
||||
|
||||
path4 := path.Join(global.Dir.BaseDir, oldUpgradePath)
|
||||
list4 := loadTreeWithDir(true, "old_upgrade", path4, fileOp)
|
||||
itemSize := uint64(0)
|
||||
for _, item := range list4 {
|
||||
itemSize += item.Size
|
||||
}
|
||||
if len(list4) != 0 {
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "old_upgrade", Size: itemSize, Children: list4, Type: "old_upgrade"})
|
||||
}
|
||||
return treeData
|
||||
}
|
||||
|
||||
func loadUploadTree(fileOp fileUtils.FileOp) []dto.CleanTree {
|
||||
var treeData []dto.CleanTree
|
||||
|
||||
path0 := path.Join(global.Dir.BaseDir, tmpUploadPath)
|
||||
list0 := loadTreeWithAllFile(true, path0, "upload_tmp", path0, fileOp)
|
||||
size0, _ := fileOp.GetDirSize(path0)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "upload_tmp", Size: uint64(size0), Children: list0, Type: "upload_tmp", IsRecommend: true})
|
||||
|
||||
path1 := path.Join(global.Dir.BaseDir, uploadPath, "app")
|
||||
list1 := loadTreeWithAllFile(true, path1, "upload_app", path1, fileOp)
|
||||
size1, _ := fileOp.GetDirSize(path1)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "upload_app", Size: uint64(size1), Children: list1, Type: "upload_app", IsRecommend: true})
|
||||
|
||||
path2 := path.Join(global.Dir.BaseDir, uploadPath, "website")
|
||||
list2 := loadTreeWithAllFile(true, path2, "upload_website", path2, fileOp)
|
||||
size2, _ := fileOp.GetDirSize(path2)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "upload_website", Size: uint64(size2), Children: list2, Type: "upload_website", IsRecommend: true})
|
||||
|
||||
path3 := path.Join(global.Dir.BaseDir, uploadPath, "database")
|
||||
list3 := loadTreeWithAllFile(true, path3, "upload_database", path3, fileOp)
|
||||
size3, _ := fileOp.GetDirSize(path3)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "upload_database", Size: uint64(size3), Children: list3, Type: "upload_database", IsRecommend: true})
|
||||
|
||||
path4 := path.Join(global.Dir.BaseDir, uploadPath, "directory")
|
||||
list4 := loadTreeWithAllFile(true, path4, "upload_directory", path4, fileOp)
|
||||
size4, _ := fileOp.GetDirSize(path4)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "upload_directory", Size: uint64(size4), Children: list4, Type: "upload_directory", IsRecommend: true})
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, tmpUploadPath), "upload_tmp", fileOp)
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, uploadPath, "app"), "upload_app", fileOp)
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, uploadPath, "website"), "upload_website", fileOp)
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, uploadPath, "database"), "upload_database", fileOp)
|
||||
|
||||
path5 := path.Join(global.Dir.BaseDir, uploadPath)
|
||||
uploadTreeData := loadTreeWithAllFile(true, path5, "upload", path5, fileOp)
|
||||
@@ -477,25 +363,9 @@ func loadUploadTree(fileOp fileUtils.FileOp) []dto.CleanTree {
|
||||
|
||||
func loadDownloadTree(fileOp fileUtils.FileOp) []dto.CleanTree {
|
||||
var treeData []dto.CleanTree
|
||||
path1 := path.Join(global.Dir.BaseDir, downloadPath, "app")
|
||||
list1 := loadTreeWithAllFile(true, path1, "download_app", path1, fileOp)
|
||||
size1, _ := fileOp.GetDirSize(path1)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "download_app", Size: uint64(size1), Children: list1, Type: "download_app", IsRecommend: true})
|
||||
|
||||
path2 := path.Join(global.Dir.BaseDir, downloadPath, "website")
|
||||
list2 := loadTreeWithAllFile(true, path2, "download_website", path2, fileOp)
|
||||
size2, _ := fileOp.GetDirSize(path2)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "download_website", Size: uint64(size2), Children: list2, Type: "download_website", IsRecommend: true})
|
||||
|
||||
path3 := path.Join(global.Dir.BaseDir, downloadPath, "database")
|
||||
list3 := loadTreeWithAllFile(true, path3, "download_database", path3, fileOp)
|
||||
size3, _ := fileOp.GetDirSize(path3)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "download_database", Size: uint64(size3), Children: list3, Type: "download_database", IsRecommend: true})
|
||||
|
||||
path4 := path.Join(global.Dir.BaseDir, downloadPath, "directory")
|
||||
list4 := loadTreeWithAllFile(true, path4, "download_directory", path4, fileOp)
|
||||
size4, _ := fileOp.GetDirSize(path4)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "download_directory", Size: uint64(size4), Children: list4, Type: "download_directory", IsRecommend: true})
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, downloadPath, "app"), "download_app", fileOp)
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, downloadPath, "website"), "download_website", fileOp)
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, downloadPath, "database"), "download_database", fileOp)
|
||||
|
||||
path5 := path.Join(global.Dir.BaseDir, downloadPath)
|
||||
uploadTreeData := loadTreeWithAllFile(true, path5, "download", path5, fileOp)
|
||||
@@ -558,7 +428,7 @@ func loadContainerTree() []dto.CleanTree {
|
||||
volumeSize += uint64(file.UsageData.Size)
|
||||
}
|
||||
}
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "container_volumes", Size: volumeSize, Children: nil, Type: "volumes", IsRecommend: true})
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "container_volumes", Size: volumeSize, IsCheck: volumeSize > 0, Children: nil, Type: "volumes", IsRecommend: true})
|
||||
|
||||
var buildCacheTotalSize int64
|
||||
for _, cache := range diskUsage.BuildCache {
|
||||
@@ -566,7 +436,17 @@ func loadContainerTree() []dto.CleanTree {
|
||||
buildCacheTotalSize += cache.Size
|
||||
}
|
||||
}
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "build_cache", Size: uint64(buildCacheTotalSize), Type: "build_cache", IsRecommend: true})
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "build_cache", Size: uint64(buildCacheTotalSize), IsCheck: buildCacheTotalSize > 0, Type: "build_cache", IsRecommend: true})
|
||||
return treeData
|
||||
}
|
||||
|
||||
func loadTreeWithCheck(treeData []dto.CleanTree, pathItem, treeType string, fileOp fileUtils.FileOp) []dto.CleanTree {
|
||||
size, _ := fileOp.GetDirSize(pathItem)
|
||||
if size == 0 {
|
||||
return treeData
|
||||
}
|
||||
list := loadTreeWithAllFile(true, pathItem, treeType, pathItem, fileOp)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: treeType, Size: uint64(size), IsCheck: size > 0, Children: list, Type: treeType, IsRecommend: true})
|
||||
return treeData
|
||||
}
|
||||
|
||||
@@ -577,9 +457,6 @@ func loadTreeWithDir(isCheck bool, treeType, pathItem string, fileOp fileUtils.F
|
||||
return lists
|
||||
}
|
||||
for _, file := range files {
|
||||
if treeType == "old_upgrade" {
|
||||
continue
|
||||
}
|
||||
if file.Name() == "ssl" {
|
||||
continue
|
||||
}
|
||||
@@ -659,67 +536,79 @@ func dropFileOrDir(itemPath string) {
|
||||
}
|
||||
}
|
||||
|
||||
func dropBuildCache() {
|
||||
func dropBuildCache() (int, int) {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do not get docker client")
|
||||
return 0, 0
|
||||
}
|
||||
opts := build.CachePruneOptions{}
|
||||
opts.All = true
|
||||
_, err = client.BuildCachePrune(context.Background(), opts)
|
||||
res, err := client.BuildCachePrune(context.Background(), opts)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("drop build cache failed, err %v", err)
|
||||
return 0, 0
|
||||
}
|
||||
return len(res.CachesDeleted), int(res.SpaceReclaimed)
|
||||
}
|
||||
|
||||
func dropImages() {
|
||||
func dropImages() (int, int) {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do not get docker client")
|
||||
return 0, 0
|
||||
}
|
||||
pruneFilters := filters.NewArgs()
|
||||
pruneFilters.Add("dangling", "false")
|
||||
_, err = client.ImagesPrune(context.Background(), pruneFilters)
|
||||
res, err := client.ImagesPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("drop images failed, err %v", err)
|
||||
return 0, 0
|
||||
}
|
||||
return len(res.ImagesDeleted), int(res.SpaceReclaimed)
|
||||
}
|
||||
|
||||
func dropContainers() {
|
||||
func dropContainers() (int, int) {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do not get docker client")
|
||||
return 0, 0
|
||||
}
|
||||
pruneFilters := filters.NewArgs()
|
||||
_, err = client.ContainersPrune(context.Background(), pruneFilters)
|
||||
res, err := client.ContainersPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("drop containers failed, err %v", err)
|
||||
return 0, 0
|
||||
}
|
||||
return len(res.ContainersDeleted), int(res.SpaceReclaimed)
|
||||
}
|
||||
|
||||
func dropVolumes() {
|
||||
func dropVolumes() (int, int) {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do not get docker client")
|
||||
return 0, 0
|
||||
}
|
||||
pruneFilters := filters.NewArgs()
|
||||
versions, err := client.ServerVersion(context.Background())
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do not get docker api versions")
|
||||
return 0, 0
|
||||
}
|
||||
if common.ComparePanelVersion(versions.APIVersion, "1.42") {
|
||||
pruneFilters.Add("all", "true")
|
||||
}
|
||||
_, err = client.VolumesPrune(context.Background(), pruneFilters)
|
||||
res, err := client.VolumesPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("drop volumes failed, err %v", err)
|
||||
return 0, 0
|
||||
}
|
||||
return len(res.VolumesDeleted), int(res.SpaceReclaimed)
|
||||
}
|
||||
|
||||
func dropWithExclude(pathToDelete string, excludeSubDirs []string, taskItem *task.Task, size *int64, count *int) {
|
||||
entries, err := os.ReadDir(pathToDelete)
|
||||
if err != nil {
|
||||
taskItem.LogFailed(fmt.Sprintf("read dir %s failed: %v", pathToDelete, err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -747,11 +636,11 @@ func dropWithTask(itemPath string, taskItem *task.Task, size *int64, count *int)
|
||||
*size += itemSize
|
||||
*count += itemCount
|
||||
if err := os.RemoveAll(itemPath); err != nil {
|
||||
taskItem.LogFailed(fmt.Sprintf("drop file %s, err %v", itemPath, err))
|
||||
taskItem.Log(i18n.GetWithNameAndErr("FileDropFailed", itemPath, err))
|
||||
return
|
||||
}
|
||||
if itemCount != 0 {
|
||||
taskItem.LogSuccessF("drop file %s, size: %s, count: %d", itemPath, common.LoadSizeUnit2F(float64(itemSize)), itemCount)
|
||||
taskItem.Log(i18n.GetMsgWithMap("FileDropSuccess", map[string]interface{}{"name": itemPath, "count": itemCount, "size": common.LoadSizeUnit2F(float64(itemSize))}))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,4 +50,5 @@ var (
|
||||
taskRepo = repo.NewITaskRepo()
|
||||
|
||||
groupRepo = repo.NewIGroupRepo()
|
||||
alertRepo = repo.NewIAlertRepo()
|
||||
)
|
||||
|
||||
+88
-39
@@ -4,10 +4,6 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/jinzhu/copier"
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
@@ -18,6 +14,12 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/jinzhu/copier"
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
|
||||
@@ -96,19 +98,25 @@ func (f *FileService) SearchUploadWithPage(req request.SearchUploadWithPage) (in
|
||||
files []response.UploadInfo
|
||||
backData []response.UploadInfo
|
||||
)
|
||||
_ = filepath.Walk(req.Path, func(path string, info os.FileInfo, err error) error {
|
||||
fileList, err := os.ReadDir(req.Path)
|
||||
if err != nil {
|
||||
return 0, files, nil
|
||||
}
|
||||
for _, item := range fileList {
|
||||
if item.IsDir() {
|
||||
continue
|
||||
}
|
||||
fileItem, err := item.Info()
|
||||
if err != nil {
|
||||
return nil
|
||||
continue
|
||||
}
|
||||
if !info.IsDir() {
|
||||
files = append(files, response.UploadInfo{
|
||||
CreatedAt: info.ModTime().Format(constant.DateTimeLayout),
|
||||
Size: int(info.Size()),
|
||||
Name: info.Name(),
|
||||
})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
files = append(files, response.UploadInfo{
|
||||
CreatedAt: fileItem.ModTime().Format(constant.DateTimeLayout),
|
||||
Size: int(fileItem.Size()),
|
||||
Name: item.Name(),
|
||||
})
|
||||
}
|
||||
|
||||
total, start, end := len(files), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||
if start > total {
|
||||
backData = make([]response.UploadInfo, 0)
|
||||
@@ -334,22 +342,24 @@ func (f *FileService) GetContent(op request.FileContentReq) (response.FileInfo,
|
||||
if len(content) > 1024 {
|
||||
content = content[:1024]
|
||||
}
|
||||
_, decodeName, _ := charset.DetermineEncoding(content, "")
|
||||
decoder := files.GetDecoderByName(decodeName)
|
||||
if decoder != nil {
|
||||
reader := strings.NewReader(info.Content)
|
||||
var dec *encoding.Decoder
|
||||
if decodeName == "windows-1252" {
|
||||
dec = simplifiedchinese.GBK.NewDecoder()
|
||||
} else {
|
||||
dec = decoder.NewDecoder()
|
||||
if !utf8.Valid(content) {
|
||||
_, decodeName, _ := charset.DetermineEncoding(content, "")
|
||||
decoder := files.GetDecoderByName(decodeName)
|
||||
if decoder != nil {
|
||||
reader := strings.NewReader(info.Content)
|
||||
var dec *encoding.Decoder
|
||||
if decodeName == "windows-1252" {
|
||||
dec = simplifiedchinese.GBK.NewDecoder()
|
||||
} else {
|
||||
dec = decoder.NewDecoder()
|
||||
}
|
||||
decodedReader := transform.NewReader(reader, dec)
|
||||
contents, err := io.ReadAll(decodedReader)
|
||||
if err != nil {
|
||||
return response.FileInfo{}, err
|
||||
}
|
||||
info.Content = string(contents)
|
||||
}
|
||||
decodedReader := transform.NewReader(reader, dec)
|
||||
contents, err := io.ReadAll(decodedReader)
|
||||
if err != nil {
|
||||
return response.FileInfo{}, err
|
||||
}
|
||||
info.Content = string(contents)
|
||||
}
|
||||
return response.FileInfo{FileInfo: *info}, nil
|
||||
}
|
||||
@@ -413,6 +423,14 @@ func (f *FileService) MvFile(m request.FileMove) error {
|
||||
global.LOG.Errorf("copy file [%s] to [%s] failed, err: %s", src, m.NewPath, err.Error())
|
||||
}
|
||||
}
|
||||
if len(m.CoverPaths) > 0 {
|
||||
for _, src := range m.CoverPaths {
|
||||
if err := fo.CopyAndReName(src, m.NewPath, "", true); err != nil {
|
||||
errs = append(errs, err)
|
||||
global.LOG.Errorf("copy file [%s] to [%s] failed, err: %s", src, m.NewPath, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var errString string
|
||||
@@ -475,6 +493,7 @@ func (f *FileService) DepthDirSize(req request.DirSizeReq) ([]response.DepthDirS
|
||||
|
||||
func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error) {
|
||||
logFilePath := ""
|
||||
taskStatus := ""
|
||||
switch req.Type {
|
||||
case constant.TypeWebsite:
|
||||
website, err := websiteRepo.GetFirst(repo.WithByID(req.ID))
|
||||
@@ -530,29 +549,59 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
|
||||
return nil, err
|
||||
}
|
||||
logFilePath = taskModel.LogFile
|
||||
taskStatus = taskModel.Status
|
||||
case "mysql-slow-logs":
|
||||
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))
|
||||
}
|
||||
|
||||
lines, isEndOfFile, total, err := files.ReadFileByLine(logFilePath, req.Page, req.PageSize, req.Latest)
|
||||
file, err := os.Open(logFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.Latest && req.Page == 1 && len(lines) < 1000 && total > 1 {
|
||||
preLines, _, _, err := files.ReadFileByLine(logFilePath, total-1, req.PageSize, false)
|
||||
defer file.Close()
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var (
|
||||
lines []string
|
||||
isEndOfFile bool
|
||||
scope string
|
||||
logFileRes *dto.LogFileRes
|
||||
)
|
||||
if stat.Size() > 500*1024*1024 {
|
||||
lines, err = files.TailFromEnd(logFilePath, req.PageSize)
|
||||
isEndOfFile = true
|
||||
scope = "tail"
|
||||
} else {
|
||||
logFileRes, err = files.ReadFileByLine(logFilePath, req.Page, req.PageSize, req.Latest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lines = append(preLines, lines...)
|
||||
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
|
||||
}
|
||||
|
||||
res := &response.FileLineContent{
|
||||
Content: strings.Join(lines, "\n"),
|
||||
End: isEndOfFile,
|
||||
Path: logFilePath,
|
||||
Total: total,
|
||||
Lines: lines,
|
||||
End: isEndOfFile,
|
||||
Path: logFilePath,
|
||||
TaskStatus: taskStatus,
|
||||
Lines: lines,
|
||||
Scope: scope,
|
||||
}
|
||||
if logFileRes != nil {
|
||||
res.TotalLines = logFileRes.TotalLines
|
||||
res.Total = logFileRes.TotalPages
|
||||
res.End = logFileRes.IsEndOfFile
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -323,6 +323,7 @@ func handleProcess(supervisordDir string, req request.SupervisorProcessConfig, c
|
||||
_, _ = section.NewKey("command", strings.TrimSpace(req.Command))
|
||||
_, _ = section.NewKey("directory", req.Dir)
|
||||
_, _ = section.NewKey("autorestart", req.AutoRestart)
|
||||
_, _ = section.NewKey("autostart", req.AutoStart)
|
||||
_, _ = section.NewKey("startsecs", "3")
|
||||
_, _ = section.NewKey("stdout_logfile", outLog)
|
||||
_, _ = section.NewKey("stderr_logfile", errLog)
|
||||
@@ -360,6 +361,8 @@ func handleProcess(supervisordDir string, req request.SupervisorProcessConfig, c
|
||||
numprocsKey.SetValue(req.Numprocs)
|
||||
autoRestart := section.Key("autorestart")
|
||||
autoRestart.SetValue(req.AutoRestart)
|
||||
autoStart := section.Key("autostart")
|
||||
autoStart.SetValue(req.AutoStart)
|
||||
|
||||
if err = configFile.SaveTo(iniPath); err != nil {
|
||||
return err
|
||||
@@ -429,6 +432,9 @@ func handleProcessConfig(configDir, containerName string) ([]response.Supervisor
|
||||
if autoRestart, _ := section.GetKey("autorestart"); autoRestart != nil {
|
||||
config.AutoRestart = autoRestart.Value()
|
||||
}
|
||||
if autoStart, _ := section.GetKey("autostart"); autoStart != nil {
|
||||
config.AutoStart = autoStart.Value()
|
||||
}
|
||||
_ = getProcessStatus(&config, containerName)
|
||||
result = append(result, config)
|
||||
}
|
||||
@@ -609,7 +615,7 @@ func getProcessStatus(config *response.SupervisorProcessConfig, containerName st
|
||||
for _, line := range lines {
|
||||
line = strings.TrimPrefix(line, "stdout:")
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) >= 5 {
|
||||
if len(fields) >= 4 {
|
||||
status := response.ProcessStatus{
|
||||
Name: fields[0],
|
||||
Status: fields[1],
|
||||
|
||||
+77
-46
@@ -10,6 +10,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -21,6 +22,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/docker/docker/api/types/build"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
@@ -33,7 +35,7 @@ import (
|
||||
type ImageService struct{}
|
||||
|
||||
type IImageService interface {
|
||||
Page(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
Page(req dto.PageImage) (int64, interface{}, error)
|
||||
List() ([]dto.Options, error)
|
||||
ListAll() ([]dto.ImageInfo, error)
|
||||
ImageBuild(req dto.ImageBuild) error
|
||||
@@ -41,14 +43,14 @@ type IImageService interface {
|
||||
ImageLoad(req dto.ImageLoad) error
|
||||
ImageSave(req dto.ImageSave) error
|
||||
ImagePush(req dto.ImagePush) error
|
||||
ImageRemove(req dto.BatchDelete) (dto.ContainerPruneReport, error)
|
||||
ImageRemove(req dto.BatchDelete) error
|
||||
ImageTag(req dto.ImageTag) error
|
||||
}
|
||||
|
||||
func NewIImageService() IImageService {
|
||||
return &ImageService{}
|
||||
}
|
||||
func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error) {
|
||||
func (u *ImageService) Page(req dto.PageImage) (int64, interface{}, error) {
|
||||
var (
|
||||
list []image.Summary
|
||||
records []dto.ImageInfo
|
||||
@@ -64,12 +66,12 @@ func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
return 0, nil, err
|
||||
}
|
||||
containers, _ := client.ContainerList(context.Background(), container.ListOptions{All: true})
|
||||
if len(req.Info) != 0 {
|
||||
if len(req.Name) != 0 {
|
||||
length, count := len(list), 0
|
||||
for count < length {
|
||||
hasTag := false
|
||||
for _, tag := range list[count].RepoTags {
|
||||
if strings.Contains(tag, req.Info) {
|
||||
if strings.Contains(tag, req.Name) {
|
||||
hasTag = true
|
||||
break
|
||||
}
|
||||
@@ -84,15 +86,48 @@ func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
}
|
||||
|
||||
for _, image := range list {
|
||||
size := formatFileSize(image.Size)
|
||||
records = append(records, dto.ImageInfo{
|
||||
ID: image.ID,
|
||||
Tags: image.RepoTags,
|
||||
IsUsed: checkUsed(image.ID, containers),
|
||||
CreatedAt: time.Unix(image.Created, 0),
|
||||
Size: size,
|
||||
Size: image.Size,
|
||||
})
|
||||
}
|
||||
switch req.OrderBy {
|
||||
case "size":
|
||||
sort.Slice(records, func(i, j int) bool {
|
||||
if req.Order == constant.OrderAsc {
|
||||
return records[i].Size < records[j].Size
|
||||
}
|
||||
return records[i].Size > records[j].Size
|
||||
})
|
||||
case "isUsed":
|
||||
sort.Slice(records, func(i, j int) bool {
|
||||
if req.Order == constant.OrderAsc {
|
||||
return !records[i].IsUsed
|
||||
}
|
||||
return records[i].IsUsed
|
||||
})
|
||||
case "tags":
|
||||
sort.Slice(records, func(i, j int) bool {
|
||||
if len(records[i].Tags) == 0 || len(records[j].Tags) == 0 {
|
||||
return true
|
||||
}
|
||||
if req.Order == constant.OrderAsc {
|
||||
return records[i].Tags[0] < records[j].Tags[0]
|
||||
}
|
||||
return records[i].Tags[0] > records[j].Tags[0]
|
||||
})
|
||||
default:
|
||||
sort.Slice(records, func(i, j int) bool {
|
||||
if req.Order == constant.OrderAsc {
|
||||
return records[i].CreatedAt.Before(records[j].CreatedAt)
|
||||
}
|
||||
return records[i].CreatedAt.After(records[j].CreatedAt)
|
||||
})
|
||||
}
|
||||
|
||||
total, start, end := len(records), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||
if start > total {
|
||||
backDatas = make([]dto.ImageInfo, 0)
|
||||
@@ -119,13 +154,12 @@ func (u *ImageService) ListAll() ([]dto.ImageInfo, error) {
|
||||
}
|
||||
containers, _ := client.ContainerList(context.Background(), container.ListOptions{All: true})
|
||||
for _, image := range list {
|
||||
size := formatFileSize(image.Size)
|
||||
records = append(records, dto.ImageInfo{
|
||||
ID: image.ID,
|
||||
Tags: image.RepoTags,
|
||||
IsUsed: checkUsed(image.ID, containers),
|
||||
CreatedAt: time.Unix(image.Created, 0),
|
||||
Size: size,
|
||||
Size: image.Size,
|
||||
})
|
||||
}
|
||||
return records, nil
|
||||
@@ -386,50 +420,47 @@ func (u *ImageService) ImagePush(req dto.ImagePush) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ImageService) ImageRemove(req dto.BatchDelete) (dto.ContainerPruneReport, error) {
|
||||
report := dto.ContainerPruneReport{}
|
||||
func (u *ImageService) ImageRemove(req dto.BatchDelete) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return report, err
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
for _, id := range req.Names {
|
||||
imageItem, _, err := client.ImageInspectWithRaw(context.TODO(), id)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
if _, err := client.ImageRemove(context.TODO(), id, image.RemoveOptions{Force: req.Force, PruneChildren: true}); err != nil {
|
||||
if strings.Contains(err.Error(), "image is being used") || strings.Contains(err.Error(), "is using") {
|
||||
if strings.Contains(id, "sha256:") {
|
||||
return report, buserr.New("ErrObjectInUsed")
|
||||
}
|
||||
return report, buserr.WithDetail("ErrInUsed", id, nil)
|
||||
}
|
||||
if strings.Contains(err.Error(), "image has dependent") {
|
||||
return report, buserr.New("ErrObjectBeDependent")
|
||||
}
|
||||
return report, err
|
||||
}
|
||||
report.DeletedNumber++
|
||||
report.SpaceReclaimed += int(imageItem.Size)
|
||||
taskItem, err := task.NewTaskWithOps(task.TaskScopeImage, task.TaskDelete, task.TaskScopeContainer, req.TaskID, 1)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("new task for create container failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
return report, nil
|
||||
}
|
||||
|
||||
func formatFileSize(fileSize int64) (size string) {
|
||||
if fileSize < 1024 {
|
||||
return fmt.Sprintf("%.2fB", float64(fileSize)/float64(1))
|
||||
} else if fileSize < (1024 * 1024) {
|
||||
return fmt.Sprintf("%.2fKB", float64(fileSize)/float64(1024))
|
||||
} else if fileSize < (1024 * 1024 * 1024) {
|
||||
return fmt.Sprintf("%.2fMB", float64(fileSize)/float64(1024*1024))
|
||||
} else if fileSize < (1024 * 1024 * 1024 * 1024) {
|
||||
return fmt.Sprintf("%.2fGB", float64(fileSize)/float64(1024*1024*1024))
|
||||
} else if fileSize < (1024 * 1024 * 1024 * 1024 * 1024) {
|
||||
return fmt.Sprintf("%.2fTB", float64(fileSize)/float64(1024*1024*1024*1024))
|
||||
} else {
|
||||
return fmt.Sprintf("%.2fEB", float64(fileSize)/float64(1024*1024*1024*1024*1024))
|
||||
for _, id := range req.Names {
|
||||
taskItem.AddSubTask(i18n.GetMsgByKey("TaskDelete")+id, func(t *task.Task) error {
|
||||
imageItem, err := client.ImageInspect(context.TODO(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := client.ImageRemove(context.TODO(), id, image.RemoveOptions{Force: req.Force, PruneChildren: true}); err != nil {
|
||||
if strings.Contains(err.Error(), "image is being used") || strings.Contains(err.Error(), "is using") {
|
||||
if strings.Contains(id, "sha256:") {
|
||||
return buserr.New("ErrObjectInUsed")
|
||||
}
|
||||
return buserr.WithDetail("ErrInUsed", id, nil)
|
||||
}
|
||||
if strings.Contains(err.Error(), "image has dependent") {
|
||||
return buserr.New("ErrObjectBeDependent")
|
||||
}
|
||||
return err
|
||||
}
|
||||
taskItem.Log(i18n.GetMsgWithMap("ImageRemoveHelper", map[string]interface{}{"name": id, "size": common.LoadSizeUnit2F(float64(imageItem.Size))}))
|
||||
return nil
|
||||
}, nil)
|
||||
}
|
||||
if len(req.TaskID) == 0 {
|
||||
return taskItem.Execute()
|
||||
}
|
||||
go func() {
|
||||
_ = taskItem.Execute()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkUsed(imageID string, containers []container.Summary) bool {
|
||||
|
||||
@@ -164,10 +164,12 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
|
||||
needRestart = repo.DownloadUrl == req.DownloadUrl
|
||||
}
|
||||
if repo.Protocol == "https" && req.Protocol == "http" {
|
||||
if err := u.handleRegistries(req.DownloadUrl, "", "create"); err != nil {
|
||||
return fmt.Errorf("create registry %s failed, err: %v", req.DownloadUrl, err)
|
||||
if req.DownloadUrl != repo.DownloadUrl {
|
||||
if err := u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update"); err != nil {
|
||||
return fmt.Errorf("update registry %s => %s failed, err: %v", repo.DownloadUrl, req.DownloadUrl, err)
|
||||
}
|
||||
needRestart = true
|
||||
}
|
||||
needRestart = true
|
||||
}
|
||||
if needRestart {
|
||||
if err := stopBeforeUpdateRepo(); err != nil {
|
||||
|
||||
@@ -86,6 +86,11 @@ func (m McpServerService) Page(req request.McpServerSearch) response.McpServersR
|
||||
}
|
||||
|
||||
func (m McpServerService) Update(req request.McpServerUpdate) error {
|
||||
go func() {
|
||||
if err := docker.PullImage("supercorp/supergateway:latest"); err != nil {
|
||||
global.LOG.Errorf("docker pull mcp image error: %s", err.Error())
|
||||
}
|
||||
}()
|
||||
mcpServer, err := mcpServerRepo.GetFirst(repo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -108,6 +113,8 @@ func (m McpServerService) Update(req request.McpServerUpdate) error {
|
||||
mcpServer.BaseURL = req.BaseURL
|
||||
mcpServer.SsePath = req.SsePath
|
||||
mcpServer.HostIP = req.HostIP
|
||||
mcpServer.StreamableHttpPath = req.StreamableHttpPath
|
||||
mcpServer.OutputTransport = req.OutputTransport
|
||||
if err := handleCreateParams(mcpServer, req.Environments, req.Volumes); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -130,6 +137,11 @@ func (m McpServerService) Update(req request.McpServerUpdate) error {
|
||||
}
|
||||
|
||||
func (m McpServerService) Create(create request.McpServerCreate) error {
|
||||
go func() {
|
||||
if err := docker.PullImage("supercorp/supergateway:latest"); err != nil {
|
||||
global.LOG.Errorf("docker pull mcp image error: %s", err.Error())
|
||||
}
|
||||
}()
|
||||
servers, _ := mcpServerRepo.List()
|
||||
for _, server := range servers {
|
||||
if server.Port == create.Port {
|
||||
@@ -154,15 +166,17 @@ func (m McpServerService) Create(create request.McpServerCreate) error {
|
||||
}
|
||||
mcpDir := path.Join(global.Dir.McpDir, create.Name)
|
||||
mcpServer := &model.McpServer{
|
||||
Name: create.Name,
|
||||
ContainerName: create.ContainerName,
|
||||
Port: create.Port,
|
||||
Command: create.Command,
|
||||
Status: constant.StatusStarting,
|
||||
BaseURL: create.BaseURL,
|
||||
SsePath: create.SsePath,
|
||||
Dir: mcpDir,
|
||||
HostIP: create.HostIP,
|
||||
Name: create.Name,
|
||||
ContainerName: create.ContainerName,
|
||||
Port: create.Port,
|
||||
Command: create.Command,
|
||||
Status: constant.StatusStarting,
|
||||
BaseURL: create.BaseURL,
|
||||
SsePath: create.SsePath,
|
||||
Dir: mcpDir,
|
||||
HostIP: create.HostIP,
|
||||
StreamableHttpPath: create.StreamableHttpPath,
|
||||
OutputTransport: create.OutputTransport,
|
||||
}
|
||||
if err := handleCreateParams(mcpServer, create.Environments, create.Volumes); err != nil {
|
||||
return err
|
||||
@@ -524,6 +538,8 @@ func handleEnv(mcpServer *model.McpServer) gotenv.Env {
|
||||
env["BASE_URL"] = mcpServer.BaseURL
|
||||
env["SSE_PATH"] = mcpServer.SsePath
|
||||
env["HOST_IP"] = mcpServer.HostIP
|
||||
env["STREAMABLE_HTTP_PATH"] = mcpServer.StreamableHttpPath
|
||||
env["OUTPUT_TRANSPORT"] = mcpServer.OutputTransport
|
||||
envStr, _ := gotenv.Marshal(env)
|
||||
mcpServer.Env = envStr
|
||||
return env
|
||||
|
||||
@@ -16,11 +16,11 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/robfig/cron/v3"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"github.com/shirou/gopsutil/v3/load"
|
||||
"github.com/shirou/gopsutil/v3/mem"
|
||||
"github.com/shirou/gopsutil/v3/net"
|
||||
"github.com/shirou/gopsutil/v4/cpu"
|
||||
"github.com/shirou/gopsutil/v4/disk"
|
||||
"github.com/shirou/gopsutil/v4/load"
|
||||
"github.com/shirou/gopsutil/v4/mem"
|
||||
"github.com/shirou/gopsutil/v4/net"
|
||||
)
|
||||
|
||||
type MonitorService struct {
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -42,6 +44,9 @@ type INginxService interface {
|
||||
Build(req request.NginxBuildReq) error
|
||||
GetModules() (*response.NginxBuildConfig, error)
|
||||
UpdateModule(req request.NginxModuleUpdate) error
|
||||
|
||||
OperateDefaultHTTPs(req request.NginxOperateReq) error
|
||||
GetDefaultHttpsStatus() (*response.NginxConfigRes, error)
|
||||
}
|
||||
|
||||
func NewINginxService() INginxService {
|
||||
@@ -348,3 +353,70 @@ func (n NginxService) UpdateModule(req request.NginxModuleUpdate) error {
|
||||
}
|
||||
return fileOp.SaveFileWithByte(moduleConfigPath, moduleByte, constant.DirPerm)
|
||||
}
|
||||
|
||||
func (n NginxService) OperateDefaultHTTPs(req request.NginxOperateReq) error {
|
||||
appInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
websites, _ := websiteRepo.List()
|
||||
hasDefaultWebsite := false
|
||||
for _, website := range websites {
|
||||
if website.DefaultServer {
|
||||
hasDefaultWebsite = true
|
||||
break
|
||||
}
|
||||
}
|
||||
defaultConfigPath := path.Join(appInstall.GetPath(), "conf", "default", "00.default.conf")
|
||||
content, err := os.ReadFile(defaultConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req.Operate == "enable" {
|
||||
if err := handleSSLConfig(&appInstall, hasDefaultWebsite); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if req.Operate == "disable" {
|
||||
defaultConfig, err := parser.NewStringParser(string(content)).Parse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defaultConfig.FilePath = defaultConfigPath
|
||||
defaultServer := defaultConfig.FindServers()[0]
|
||||
defaultServer.RemoveListen(fmt.Sprintf("%d", appInstall.HttpsPort))
|
||||
defaultServer.RemoveListen(fmt.Sprintf("[::]:%d", appInstall.HttpsPort))
|
||||
defaultServer.RemoveDirective("include", []string{"/usr/local/openresty/nginx/conf/ssl/root_ssl.conf"})
|
||||
defaultServer.RemoveDirective("http2", []string{"on"})
|
||||
if err = nginx.WriteConfig(defaultConfig, nginx.IndentedStyle); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nginxCheckAndReload(string(content), defaultConfigPath, appInstall.ContainerName)
|
||||
}
|
||||
|
||||
func (n NginxService) GetDefaultHttpsStatus() (*response.NginxConfigRes, error) {
|
||||
appInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defaultConfigPath := path.Join(appInstall.GetPath(), "conf", "default", "00.default.conf")
|
||||
content, err := os.ReadFile(defaultConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defaultConfig, err := parser.NewStringParser(string(content)).Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defaultConfig.FilePath = defaultConfigPath
|
||||
defaultServer := defaultConfig.FindServers()[0]
|
||||
res := &response.NginxConfigRes{}
|
||||
for _, directive := range defaultServer.GetDirectives() {
|
||||
if directive.GetName() == "include" && directive.GetParameters()[0] == "/usr/local/openresty/nginx/conf/ssl/root_ssl.conf" {
|
||||
return &response.NginxConfigRes{
|
||||
Https: true,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
"github.com/shirou/gopsutil/v3/process"
|
||||
"github.com/shirou/gopsutil/v4/process"
|
||||
)
|
||||
|
||||
type ProcessService struct{}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"github.com/shirou/gopsutil/v4/disk"
|
||||
)
|
||||
|
||||
type RecycleBinService struct {
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
fcgiclient "github.com/tomasen/fcgi_client"
|
||||
"maps"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -71,6 +73,8 @@ type IRuntimeService interface {
|
||||
GetSupervisorProcess(id uint) ([]response.SupervisorProcessConfig, error)
|
||||
OperateSupervisorProcess(req request.PHPSupervisorProcessConfig) error
|
||||
OperateSupervisorProcessFile(req request.PHPSupervisorProcessFileReq) (string, error)
|
||||
|
||||
GetFPMStatus(runtimeID uint) ([]response.FpmStatusItem, error)
|
||||
}
|
||||
|
||||
func NewRuntimeService() IRuntimeService {
|
||||
@@ -211,6 +215,9 @@ func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.Runt
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if len(runtimes) == 0 {
|
||||
return 0, res, nil
|
||||
}
|
||||
if err = SyncRuntimesStatus(runtimes); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
@@ -447,6 +454,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
||||
if err := checkContainerName(containerName.(string)); err != nil {
|
||||
return err
|
||||
}
|
||||
runtime.ContainerName = containerName.(string)
|
||||
}
|
||||
|
||||
projectDir := path.Join(global.Dir.RuntimeDir, runtime.Type, runtime.Name)
|
||||
@@ -987,6 +995,9 @@ func (r *RuntimeService) UpdateFPMConfig(req request.FPMConfig) error {
|
||||
if err := cfg.SaveTo(runtime.GetFPMPath()); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := compose.Restart(runtime.GetComposePath()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1082,9 +1093,7 @@ func (r *RuntimeService) UpdatePHPContainer(req request.PHPContainerConfig) erro
|
||||
}
|
||||
newMap := make(map[string]string)
|
||||
handleMap(create.Params, newMap)
|
||||
for k, v := range newMap {
|
||||
envs[k] = v
|
||||
}
|
||||
maps.Copy(envs, newMap)
|
||||
envs["PANEL_APP_PORT_HTTP"] = runtime.Port
|
||||
envStr, err := gotenv.Marshal(envs)
|
||||
if err != nil {
|
||||
@@ -1158,3 +1167,61 @@ func (r *RuntimeService) UpdateRemark(req request.RuntimeRemark) error {
|
||||
runtime.Remark = req.Remark
|
||||
return runtimeRepo.Save(runtime)
|
||||
}
|
||||
|
||||
func (r *RuntimeService) GetFPMStatus(runtimeID uint) ([]response.FpmStatusItem, error) {
|
||||
runtime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(runtimeID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fcgiClient, err := fcgiclient.DialTimeout("tcp", "127.0.0.1:"+runtime.Port, 10*time.Second)
|
||||
if err != nil {
|
||||
return nil, errors.New("<UNK> FastCGI <UNK>: " + err.Error())
|
||||
}
|
||||
defer fcgiClient.Close()
|
||||
|
||||
reqEnv := map[string]string{
|
||||
"REQUEST_METHOD": "GET",
|
||||
"REQUEST_URI": "/status",
|
||||
"SCRIPT_FILENAME": "/status",
|
||||
"SCRIPT_NAME": "/status",
|
||||
"QUERY_STRING": "",
|
||||
"CONTENT_TYPE": "",
|
||||
"CONTENT_LENGTH": "0",
|
||||
"SERVER_SOFTWARE": "go-fcgi-client",
|
||||
"SERVER_NAME": "localhost",
|
||||
"SERVER_PORT": runtime.Port,
|
||||
"REMOTE_ADDR": "127.0.0.1",
|
||||
"GATEWAY_INTERFACE": "CGI/1.1",
|
||||
}
|
||||
|
||||
resp, err := fcgiClient.Get(reqEnv)
|
||||
if err != nil {
|
||||
return nil, errors.New("<UNK> FastCGI <UNK>: " + err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var status []response.FpmStatusItem
|
||||
scanner := bufio.NewScanner(resp.Body)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
parts := strings.SplitN(line, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(parts[0])
|
||||
value := strings.TrimSpace(parts[1])
|
||||
status = append(status, response.FpmStatusItem{
|
||||
Key: key,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("<UNK> FastCGI <UNK>: %v", err))
|
||||
}
|
||||
return status, nil
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
@@ -522,9 +523,7 @@ func handleParams(create request.RuntimeCreate, projectDir string) (composeConte
|
||||
|
||||
newMap := make(map[string]string)
|
||||
handleMap(create.Params, newMap)
|
||||
for k, v := range newMap {
|
||||
env[k] = v
|
||||
}
|
||||
maps.Copy(env, newMap)
|
||||
|
||||
envStr, err := gotenv.Marshal(env)
|
||||
if err != nil {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user