Compare commits

...
82 Commits
Author SHA1 Message Date
ssongliu b3a55ea44d fix: Fix menu display exception issue (#9883) 2025-08-06 22:00:31 +08:00
ssongliu 270411f7fd fix: Fix menu display exception issue (#9882) 2025-08-06 16:44:54 +08:00
KOMATA 3c01198e61 fix: Improve config handling for proxy configuration in GetProxies (#9879) 2025-08-06 16:02:27 +08:00
ssongliu 483b47ad63 fix: Fix node ip switch exception issue (#9874) 2025-08-06 12:12:19 +08:00
ssongliu b10de66ed3 style: Update list styling for cronjob (#9872) 2025-08-06 10:30:37 +08:00
ssongliu 305eff048c fix: Fix the problem of initializing planned task groups (#9870) 2025-08-05 23:15:03 +08:00
ssongliu da7968d364 fix: Fix firewall routing jump exception issue (#9869) 2025-08-05 22:10:12 +08:00
ssongliu d3e2c8e9d3 fix: Add remote database connection timeout record (#9868) 2025-08-05 11:15:00 +00:00
ssongliu e3a9f2dfb7 fix: Fix PostgreSQL database recovery failures (#9867) 2025-08-05 18:21:18 +08:00
CityFun 3c967f8906 fix: Fix the issue where the list keeps loading when autostart = false is set in the Supervisor configuration. (#9866) 2025-08-05 10:20:59 +00:00
ssongliu 39bb7acdba fix: Add non-standard log filtering for SSH (#9865) 2025-08-05 17:54:41 +08:00
蘭 a30066989b fix: Reset data before copying files (#9864) 2025-08-05 08:44:59 +00:00
CityFun 0c30dfa092 style: Adjust the style of task logs (#9863) 2025-08-05 16:37:04 +08:00
ssongliu 7a44493d13 fix: Fix menu name display issue (#9862) 2025-08-05 08:35:00 +00:00
ssongliu 9b726d5d22 fix: Fix menu name display issue (#9861) 2025-08-05 16:24:06 +08:00
ssongliu 78e3d9e5fb fix: Enhance archive extraction command compatibility (#9860)
Refs #9856
2025-08-05 08:14:59 +00:00
CityFun d74b41a5f2 style: Adjust the column width of the PHP runtime status (#9858) 2025-08-05 07:56:59 +00:00
CityFun f122afdbec feat: Add master node IP validation when installing a cluster slave node (#9857)
#9840
2025-08-05 07:55:00 +00:00
ssongliu 37308b5c42 fix: Add custom timeout support for remote database (#9859) 2025-08-05 15:54:48 +08:00
ssongliu 9c5a6e3925 style: Adjust menu tab styling (#9855) 2025-08-05 06:52:59 +00:00
ssongliu 58b95ef2ba fix: Fix PostgreSQL database backup/restore failures (#9854) 2025-08-05 14:30:34 +08:00
CityFun f617c9d5dd fix: Fix the issue where viewing logs during certificate application results in an error. (#9853) 2025-08-05 14:30:18 +08:00
CityFun c661cc37b3 feat: Delete app while ignoring it during synchronization. (#9852) 2025-08-05 14:29:50 +08:00
ssongliu 7c49f6c79b fix: Fix redis configuration modification issues (#9848)
Refs #9832
2025-08-05 03:26:59 +00:00
蘭 12b9356c17 ref: Optimize code (#9845) 2025-08-05 02:46:59 +00:00
g0l4 d3a34293f7 fix: Move inline editorconfig comments (#9839) 2025-08-05 10:38:50 +08:00
ssongliu c131061f81 fix: Fix grouping exception issue for cronjob (#9841) 2025-08-04 22:20:06 +08:00
CityFun 6a5e1ebaa5 feat: Add OpenResty upgrade notification. (#9837) 2025-08-04 22:19:28 +08:00
ssongliu a07d016951 fix: Fix menu tab display issues (#9838) 2025-08-04 21:32:21 +08:00
CityFun 9d343a90db feat: add DenySpider Config for waf (#9835) 2025-08-04 10:06:31 +00:00
蘭 4c813be09d style: file code editor style (#9834)
refs #9781
2025-08-04 10:04:29 +00:00
CityFun d48166f546 fix: Add autostart configuration for supervisor (#9831) 2025-08-04 14:20:53 +08:00
ssongliu 0f138c1b7c fix: Optimize SSH key synchronization (#9833) 2025-08-04 06:18:29 +00:00
ssongliu 8542323c0b fix: Add custom timeout support for remote database (#9830) 2025-08-04 13:40:35 +08:00
CityFun 00f103c66a fix: Fixed issue with where runtime website fails to start after being stopped (#9827) 2025-08-04 13:40:21 +08:00
ssongliu 430c75e7ed fix: Fix redis cluster connection info modification issue (#9799)
Refs #9798
2025-08-01 10:47:23 +00:00
hang 67ffb5e938 feat: support autocomplete for login form fields to enhance password manager compatibility (#9794) 2025-08-01 16:28:20 +08:00
ssongliu 9c292bebf0 fix: Optimize container cleanup mechanism (#9795)
Refs #7444

涉及到容器、镜像、网络、存储卷、构建缓存清理
2025-08-01 08:07:24 +00:00
ssongliu ba4307c1dc fix: Optimize backup file upload display (#9797) 2025-08-01 08:03:23 +00:00
蘭 b8b2041a0c fix: Fix Docker proxy error issue (#9790)
refs #9780
2025-08-01 08:01:24 +00:00
蘭 c45bb60b50 fix: Fix file copy overwrite error (#9796) 2025-08-01 16:00:11 +08:00
ssongliu fba46be47a fix: Fix overview page refresh issue (#9785)
Refs #9756 #9769
2025-08-01 02:47:22 +00:00
ssongliu be0a460935 feat: Optimize SSH key management (#9782) 2025-08-01 02:43:20 +00:00
CityFun c0a041d024 feat: upgrade github.com/docker/docker version to v28.3.3 (#9779) 2025-07-31 19:13:41 +08:00
ssongliu 2ce5ad82ce fix: Fix SSH SDK timeout and disconnection issues (#9776) 2025-07-31 17:22:19 +08:00
CityFun 50dcb6c7e4 fix: Fix the issue where certificate deletion fails due to network issues (#9775) 2025-07-31 17:21:33 +08:00
CityFun 017424bf7b fix: Fixed issue where syncing remote apps causes local app tags to be lost (#9774) 2025-07-31 17:21:08 +08:00
CityFun c7e7407f7c fix: Fix the issue where updating the image in the Compose file during upgrade has no effect (#9772) 2025-07-31 17:20:55 +08:00
CityFun cab42b3c7d feat: Mcp Server support streamableHttp (#9761)
Refs https://github.com/1Panel-dev/1Panel/issues/8482
2025-07-30 10:54:51 +00:00
CityFun 78f280c88f feat: Support Enter key to confirm in the delete application dialog (#9758)
Refs https://github.com/1Panel-dev/1Panel/issues/9754
2025-07-30 10:52:52 +00:00
CityFun 22f5a975c3 perf: Optimize website log reading (#9755)
Refs https://github.com/1Panel-dev/1Panel/issues/9383
2025-07-30 10:50:52 +00:00
CityFun 90ba036e39 feat: Restrict deletion of certificates that are being applied for. (#9753)
Refs https://github.com/1Panel-dev/1Panel/issues/9737#issuecomment-3134445730
2025-07-30 10:48:52 +00:00
CityFun d1e18e43ee prompt: Optimize the timeout prompt when pulling application during installation (#9757) 2025-07-30 18:48:43 +08:00
ssongliu a801140a82 feat: support group management for cronjob (#9735)
Refs #9258
2025-07-30 04:08:50 +00:00
CityFun 951ab2502e fix: Resolve the issue of ignoring MySQL failures (#9734)
Refs https://github.com/1Panel-dev/1Panel/issues/9718
2025-07-30 04:06:51 +00:00
CityFun 0316cb0662 feat: Add HTTPS fallback prevention configuration in OpenResty (#9703) 2025-07-30 04:04:51 +00:00
CityFun 04b9cbd87a fix: Fixed issue with recover postgres-cluster database failed (#9732) 2025-07-29 16:19:39 +08:00
ssongliu 4565c5b300 fix: Fix fatabase backup cronjobimport failures (#9731) 2025-07-29 16:03:55 +08:00
ssongliu bf9a9bf17a fix: Fix execution refresh issues for cronjob (#9728) 2025-07-29 14:51:34 +08:00
ssongliu 70df4dc6c4 fix: Fix import failures for cronjob (#9727) 2025-07-29 14:32:25 +08:00
ssongliu ed3e738fff fix: Fix import issues of shell cronjob (#9726) 2025-07-29 04:09:49 +00:00
ssongliu bc376ec495 fix: script-type scheduled task import/export issues (#9713) 2025-07-28 19:04:35 +08:00
Snrat 40314e9079 Fix Issue with Podman Unable to Change PHP Runtime in Website Page (#9711)
* Update container.go

* Update website.go
2025-07-28 18:34:05 +08:00
ssongliu 82f978c683 fix: Add file upload restrictions for cronjob (#9710) 2025-07-28 18:33:15 +08:00
CityFun ed288368ea fix: Fixed issue with recover mysql-cluster with backup files (#9707) 2025-07-28 18:01:05 +08:00
sheng.wang db8fa144eb fix: Fix crash when adding custom spec if specs is null (#9708) 2025-07-28 18:00:35 +08:00
ssongliu 57b76d97ec fix: Optimize task log tracking mechanism (#9704) 2025-07-28 17:12:42 +08:00
蘭 28d8210ce6 fix: add nil checks for transport.DialContext (#9702) 2025-07-28 17:02:46 +08:00
ssongliu 80ce60c56c fix: Fix port forwarding error issues (#9700)
Refs #9658
2025-07-28 08:29:40 +00:00
Snrat 2758ab07dc Fix Issue with Podman Unable to Create PHP Runtime Website (#9697)
* Update website.go

* Update container.go
2025-07-28 15:38:27 +08:00
CityFun 0828804de2 fix: Fix the issue where the host IP is shown as 127.0.0.1 after enabling 'allowPort' in the application parameter settings. (#9699) 2025-07-28 15:36:25 +08:00
ssongliu 134b17d2f0 fix: Resolve certificate validate failure Issues (#9698) 2025-07-28 15:36:10 +08:00
ssongliu 3af254cdda fix: Add lightweight object storage compatibility for COS backup acco… (#9673)
Refs #9494
2025-07-25 11:07:36 +00:00
蘭 f3d3d54722 feat: Customize login page buttons and link colors (#9671)
refs  #9061
2025-07-25 11:05:37 +00:00
CityFun fa419809f6 fix: Fixed issue with create website with openresty defaultHttpsPort (#9672) 2025-07-25 19:04:19 +08:00
CityFun da606826c6 fix: Fixed Changing the runtime container name may cause it to fail to start (#9670)
Refs https://github.com/1Panel-dev/1Panel/issues/9666
2025-07-25 11:03:35 +00:00
ssongliu b3d9cc156a fix: Optimize filename generation for directory backup (#9669)
Refs #9433
2025-07-25 09:21:35 +00:00
ssongliu ba03d26b2c fix: Optimize cronjob report loading (#9665)
Refs #9412
2025-07-25 08:35:36 +00:00
ssongliu 40f02889fe feat: Add import/export support for cronjob (#9663)
Refs #5157
2025-07-25 07:15:35 +00:00
CityFun 4f8fb26d89 feat: Adapt default website domain to non-standard OpenResty port during creation (#9664) 2025-07-25 15:15:33 +08:00
ssongliu 0ca11e2b88 fix: Fix node switching style issues (#9641) 2025-07-24 11:30:08 +08:00
ssongliu 0daf0d690c fix: Resolve deleted node status display issue (#9633) 2025-07-24 10:23:45 +08:00
227 changed files with 4167 additions and 1220 deletions
+2 -2
View File
@@ -466,12 +466,12 @@ func (b *BaseApi) RecoverByUpload(c *gin.Context) {
} }
switch req.Type { switch req.Type {
case "mysql", "mariadb": case "mysql", "mariadb", constant.AppMysqlCluster:
if err := backupService.MysqlRecoverByUpload(req); err != nil { if err := backupService.MysqlRecoverByUpload(req); err != nil {
helper.InternalServer(c, err) helper.InternalServer(c, err)
return return
} }
case constant.AppPostgresql: case constant.AppPostgresql, constant.AppPostgresqlCluster:
if err := backupService.PostgresqlRecoverByUpload(req); err != nil { if err := backupService.PostgresqlRecoverByUpload(req); err != nil {
helper.InternalServer(c, err) helper.InternalServer(c, err)
return return
+3 -4
View File
@@ -315,7 +315,7 @@ func (b *BaseApi) ContainerUpgrade(c *gin.Context) {
// @Summary Clean container // @Summary Clean container
// @Accept json // @Accept json
// @Param request body dto.ContainerPrune true "request" // @Param request body dto.ContainerPrune true "request"
// @Success 200 {object} dto.ContainerPruneReport // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Security Timestamp // @Security Timestamp
// @Router /containers/prune [post] // @Router /containers/prune [post]
@@ -326,12 +326,11 @@ func (b *BaseApi) ContainerPrune(c *gin.Context) {
return return
} }
report, err := containerService.Prune(req) if err := containerService.Prune(req); err != nil {
if err != nil {
helper.InternalServer(c, err) helper.InternalServer(c, err)
return return
} }
helper.SuccessWithData(c, report) helper.Success(c)
} }
// @Tags Container // @Tags Container
+67
View File
@@ -1,6 +1,8 @@
package v2 package v2
import ( import (
"net/http"
"strings"
"time" "time"
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper" "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) 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 // @Tags Cronjob
// @Summary Load script options // @Summary Load script options
// @Success 200 {array} dto.ScriptOptions // @Success 200 {array} dto.ScriptOptions
@@ -225,6 +270,28 @@ func (b *BaseApi) UpdateCronjob(c *gin.Context) {
helper.Success(c) 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 // @Tags Cronjob
// @Summary Update cronjob status // @Summary Update cronjob status
// @Accept json // @Accept json
+3 -4
View File
@@ -138,7 +138,7 @@ func (b *BaseApi) ImagePush(c *gin.Context) {
// @Summary Delete image // @Summary Delete image
// @Accept json // @Accept json
// @Param request body dto.BatchDelete true "request" // @Param request body dto.BatchDelete true "request"
// @Success 200 {object} dto.ContainerPruneReport // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Security Timestamp // @Security Timestamp
// @Router /containers/image/remove [post] // @Router /containers/image/remove [post]
@@ -149,13 +149,12 @@ func (b *BaseApi) ImageRemove(c *gin.Context) {
return return
} }
data, err := imageService.ImageRemove(req) if err := imageService.ImageRemove(req); err != nil {
if err != nil {
helper.InternalServer(c, err) helper.InternalServer(c, err)
return return
} }
helper.SuccessWithData(c, data) helper.Success(c)
} }
// @Tags Container Image // @Tags Container Image
+36
View File
@@ -157,3 +157,39 @@ func (b *BaseApi) GetNginxModules(c *gin.Context) {
} }
helper.SuccessWithData(c, modules) 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)
}
+1 -1
View File
@@ -113,7 +113,7 @@ func loadLocalConn() (*ssh.SSHClient, error) {
itemPath = path.Join(currentInfo.HomeDir, ".ssh/id_ed25519_1panel") itemPath = path.Join(currentInfo.HomeDir, ".ssh/id_ed25519_1panel")
} }
if _, err := os.Stat(itemPath); err != nil { if _, err := os.Stat(itemPath); err != nil {
_ = sshService.GenerateSSH(dto.GenerateSSH{EncryptionMode: "ed25519", Name: "_1panel"}) _ = sshService.CreateRootCert(dto.CreateRootCert{EncryptionMode: "ed25519", Name: "1panel", Description: "1Panel Terminal"})
} }
privateKey, _ := os.ReadFile(itemPath) privateKey, _ := os.ReadFile(itemPath)
+78 -12
View File
@@ -1,6 +1,8 @@
package v2 package v2
import ( import (
"encoding/base64"
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper" "github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto" "github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -90,19 +92,58 @@ func (b *BaseApi) UpdateSSHByfile(c *gin.Context) {
// @Tags SSH // @Tags SSH
// @Summary Generate host SSH secret // @Summary Generate host SSH secret
// @Accept json // @Accept json
// @Param request body dto.GenerateSSH true "request" // @Param request body dto.CreateRootCert true "request"
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Security Timestamp // @Security Timestamp
// @Router /hosts/ssh/generate [post] // @Router /hosts/ssh/cert [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"生成 SSH 密钥 ","formatEN":"generate SSH secret"} // @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"生成 SSH 密钥 ","formatEN":"generate SSH secret"}
func (b *BaseApi) GenerateSSH(c *gin.Context) { func (b *BaseApi) CreateRootCert(c *gin.Context) {
var req dto.GenerateSSH var req dto.CreateRootCert
if err := helper.CheckBindAndValidate(&req, c); err != nil { if err := helper.CheckBindAndValidate(&req, c); err != nil {
return 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) helper.InternalServer(c, err)
return return
} }
@@ -112,23 +153,48 @@ func (b *BaseApi) GenerateSSH(c *gin.Context) {
// @Tags SSH // @Tags SSH
// @Summary Load host SSH secret // @Summary Load host SSH secret
// @Accept json // @Accept json
// @Param request body dto.GenerateLoad true "request" // @Param request body dto.SearchWithPage true "request"
// @Success 200 {string} secret // @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Security Timestamp // @Security Timestamp
// @Router /hosts/ssh/secret [post] // @Router /hosts/ssh/cert/search [post]
func (b *BaseApi) LoadSSHSecret(c *gin.Context) { func (b *BaseApi) SearchRootCert(c *gin.Context) {
var req dto.GenerateLoad var req dto.SearchWithPage
if err := helper.CheckBindAndValidate(&req, c); err != nil { if err := helper.CheckBindAndValidate(&req, c); err != nil {
return return
} }
data, err := sshService.LoadSSHSecret(req.EncryptionMode) total, data, err := sshService.SearchRootCerts(req)
if err != nil { if err != nil {
helper.InternalServer(c, err) helper.InternalServer(c, err)
return 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 // @Tags SSH
+8
View File
@@ -28,6 +28,9 @@ type OperationWithName struct {
type OperateByID struct { type OperateByID struct {
ID uint `json:"id" validate:"required"` ID uint `json:"id" validate:"required"`
} }
type OperateByIDs struct {
IDs []uint `json:"ids"`
}
type Operate struct { type Operate struct {
Operation string `json:"operation" validate:"required"` Operation string `json:"operation" validate:"required"`
@@ -76,3 +79,8 @@ type ForceDelete struct {
IDs []uint `json:"ids"` IDs []uint `json:"ids"`
ForceDelete bool `json:"forceDelete"` ForceDelete bool `json:"forceDelete"`
} }
type ChangeGroup struct {
ID uint `json:"id" validate:"required"`
GroupID uint `json:"groupID" validate:"required"`
}
+4 -7
View File
@@ -174,15 +174,11 @@ type ContainerCommit struct {
} }
type ContainerPrune struct { type ContainerPrune struct {
TaskID string `json:"taskID"`
PruneType string `json:"pruneType" validate:"required,oneof=container image volume network buildcache"` PruneType string `json:"pruneType" validate:"required,oneof=container image volume network buildcache"`
WithTagAll bool `json:"withTagAll"` WithTagAll bool `json:"withTagAll"`
} }
type ContainerPruneReport struct {
DeletedNumber int `json:"deletedNumber"`
SpaceReclaimed int `json:"spaceReclaimed"`
}
type Network struct { type Network struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
@@ -227,8 +223,9 @@ type VolumeCreate struct {
} }
type BatchDelete struct { type BatchDelete struct {
Force bool `json:"force"` TaskID string `json:"taskID"`
Names []string `json:"names" validate:"required"` Force bool `json:"force"`
Names []string `json:"names" validate:"required"`
} }
type ComposeInfo struct { type ComposeInfo struct {
+58 -3
View File
@@ -6,9 +6,10 @@ import (
type PageCronjob struct { type PageCronjob struct {
PageInfo PageInfo
Info string `json:"info"` Info string `json:"info"`
OrderBy string `json:"orderBy" validate:"required,oneof=name status createdAt"` GroupIDs []uint `json:"groupIDs"`
Order string `json:"order" validate:"required,oneof=null ascending descending"` OrderBy string `json:"orderBy" validate:"required,oneof=name status createdAt"`
Order string `json:"order" validate:"required,oneof=null ascending descending"`
} }
type CronjobSpec struct { type CronjobSpec struct {
@@ -19,6 +20,7 @@ type CronjobOperate struct {
ID uint `json:"id"` ID uint `json:"id"`
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"` Type string `json:"type" validate:"required"`
GroupID uint `json:"groupID"`
SpecCustom bool `json:"specCustom"` SpecCustom bool `json:"specCustom"`
Spec string `json:"spec" validate:"required"` Spec string `json:"spec" validate:"required"`
@@ -85,6 +87,7 @@ type CronjobInfo struct {
ID uint `json:"id"` ID uint `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Type string `json:"type"` Type string `json:"type"`
GroupID uint `json:"groupID"`
SpecCustom bool `json:"specCustom"` SpecCustom bool `json:"specCustom"`
Spec string `json:"spec"` Spec string `json:"spec"`
@@ -123,6 +126,58 @@ type CronjobInfo struct {
AlertCount uint `json:"alertCount"` 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 { type ScriptOptions struct {
ID uint `json:"id"` ID uint `json:"id"`
Name string `json:"name"` Name string `json:"name"`
+5 -2
View File
@@ -4,14 +4,14 @@ import "time"
// common // common
type DBConfUpdateByFile struct { type DBConfUpdateByFile struct {
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql redis mysql-cluster postgresql-cluster"` Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql redis mysql-cluster postgresql-cluster redis-cluster"`
Database string `json:"database" validate:"required"` Database string `json:"database" validate:"required"`
File string `json:"file"` File string `json:"file"`
} }
type ChangeDBInfo struct { type ChangeDBInfo struct {
ID uint `json:"id"` ID uint `json:"id"`
From string `json:"from" validate:"required,oneof=local remote"` From string `json:"from" validate:"required,oneof=local remote"`
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql mysql-cluster postgresql-cluster"` Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql redis mysql-cluster postgresql-cluster redis-cluster"`
Database string `json:"database" validate:"required"` Database string `json:"database" validate:"required"`
Value string `json:"value" validate:"required"` Value string `json:"value" validate:"required"`
} }
@@ -260,6 +260,7 @@ type DatabaseInfo struct {
ClientCert string `json:"clientCert"` ClientCert string `json:"clientCert"`
SkipVerify bool `json:"skipVerify"` SkipVerify bool `json:"skipVerify"`
Timeout uint `json:"timeout"`
Description string `json:"description"` Description string `json:"description"`
} }
@@ -295,6 +296,7 @@ type DatabaseCreate struct {
ClientCert string `json:"clientCert"` ClientCert string `json:"clientCert"`
SkipVerify bool `json:"skipVerify"` SkipVerify bool `json:"skipVerify"`
Timeout uint `json:"timeout"`
Description string `json:"description"` Description string `json:"description"`
} }
@@ -313,6 +315,7 @@ type DatabaseUpdate struct {
ClientCert string `json:"clientCert"` ClientCert string `json:"clientCert"`
SkipVerify bool `json:"skipVerify"` SkipVerify bool `json:"skipVerify"`
Timeout uint `json:"timeout"`
Description string `json:"description"` Description string `json:"description"`
} }
+3 -9
View File
@@ -60,26 +60,20 @@ type PostgresqlPrivileges struct {
type PostgresqlLoadDB struct { type PostgresqlLoadDB struct {
From string `json:"from" validate:"required,oneof=local remote"` 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"` Database string `json:"database" validate:"required"`
} }
type PostgresqlDBDeleteCheck struct { type PostgresqlDBDeleteCheck struct {
ID uint `json:"id" validate:"required"` 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"` Database string `json:"database" validate:"required"`
} }
type PostgresqlDBDelete struct { type PostgresqlDBDelete struct {
ID uint `json:"id" validate:"required"` 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"` Database string `json:"database" validate:"required"`
ForceDelete bool `json:"forceDelete"` ForceDelete bool `json:"forceDelete"`
DeleteBackup bool `json:"deleteBackup"` 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"`
}
+1
View File
@@ -33,6 +33,7 @@ type SupervisorProcessConfig struct {
Dir string `json:"dir"` Dir string `json:"dir"`
Numprocs string `json:"numprocs"` Numprocs string `json:"numprocs"`
AutoRestart string `json:"autoRestart"` AutoRestart string `json:"autoRestart"`
AutoStart string `json:"autoStart"`
} }
type SupervisorProcessFileReq struct { type SupervisorProcessFileReq struct {
+11 -9
View File
@@ -9,15 +9,17 @@ type McpServerSearch struct {
} }
type McpServerCreate struct { type McpServerCreate struct {
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
Command string `json:"command" validate:"required"` Command string `json:"command" validate:"required"`
Environments []Environment `json:"environments"` Environments []Environment `json:"environments"`
Volumes []Volume `json:"volumes"` Volumes []Volume `json:"volumes"`
Port int `json:"port" validate:"required"` Port int `json:"port" validate:"required"`
ContainerName string `json:"containerName"` ContainerName string `json:"containerName"`
BaseURL string `json:"baseUrl"` BaseURL string `json:"baseUrl"`
SsePath string `json:"ssePath"` SsePath string `json:"ssePath"`
HostIP string `json:"hostIP"` HostIP string `json:"hostIP"`
StreamableHttpPath string `json:"streamableHttpPath"`
OutputTransport string `json:"outputTransport" validate:"required"`
} }
type McpServerUpdate struct { type McpServerUpdate struct {
+4
View File
@@ -126,3 +126,7 @@ type NginxModuleUpdate struct {
Enable bool `json:"enable"` Enable bool `json:"enable"`
Params string `json:"params"` Params string `json:"params"`
} }
type NginxOperateReq struct {
Operate string `json:"operate" validate:"required,oneof=enable disable"`
}
+1 -1
View File
@@ -6,5 +6,5 @@ type AppIgnoreUpgradeDTO struct {
AppDetailID uint `json:"appDetailID"` AppDetailID uint `json:"appDetailID"`
Scope string `json:"scope"` Scope string `json:"scope"`
Version string `json:"version"` Version string `json:"version"`
Icon string `json:"icon"` Name string `json:"name"`
} }
+8 -6
View File
@@ -1,8 +1,9 @@
package response package response
import ( import (
"github.com/1Panel-dev/1Panel/agent/utils/files"
"time" "time"
"github.com/1Panel-dev/1Panel/agent/utils/files"
) )
type FileInfo struct { type FileInfo struct {
@@ -37,11 +38,12 @@ type FileWgetRes struct {
} }
type FileLineContent struct { type FileLineContent struct {
Content string `json:"content"` End bool `json:"end"`
End bool `json:"end"` Path string `json:"path"`
Path string `json:"path"` Total int `json:"total"`
Total int `json:"total"` TaskStatus string `json:"taskStatus"`
Lines []string `json:"lines"` Lines []string `json:"lines"`
Scope string `json:"scope"`
} }
type FileExist struct { type FileExist struct {
+1
View File
@@ -31,6 +31,7 @@ type SupervisorProcessConfig struct {
Msg string `json:"msg"` Msg string `json:"msg"`
Status []ProcessStatus `json:"status"` Status []ProcessStatus `json:"status"`
AutoRestart string `json:"autoRestart"` AutoRestart string `json:"autoRestart"`
AutoStart string `json:"autoStart"`
} }
type ProcessStatus struct { type ProcessStatus struct {
+4
View File
@@ -80,3 +80,7 @@ type NginxBuildConfig struct {
Mirror string `json:"mirror"` Mirror string `json:"mirror"`
Modules []NginxModule `json:"modules"` Modules []NginxModule `json:"modules"`
} }
type NginxConfigRes struct {
Https bool `json:"https"`
}
+18 -3
View File
@@ -19,12 +19,27 @@ type SSHInfo struct {
PubkeyAuthentication string `json:"pubkeyAuthentication"` PubkeyAuthentication string `json:"pubkeyAuthentication"`
PermitRootLogin string `json:"permitRootLogin"` PermitRootLogin string `json:"permitRootLogin"`
UseDNS string `json:"useDNS"` UseDNS string `json:"useDNS"`
CurrentUser string `json:"currentUser"`
} }
type GenerateSSH struct { type CreateRootCert struct {
EncryptionMode string `json:"encryptionMode" validate:"required,oneof=rsa ed25519 ecdsa dsa"`
Password string `json:"password"`
Name string `json:"name"` 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 { type GenerateLoad struct {
+2 -2
View File
@@ -18,7 +18,7 @@ type AlertTask struct {
Type string `gorm:"type:varchar(64);not null" json:"type"` Type string `gorm:"type:varchar(64);not null" json:"type"`
Quota string `gorm:"type:varchar(64)" json:"quota"` Quota string `gorm:"type:varchar(64)" json:"quota"`
QuotaType string `gorm:"type:varchar(64)" json:"quotaType"` QuotaType string `gorm:"type:varchar(64)" json:"quotaType"`
Method string `gorm:"type:varchar(64);not null;default:'sms" json:"method"` Method string `gorm:"type:varchar(64);not null;default:'sms'" json:"method"`
} }
type AlertLog struct { type AlertLog struct {
@@ -33,7 +33,7 @@ type AlertLog struct {
Message string `gorm:"type:varchar(256);" json:"message"` Message string `gorm:"type:varchar(256);" json:"message"`
RecordId uint `gorm:"type:integer;" json:"recordId"` RecordId uint `gorm:"type:integer;" json:"recordId"`
LicenseId string `gorm:"type:varchar(256);not null;" json:"licenseId" ` LicenseId string `gorm:"type:varchar(256);not null;" json:"licenseId" `
Method string `gorm:"type:varchar(64);not null;default:'sms" json:"method"` Method string `gorm:"type:varchar(64);not null;default:'sms'" json:"method"`
} }
type AlertConfig struct { type AlertConfig struct {
+1
View File
@@ -11,6 +11,7 @@ type Cronjob struct {
Name string `gorm:"not null" json:"name"` Name string `gorm:"not null" json:"name"`
Type string `gorm:"not null" json:"type"` Type string `gorm:"not null" json:"type"`
GroupID uint `json:"groupID"`
SpecCustom bool `json:"specCustom"` SpecCustom bool `json:"specCustom"`
Spec string `gorm:"not null" json:"spec"` Spec string `gorm:"not null" json:"spec"`
+1
View File
@@ -18,5 +18,6 @@ type Database struct {
ClientCert string `json:"clientCert"` ClientCert string `json:"clientCert"`
SkipVerify bool `json:"skipVerify"` SkipVerify bool `json:"skipVerify"`
Timeout uint `json:"timeout"`
Description string `json:"description"` Description string `json:"description"`
} }
+15 -13
View File
@@ -2,17 +2,19 @@ package model
type McpServer struct { type McpServer struct {
BaseModel BaseModel
Name string `json:"name"` Name string `json:"name"`
DockerCompose string `json:"dockerCompose"` DockerCompose string `json:"dockerCompose"`
Command string `json:"command"` Command string `json:"command"`
ContainerName string `json:"containerName"` ContainerName string `json:"containerName"`
Message string `json:"message"` Message string `json:"message"`
Port int `json:"port"` Port int `json:"port"`
Status string `json:"status"` Status string `json:"status"`
Env string `json:"env"` Env string `json:"env"`
BaseURL string `json:"baseUrl"` BaseURL string `json:"baseUrl"`
SsePath string `json:"ssePath"` SsePath string `json:"ssePath"`
WebsiteID int `json:"websiteID"` WebsiteID int `json:"websiteID"`
Dir string `json:"dir"` Dir string `json:"dir"`
HostIP string `json:"hostIP"` HostIP string `json:"hostIP"`
StreamableHttpPath string `json:"streamableHttpPath"`
OutputTransport string `json:"outputTransport"`
} }
+11
View File
@@ -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"`
}
+7
View File
@@ -11,6 +11,7 @@ type AppIgnoreUpgradeRepo struct {
type IAppIgnoreUpgradeRepo interface { type IAppIgnoreUpgradeRepo interface {
WithScope(scope string) DBOption WithScope(scope string) DBOption
WithAppID(appID uint) DBOption
List(opts ...DBOption) ([]model.AppIgnoreUpgrade, error) List(opts ...DBOption) ([]model.AppIgnoreUpgrade, error)
Create(appIgnoreUpgrade *model.AppIgnoreUpgrade) error Create(appIgnoreUpgrade *model.AppIgnoreUpgrade) error
Delete(opts ...DBOption) 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) { func (a AppIgnoreUpgradeRepo) List(opts ...DBOption) ([]model.AppIgnoreUpgrade, error) {
var appIgnoreUpgradeList []model.AppIgnoreUpgrade var appIgnoreUpgradeList []model.AppIgnoreUpgrade
err := getDb(opts...).Find(&appIgnoreUpgradeList).Error err := getDb(opts...).Find(&appIgnoreUpgradeList).Error
+21
View File
@@ -36,6 +36,7 @@ type IAppInstallRepo interface {
Page(page, size int, opts ...DBOption) (int64, []model.AppInstall, error) Page(page, size int, opts ...DBOption) (int64, []model.AppInstall, error)
BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error
LoadBaseInfo(key string, name string) (*RootInfo, 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) GetFirstByCtx(ctx context.Context, opts ...DBOption) (model.AppInstall, error)
} }
@@ -246,3 +247,23 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
info.Status = appInstall.Status info.Status = appInstall.Status
return &info, nil 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
View File
@@ -17,8 +17,10 @@ type IAppTagRepo interface {
GetByAppId(appId uint) ([]model.AppTag, error) GetByAppId(appId uint) ([]model.AppTag, error)
GetByTagIds(tagIds []uint) ([]model.AppTag, error) GetByTagIds(tagIds []uint) ([]model.AppTag, error)
DeleteBy(ctx context.Context, opts ...DBOption) error DeleteBy(ctx context.Context, opts ...DBOption) error
GetFirst(opts ...DBOption) (*model.AppTag, error)
WithByTagID(tagID uint) DBOption WithByTagID(tagID uint) DBOption
WithByAppID(appId uint) DBOption
} }
func NewIAppTagRepo() IAppTagRepo { 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 { func (a AppTagRepo) BatchCreate(ctx context.Context, tags []*model.AppTag) error {
return getTx(ctx).Create(&tags).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 { func (a AppTagRepo) DeleteBy(ctx context.Context, opts ...DBOption) error {
return getTx(ctx, opts...).Delete(&model.AppTag{}).Error return getTx(ctx, opts...).Delete(&model.AppTag{}).Error
} }
func (a AppTagRepo) GetFirst(opts ...DBOption) (*model.AppTag, error) {
var appTag model.AppTag
if err := getDb(opts...).First(&appTag).Error; err != nil {
return nil, err
}
return &appTag, nil
}
+9
View File
@@ -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 { func WithByCreatedAt(startTime, endTime time.Time) DBOption {
return func(g *gorm.DB) *gorm.DB { return func(g *gorm.DB) *gorm.DB {
return g.Where("created_at > ? AND created_at < ?", startTime, endTime) return g.Where("created_at > ? AND created_at < ?", startTime, endTime)
+88
View File
@@ -3,6 +3,7 @@ package repo
import ( import (
"github.com/1Panel-dev/1Panel/agent/app/model" "github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
) )
type HostRepo struct{} type HostRepo struct{}
@@ -13,6 +14,14 @@ type IHostRepo interface {
SaveFirewallRecord(firewall *model.Firewall) error SaveFirewallRecord(firewall *model.Firewall) error
DeleteFirewallRecordByID(id uint) error DeleteFirewallRecordByID(id uint) error
DeleteFirewallRecord(fType, port, protocol, address, strategy string) 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 { 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 { 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 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
}
+9
View File
@@ -19,6 +19,7 @@ type ITagRepo interface {
DeleteByID(ctx context.Context, id uint) error DeleteByID(ctx context.Context, id uint) error
Create(ctx context.Context, tag *model.Tag) error Create(ctx context.Context, tag *model.Tag) error
Save(ctx context.Context, tag *model.Tag) error Save(ctx context.Context, tag *model.Tag) error
GetByKey(key string) (*model.Tag, error)
} }
func NewITagRepo() ITagRepo { func NewITagRepo() ITagRepo {
@@ -41,6 +42,14 @@ func (t TagRepo) All() ([]model.Tag, error) {
return tags, nil 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) { func (t TagRepo) GetByIds(ids []uint) ([]model.Tag, error) {
var tags []model.Tag var tags []model.Tag
if err := getDb().Where("id in (?)", ids).Find(&tags).Error; err != nil { if err := getDb().Where("id in (?)", ids).Find(&tags).Error; err != nil {
+13 -24
View File
@@ -928,18 +928,11 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
return err return err
} }
var ( var (
tags []*model.Tag
appTags []*model.AppTag appTags []*model.AppTag
oldAppIds []uint oldAppIds []uint
) )
for _, tag := range list.Extra.Tags { if err = SyncTags(list.Extra); err != nil {
translations, _ := json.Marshal(tag.Locales) return err
tags = append(tags, &model.Tag{
Name: tag.Name,
Translations: string(translations),
Sort: tag.Sort,
Key: tag.Key,
})
} }
deleteCustomApp() deleteCustomApp()
oldApps, err := appRepo.GetBy(appRepo.WithNotLocal()) oldApps, err := appRepo.GetBy(appRepo.WithNotLocal())
@@ -1015,6 +1008,7 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
} }
t.LogSuccess(i18n.GetMsgByKey("SyncAppDetail")) t.LogSuccess(i18n.GetMsgByKey("SyncAppDetail"))
tags, _ := tagRepo.All()
var ( var (
addAppArray []model.App addAppArray []model.App
updateAppArray []model.App updateAppArray []model.App
@@ -1061,16 +1055,8 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
return return
} }
} }
if err = tagRepo.DeleteAll(ctx); err != nil { for _, tag := range tags {
return tagMap[tag.Key] = tag.ID
}
if len(tags) > 0 {
if err = tagRepo.BatchCreate(ctx, tags); err != nil {
return
}
for _, tag := range tags {
tagMap[tag.Key] = tag.ID
}
} }
for _, update := range updateAppArray { for _, update := range updateAppArray {
if err = appRepo.Save(ctx, &update); err != nil { if err = appRepo.Save(ctx, &update); err != nil {
@@ -1088,10 +1074,13 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
for _, tag := range app.TagsKey { for _, tag := range app.TagsKey {
tagId, ok := tagMap[tag] tagId, ok := tagMap[tag]
if ok { if ok {
appTags = append(appTags, &model.AppTag{ exist, _ := appTagRepo.GetFirst(appTagRepo.WithByTagID(tagId), appTagRepo.WithByAppID(app.ID))
AppId: app.ID, if exist != nil {
TagId: tagId, appTags = append(appTags, &model.AppTag{
}) AppId: app.ID,
TagId: tagId,
})
}
} }
} }
for _, d := range app.Details { for _, d := range app.Details {
@@ -1134,7 +1123,7 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
} }
if len(oldAppIds) > 0 { if len(oldAppIds) > 0 {
if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil { if err = appTagRepo.DeleteByAppIds(ctx, deleteIds); err != nil {
return return
} }
} }
+1 -1
View File
@@ -40,7 +40,7 @@ func (a AppIgnoreUpgradeService) List() ([]response.AppIgnoreUpgradeDTO, error)
_ = appIgnoreUpgradeRepo.Delete(repo.WithByID(ignore.ID)) _ = appIgnoreUpgradeRepo.Delete(repo.WithByID(ignore.ID))
continue continue
} }
dto.Icon = app.Icon dto.Name = app.Name
if ignore.Scope == "version" { if ignore.Scope == "version" {
appDetail, err := appDetailRepo.GetFirst(repo.WithByID(ignore.AppDetailID)) appDetail, err := appDetailRepo.GetFirst(repo.WithByID(ignore.AppDetailID))
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
+1 -1
View File
@@ -672,7 +672,7 @@ func (a *AppInstallService) GetDefaultConfigByKey(key, name string) (string, err
if key == constant.AppMysql || key == constant.AppMariaDB || key == constant.AppMysqlCluster { if key == constant.AppMysql || key == constant.AppMariaDB || key == constant.AppMysqlCluster {
filePath = path.Join(filePath, "my.cnf") filePath = path.Join(filePath, "my.cnf")
} }
if key == constant.AppRedis { if key == constant.AppRedis || key == constant.AppRedisCluster {
filePath = path.Join(filePath, "redis.conf") filePath = path.Join(filePath, "redis.conf")
} }
if key == constant.AppOpenresty { if key == constant.AppOpenresty {
+73 -6
View File
@@ -440,6 +440,11 @@ func deleteAppInstall(deleteReq request.AppInstallDelete) error {
_ = op.DeleteDir(parentDir) _ = op.DeleteDir(parentDir)
} }
tx.Commit() tx.Commit()
existApps, _ := appInstallRepo.ListBy(context.Background(), appInstallRepo.WithAppId(install.AppId))
if len(existApps) == 0 {
_ = appIgnoreUpgradeRepo.Delete(appIgnoreUpgradeRepo.WithAppID(install.AppId))
}
return nil return nil
} }
uninstallTask.AddSubTask(task.GetTaskName(install.Name, task.TaskUninstall, task.TaskScopeApp), uninstall, nil) uninstallTask.AddSubTask(task.GetTaskName(install.Name, task.TaskUninstall, task.TaskScopeApp), uninstall, nil)
@@ -650,7 +655,11 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
} }
dockerCLi, _ := docker.NewClient() dockerCLi, _ := docker.NewClient()
if req.PullImage { 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.GetDockerComposeImagesV2(content, composeContent)
if err != nil { if err != nil {
return err return err
} }
@@ -1028,7 +1037,6 @@ func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) error
errMsg string errMsg string
) )
if pullImages && appInstall.App.Type != "php" { if pullImages && appInstall.App.Type != "php" {
//projectName := strings.ToLower(appInstall.Name)
envByte, err := files.NewFileOp().GetContent(appInstall.GetEnvPath()) envByte, err := files.NewFileOp().GetContent(appInstall.GetEnvPath())
if err != nil { if err != nil {
return err return err
@@ -1058,8 +1066,8 @@ func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) error
if strings.Contains(errOur, "no such host") { if strings.Contains(errOur, "no such host") {
errMsg = i18n.GetMsgByKey("ErrNoSuchHost") + ":" errMsg = i18n.GetMsgByKey("ErrNoSuchHost") + ":"
} }
if strings.Contains(errOur, "timeout") { if strings.Contains(errOur, "Error response from daemon") {
errMsg = i18n.GetMsgByKey("ErrImagePullTimeOut") + ":" errMsg = i18n.GetMsgByKey("PullImageTimeout") + ":"
} }
} }
appInstall.Message = errMsg + errOur appInstall.Message = errMsg + errOur
@@ -1764,6 +1772,10 @@ func ignoreUpdate(installed model.AppInstall) bool {
if installed.App.Type == "php" || installed.Status == constant.StatusInstalling { if installed.App.Type == "php" || installed.Status == constant.StatusInstalling {
return true return true
} }
ignores, _ := appIgnoreUpgradeRepo.List(appDetailRepo.WithAppId(installed.AppId), appIgnoreUpgradeRepo.WithScope("all"))
if len(ignores) > 0 {
return true
}
if installed.App.Key == constant.AppMysql { if installed.App.Key == constant.AppMysql {
majorVersion := getMajorVersion(installed.Version) majorVersion := getMajorVersion(installed.Version)
appDetails, _ := appDetailRepo.GetBy(appDetailRepo.WithAppId(installed.App.ID)) appDetails, _ := appDetailRepo.GetBy(appDetailRepo.WithAppId(installed.App.ID))
@@ -1774,8 +1786,7 @@ func ignoreUpdate(installed model.AppInstall) bool {
} }
return true return true
} }
ignores, _ := appIgnoreUpgradeRepo.List(appDetailRepo.WithAppId(installed.AppId), appIgnoreUpgradeRepo.WithScope("all")) return false
return len(ignores) > 0
} }
func RequestDownloadCallBack(downloadCallBackUrl string) { func RequestDownloadCallBack(downloadCallBackUrl string) {
@@ -1899,3 +1910,59 @@ func handleSSLConfig(appInstall *model.AppInstall, hasDefaultWebsite bool) error
} }
return nil 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
}
+2 -2
View File
@@ -119,7 +119,7 @@ func handlePostgresqlBackup(db DatabaseHelper, parentTask *task.Task, targetDir,
} }
itemHandler := func() error { return doPostgresqlgBackup(db, targetDir, fileName) } 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 { if parentTask != nil {
return itemHandler() return itemHandler()
} }
@@ -137,7 +137,7 @@ func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRol
} }
itemTask = parentTask itemTask = parentTask
if parentTask == nil { 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 { if err != nil {
return err return err
} }
+104 -51
View File
@@ -82,7 +82,7 @@ type IContainerService interface {
CreateVolume(req dto.VolumeCreate) error CreateVolume(req dto.VolumeCreate) error
TestCompose(req dto.ComposeCreate) (bool, error) TestCompose(req dto.ComposeCreate) (bool, error)
ComposeUpdate(req dto.ComposeUpdate) error ComposeUpdate(req dto.ComposeUpdate) error
Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error) Prune(req dto.ContainerPrune) error
LoadUsers(req dto.OperationWithName) []string LoadUsers(req dto.OperationWithName) []string
@@ -417,66 +417,93 @@ func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
return string(bytes), nil return string(bytes), nil
} }
func (u *ContainerService) Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error) { func (u *ContainerService) Prune(req dto.ContainerPrune) error {
report := dto.ContainerPruneReport{}
client, err := docker.NewDockerClient() client, err := docker.NewDockerClient()
if err != nil { if err != nil {
return report, err return err
} }
defer client.Close() defer client.Close()
pruneFilters := filters.NewArgs() name := ""
if req.WithTagAll {
pruneFilters.Add("dangling", "false")
if req.PruneType != "image" {
pruneFilters.Add("until", "24h")
}
}
switch req.PruneType { switch req.PruneType {
case "container": case "container":
rep, err := client.ContainersPrune(context.Background(), pruneFilters) name = "Container"
if err != nil {
return report, err
}
report.DeletedNumber = len(rep.ContainersDeleted)
report.SpaceReclaimed = int(rep.SpaceReclaimed)
case "image": case "image":
rep, err := client.ImagesPrune(context.Background(), pruneFilters) name = "Image"
if err != nil {
return report, err
}
report.DeletedNumber = len(rep.ImagesDeleted)
report.SpaceReclaimed = int(rep.SpaceReclaimed)
case "network":
rep, err := client.NetworksPrune(context.Background(), pruneFilters)
if err != nil {
return report, err
}
report.DeletedNumber = len(rep.NetworksDeleted)
case "volume": case "volume":
versions, err := client.ServerVersion(context.Background()) name = "Volume"
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)
case "buildcache": case "buildcache":
opts := build.CachePruneOptions{} name = "BuildCache"
opts.All = true case "network":
rep, err := client.BuildCachePrune(context.Background(), opts) name = "Network"
if err != nil {
return report, err
}
report.DeletedNumber = len(rep.CachesDeleted)
report.SpaceReclaimed = int(rep.SpaceReclaimed)
} }
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) { func (u *ContainerService) LoadResourceLimit() (*dto.ResourceLimit, error) {
@@ -1240,6 +1267,32 @@ func checkImageExist(client *client.Client, imageItem string) bool {
return false 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 { func pullImages(task *task.Task, client *client.Client, imageName string) error {
dockerCli := docker.NewClientWithExist(client) dockerCli := docker.NewClientWithExist(client)
options := image.PullOptions{} options := image.PullOptions{}
+309 -1
View File
@@ -1,6 +1,7 @@
package service package service
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
@@ -15,6 +16,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/buserr" "github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant" "github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/jinzhu/copier" "github.com/jinzhu/copier"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/robfig/cron/v3" "github.com/robfig/cron/v3"
@@ -30,11 +32,14 @@ type ICronjobService interface {
HandleOnce(id uint) error HandleOnce(id uint) error
Update(id uint, req dto.CronjobOperate) error Update(id uint, req dto.CronjobOperate) error
UpdateStatus(id uint, status string) error UpdateStatus(id uint, status string) error
UpdateGroup(req dto.ChangeGroup) error
Delete(req dto.CronjobBatchDelete) error Delete(req dto.CronjobBatchDelete) error
Download(down dto.CronjobDownload) (string, error) Download(down dto.CronjobDownload) (string, error)
StartJob(cronjob *model.Cronjob, isUpdate bool) (string, error) StartJob(cronjob *model.Cronjob, isUpdate bool) (string, error)
CleanRecord(req dto.CronjobClean) error CleanRecord(req dto.CronjobClean) error
Export(req dto.OperateByIDs) (string, error)
Import(req []dto.CronjobTrans) error
LoadScriptOptions() []dto.ScriptOptions LoadScriptOptions() []dto.ScriptOptions
LoadInfo(req dto.OperateByID) (*dto.CronjobOperate, error) LoadInfo(req dto.OperateByID) (*dto.CronjobOperate, error)
@@ -46,7 +51,11 @@ func NewICronjobService() ICronjobService {
} }
func (u *CronjobService) SearchWithPage(search dto.PageCronjob) (int64, interface{}, error) { 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 var dtoCronjobs []dto.CronjobInfo
for _, cronjob := range cronjobs { for _, cronjob := range cronjobs {
var item dto.CronjobInfo var item dto.CronjobInfo
@@ -102,6 +111,292 @@ func (u *CronjobService) LoadInfo(req dto.OperateByID) (*dto.CronjobOperate, err
return &item, 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 {
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 { func (u *CronjobService) LoadScriptOptions() []dto.ScriptOptions {
scripts, err := scriptRepo.List() scripts, err := scriptRepo.List()
if err != nil { if err != nil {
@@ -411,7 +706,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["name"] = req.Name
upMap["group_id"] = req.GroupID
upMap["spec_custom"] = req.SpecCustom upMap["spec_custom"] = req.SpecCustom
upMap["spec"] = spec upMap["spec"] = spec
upMap["script"] = req.Script upMap["script"] = req.Script
@@ -428,6 +727,7 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate) error {
upMap["db_type"] = req.DBType upMap["db_type"] = req.DBType
upMap["db_name"] = req.DBName upMap["db_name"] = req.DBName
upMap["url"] = req.URL upMap["url"] = req.URL
upMap["is_dir"] = req.IsDir
upMap["source_dir"] = req.SourceDir upMap["source_dir"] = req.SourceDir
upMap["snapshot_rule"] = cronjob.SnapshotRule upMap["snapshot_rule"] = cronjob.SnapshotRule
@@ -482,6 +782,14 @@ func (u *CronjobService) UpdateStatus(id uint, status string) error {
return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"status": status, "entry_ids": entryIDs}) 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) { func (u *CronjobService) AddCronJob(cronjob *model.Cronjob) (int, error) {
addFunc := func() { addFunc := func() {
u.HandleJob(cronjob) u.HandleJob(cronjob)
+34 -17
View File
@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"os" "os"
"path" "path"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@@ -24,22 +25,7 @@ import (
) )
func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error { func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
var apps []model.AppInstall apps := loadAppsForJob(cronjob)
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
}
if len(apps) == 0 { if len(apps) == 0 {
return errors.New("no such app in database!") return errors.New("no such app in database!")
} }
@@ -214,7 +200,10 @@ func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.T
if err != nil { if err != nil {
return err return err
} }
fileName := fmt.Sprintf("%s.tar.gz", startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)) 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)
}
backupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf("tmp/%s/%s", cronjob.Type, cronjob.Name)) backupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf("tmp/%s/%s", cronjob.Type, cronjob.Name))
fileOp := files.NewFileOp() fileOp := files.NewFileOp()
@@ -344,6 +333,23 @@ func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.J
return nil 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 { type DatabaseHelper struct {
ID uint ID uint
DBType string DBType string
@@ -546,3 +552,14 @@ func loadSnapWithRule(cronjob model.Cronjob) (dto.SnapshotData, error) {
} }
return itemData, nil 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))
}
+20 -12
View File
@@ -6,19 +6,17 @@ import (
"os" "os"
"path" "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/dto"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr" "github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant" "github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/encrypt" "github.com/1Panel-dev/1Panel/agent/utils/encrypt"
"github.com/1Panel-dev/1Panel/agent/utils/mysql" "github.com/1Panel-dev/1Panel/agent/utils/mysql"
"github.com/1Panel-dev/1Panel/agent/utils/mysql/client" "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/jinzhu/copier"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@@ -118,6 +116,9 @@ func (u *DatabaseService) LoadItems(dbType string) ([]dto.DatabaseItem, error) {
} }
func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool { func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
if req.Timeout == 0 {
req.Timeout = 30
}
switch req.Type { switch req.Type {
case constant.AppPostgresql: case constant.AppPostgresql:
_, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{ _, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{
@@ -126,7 +127,7 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
Port: req.Port, Port: req.Port,
Username: req.Username, Username: req.Username,
Password: req.Password, Password: req.Password,
Timeout: 6, Timeout: req.Timeout,
}) })
return err == nil return err == nil
case constant.AppRedis: case constant.AppRedis:
@@ -134,6 +135,7 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
Address: req.Address, Address: req.Address,
Port: req.Port, Port: req.Port,
Password: req.Password, Password: req.Password,
Timeout: req.Timeout,
}) })
return err == nil return err == nil
case "mysql", "mariadb": case "mysql", "mariadb":
@@ -149,7 +151,7 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
ClientKey: req.ClientKey, ClientKey: req.ClientKey,
ClientCert: req.ClientCert, ClientCert: req.ClientCert,
SkipVerify: req.SkipVerify, SkipVerify: req.SkipVerify,
Timeout: 6, Timeout: req.Timeout,
}) })
return err == nil return err == nil
} }
@@ -158,6 +160,9 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
} }
func (u *DatabaseService) Create(req dto.DatabaseCreate) error { func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
if req.Timeout == 0 {
req.Timeout = 30
}
db, _ := databaseRepo.Get(repo.WithByName(req.Name)) db, _ := databaseRepo.Get(repo.WithByName(req.Name))
if db.ID != 0 { if db.ID != 0 {
if db.From == "local" { if db.From == "local" {
@@ -173,7 +178,7 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
Port: req.Port, Port: req.Port,
Username: req.Username, Username: req.Username,
Password: req.Password, Password: req.Password,
Timeout: 6, Timeout: req.Timeout,
}); err != nil { }); err != nil {
return err return err
} }
@@ -182,6 +187,7 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
Address: req.Address, Address: req.Address,
Port: req.Port, Port: req.Port,
Password: req.Password, Password: req.Password,
Timeout: req.Timeout,
}); err != nil { }); err != nil {
return err return err
} }
@@ -198,7 +204,7 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
ClientKey: req.ClientKey, ClientKey: req.ClientKey,
ClientCert: req.ClientCert, ClientCert: req.ClientCert,
SkipVerify: req.SkipVerify, SkipVerify: req.SkipVerify,
Timeout: 6, Timeout: req.Timeout,
}); err != nil { }); err != nil {
return err return err
} }
@@ -273,7 +279,7 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
Port: req.Port, Port: req.Port,
Username: req.Username, Username: req.Username,
Password: req.Password, Password: req.Password,
Timeout: 300, Timeout: req.Timeout,
}); err != nil { }); err != nil {
return err return err
} }
@@ -282,6 +288,7 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
Address: req.Address, Address: req.Address,
Port: req.Port, Port: req.Port,
Password: req.Password, Password: req.Password,
Timeout: req.Timeout,
}); err != nil { }); err != nil {
return err return err
} }
@@ -298,7 +305,7 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
ClientKey: req.ClientKey, ClientKey: req.ClientKey,
ClientCert: req.ClientCert, ClientCert: req.ClientCert,
SkipVerify: req.SkipVerify, SkipVerify: req.SkipVerify,
Timeout: 300, Timeout: req.Timeout,
}); err != nil { }); err != nil {
return err return err
} }
@@ -318,6 +325,7 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
upMap["port"] = req.Port upMap["port"] = req.Port
upMap["username"] = req.Username upMap["username"] = req.Username
upMap["password"] = pass upMap["password"] = pass
upMap["timeout"] = req.Timeout
upMap["description"] = req.Description upMap["description"] = req.Description
upMap["ssl"] = req.SSL upMap["ssl"] = req.SSL
upMap["client_key"] = req.ClientKey upMap["client_key"] = req.ClientKey
+3 -3
View File
@@ -74,11 +74,11 @@ func (u *DBCommonService) UpdateConfByFile(req dto.DBConfUpdateByFile) error {
} }
path := "" path := ""
switch req.Type { 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) 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) 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) 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) file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
+5 -1
View File
@@ -53,7 +53,7 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
if err := confSet(redisInfo.Name, req.DBType, "", confs); err != nil { if err := confSet(redisInfo.Name, req.DBType, "", confs); err != nil {
return err 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 err
} }
@@ -244,6 +244,10 @@ func confSet(redisName string, redisType string, updateType string, changeConf [
} }
newFiles = append(newFiles, files[i]) newFiles = append(newFiles, files[i])
} }
if startIndex == 0 {
newFiles = append(newFiles, "# Redis configuration rewrite by 1Panel")
startIndex = len(newFiles) - 1
}
endIndex = endIndex - emptyLine endIndex = endIndex - emptyLine
for _, item := range changeConf { for _, item := range changeConf {
if item.key == "save" { if item.key == "save" {
+66 -24
View File
@@ -4,10 +4,6 @@ import (
"bufio" "bufio"
"context" "context"
"fmt" "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"
"io/fs" "io/fs"
"os" "os"
@@ -20,6 +16,11 @@ import (
"time" "time"
"unicode/utf8" "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" "github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/app/dto/request" "github.com/1Panel-dev/1Panel/agent/app/dto/request"
@@ -97,19 +98,25 @@ func (f *FileService) SearchUploadWithPage(req request.SearchUploadWithPage) (in
files []response.UploadInfo files []response.UploadInfo
backData []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 { if err != nil {
return nil continue
} }
if !info.IsDir() { files = append(files, response.UploadInfo{
files = append(files, response.UploadInfo{ CreatedAt: fileItem.ModTime().Format(constant.DateTimeLayout),
CreatedAt: info.ModTime().Format(constant.DateTimeLayout), Size: int(fileItem.Size()),
Size: int(info.Size()), Name: item.Name(),
Name: info.Name(), })
}) }
}
return nil
})
total, start, end := len(files), (req.Page-1)*req.PageSize, req.Page*req.PageSize total, start, end := len(files), (req.Page-1)*req.PageSize, req.Page*req.PageSize
if start > total { if start > total {
backData = make([]response.UploadInfo, 0) backData = make([]response.UploadInfo, 0)
@@ -416,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()) 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 var errString string
@@ -478,6 +493,7 @@ func (f *FileService) DepthDirSize(req request.DirSizeReq) ([]response.DepthDirS
func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error) { func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error) {
logFilePath := "" logFilePath := ""
taskStatus := ""
switch req.Type { switch req.Type {
case constant.TypeWebsite: case constant.TypeWebsite:
website, err := websiteRepo.GetFirst(repo.WithByID(req.ID)) website, err := websiteRepo.GetFirst(repo.WithByID(req.ID))
@@ -533,29 +549,55 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
return nil, err return nil, err
} }
logFilePath = taskModel.LogFile logFilePath = taskModel.LogFile
taskStatus = taskModel.Status
case "mysql-slow-logs": case "mysql-slow-logs":
logFilePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name)) logFilePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
case "mariadb-slow-logs": case "mariadb-slow-logs":
logFilePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/mariadb/%s/db/data/1Panel-slow.log", req.Name)) 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 { if err != nil {
return nil, err return nil, err
} }
if req.Latest && req.Page == 1 && len(lines) < 1000 && total > 1 { defer file.Close()
preLines, _, _, err := files.ReadFileByLine(logFilePath, total-1, req.PageSize, false) stat, err := file.Stat()
if err != nil {
return nil, err
}
var (
lines []string
isEndOfFile bool
total int
scope string
)
if stat.Size() > 500*1024*1024 {
lines, err = files.TailFromEnd(logFilePath, req.PageSize)
isEndOfFile = true
scope = "tail"
} else {
lines, isEndOfFile, total, err = files.ReadFileByLine(logFilePath, req.Page, req.PageSize, req.Latest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
lines = append(preLines, lines...) if req.Latest && req.Page == 1 && len(lines) < 1000 && total > 1 {
preLines, _, _, err := files.ReadFileByLine(logFilePath, total-1, req.PageSize, false)
if err != nil {
return nil, err
}
lines = append(preLines, lines...)
}
scope = "page"
} }
res := &response.FileLineContent{ res := &response.FileLineContent{
Content: strings.Join(lines, "\n"), End: isEndOfFile,
End: isEndOfFile, Path: logFilePath,
Path: logFilePath, Total: total,
Total: total, TaskStatus: taskStatus,
Lines: lines, Lines: lines,
Scope: scope,
} }
return res, nil return res, nil
} }
+7 -1
View File
@@ -323,6 +323,7 @@ func handleProcess(supervisordDir string, req request.SupervisorProcessConfig, c
_, _ = section.NewKey("command", strings.TrimSpace(req.Command)) _, _ = section.NewKey("command", strings.TrimSpace(req.Command))
_, _ = section.NewKey("directory", req.Dir) _, _ = section.NewKey("directory", req.Dir)
_, _ = section.NewKey("autorestart", req.AutoRestart) _, _ = section.NewKey("autorestart", req.AutoRestart)
_, _ = section.NewKey("autostart", req.AutoStart)
_, _ = section.NewKey("startsecs", "3") _, _ = section.NewKey("startsecs", "3")
_, _ = section.NewKey("stdout_logfile", outLog) _, _ = section.NewKey("stdout_logfile", outLog)
_, _ = section.NewKey("stderr_logfile", errLog) _, _ = section.NewKey("stderr_logfile", errLog)
@@ -360,6 +361,8 @@ func handleProcess(supervisordDir string, req request.SupervisorProcessConfig, c
numprocsKey.SetValue(req.Numprocs) numprocsKey.SetValue(req.Numprocs)
autoRestart := section.Key("autorestart") autoRestart := section.Key("autorestart")
autoRestart.SetValue(req.AutoRestart) autoRestart.SetValue(req.AutoRestart)
autoStart := section.Key("autostart")
autoStart.SetValue(req.AutoStart)
if err = configFile.SaveTo(iniPath); err != nil { if err = configFile.SaveTo(iniPath); err != nil {
return err return err
@@ -429,6 +432,9 @@ func handleProcessConfig(configDir, containerName string) ([]response.Supervisor
if autoRestart, _ := section.GetKey("autorestart"); autoRestart != nil { if autoRestart, _ := section.GetKey("autorestart"); autoRestart != nil {
config.AutoRestart = autoRestart.Value() config.AutoRestart = autoRestart.Value()
} }
if autoStart, _ := section.GetKey("autostart"); autoStart != nil {
config.AutoStart = autoStart.Value()
}
_ = getProcessStatus(&config, containerName) _ = getProcessStatus(&config, containerName)
result = append(result, config) result = append(result, config)
} }
@@ -609,7 +615,7 @@ func getProcessStatus(config *response.SupervisorProcessConfig, containerName st
for _, line := range lines { for _, line := range lines {
line = strings.TrimPrefix(line, "stdout:") line = strings.TrimPrefix(line, "stdout:")
fields := strings.Fields(line) fields := strings.Fields(line)
if len(fields) >= 5 { if len(fields) >= 4 {
status := response.ProcessStatus{ status := response.ProcessStatus{
Name: fields[0], Name: fields[0],
Status: fields[1], Status: fields[1],
+35 -24
View File
@@ -21,6 +21,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/constant" "github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/i18n" "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/1Panel-dev/1Panel/agent/utils/docker"
"github.com/docker/docker/api/types/build" "github.com/docker/docker/api/types/build"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
@@ -41,7 +42,7 @@ type IImageService interface {
ImageLoad(req dto.ImageLoad) error ImageLoad(req dto.ImageLoad) error
ImageSave(req dto.ImageSave) error ImageSave(req dto.ImageSave) error
ImagePush(req dto.ImagePush) error ImagePush(req dto.ImagePush) error
ImageRemove(req dto.BatchDelete) (dto.ContainerPruneReport, error) ImageRemove(req dto.BatchDelete) error
ImageTag(req dto.ImageTag) error ImageTag(req dto.ImageTag) error
} }
@@ -386,34 +387,44 @@ func (u *ImageService) ImagePush(req dto.ImagePush) error {
return nil return nil
} }
func (u *ImageService) ImageRemove(req dto.BatchDelete) (dto.ContainerPruneReport, error) { func (u *ImageService) ImageRemove(req dto.BatchDelete) error {
report := dto.ContainerPruneReport{}
client, err := docker.NewDockerClient() client, err := docker.NewDockerClient()
if err != nil { if err != nil {
return report, err return err
} }
defer client.Close() defer client.Close()
for _, id := range req.Names { taskItem, err := task.NewTaskWithOps(task.TaskScopeImage, task.TaskDelete, task.TaskScopeContainer, req.TaskID, 1)
imageItem, _, err := client.ImageInspectWithRaw(context.TODO(), id) if err != nil {
if err != nil { global.LOG.Errorf("new task for create container failed, err: %v", err)
return report, err 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 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)
} }
return report, nil
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)
}
go func() {
_ = taskItem.Execute()
}()
return nil
} }
func formatFileSize(fileSize int64) (size string) { func formatFileSize(fileSize int64) (size string) {
+25 -9
View File
@@ -86,6 +86,11 @@ func (m McpServerService) Page(req request.McpServerSearch) response.McpServersR
} }
func (m McpServerService) Update(req request.McpServerUpdate) error { 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)) mcpServer, err := mcpServerRepo.GetFirst(repo.WithByID(req.ID))
if err != nil { if err != nil {
return err return err
@@ -108,6 +113,8 @@ func (m McpServerService) Update(req request.McpServerUpdate) error {
mcpServer.BaseURL = req.BaseURL mcpServer.BaseURL = req.BaseURL
mcpServer.SsePath = req.SsePath mcpServer.SsePath = req.SsePath
mcpServer.HostIP = req.HostIP mcpServer.HostIP = req.HostIP
mcpServer.StreamableHttpPath = req.StreamableHttpPath
mcpServer.OutputTransport = req.OutputTransport
if err := handleCreateParams(mcpServer, req.Environments, req.Volumes); err != nil { if err := handleCreateParams(mcpServer, req.Environments, req.Volumes); err != nil {
return err return err
} }
@@ -130,6 +137,11 @@ func (m McpServerService) Update(req request.McpServerUpdate) error {
} }
func (m McpServerService) Create(create request.McpServerCreate) 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() servers, _ := mcpServerRepo.List()
for _, server := range servers { for _, server := range servers {
if server.Port == create.Port { 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) mcpDir := path.Join(global.Dir.McpDir, create.Name)
mcpServer := &model.McpServer{ mcpServer := &model.McpServer{
Name: create.Name, Name: create.Name,
ContainerName: create.ContainerName, ContainerName: create.ContainerName,
Port: create.Port, Port: create.Port,
Command: create.Command, Command: create.Command,
Status: constant.StatusStarting, Status: constant.StatusStarting,
BaseURL: create.BaseURL, BaseURL: create.BaseURL,
SsePath: create.SsePath, SsePath: create.SsePath,
Dir: mcpDir, Dir: mcpDir,
HostIP: create.HostIP, HostIP: create.HostIP,
StreamableHttpPath: create.StreamableHttpPath,
OutputTransport: create.OutputTransport,
} }
if err := handleCreateParams(mcpServer, create.Environments, create.Volumes); err != nil { if err := handleCreateParams(mcpServer, create.Environments, create.Volumes); err != nil {
return err return err
@@ -524,6 +538,8 @@ func handleEnv(mcpServer *model.McpServer) gotenv.Env {
env["BASE_URL"] = mcpServer.BaseURL env["BASE_URL"] = mcpServer.BaseURL
env["SSE_PATH"] = mcpServer.SsePath env["SSE_PATH"] = mcpServer.SsePath
env["HOST_IP"] = mcpServer.HostIP env["HOST_IP"] = mcpServer.HostIP
env["STREAMABLE_HTTP_PATH"] = mcpServer.StreamableHttpPath
env["OUTPUT_TRANSPORT"] = mcpServer.OutputTransport
envStr, _ := gotenv.Marshal(env) envStr, _ := gotenv.Marshal(env)
mcpServer.Env = envStr mcpServer.Env = envStr
return env return env
+72
View File
@@ -4,6 +4,8 @@ import (
"bufio" "bufio"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
"io" "io"
"net/http" "net/http"
"os" "os"
@@ -42,6 +44,9 @@ type INginxService interface {
Build(req request.NginxBuildReq) error Build(req request.NginxBuildReq) error
GetModules() (*response.NginxBuildConfig, error) GetModules() (*response.NginxBuildConfig, error)
UpdateModule(req request.NginxModuleUpdate) error UpdateModule(req request.NginxModuleUpdate) error
OperateDefaultHTTPs(req request.NginxOperateReq) error
GetDefaultHttpsStatus() (*response.NginxConfigRes, error)
} }
func NewINginxService() INginxService { func NewINginxService() INginxService {
@@ -348,3 +353,70 @@ func (n NginxService) UpdateModule(req request.NginxModuleUpdate) error {
} }
return fileOp.SaveFileWithByte(moduleConfigPath, moduleByte, constant.DirPerm) 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
}
+1
View File
@@ -450,6 +450,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
if err := checkContainerName(containerName.(string)); err != nil { if err := checkContainerName(containerName.(string)); err != nil {
return err return err
} }
runtime.ContainerName = containerName.(string)
} }
projectDir := path.Join(global.Dir.RuntimeDir, runtime.Type, runtime.Name) projectDir := path.Join(global.Dir.RuntimeDir, runtime.Type, runtime.Name)
+184 -52
View File
@@ -1,24 +1,30 @@
package service package service
import ( import (
"bytes"
"encoding/base64"
"fmt" "fmt"
"os" "os"
"os/user" "os/user"
"path" "path"
"sort" "sort"
"strconv"
"strings" "strings"
"time" "time"
"github.com/1Panel-dev/1Panel/agent/utils/copier"
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
"github.com/1Panel-dev/1Panel/agent/utils/geo" "github.com/1Panel-dev/1Panel/agent/utils/geo"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/1Panel-dev/1Panel/agent/app/dto" "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/buserr"
"github.com/1Panel-dev/1Panel/agent/constant" "github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd" "github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common" "github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/systemctl" "github.com/1Panel-dev/1Panel/agent/utils/systemctl"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@@ -32,11 +38,13 @@ type ISSHService interface {
OperateSSH(operation string) error OperateSSH(operation string) error
UpdateByFile(value string) error UpdateByFile(value string) error
Update(req dto.SSHUpdate) error Update(req dto.SSHUpdate) error
GenerateSSH(req dto.GenerateSSH) error
LoadSSHSecret(mode string) (string, error)
LoadLog(ctx *gin.Context, req dto.SearchSSHLog) (*dto.SSHLog, error) LoadLog(ctx *gin.Context, req dto.SearchSSHLog) (*dto.SSHLog, error)
LoadSSHConf() (string, error) LoadSSHConf() (string, error)
SyncRootCert() error
CreateRootCert(req dto.CreateRootCert) error
SearchRootCerts(req dto.SearchWithPage) (int64, interface{}, error)
DeleteRootCerts(req dto.ForceDelete) error
} }
func NewISSHService() ISSHService { func NewISSHService() ISSHService {
@@ -110,6 +118,14 @@ func (u *SSHService) GetSSHInfo() (*dto.SSHInfo, error) {
data.UseDNS = strings.ReplaceAll(line, "UseDNS ", "") data.UseDNS = strings.ReplaceAll(line, "UseDNS ", "")
} }
} }
currentUser, err := user.Current()
if err != nil || len(currentUser.Name) == 0 {
data.CurrentUser = "root"
} else {
data.CurrentUser = currentUser.Name
}
return &data, nil return &data, nil
} }
@@ -214,70 +230,163 @@ func (u *SSHService) UpdateByFile(value string) error {
return nil return nil
} }
func (u *SSHService) GenerateSSH(req dto.GenerateSSH) error { func (u *SSHService) SyncRootCert() error {
if cmd.CheckIllegal(req.EncryptionMode, req.Password) { currentUser, err := user.Current()
if err != nil {
return fmt.Errorf("load current user failed, err: %v", err)
}
sshDir := fmt.Sprintf("%s/.ssh", currentUser.HomeDir)
fileList, err := os.ReadDir(sshDir)
if err != nil {
return err
}
var rootCerts []model.RootCert
fileMap := make(map[string]bool)
for _, item := range fileList {
if !item.IsDir() {
fileMap[item.Name()] = true
}
}
for item := range fileMap {
if !strings.HasSuffix(item, ".pub") {
continue
}
if !fileMap[strings.TrimSuffix(item, ".pub")] {
continue
}
cert := model.RootCert{Name: strings.TrimSuffix(item, ".pub"), PublicKeyPath: path.Join(sshDir, item), PrivateKeyPath: path.Join(sshDir, strings.TrimSuffix(item, ".pub"))}
pubItem, err := os.ReadFile(path.Join(sshDir, item))
if err != nil {
global.LOG.Errorf("read pubic key of %s for sync failed, err: %v", item, err)
continue
}
cert.EncryptionMode = loadEncryptioMode(string(pubItem))
rootCerts = append(rootCerts, cert)
}
return hostRepo.SyncCert(rootCerts)
}
func (u *SSHService) CreateRootCert(req dto.CreateRootCert) error {
if cmd.CheckIllegal(req.EncryptionMode, req.PassPhrase) {
return buserr.New("ErrCmdIllegal") return buserr.New("ErrCmdIllegal")
} }
certItem, _ := hostRepo.GetCert(repo.WithByName(req.Name))
if certItem.ID != 0 {
return buserr.New("ErrRecordExist")
}
currentUser, err := user.Current() currentUser, err := user.Current()
if err != nil { if err != nil {
return fmt.Errorf("load current user failed, err: %v", err) return fmt.Errorf("load current user failed, err: %v", err)
} }
secretFile := fmt.Sprintf("%s/.ssh/id_item_%s", currentUser.HomeDir, req.EncryptionMode) var cert model.RootCert
secretPubFile := fmt.Sprintf("%s/.ssh/id_item_%s.pub", currentUser.HomeDir, req.EncryptionMode) if err := copier.Copy(&cert, req); err != nil {
return err
}
privatePath := fmt.Sprintf("%s/.ssh/%s", currentUser.HomeDir, req.Name)
publicPath := fmt.Sprintf("%s/.ssh/%s.pub", currentUser.HomeDir, req.Name)
authFilePath := currentUser.HomeDir + "/.ssh/authorized_keys" authFilePath := currentUser.HomeDir + "/.ssh/authorized_keys"
command := fmt.Sprintf("ssh-keygen -t %s -f %s/.ssh/id_item_%s | echo y", req.EncryptionMode, currentUser.HomeDir, req.EncryptionMode) if req.Mode == "input" || req.Mode == "import" {
if len(req.Password) != 0 { if err := os.WriteFile(privatePath, []byte(req.PrivateKey), constant.FilePerm); err != nil {
command = fmt.Sprintf("ssh-keygen -t %s -P %s -f %s/.ssh/id_item_%s | echo y", req.EncryptionMode, req.Password, currentUser.HomeDir, req.EncryptionMode) return err
}
if err := os.WriteFile(publicPath, []byte(req.PublicKey), constant.FilePerm); err != nil {
return err
}
} else {
command := fmt.Sprintf("ssh-keygen -t %s -f %s/.ssh/%s -N ''", req.EncryptionMode, currentUser.HomeDir, req.Name)
if len(req.PassPhrase) != 0 {
command = fmt.Sprintf("ssh-keygen -t %s -P %s -f %s/.ssh/%s | echo y", req.EncryptionMode, req.PassPhrase, currentUser.HomeDir, req.Name)
}
stdout, err := cmd.RunDefaultWithStdoutBashC(command)
if err != nil {
return fmt.Errorf("generate failed, err: %v, message: %s", err, stdout)
}
} }
stdout, err := cmd.RunDefaultWithStdoutBashC(command)
stdout, err := cmd.RunDefaultWithStdoutBashCf("cat %s >> %s", publicPath, authFilePath)
if err != nil { if err != nil {
return fmt.Errorf("generate failed, err: %v, message: %s", err, stdout) return fmt.Errorf("generate failed, err: %v, message: %s", err, stdout)
} }
defer func() {
_ = os.Remove(secretFile)
}()
defer func() {
_ = os.Remove(secretPubFile)
}()
if _, err := os.Stat(authFilePath); err != nil && errors.Is(err, os.ErrNotExist) { cert.PrivateKeyPath = privatePath
authFile, err := os.Create(authFilePath) cert.PublicKeyPath = publicPath
if err != nil { if len(cert.PassPhrase) != 0 {
cert.PassPhrase, _ = encrypt.StringEncrypt(cert.PassPhrase)
}
return hostRepo.CreateCert(&cert)
}
func (u *SSHService) SearchRootCerts(req dto.SearchWithPage) (int64, interface{}, error) {
total, records, err := hostRepo.PageCert(req.Page, req.PageSize)
if err != nil {
return 0, nil, err
}
var datas []dto.RootCert
for i := 0; i < len(records); i++ {
publicItem, err := os.ReadFile(records[i].PublicKeyPath)
var publicBase64 string
if err == nil && len(publicItem) != 0 {
publicBase64 = base64.StdEncoding.EncodeToString(publicItem)
}
privateItem, _ := os.ReadFile(records[i].PrivateKeyPath)
var privateBase64 string
if err == nil && len(publicItem) != 0 {
privateBase64 = base64.StdEncoding.EncodeToString(privateItem)
}
passPhrase, _ := encrypt.StringDecryptWithBase64(records[i].PassPhrase)
datas = append(datas, dto.RootCert{
ID: records[i].ID,
CreatedAt: records[i].CreatedAt,
Name: records[i].Name,
EncryptionMode: records[i].EncryptionMode,
PassPhrase: passPhrase,
PublicKey: publicBase64,
PrivateKey: privateBase64,
Description: records[i].Description,
})
}
return total, datas, err
}
func (u *SSHService) DeleteRootCerts(req dto.ForceDelete) error {
currentUser, err := user.Current()
if err != nil && !req.ForceDelete {
return fmt.Errorf("load current user failed, err: %v", err)
}
authFilePath := currentUser.HomeDir + "/.ssh/authorized_keys"
authItem, err := os.ReadFile(authFilePath)
if err != nil && !req.ForceDelete {
return err
}
for _, id := range req.IDs {
cert, _ := hostRepo.GetCert(repo.WithByID(id))
if cert.ID == 0 {
if !req.ForceDelete {
return buserr.New("ErrRecordNotFound")
} else {
continue
}
}
publicItem, err := os.ReadFile(cert.PublicKeyPath)
if err != nil && !req.ForceDelete {
return err
}
newFile := bytes.ReplaceAll(authItem, publicItem, nil)
if err := os.WriteFile(authFilePath, newFile, constant.FilePerm); err != nil && !req.ForceDelete {
return fmt.Errorf("refresh authorized_keys failed, err: %v", err)
}
_ = os.Remove(cert.PublicKeyPath)
_ = os.Remove(cert.PrivateKeyPath)
if err := hostRepo.DeleteCert(repo.WithByID(id)); err != nil && !req.ForceDelete {
return err return err
} }
defer authFile.Close()
}
stdout1, err := cmd.RunDefaultWithStdoutBashCf("cat %s >> %s/.ssh/authorized_keys", secretPubFile, currentUser.HomeDir)
if err != nil {
return fmt.Errorf("generate failed, err: %v, message: %s", err, stdout1)
}
fileOp := files.NewFileOp()
if err := fileOp.Rename(secretFile, fmt.Sprintf("%s/.ssh/id_%s%s", currentUser.HomeDir, req.EncryptionMode, req.Name)); err != nil {
return err
}
if err := fileOp.Rename(secretPubFile, fmt.Sprintf("%s/.ssh/id_%s%s.pub", currentUser.HomeDir, req.EncryptionMode, req.Name)); err != nil {
return err
} }
return nil return nil
} }
func (u *SSHService) LoadSSHSecret(mode string) (string, error) {
currentUser, err := user.Current()
if err != nil {
return "", fmt.Errorf("load current user failed, err: %v", err)
}
homeDir := currentUser.HomeDir
if _, err := os.Stat(fmt.Sprintf("%s/.ssh/id_%s", homeDir, mode)); err != nil {
return "", nil
}
file, err := os.ReadFile(fmt.Sprintf("%s/.ssh/id_%s", homeDir, mode))
return string(file), err
}
type sshFileItem struct { type sshFileItem struct {
Name string Name string
Year int Year int
@@ -433,7 +542,7 @@ func loadSSHData(ctx *gin.Context, command string, showCountFrom, showCountTo, c
switch { switch {
case strings.Contains(lines[i], "Failed password for"): case strings.Contains(lines[i], "Failed password for"):
itemData = loadFailedSecureDatas(lines[i]) itemData = loadFailedSecureDatas(lines[i])
if len(itemData.Address) != 0 { if checkIsStandard(itemData) {
if successCount+failedCount >= showCountFrom && successCount+failedCount < showCountTo { if successCount+failedCount >= showCountFrom && successCount+failedCount < showCountTo {
itemData.Area, _ = geo.GetIPLocation(getLoc, itemData.Address, common.GetLang(ctx)) itemData.Area, _ = geo.GetIPLocation(getLoc, itemData.Address, common.GetLang(ctx))
itemData.Date = loadDate(currentYear, itemData.DateStr, nyc) itemData.Date = loadDate(currentYear, itemData.DateStr, nyc)
@@ -443,7 +552,7 @@ func loadSSHData(ctx *gin.Context, command string, showCountFrom, showCountTo, c
} }
case strings.Contains(lines[i], "Connection closed by authenticating user"): case strings.Contains(lines[i], "Connection closed by authenticating user"):
itemData = loadFailedAuthDatas(lines[i]) itemData = loadFailedAuthDatas(lines[i])
if len(itemData.Address) != 0 { if checkIsStandard(itemData) {
if successCount+failedCount >= showCountFrom && successCount+failedCount < showCountTo { if successCount+failedCount >= showCountFrom && successCount+failedCount < showCountTo {
itemData.Area, _ = geo.GetIPLocation(getLoc, itemData.Address, common.GetLang(ctx)) itemData.Area, _ = geo.GetIPLocation(getLoc, itemData.Address, common.GetLang(ctx))
itemData.Date = loadDate(currentYear, itemData.DateStr, nyc) itemData.Date = loadDate(currentYear, itemData.DateStr, nyc)
@@ -453,7 +562,7 @@ func loadSSHData(ctx *gin.Context, command string, showCountFrom, showCountTo, c
} }
case strings.Contains(lines[i], "Accepted "): case strings.Contains(lines[i], "Accepted "):
itemData = loadSuccessDatas(lines[i]) itemData = loadSuccessDatas(lines[i])
if len(itemData.Address) != 0 { if checkIsStandard(itemData) {
if successCount+failedCount >= showCountFrom && successCount+failedCount < showCountTo { if successCount+failedCount >= showCountFrom && successCount+failedCount < showCountTo {
itemData.Area, _ = geo.GetIPLocation(getLoc, itemData.Address, common.GetLang(ctx)) itemData.Area, _ = geo.GetIPLocation(getLoc, itemData.Address, common.GetLang(ctx))
itemData.Date = loadDate(currentYear, itemData.DateStr, nyc) itemData.Date = loadDate(currentYear, itemData.DateStr, nyc)
@@ -481,7 +590,6 @@ func loadSuccessDatas(line string) dto.SSHHistory {
data.Status = constant.StatusSuccess data.Status = constant.StatusSuccess
return data return data
} }
func loadFailedAuthDatas(line string) dto.SSHHistory { func loadFailedAuthDatas(line string) dto.SSHHistory {
var data dto.SSHHistory var data dto.SSHHistory
parts := strings.Fields(line) parts := strings.Fields(line)
@@ -531,6 +639,14 @@ func loadFailedSecureDatas(line string) dto.SSHHistory {
return data return data
} }
func checkIsStandard(item dto.SSHHistory) bool {
if len(item.Address) == 0 {
return false
}
portItem, _ := strconv.Atoi(item.Port)
return portItem != 0
}
func handleGunzip(path string) error { func handleGunzip(path string) error {
if _, err := cmd.RunDefaultWithStdoutBashCf("gunzip %s", path); err != nil { if _, err := cmd.RunDefaultWithStdoutBashCf("gunzip %s", path); err != nil {
return err return err
@@ -576,3 +692,19 @@ func analyzeDateStr(parts []string) (int, string) {
} }
return 2, fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]) return 2, fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2])
} }
func loadEncryptioMode(content string) string {
if strings.HasPrefix(content, "ssh-rsa") {
return "rsa"
}
if strings.HasPrefix(content, "ssh-ed25519") {
return "ed25519"
}
if strings.HasPrefix(content, "ssh-ecdsa") {
return "ecdsa"
}
if strings.HasPrefix(content, "ssh-dsa") {
return "dsa"
}
return ""
}
+14 -8
View File
@@ -257,7 +257,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
var ( var (
domains []model.WebsiteDomain domains []model.WebsiteDomain
) )
domains, _, _, err = getWebsiteDomains(create.Domains, defaultHttpPort, 0) domains, _, _, err = getWebsiteDomains(create.Domains, defaultHttpPort, nginxInstall.HttpsPort, 0)
if err != nil { if err != nil {
return err return err
} }
@@ -388,7 +388,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
switch runtime.Type { switch runtime.Type {
case constant.RuntimePHP: case constant.RuntimePHP:
if runtime.Resource == constant.ResourceAppstore { if runtime.Resource == constant.ResourceAppstore {
if !checkImageExist(nil, runtime.Image) { if !checkImageLike(nil, runtime.Image) {
return buserr.WithName("ErrImageNotExist", runtime.Name) return buserr.WithName("ErrImageNotExist", runtime.Name)
} }
website.Proxy = fmt.Sprintf("127.0.0.1:%s", runtime.Port) website.Proxy = fmt.Sprintf("127.0.0.1:%s", runtime.Port)
@@ -680,7 +680,7 @@ func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate)
domainModels []model.WebsiteDomain domainModels []model.WebsiteDomain
addPorts []int addPorts []int
) )
httpPort, _, err := getAppInstallPort(constant.AppOpenresty) httpPort, httpsPort, err := getAppInstallPort(constant.AppOpenresty)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -689,7 +689,7 @@ func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate)
return nil, err return nil, err
} }
domainModels, addPorts, _, err = getWebsiteDomains(create.Domains, httpPort, create.WebsiteID) domainModels, addPorts, _, err = getWebsiteDomains(create.Domains, httpPort, httpsPort, create.WebsiteID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -1354,7 +1354,7 @@ func (w WebsiteService) ChangePHPVersion(req request.WebsitePHPVersionReq) error
return err return err
} }
defer client.Close() defer client.Close()
if !checkImageExist(client, oldRuntime.Image) { if !checkImageLike(client, oldRuntime.Image) {
return buserr.WithName("ErrImageNotExist", oldRuntime.Name) return buserr.WithName("ErrImageNotExist", oldRuntime.Name)
} }
} }
@@ -1787,9 +1787,15 @@ func (w WebsiteService) GetProxies(id uint) (res []request.WebsiteProxyConfig, e
} }
directives := config.GetDirectives() directives := config.GetDirectives()
location, ok := directives[0].(*components.Location) var location *components.Location
if !ok { for _, directive := range directives {
err = errors.New("error") if loc, ok := directive.(*components.Location); ok {
location = loc
break
}
}
if location == nil {
err = errors.New("invalid proxy config, no location found")
return return
} }
proxyConfig.ProxyPass = location.ProxyPass proxyConfig.ProxyPass = location.ProxyPass
+31 -13
View File
@@ -185,6 +185,8 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
return res, err return res, err
} }
create.ID = websiteSSL.ID create.ID = websiteSSL.ID
logFile, _ := os.OpenFile(path.Join(global.Dir.SSLLogDir, fmt.Sprintf("%s-ssl-%d.log", websiteSSL.PrimaryDomain, websiteSSL.ID)), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, constant.FilePerm)
logFile.Close()
go func() { go func() {
if create.Provider != constant.DnsManual { if create.Provider != constant.DnsManual {
if err = w.ObtainSSL(request.WebsiteSSLApply{ if err = w.ObtainSSL(request.WebsiteSSLApply{
@@ -442,12 +444,15 @@ func (w WebsiteSSLService) GetWebsiteSSL(websiteId uint) (response.WebsiteSSLDTO
} }
func (w WebsiteSSLService) Delete(ids []uint) error { func (w WebsiteSSLService) Delete(ids []uint) error {
var names []string var (
websiteSSLS []string
applySSLS []string
)
for _, id := range ids { for _, id := range ids {
if websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(id)); len(websites) > 0 { if websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(id)); len(websites) > 0 {
oldSSL, _ := websiteSSLRepo.GetFirst(repo.WithByID(id)) oldSSL, _ := websiteSSLRepo.GetFirst(repo.WithByID(id))
if oldSSL.ID > 0 { if oldSSL.ID > 0 {
names = append(names, oldSSL.PrimaryDomain) websiteSSLS = append(websiteSSLS, oldSSL.PrimaryDomain)
} }
continue continue
} }
@@ -463,23 +468,36 @@ func (w WebsiteSSLService) Delete(ids []uint) error {
if err != nil { if err != nil {
return err return err
} }
if websiteSSL.Status == constant.SSLApply {
applySSLS = append(applySSLS, websiteSSL.PrimaryDomain)
continue
}
if websiteSSL.Provider != constant.Manual && websiteSSL.Provider != constant.SelfSigned { if websiteSSL.Provider != constant.Manual && websiteSSL.Provider != constant.SelfSigned {
acmeAccount, err := websiteAcmeRepo.GetFirst(repo.WithByID(websiteSSL.AcmeAccountID))
if err != nil {
return err
}
client, err := ssl.NewAcmeClient(acmeAccount, getSystemProxy(acmeAccount.UseProxy))
if err != nil {
return err
}
go func() { go func() {
_ = client.RevokeSSL([]byte(websiteSSL.Pem)) acmeAccount, err := websiteAcmeRepo.GetFirst(repo.WithByID(websiteSSL.AcmeAccountID))
if err != nil {
global.LOG.Errorf("Failed to get acme account for SSL revoke, err: %v", err)
return
}
client, err := ssl.NewAcmeClient(acmeAccount, getSystemProxy(acmeAccount.UseProxy))
if err != nil {
global.LOG.Errorf("Failed to create ACME client for SSL revoke, err: %v", err)
return
}
err = client.RevokeSSL([]byte(websiteSSL.Pem))
if err != nil {
global.LOG.Errorf("Failed to revoke SSL for domain %s, err: %v", websiteSSL.PrimaryDomain, err)
return
}
}() }()
} }
_ = websiteSSLRepo.DeleteBy(repo.WithByID(id)) _ = websiteSSLRepo.DeleteBy(repo.WithByID(id))
} }
if len(names) > 0 { if len(websiteSSLS) > 0 {
return buserr.WithName("ErrSSLCannotDelete", strings.Join(names, ",")) return buserr.WithName("ErrSSLCannotDelete", strings.Join(websiteSSLS, ","))
}
if len(applySSLS) > 0 {
return buserr.WithName("ErrApplySSLCanNotDelete", strings.Join(applySSLS, ","))
} }
return nil return nil
} }
+5 -5
View File
@@ -955,7 +955,7 @@ func opWebsite(website *model.Website, operate string) error {
} }
server.UpdatePHPProxy([]string{website.Proxy}, localPath) server.UpdatePHPProxy([]string{website.Proxy}, localPath)
} else { } else {
proxy := fmt.Sprintf("http://127.0.0.1:%s", runtime.Port) proxy := fmt.Sprintf("http://%s", website.Proxy)
server.UpdateRootProxy([]string{proxy}) server.UpdateRootProxy([]string{proxy})
} }
} }
@@ -1031,7 +1031,7 @@ func chownRootDir(path string) error {
return nil return nil
} }
func getWebsiteDomains(domains []request.WebsiteDomain, defaultPort int, websiteID uint) (domainModels []model.WebsiteDomain, addPorts []int, addDomains []string, err error) { func getWebsiteDomains(domains []request.WebsiteDomain, defaultHTTPPort, defaultHTTPsPort int, websiteID uint) (domainModels []model.WebsiteDomain, addPorts []int, addDomains []string, err error) {
var ( var (
ports = make(map[int]struct{}) ports = make(map[int]struct{})
existPort = make(map[int]struct{}) existPort = make(map[int]struct{})
@@ -1056,7 +1056,7 @@ func getWebsiteDomains(domains []request.WebsiteDomain, defaultPort int, website
domainModel.Domain = strings.ToLower(domainModel.Domain) domainModel.Domain = strings.ToLower(domainModel.Domain)
domainModel.Port = domain.Port domainModel.Port = domain.Port
if domain.Port == 0 { if domain.Port == 0 {
domain.Port = defaultPort domain.Port = defaultHTTPPort
} }
domainModel.SSL = domain.SSL domainModel.SSL = domain.SSL
domainModel.WebsiteID = websiteID domainModel.WebsiteID = websiteID
@@ -1077,7 +1077,7 @@ func getWebsiteDomains(domains []request.WebsiteDomain, defaultPort int, website
} }
for port := range ports { for port := range ports {
if port == defaultPort { if port == defaultHTTPPort || port == defaultHTTPsPort {
addPorts = append(addPorts, port) addPorts = append(addPorts, port)
continue continue
} }
@@ -1098,7 +1098,7 @@ func getWebsiteDomains(domains []request.WebsiteDomain, defaultPort int, website
err = buserr.WithMap("ErrPortExist", errMap, nil) err = buserr.WithMap("ErrPortExist", errMap, nil)
return return
} }
if port != 443 && common.ScanPort(port) { if port != defaultHTTPsPort && common.ScanPort(port) {
err = buserr.WithDetail("ErrPortInUsed", port, nil) err = buserr.WithDetail("ErrPortInUsed", port, nil)
return return
} }
+1
View File
@@ -65,6 +65,7 @@ const (
TaskPull = "TaskPull" TaskPull = "TaskPull"
TaskCommit = "TaskCommit" TaskCommit = "TaskCommit"
TaskPush = "TaskPush" TaskPush = "TaskPush"
TaskClean = "TaskClean"
TaskHandle = "TaskHandle" TaskHandle = "TaskHandle"
) )
+2
View File
@@ -7,9 +7,11 @@ services:
- "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${PANEL_APP_PORT_HTTP}" - "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${PANEL_APP_PORT_HTTP}"
command: [ command: [
"--stdio", "${COMMAND}", "--stdio", "${COMMAND}",
"--outputTransport","${OUTPUT_TRANSPORT}",
"--port", "${PANEL_APP_PORT_HTTP}", "--port", "${PANEL_APP_PORT_HTTP}",
"--baseUrl", "${BASE_URL}", "--baseUrl", "${BASE_URL}",
"--ssePath", "${SSE_PATH}", "--ssePath", "${SSE_PATH}",
"--streamableHttpPath", "${STREAMABLE_HTTP_PATH}",
"--messagePath", "${SSE_PATH}/messages" "--messagePath", "${SSE_PATH}/messages"
] ]
networks: networks:
+1
View File
@@ -5,6 +5,7 @@ const (
StatusCanceled = "Canceled" StatusCanceled = "Canceled"
StatusDone = "Done" StatusDone = "Done"
StatusWaiting = "Waiting" StatusWaiting = "Waiting"
StatusPending = "Pending"
StatusSuccess = "Success" StatusSuccess = "Success"
StatusFailed = "Failed" StatusFailed = "Failed"
StatusUploading = "Uploading" StatusUploading = "Uploading"
+1 -1
View File
@@ -8,7 +8,7 @@ require (
github.com/compose-spec/compose-go/v2 v2.6.4 github.com/compose-spec/compose-go/v2 v2.6.4
github.com/creack/pty v1.1.24 github.com/creack/pty v1.1.24
github.com/docker/compose/v2 v2.37.0 github.com/docker/compose/v2 v2.37.0
github.com/docker/docker v28.2.2+incompatible github.com/docker/docker v28.3.3+incompatible
github.com/docker/go-connections v0.5.0 github.com/docker/go-connections v0.5.0
github.com/fsnotify/fsnotify v1.9.0 github.com/fsnotify/fsnotify v1.9.0
github.com/gin-gonic/gin v1.10.0 github.com/gin-gonic/gin v1.10.0
+2
View File
@@ -202,6 +202,8 @@ github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBi
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw= github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw=
github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI=
github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
+8 -1
View File
@@ -62,7 +62,6 @@ ErrHttpReqTimeOut: 'Request timed out {{ .err }}'
ErrHttpReqFailed: 'Request failed {{ .err }}' ErrHttpReqFailed: 'Request failed {{ .err }}'
ErrNoSuchHost: 'Unable to find the requested server {{ .err }}' ErrNoSuchHost: 'Unable to find the requested server {{ .err }}'
ErrHttpReqNotFound: 'The requested resource {{ .err }} could not be found' ErrHttpReqNotFound: 'The requested resource {{ .err }} could not be found'
ErrImagePullTimeOut: 'Image pull timeout'
ErrContainerNotFound: '{{ .name }} container does not exist' ErrContainerNotFound: '{{ .name }} container does not exist'
ErrContainerMsg: '{{ .name }} container is abnormal. Please check the log on the container page for details' ErrContainerMsg: '{{ .name }} container is abnormal. Please check the log on the container page for details'
ErrAppBackup: '{{ .name }} application backup failed {{ .err }}' ErrAppBackup: '{{ .name }} application backup failed {{ .err }}'
@@ -156,6 +155,7 @@ ExecShellSuccess: 'Script execution successful'
StartUpdateSystemSSL: 'Start updating system certificate' StartUpdateSystemSSL: 'Start updating system certificate'
UpdateSystemSSLSuccess: 'Update system certificate successfully' UpdateSystemSSLSuccess: 'Update system certificate successfully'
ErrWildcardDomain: 'Unable to apply for wildcard domain name certificate in HTTP mode' ErrWildcardDomain: 'Unable to apply for wildcard domain name certificate in HTTP mode'
ErrApplySSLCanNotDelete: "The certificate {{.name}} being applied for cannot be deleted, please try again later."
#mysql #mysql
ErrUserIsExist: 'The current user already exists, please re-enter' ErrUserIsExist: 'The current user already exists, please re-enter'
@@ -173,6 +173,11 @@ ErrObjectInUsed: 'The object is in use and cannot be deleted'
ErrObjectBeDependent: 'This image depends on other images and cannot be deleted' ErrObjectBeDependent: 'This image depends on other images and cannot be deleted'
ErrPortRules: 'Port number does not match, please re-enter!' ErrPortRules: 'Port number does not match, please re-enter!'
ErrPgImagePull: 'Image pull timed out, please configure image acceleration or manually pull the {{ .name }} image and try again' ErrPgImagePull: 'Image pull timed out, please configure image acceleration or manually pull the {{ .name }} image and try again'
PruneHelper: "This cleanup removed {{ .name }} {{ .count }} items, freeing {{ .size }} disk space"
ImageRemoveHelper: "Deleted image {{ .name }}, freeing {{ .size }} disk space"
BuildCache: "Build cache"
Volume: "Storage volume"
Network: "Network"
#runtime #runtime
ErrFileNotExist: '{{ .detail }} file does not exist! Please check the integrity of the source file!' ErrFileNotExist: '{{ .detail }} file does not exist! Please check the integrity of the source file!'
@@ -266,6 +271,7 @@ allow: 'allow'
deny: 'deny' deny: 'deny'
OpenrestyNotFound: 'Openresty is not installed' OpenrestyNotFound: 'Openresty is not installed'
remoteIpIsNull: "IP list is empty" remoteIpIsNull: "IP list is empty"
OpenrestyVersionErr: "Openresty version is too low, please upgrade Openresty to 1.27.1.2-2-2-focal"
#task #task
TaskStart: '{{ .name }} task starts [START]' TaskStart: '{{ .name }} task starts [START]'
@@ -290,6 +296,7 @@ TaskPull: 'Pull'
TaskCommit: 'Commit' TaskCommit: 'Commit'
TaskBuild: 'Build' TaskBuild: 'Build'
TaskPush: 'Push' TaskPush: 'Push'
TaskClean: "Cleanup"
TaskHandle: 'Execute' TaskHandle: 'Execute'
Website: 'Website' Website: 'Website'
App: 'Application' App: 'Application'
+8 -1
View File
@@ -62,7 +62,6 @@ ErrHttpReqTimeOut: 'リクエストがタイムアウトしました {{ .err }}'
ErrHttpReqFailed: 'リクエストが失敗しました {{ .err }}' ErrHttpReqFailed: 'リクエストが失敗しました {{ .err }}'
ErrNoSuchHost: '要求されたサーバー {{ .err }} が見つかりません' ErrNoSuchHost: '要求されたサーバー {{ .err }} が見つかりません'
ErrHttpReqNotFound: '要求されたリソース {{ .err }} が見つかりませんでした' ErrHttpReqNotFound: '要求されたリソース {{ .err }} が見つかりませんでした'
ErrImagePullTimeOut: 'イメージ プル タイムアウト'
ErrContainerNotFound: '{{ .name }} コンテナが存在しません' ErrContainerNotFound: '{{ .name }} コンテナが存在しません'
ErrContainerMsg: '{{ .name }} コンテナが異常です。詳細についてはコンテナページのログを確認してください。' ErrContainerMsg: '{{ .name }} コンテナが異常です。詳細についてはコンテナページのログを確認してください。'
ErrAppBackup: '{{ .name }} アプリケーションのバックアップに失敗しました。エラー {{ .err }}' ErrAppBackup: '{{ .name }} アプリケーションのバックアップに失敗しました。エラー {{ .err }}'
@@ -156,6 +155,7 @@ ExecShellSuccess: 'スクリプトの実行が成功しました'
StartUpdateSystemSSL: 'システム証明書の更新を開始します' StartUpdateSystemSSL: 'システム証明書の更新を開始します'
UpdateSystemSSLSuccess: 'システム証明書を正常に更新しました' UpdateSystemSSLSuccess: 'システム証明書を正常に更新しました'
ErrWildcardDomain: 'HTTP モードでワイルドカード ドメイン名証明書を申請できません' ErrWildcardDomain: 'HTTP モードでワイルドカード ドメイン名証明書を申請できません'
ErrApplySSLCanNotDelete: "申請中の証明書 {{.name}} は削除できません。しばらくしてからもう一度お試しください。"
#mysql #mysql
ErrUserIsExist: '現在のユーザーは既に存在します。再入力してください' ErrUserIsExist: '現在のユーザーは既に存在します。再入力してください'
@@ -173,6 +173,11 @@ ErrObjectInUsed: 'オブジェクトは使用中のため削除できません'
ErrObjectBeDependent: 'このイメージは他のイメージに依存しているため、削除できません' ErrObjectBeDependent: 'このイメージは他のイメージに依存しているため、削除できません'
ErrPortRules: 'ポート番号が一致しません。再入力してください。' ErrPortRules: 'ポート番号が一致しません。再入力してください。'
ErrPgImagePull: 'イメージのプルがタイムアウトしました。イメージのアクセラレーションを設定するか、{{ .name }} イメージを手動でプルして再試行してください' ErrPgImagePull: 'イメージのプルがタイムアウトしました。イメージのアクセラレーションを設定するか、{{ .name }} イメージを手動でプルして再試行してください'
PruneHelper: "今回のクリーンアップで{{ .name }} {{ .count }}個を削除し、{{ .size }}のディスク領域を解放しました"
ImageRemoveHelper: "イメージ{{ .name }}を削除し、{{ .size }}のディスク領域を解放しました"
BuildCache: "ビルドキャッシュ"
Volume: "ストレージボリューム"
Network: "ネットワーク"
#runtime #runtime
ErrFileNotExist: '{{ .detail }} ファイルが存在しません。ソース ファイルの整合性を確認してください。' ErrFileNotExist: '{{ .detail }} ファイルが存在しません。ソース ファイルの整合性を確認してください。'
@@ -266,6 +271,7 @@ allow: '許可'
deny: '拒否' deny: '拒否'
OpenrestyNotFound: 'Openresty がインストールされていません' OpenrestyNotFound: 'Openresty がインストールされていません'
remoteIpIsNull: "IPリストが空です" remoteIpIsNull: "IPリストが空です"
OpenrestyVersionErr: "Openrestyのバージョンが低すぎます。Openrestyを1.27.1.2-2-2-focalにアップグレードしてください"
#task #task
TaskStart: '{{ .name }} タスクが開始されました [START]' TaskStart: '{{ .name }} タスクが開始されました [START]'
@@ -290,6 +296,7 @@ TaskPull: 'プル'
TaskCommit: 'コミット' TaskCommit: 'コミット'
TaskBuild: 'ビルド' TaskBuild: 'ビルド'
TaskPush: 'プッシュ' TaskPush: 'プッシュ'
TaskClean: "クリーンアップ"
TaskHandle: '実行' TaskHandle: '実行'
Website: 'ウェブサイト' Website: 'ウェブサイト'
App: 'アプリケーション' App: 'アプリケーション'
+8 -1
View File
@@ -62,7 +62,6 @@ ErrHttpReqTimeOut: '요청 시간이 초과되었습니다 {{ .err }}'
ErrHttpReqFailed: '요청이 실패했습니다 {{ .err }}' ErrHttpReqFailed: '요청이 실패했습니다 {{ .err }}'
ErrNoSuchHost: '요청한 서버 {{ .err }}을 찾을 수 없습니다' ErrNoSuchHost: '요청한 서버 {{ .err }}을 찾을 수 없습니다'
ErrHttpReqNotFound: '요청한 리소스 {{ .err }}을 찾을 수 없습니다.' ErrHttpReqNotFound: '요청한 리소스 {{ .err }}을 찾을 수 없습니다.'
ErrImagePullTimeOut: '이미지 풀링 시간 초과'
ErrContainerNotFound: '{{ .name }} 컨테이너가 존재하지 않습니다' ErrContainerNotFound: '{{ .name }} 컨테이너가 존재하지 않습니다'
ErrContainerMsg: '{{ .name }} 컨테이너가 비정상입니다. 자세한 내용은 컨테이너 페이지의 로그를 확인하세요.' ErrContainerMsg: '{{ .name }} 컨테이너가 비정상입니다. 자세한 내용은 컨테이너 페이지의 로그를 확인하세요.'
ErrAppBackup: '{{ .name }} 애플리케이션 백업에 실패했습니다 {{ .err }}' ErrAppBackup: '{{ .name }} 애플리케이션 백업에 실패했습니다 {{ .err }}'
@@ -156,6 +155,7 @@ ExecShellSuccess: '스크립트 실행 성공'
StartUpdateSystemSSL: '시스템 인증서 업데이트 시작' StartUpdateSystemSSL: '시스템 인증서 업데이트 시작'
UpdateSystemSSLSuccess: '시스템 인증서 업데이트가 성공적으로 완료되었습니다.' UpdateSystemSSLSuccess: '시스템 인증서 업데이트가 성공적으로 완료되었습니다.'
ErrWildcardDomain: 'HTTP 모드에서 와일드카드 도메인 이름 인증서를 신청할 수 없습니다' ErrWildcardDomain: 'HTTP 모드에서 와일드카드 도메인 이름 인증서를 신청할 수 없습니다'
ErrApplySSLCanNotDelete: "신청 중인 인증서 {{.name}}는 삭제할 수 없습니다. 나중에 다시 시도해 주세요."
#마이SQL #마이SQL
ErrUserIsExist: '현재 사용자가 이미 존재합니다. 다시 입력하세요' ErrUserIsExist: '현재 사용자가 이미 존재합니다. 다시 입력하세요'
@@ -173,6 +173,11 @@ ErrObjectInUsed: '개체가 사용 중이므로 삭제할 수 없습니다'
ErrObjectBeDependent: '이 이미지는 다른 이미지에 의존하므로 삭제할 수 없습니다' ErrObjectBeDependent: '이 이미지는 다른 이미지에 의존하므로 삭제할 수 없습니다'
ErrPortRules: '포트 번호가 일치하지 않습니다. 다시 입력하세요!' ErrPortRules: '포트 번호가 일치하지 않습니다. 다시 입력하세요!'
ErrPgImagePull: '이미지 풀링 시간이 초과되었습니다. 이미지 가속을 구성하거나 {{ .name }} 이미지를 수동으로 풀링한 다음 다시 시도하세요.' ErrPgImagePull: '이미지 풀링 시간이 초과되었습니다. 이미지 가속을 구성하거나 {{ .name }} 이미지를 수동으로 풀링한 다음 다시 시도하세요.'
PruneHelper: "이번 정리에서 {{ .name }} {{ .count }}개를 제거하여 {{ .size }} 디스크 공간을 확보했습니다"
ImageRemoveHelper: "이미지 {{ .name }} 삭제, {{ .size }} 디스크 공간 확보"
BuildCache: "빌드 캐시"
Volume: "스토리지 볼륨"
Network: "네트워크"
#실행 시간 #실행 시간
ErrFileNotExist: '{{ .detail }} 파일이 존재하지 않습니다! 소스 파일의 무결성을 확인하세요!' ErrFileNotExist: '{{ .detail }} 파일이 존재하지 않습니다! 소스 파일의 무결성을 확인하세요!'
@@ -266,6 +271,7 @@ allow: '허용하다'
deny: '거부하다' deny: '거부하다'
OpenrestyNotFound: 'Openresty가 설치되지 않았습니다' OpenrestyNotFound: 'Openresty가 설치되지 않았습니다'
remoteIpIsNull: "IP 목록이 비어 있습니다" remoteIpIsNull: "IP 목록이 비어 있습니다"
OpenrestyVersionErr: "Openresty 버전이 너무 낮습니다. Openresty를 1.27.1.2-2-2-focal로 업그레이드하세요"
#일 #일
TaskStart: '[START] {{ .name }} 작업이 시작됩니다.' TaskStart: '[START] {{ .name }} 작업이 시작됩니다.'
@@ -290,6 +296,7 @@ TaskPull: '당기기'
TaskCommit: '커밋' TaskCommit: '커밋'
TaskBuild: '빌드' TaskBuild: '빌드'
TaskPush: '푸시' TaskPush: '푸시'
TaskClean: "정리"
TaskHandle: '실행' TaskHandle: '실행'
Website: '웹사이트' Website: '웹사이트'
App: '애플리케이션' App: '애플리케이션'
+8 -1
View File
@@ -62,7 +62,6 @@ ErrHttpReqTimeOut: 'Permintaan tamat masa {{ .err }}'
ErrHttpReqFailed: 'Permintaan gagal {{ .err }}' ErrHttpReqFailed: 'Permintaan gagal {{ .err }}'
ErrNoSuchHost: 'Tidak dapat mencari pelayan yang diminta {{ .err }}' ErrNoSuchHost: 'Tidak dapat mencari pelayan yang diminta {{ .err }}'
ErrHttpReqNotFound: 'Sumber yang diminta {{ .err }} tidak dapat ditemui' ErrHttpReqNotFound: 'Sumber yang diminta {{ .err }} tidak dapat ditemui'
ErrImagePullTimeOut: 'Tamat masa tarik imej'
ErrContainerNotFound: 'Bekas {{ .name }} tidak wujud' ErrContainerNotFound: 'Bekas {{ .name }} tidak wujud'
ErrContainerMsg: Bekas '{{ .name }} adalah tidak normal. Sila semak log pada halaman kontena untuk butiran' ErrContainerMsg: Bekas '{{ .name }} adalah tidak normal. Sila semak log pada halaman kontena untuk butiran'
ErrAppBackup: '{{ .name }} sandaran aplikasi gagal {{ .err }}' ErrAppBackup: '{{ .name }} sandaran aplikasi gagal {{ .err }}'
@@ -155,6 +154,7 @@ ExecShellSuccess: 'Pelaksanaan skrip berjaya'
StartUpdateSystemSSL: 'Mula mengemas kini sijil sistem' StartUpdateSystemSSL: 'Mula mengemas kini sijil sistem'
UpdateSystemSSLSuccess: 'Berjaya kemas kini sijil sistem' UpdateSystemSSLSuccess: 'Berjaya kemas kini sijil sistem'
ErrWildcardDomain: 'Tidak dapat memohon sijil nama domain kad bebas dalam mod HTTP' ErrWildcardDomain: 'Tidak dapat memohon sijil nama domain kad bebas dalam mod HTTP'
ErrApplySSLCanNotDelete: "Sertifikat {{.name}} yang sedang diajukan tidak dapat dihapus, sila cuba lagi kemudian."
#mysql #mysql
ErrUserIsExist: 'Pengguna semasa sudah wujud, sila masukkan semula' ErrUserIsExist: 'Pengguna semasa sudah wujud, sila masukkan semula'
@@ -172,6 +172,11 @@ ErrObjectInUsed: 'Objek sedang digunakan dan tidak boleh dipadamkan'
ErrObjectBeDependent: 'Imej ini bergantung pada imej lain dan tidak boleh dipadamkan' ErrObjectBeDependent: 'Imej ini bergantung pada imej lain dan tidak boleh dipadamkan'
ErrPortRules: 'Nombor port tidak sepadan, sila masukkan semula!' ErrPortRules: 'Nombor port tidak sepadan, sila masukkan semula!'
ErrPgImagePull: 'Tarikh imej tamat masa, sila konfigurasikan pecutan imej atau tarik imej {{ .name }} secara manual dan cuba lagi' ErrPgImagePull: 'Tarikh imej tamat masa, sila konfigurasikan pecutan imej atau tarik imej {{ .name }} secara manual dan cuba lagi'
PruneHelper: "Pembersihan ini membuang {{ .name }} {{ .count }} item, membebaskan {{ .size }} ruang cakera"
ImageRemoveHelper: "Padam imej {{ .name }}, membebaskan {{ .size }} ruang cakera"
BuildCache: "Cache binaan"
Volume: "Jilid storan"
Network: "Rangkaian"
#masa berjalan #masa berjalan
ErrFileNotExist: 'Fail {{ .detail }} tidak wujud! Sila semak integriti fail sumber!' ErrFileNotExist: 'Fail {{ .detail }} tidak wujud! Sila semak integriti fail sumber!'
@@ -265,6 +270,7 @@ allow: 'membenarkan'
deny: 'menafikan' deny: 'menafikan'
OpenrestyNotFound: 'Openresty tidak dipasang' OpenrestyNotFound: 'Openresty tidak dipasang'
remoteIpIsNull: "Senarai IP kosong" remoteIpIsNull: "Senarai IP kosong"
OpenrestyVersionErr: "Versi Openresty terlalu rendah, sila naik taraf Openresty ke 1.27.1.2-2-2-focal"
#tugas #tugas
TaskStart: '{{ .name }} Task mula [MULA]' TaskStart: '{{ .name }} Task mula [MULA]'
@@ -289,6 +295,7 @@ TaskPull: 'Tarik'
TaskCommit: 'Komit' TaskCommit: 'Komit'
TaskBuild: 'Bina' TaskBuild: 'Bina'
TaskPush: 'Tolak' TaskPush: 'Tolak'
TaskClean: "Pembersihan"
TaskHandle: 'Laksanakan' TaskHandle: 'Laksanakan'
Website: 'Laman web' Website: 'Laman web'
App: 'Aplikasi' App: 'Aplikasi'
+8 -1
View File
@@ -62,7 +62,6 @@ ErrHttpReqTimeOut: 'Tempo limite da solicitação esgotado {{ .err }}'
ErrHttpReqFailed: 'Falha na solicitação {{ .err }}' ErrHttpReqFailed: 'Falha na solicitação {{ .err }}'
ErrNoSuchHost: 'Não foi possível encontrar o servidor solicitado {{ .err }}' ErrNoSuchHost: 'Não foi possível encontrar o servidor solicitado {{ .err }}'
ErrHttpReqNotFound: 'O recurso solicitado {{ .err }} não pôde ser encontrado' ErrHttpReqNotFound: 'O recurso solicitado {{ .err }} não pôde ser encontrado'
ErrImagePullTimeOut: 'Tempo limite de extração de imagem'
ErrContainerNotFound: '{{ .name }} container não existe' ErrContainerNotFound: '{{ .name }} container não existe'
ErrContainerMsg: 'O contêiner {{ .name }} está anormal. Por favor, verifique o log na página do contêiner para obter detalhes.' ErrContainerMsg: 'O contêiner {{ .name }} está anormal. Por favor, verifique o log na página do contêiner para obter detalhes.'
ErrAppBackup: '{{ .name }} falha no backup do aplicativo {{ .err }}' ErrAppBackup: '{{ .name }} falha no backup do aplicativo {{ .err }}'
@@ -156,6 +155,7 @@ ExecShellSuccess: 'Execução do script bem-sucedida'
StartUpdateSystemSSL: 'Iniciar atualização do certificado do sistema' StartUpdateSystemSSL: 'Iniciar atualização do certificado do sistema'
UpdateSystemSSLSuccess: 'Atualizar certificado do sistema com sucesso' UpdateSystemSSLSuccess: 'Atualizar certificado do sistema com sucesso'
ErrWildcardDomain: 'Não é possível solicitar certificado de nome de domínio curinga no modo HTTP' ErrWildcardDomain: 'Não é possível solicitar certificado de nome de domínio curinga no modo HTTP'
ErrApplySSLCanNotDelete: "O certificado {{.name}} que está sendo solicitado não pode ser excluído, tente novamente mais tarde."
#mysql #mysql
ErrUserIsExist: 'O usuário atual já existe, digite novamente' ErrUserIsExist: 'O usuário atual já existe, digite novamente'
@@ -173,6 +173,11 @@ ErrObjectInUsed: 'O objeto está em uso e não pode ser excluído'
ErrObjectBeDependent: 'Esta imagem depende de outras imagens e não pode ser excluída' ErrObjectBeDependent: 'Esta imagem depende de outras imagens e não pode ser excluída'
ErrPortRules: 'O número da porta não corresponde, digite novamente!' ErrPortRules: 'O número da porta não corresponde, digite novamente!'
ErrPgImagePull: 'Tempo limite para extração de imagem. Configure a aceleração de imagem ou extraia manualmente a imagem {{ .name }} e tente novamente' ErrPgImagePull: 'Tempo limite para extração de imagem. Configure a aceleração de imagem ou extraia manualmente a imagem {{ .name }} e tente novamente'
PruneHelper: "Esta limpeza removeu {{ .name }} {{ .count }} itens, liberando {{ .size }} de espaço em disco"
ImageRemoveHelper: "Excluída a imagem {{ .name }}, liberando {{ .size }} de espaço em disco"
BuildCache: "Cache de construção"
Volume: "Volume de armazenamento"
Network: "Rede"
#tempo de execução #tempo de execução
ErrFileNotExist: 'O arquivo {{ .detail }} não existe! Verifique a integridade do arquivo de origem!' ErrFileNotExist: 'O arquivo {{ .detail }} não existe! Verifique a integridade do arquivo de origem!'
@@ -266,6 +271,7 @@ allow: 'permitir'
deny: 'negar' deny: 'negar'
OpenrestyNotFound: 'Openresty não está instalado' OpenrestyNotFound: 'Openresty não está instalado'
remoteIpIsNull: "A lista de IP está vazia" remoteIpIsNull: "A lista de IP está vazia"
OpenrestyVersionErr: "A versão do Openresty é muito baixa, por favor atualize o Openresty para 1.27.1.2-2-2-focal"
#tarefa #tarefa
TaskStart: '{{ .name }} A tarefa inicia [START]' TaskStart: '{{ .name }} A tarefa inicia [START]'
@@ -290,6 +296,7 @@ TaskPull: 'Puxar'
TaskCommit: 'Commit' TaskCommit: 'Commit'
TaskBuild: 'Construir' TaskBuild: 'Construir'
TaskPush: 'Empurrar' TaskPush: 'Empurrar'
TaskClean: "Limpeza"
TaskHandle: 'Executar' TaskHandle: 'Executar'
Website: 'Site' Website: 'Site'
App: 'Aplicativo' App: 'Aplicativo'
+8 -1
View File
@@ -62,7 +62,6 @@ ErrHttpReqTimeOut: 'Истекло время ожидания запроса {{
ErrHttpReqFailed: 'Запрос не выполнен {{ .err }}' ErrHttpReqFailed: 'Запрос не выполнен {{ .err }}'
ErrNoSuchHost: 'Не удалось найти запрошенный сервер {{ .err }}' ErrNoSuchHost: 'Не удалось найти запрошенный сервер {{ .err }}'
ErrHttpReqNotFound: 'Запрошенный ресурс {{ .err }} не найден' ErrHttpReqNotFound: 'Запрошенный ресурс {{ .err }} не найден'
ErrImagePullTimeOut: 'Истекло время ожидания извлечения изображения'
ErrContainerNotFound: 'Контейнер {{ .name }} не существует' ErrContainerNotFound: 'Контейнер {{ .name }} не существует'
ErrContainerMsg: 'Контейнер {{ .name }} ненормален. Подробности смотрите в журнале на странице контейнера.' ErrContainerMsg: 'Контейнер {{ .name }} ненормален. Подробности смотрите в журнале на странице контейнера.'
ErrAppBackup: '{{ .name }} резервное копирование приложения не удалось {{ .err }}' ErrAppBackup: '{{ .name }} резервное копирование приложения не удалось {{ .err }}'
@@ -156,6 +155,7 @@ ExecShellSuccess: 'Скрипт выполнен успешно'
StartUpdateSystemSSL: 'Начать обновление системного сертификата' StartUpdateSystemSSL: 'Начать обновление системного сертификата'
UpdateSystemSSLSuccess: 'Сертификат системы обновлен успешно' UpdateSystemSSLSuccess: 'Сертификат системы обновлен успешно'
ErrWildcardDomain: 'Невозможно подать заявку на получение подстановочного сертификата доменного имени в режиме HTTP' ErrWildcardDomain: 'Невозможно подать заявку на получение подстановочного сертификата доменного имени в режиме HTTP'
ErrApplySSLCanNotDelete: "Нельзя удалить сертификат {{.name}}, который находится в процессе выпуска. Пожалуйста, попробуйте позже."
#mysql #mysql
ErrUserIsExist: 'Текущий пользователь уже существует, пожалуйста, введите еще раз' ErrUserIsExist: 'Текущий пользователь уже существует, пожалуйста, введите еще раз'
@@ -173,6 +173,11 @@ ErrObjectInUsed: 'Объект используется и не может бы
ErrObjectBeDependent: 'Это изображение зависит от других изображений и не может быть удалено' ErrObjectBeDependent: 'Это изображение зависит от других изображений и не может быть удалено'
ErrPortRules: 'Номер порта не совпадает, введите заново!' ErrPortRules: 'Номер порта не совпадает, введите заново!'
ErrPgImagePull: 'Время извлечения изображения истекло. Настройте ускорение изображения или вручную извлеките изображение {{ .name }} и повторите попытку' ErrPgImagePull: 'Время извлечения изображения истекло. Настройте ускорение изображения или вручную извлеките изображение {{ .name }} и повторите попытку'
PruneHelper: "Очистка удалила {{ .name }} в количестве {{ .count }}, освободив {{ .size }} дискового пространства"
ImageRemoveHelper: "Удалён образ {{ .name }}, освобождено {{ .size }} дискового пространства"
BuildCache: "Кэш сборки"
Volume: "Том хранилища"
Network: "Сеть"
#время выполнения #время выполнения
ErrFileNotExist: 'Файл {{ .detail }} не существует! Проверьте целостность исходного файла!' ErrFileNotExist: 'Файл {{ .detail }} не существует! Проверьте целостность исходного файла!'
@@ -266,6 +271,7 @@ allow: 'разрешить'
deny: 'отрицать' deny: 'отрицать'
OpenrestyNotFound: 'Openresty не установлен' OpenrestyNotFound: 'Openresty не установлен'
remoteIpIsNull: "Список IP пуст" remoteIpIsNull: "Список IP пуст"
OpenrestyVersionErr: "Версия Openresty слишком низкая, пожалуйста, обновите Openresty до 1.27.1.2-2-2-focal"
#задача #задача
TaskStart: '{{ .name }} Задача начинается [START]' TaskStart: '{{ .name }} Задача начинается [START]'
@@ -290,6 +296,7 @@ TaskPull: 'Вытянуть'
TaskCommit: 'Kоммит' TaskCommit: 'Kоммит'
ЗадачаСборка: 'Сборка' ЗадачаСборка: 'Сборка'
TaskPush: 'Push' TaskPush: 'Push'
TaskClean: "Очистка"
TaskHandle: 'Выполнить' TaskHandle: 'Выполнить'
Website: 'Be6-сайт' Website: 'Be6-сайт'
App: 'Приложение' App: 'Приложение'
+8 -1
View File
@@ -62,7 +62,6 @@ ErrHttpReqTimeOut: 'İstek zaman aşımı {{ .err }}'
ErrHttpReqFailed: 'İstek başarısız {{ .err }}' ErrHttpReqFailed: 'İstek başarısız {{ .err }}'
ErrNoSuchHost: 'İstenen sunucu bulunamıyor {{ .err }}' ErrNoSuchHost: 'İstenen sunucu bulunamıyor {{ .err }}'
ErrHttpReqNotFound: 'İstenen kaynak {{ .err }} bulunamadı' ErrHttpReqNotFound: 'İstenen kaynak {{ .err }} bulunamadı'
ErrImagePullTimeOut: 'Image çekme zaman aşımı'
ErrContainerNotFound: '{{ .name }} konteyneri mevcut değil' ErrContainerNotFound: '{{ .name }} konteyneri mevcut değil'
ErrContainerMsg: '{{ .name }} konteyneri anormal. Lütfen ayrıntılar için konteyner sayfasındaki günlüğü kontrol edin' ErrContainerMsg: '{{ .name }} konteyneri anormal. Lütfen ayrıntılar için konteyner sayfasındaki günlüğü kontrol edin'
ErrAppBackup: '{{ .name }} uygulama yedeklemesi başarısız {{ .err }}' ErrAppBackup: '{{ .name }} uygulama yedeklemesi başarısız {{ .err }}'
@@ -156,6 +155,7 @@ ExecShellSuccess: 'Betik yürütme başarılı'
StartUpdateSystemSSL: 'Sistem sertifikası güncellemeyi başlat' StartUpdateSystemSSL: 'Sistem sertifikası güncellemeyi başlat'
UpdateSystemSSLSuccess: 'Sistem sertifikası güncelleme başarılı' UpdateSystemSSLSuccess: 'Sistem sertifikası güncelleme başarılı'
ErrWildcardDomain: 'HTTP modunda joker alan adı sertifikası başvurusu yapılamıyor' ErrWildcardDomain: 'HTTP modunda joker alan adı sertifikası başvurusu yapılamıyor'
ErrApplySSLCanNotDelete: "İşlemde olan sertifika {{.name}} silinemez, lütfen daha sonra tekrar deneyin."
#mysql #mysql
ErrUserIsExist: 'Mevcut kullanıcı zaten mevcut, lütfen yeniden girin' ErrUserIsExist: 'Mevcut kullanıcı zaten mevcut, lütfen yeniden girin'
@@ -173,6 +173,11 @@ ErrObjectInUsed: 'Nesne kullanımda ve silinemez'
ErrObjectBeDependent: 'Bu image diğer imagelere bağlı ve silinemez' ErrObjectBeDependent: 'Bu image diğer imagelere bağlı ve silinemez'
ErrPortRules: 'Port numarası eşleşmiyor, lütfen yeniden girin!' ErrPortRules: 'Port numarası eşleşmiyor, lütfen yeniden girin!'
ErrPgImagePull: 'Image çekme zaman aşımı, lütfen image hızlandırma yapılandırın veya manuel olarak {{ .name }} imageını çekin ve tekrar deneyin' ErrPgImagePull: 'Image çekme zaman aşımı, lütfen image hızlandırma yapılandırın veya manuel olarak {{ .name }} imageını çekin ve tekrar deneyin'
PruneHelper: "Bu temizleme {{ .name }} {{ .count }} öğe kaldırdı, {{ .size }} disk alanı boşalttı"
ImageRemoveHelper: "{{ .name }} imajı silindi, {{ .size }} disk alanı boşalttı"
BuildCache: "Derleme önbelleği"
Volume: "Depolama hacmi"
Network: "Ağ"
#runtime #runtime
ErrFileNotExist: '{{ .detail }} dosyası mevcut değil! Lütfen kaynak dosyanın bütünlüğünü kontrol edin!' ErrFileNotExist: '{{ .detail }} dosyası mevcut değil! Lütfen kaynak dosyanın bütünlüğünü kontrol edin!'
@@ -264,6 +269,7 @@ allow: 'izin ver'
deny: 'reddet' deny: 'reddet'
OpenrestyNotFound: 'Openresty yüklü değil' OpenrestyNotFound: 'Openresty yüklü değil'
remoteIpIsNull: "IP listesi boş" remoteIpIsNull: "IP listesi boş"
OpenrestyVersionErr: "Openresty sürümü çok düşük, lütfen Openresty'i 1.27.1.2-2-2-focal olarak güncelleyin"
#task #task
TaskStart: '{{ .name }} görevi başlıyor [BAŞLAT]' TaskStart: '{{ .name }} görevi başlıyor [BAŞLAT]'
@@ -288,6 +294,7 @@ TaskPull: 'Çek'
TaskCommit: 'işleme' TaskCommit: 'işleme'
TaskBuild: 'Yapı' TaskBuild: 'Yapı'
TaskPush: 'Gönder' TaskPush: 'Gönder'
TaskClean: "Temizleme"
TaskHandle: 'Yürüt' TaskHandle: 'Yürüt'
Website: 'Web Sitesi' Website: 'Web Sitesi'
App: 'Uygulama' App: 'Uygulama'
+8 -1
View File
@@ -62,7 +62,6 @@ ErrHttpReqTimeOut: '請求逾時{{ .err }}'
ErrHttpReqFailed: '請求失敗{{ .err }}' ErrHttpReqFailed: '請求失敗{{ .err }}'
ErrNoSuchHost: '無法找到請求的伺服器{{ .err }}' ErrNoSuchHost: '無法找到請求的伺服器{{ .err }}'
ErrHttpReqNotFound: '無法找到要求的資源{{ .err }}' ErrHttpReqNotFound: '無法找到要求的資源{{ .err }}'
ErrImagePullTimeOut: '鏡像拉取逾時'
ErrContainerNotFound: '{{ .name }} 容器不存在' ErrContainerNotFound: '{{ .name }} 容器不存在'
ErrContainerMsg: '{{ .name }} 容器異常,請在容器頁面上查看日誌' ErrContainerMsg: '{{ .name }} 容器異常,請在容器頁面上查看日誌'
ErrAppBackup: '{{ .name }} 應用備份失敗 {{ .err }}' ErrAppBackup: '{{ .name }} 應用備份失敗 {{ .err }}'
@@ -155,6 +154,7 @@ ExecShellSuccess: '腳本執行成功'
StartUpdateSystemSSL: '開始更新系統憑證' StartUpdateSystemSSL: '開始更新系統憑證'
UpdateSystemSSLSuccess: '更新系統憑證成功' UpdateSystemSSLSuccess: '更新系統憑證成功'
ErrWildcardDomain: 'HTTP 模式無法申請泛網域憑證' ErrWildcardDomain: 'HTTP 模式無法申請泛網域憑證'
ErrApplySSLCanNotDelete: "正在申請的證書 {{.name}} 無法刪除,請稍後再試"
#mysql #mysql
ErrUserIsExist: '目前使用者已存在,請重新輸入' ErrUserIsExist: '目前使用者已存在,請重新輸入'
@@ -172,6 +172,11 @@ ErrObjectInUsed: '該物件正被使用,無法刪除'
ErrObjectBeDependent: '此鏡像依賴其他鏡像,無法刪除' ErrObjectBeDependent: '此鏡像依賴其他鏡像,無法刪除'
ErrPortRules: '連接埠數目不匹配,請重新輸入!' ErrPortRules: '連接埠數目不匹配,請重新輸入!'
ErrPgImagePull: '鏡像拉取逾時,請配置鏡像加速或手動拉取{{ .name }} 鏡像後重試' ErrPgImagePull: '鏡像拉取逾時,請配置鏡像加速或手動拉取{{ .name }} 鏡像後重試'
PruneHelper: "本次清理 {{ .name }} {{ .count }} 個,釋放磁盤空間 {{ .size }}"
ImageRemoveHelper: "刪除鏡像 {{ .name }} ,釋放磁盤空間 {{ .size }}"
BuildCache: "構建緩存"
Volume: "存儲卷"
Network: "網絡"
#runtime #runtime
ErrFileNotExist: '{{ .detail }} 檔案不存在!請檢查來源檔案完整性!' ErrFileNotExist: '{{ .detail }} 檔案不存在!請檢查來源檔案完整性!'
@@ -265,6 +270,7 @@ allow: '允許'
deny: '禁止' deny: '禁止'
OpenrestyNotFound: 'Openresty 未安裝' OpenrestyNotFound: 'Openresty 未安裝'
remoteIpIsNull: "IP 列表為空" remoteIpIsNull: "IP 列表為空"
OpenrestyVersionErr: "Openresty 版本過低,請升級 Openresty 至 1.27.1.2-2-2-focal"
#task #task
TaskStart: '{{ .name }} 任務開始[START]' TaskStart: '{{ .name }} 任務開始[START]'
@@ -289,6 +295,7 @@ TaskPull: '拉取'
TaskCommit: '制作' TaskCommit: '制作'
TaskBuild: '建置' TaskBuild: '建置'
TaskPush: '推送' TaskPush: '推送'
TaskClean: "清理"
TaskHandle: '執行' TaskHandle: '執行'
Website: '網站' Website: '網站'
App: '應用程式' App: '應用程式'
+8 -1
View File
@@ -62,7 +62,6 @@ ErrHttpReqTimeOut: "请求超时 {{ .err }}"
ErrHttpReqFailed: "请求失败 {{ .err }}" ErrHttpReqFailed: "请求失败 {{ .err }}"
ErrNoSuchHost: "无法找到请求的服务器 {{ .err }}" ErrNoSuchHost: "无法找到请求的服务器 {{ .err }}"
ErrHttpReqNotFound: "无法找到请求的资源 {{ .err }}" ErrHttpReqNotFound: "无法找到请求的资源 {{ .err }}"
ErrImagePullTimeOut: '镜像拉取超时'
ErrContainerNotFound: '{{ .name }} 容器不存在' ErrContainerNotFound: '{{ .name }} 容器不存在'
ErrContainerMsg: '{{ .name }} 容器异常,具体请在容器页面查看日志' ErrContainerMsg: '{{ .name }} 容器异常,具体请在容器页面查看日志'
ErrAppBackup: '{{ .name }} 应用备份失败 {{ .err }}' ErrAppBackup: '{{ .name }} 应用备份失败 {{ .err }}'
@@ -155,6 +154,7 @@ ExecShellSuccess: "脚本执行成功"
StartUpdateSystemSSL: "开始更新系统证书" StartUpdateSystemSSL: "开始更新系统证书"
UpdateSystemSSLSuccess: "更新系统证书成功" UpdateSystemSSLSuccess: "更新系统证书成功"
ErrWildcardDomain: "HTTP 模式无法申请泛域名证书" ErrWildcardDomain: "HTTP 模式无法申请泛域名证书"
ErrApplySSLCanNotDelete: "正在申请的证书{{.name}}无法删除,请稍后再试"
#mysql #mysql
ErrUserIsExist: "当前用户已存在,请重新输入" ErrUserIsExist: "当前用户已存在,请重新输入"
@@ -172,6 +172,11 @@ ErrObjectInUsed: "该对象正被使用,无法删除"
ErrObjectBeDependent: "该镜像依赖于其他镜像,无法删除" ErrObjectBeDependent: "该镜像依赖于其他镜像,无法删除"
ErrPortRules: "端口数目不匹配,请重新输入!" ErrPortRules: "端口数目不匹配,请重新输入!"
ErrPgImagePull: "镜像拉取超时,请配置镜像加速或手动拉取 {{ .name }} 镜像后重试" ErrPgImagePull: "镜像拉取超时,请配置镜像加速或手动拉取 {{ .name }} 镜像后重试"
PruneHelper: "本次清理 {{ .name }} {{ .count }} 个,释放磁盘空间 {{ .size }}"
ImageRemoveHelper: "删除镜像 {{ .name }} ,释放磁盘空间 {{ .size }}"
BuildCache: "构建缓存"
Volume: "存储卷"
Network: "网络"
#runtime #runtime
ErrFileNotExist: "{{ .detail }} 文件不存在!请检查源文件完整性!" ErrFileNotExist: "{{ .detail }} 文件不存在!请检查源文件完整性!"
@@ -265,6 +270,7 @@ allow: "允许"
deny: "禁止" deny: "禁止"
OpenrestyNotFound: "Openresty 未安装" OpenrestyNotFound: "Openresty 未安装"
remoteIpIsNull: "IP 列表为空" remoteIpIsNull: "IP 列表为空"
OpenrestyVersionErr: "Openresty 版本过低,请升级 Openresty 至 1.27.1.2-2-2-focal"
#task #task
TaskStart: "{{ .name }} 任务开始 [START]" TaskStart: "{{ .name }} 任务开始 [START]"
@@ -289,6 +295,7 @@ TaskPull: "拉取"
TaskCommit: "制作" TaskCommit: "制作"
TaskBuild: "构建" TaskBuild: "构建"
TaskPush: "推送" TaskPush: "推送"
TaskClean: "清理"
TaskHandle: "执行" TaskHandle: "执行"
Website: "网站" Website: "网站"
App: "应用" App: "应用"
+2
View File
@@ -33,6 +33,8 @@ func InitAgentDB() {
migrations.InitAlertConfig, migrations.InitAlertConfig,
migrations.AddMethodToAlertLog, migrations.AddMethodToAlertLog,
migrations.AddMethodToAlertTask, migrations.AddMethodToAlertTask,
migrations.UpdateMcpServer,
migrations.InitCronjobGroup,
}) })
if err := m.Migrate(); err != nil { if err := m.Migrate(); err != nil {
global.LOG.Error(err) global.LOG.Error(err)
+28 -1
View File
@@ -19,7 +19,7 @@ import (
) )
var AddTable = &gormigrate.Migration{ var AddTable = &gormigrate.Migration{
ID: "20250507-add-table", ID: "20250805-add-table",
Migrate: func(tx *gorm.DB) error { Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate( return tx.AutoMigrate(
&model.AppDetail{}, &model.AppDetail{},
@@ -63,6 +63,7 @@ var AddTable = &gormigrate.Migration{
&model.Group{}, &model.Group{},
&model.AppIgnoreUpgrade{}, &model.AppIgnoreUpgrade{},
&model.McpServer{}, &model.McpServer{},
&model.RootCert{},
) )
}, },
} }
@@ -419,3 +420,29 @@ var AddMethodToAlertTask = &gormigrate.Migration{
return nil return nil
}, },
} }
var UpdateMcpServer = &gormigrate.Migration{
ID: "20250729-update-mcp-server",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.McpServer{}); err != nil {
return err
}
if err := tx.Model(&model.McpServer{}).Where("1=1").Update("output_transport", "sse").Error; err != nil {
return err
}
return nil
},
}
var InitCronjobGroup = &gormigrate.Migration{
ID: "20250805-init-cronjob-group",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.Cronjob{}); err != nil {
return err
}
if err := tx.Model(&model.Cronjob{}).Where("1=1").Updates(map[string]interface{}{"group_id": 0}).Error; err != nil {
return err
}
return nil
},
}
+20 -8
View File
@@ -1,13 +1,15 @@
package middleware package middleware
import ( import (
"errors"
"fmt" "fmt"
"net"
"net/http"
"strings" "strings"
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper" "github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd" "github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@@ -17,13 +19,8 @@ func Certificate() gin.HandlerFunc {
c.Next() c.Next()
return return
} }
if !c.Request.TLS.HandshakeComplete || len(c.Request.TLS.PeerCertificates) == 0 { if !xpack.ValidateCertificate(c) {
helper.InternalServer(c, errors.New("no such tls peer certificates")) CloseDirectly(c)
return
}
cert := c.Request.TLS.PeerCertificates[0]
if cert.Subject.CommonName != "panel_client" {
helper.InternalServer(c, fmt.Errorf("err certificate"))
return return
} }
conn := c.Request.Header.Get("Connection") conn := c.Request.Header.Get("Connection")
@@ -40,3 +37,18 @@ func Certificate() gin.HandlerFunc {
c.Next() c.Next()
} }
} }
func CloseDirectly(c *gin.Context) {
hijacker, ok := c.Writer.(http.Hijacker)
if !ok {
c.AbortWithStatus(http.StatusForbidden)
return
}
conn, _, err := hijacker.Hijack()
if err != nil {
c.AbortWithStatus(http.StatusForbidden)
return
}
_ = conn.(*net.TCPConn).SetLinger(0)
conn.Close()
}
+3
View File
@@ -13,10 +13,13 @@ func (s *CronjobRouter) InitRouter(Router *gin.RouterGroup) {
{ {
cmdRouter.POST("", baseApi.CreateCronjob) cmdRouter.POST("", baseApi.CreateCronjob)
cmdRouter.POST("/next", baseApi.LoadNextHandle) cmdRouter.POST("/next", baseApi.LoadNextHandle)
cmdRouter.POST("/import", baseApi.ImportCronjob)
cmdRouter.POST("/export", baseApi.ExportCronjob)
cmdRouter.POST("/load/info", baseApi.LoadCronjobInfo) cmdRouter.POST("/load/info", baseApi.LoadCronjobInfo)
cmdRouter.GET("/script/options", baseApi.LoadScriptOptions) cmdRouter.GET("/script/options", baseApi.LoadScriptOptions)
cmdRouter.POST("/del", baseApi.DeleteCronjob) cmdRouter.POST("/del", baseApi.DeleteCronjob)
cmdRouter.POST("/update", baseApi.UpdateCronjob) cmdRouter.POST("/update", baseApi.UpdateCronjob)
cmdRouter.POST("/group/update", baseApi.UpdateCronjobGroup)
cmdRouter.POST("/status", baseApi.UpdateCronjobStatus) cmdRouter.POST("/status", baseApi.UpdateCronjobStatus)
cmdRouter.POST("/handle", baseApi.HandleOnce) cmdRouter.POST("/handle", baseApi.HandleOnce)
cmdRouter.POST("/download", baseApi.TargetDownload) cmdRouter.POST("/download", baseApi.TargetDownload)
+5 -2
View File
@@ -32,12 +32,15 @@ func (s *HostRouter) InitRouter(Router *gin.RouterGroup) {
hostRouter.GET("/ssh/conf", baseApi.LoadSSHConf) hostRouter.GET("/ssh/conf", baseApi.LoadSSHConf)
hostRouter.POST("/ssh/search", baseApi.GetSSHInfo) hostRouter.POST("/ssh/search", baseApi.GetSSHInfo)
hostRouter.POST("/ssh/update", baseApi.UpdateSSH) hostRouter.POST("/ssh/update", baseApi.UpdateSSH)
hostRouter.POST("/ssh/generate", baseApi.GenerateSSH)
hostRouter.POST("/ssh/secret", baseApi.LoadSSHSecret)
hostRouter.POST("/ssh/log", baseApi.LoadSSHLogs) hostRouter.POST("/ssh/log", baseApi.LoadSSHLogs)
hostRouter.POST("/ssh/conffile/update", baseApi.UpdateSSHByfile) hostRouter.POST("/ssh/conffile/update", baseApi.UpdateSSHByfile)
hostRouter.POST("/ssh/operate", baseApi.OperateSSH) hostRouter.POST("/ssh/operate", baseApi.OperateSSH)
hostRouter.POST("/ssh/cert", baseApi.CreateRootCert)
hostRouter.POST("/ssh/cert/sync", baseApi.SyncRootCert)
hostRouter.POST("/ssh/cert/search", baseApi.SearchRootCert)
hostRouter.POST("/ssh/cert/delete", baseApi.DeleteRootCert)
hostRouter.POST("/tool", baseApi.GetToolStatus) hostRouter.POST("/tool", baseApi.GetToolStatus)
hostRouter.POST("/tool/init", baseApi.InitToolConfig) hostRouter.POST("/tool/init", baseApi.InitToolConfig)
hostRouter.POST("/tool/operate", baseApi.OperateTool) hostRouter.POST("/tool/operate", baseApi.OperateTool)
+2
View File
@@ -21,5 +21,7 @@ func (a *NginxRouter) InitRouter(Router *gin.RouterGroup) {
groupRouter.POST("/build", baseApi.BuildNginx) groupRouter.POST("/build", baseApi.BuildNginx)
groupRouter.POST("/modules/update", baseApi.UpdateNginxModule) groupRouter.POST("/modules/update", baseApi.UpdateNginxModule)
groupRouter.GET("/modules", baseApi.GetNginxModules) groupRouter.GET("/modules", baseApi.GetNginxModules)
groupRouter.POST("/https", baseApi.OperateDefaultHTTPs)
groupRouter.GET("/https", baseApi.GetDefaultHTTPsStatus)
} }
} }
+10 -1
View File
@@ -2,6 +2,7 @@ package server
import ( import (
"crypto/tls" "crypto/tls"
"crypto/x509"
"fmt" "fmt"
"net" "net"
"net/http" "net/http"
@@ -78,9 +79,17 @@ func Start() {
fmt.Printf("failed to load X.509 key pair: %s\n", err) fmt.Printf("failed to load X.509 key pair: %s\n", err)
return return
} }
server.TLSConfig = &tls.Config{ server.TLSConfig = &tls.Config{
Certificates: []tls.Certificate{tlsCert}, Certificates: []tls.Certificate{tlsCert},
ClientAuth: tls.RequireAnyClientCert, ClientAuth: tls.RequireAndVerifyClientCert,
}
caItem, _ := settingRepo.GetValueByKey("RootCrt")
if len(caItem) != 0 {
caCertPool := x509.NewCertPool()
rootCrt, _ := encrypt.StringDecrypt(caItem)
caCertPool.AppendCertsFromPEM([]byte(rootCrt))
server.TLSConfig.ClientCAs = caCertPool
} }
business.Init() business.Init()
global.LOG.Infof("listen at https://0.0.0.0:%s", global.CONF.Base.Port) global.LOG.Infof("listen at https://0.0.0.0:%s", global.CONF.Base.Port)
+12
View File
@@ -353,3 +353,15 @@ func logProcess(progress map[string]interface{}, task *task.Task) {
} }
_ = setLog(id, progressStr, task) _ = setLog(id, progressStr, task)
} }
func PullImage(imageName string) error {
cli, err := NewDockerClient()
if err != nil {
return err
}
defer cli.Close()
if _, err := cli.ImagePull(context.Background(), imageName, image.PullOptions{}); err != nil {
return err
}
return nil
}
+3 -3
View File
@@ -132,7 +132,7 @@ func isValidHeader(key, value string) bool {
func sendWithSSL(config SMTPConfig, addr string, toList []string, msg string, transport *http.Transport) error { func sendWithSSL(config SMTPConfig, addr string, toList []string, msg string, transport *http.Transport) error {
var err error var err error
var conn net.Conn var conn net.Conn
if transport != nil { if transport != nil && transport.DialContext != nil {
conn, err = transport.DialContext(context.Background(), "tcp", addr) conn, err = transport.DialContext(context.Background(), "tcp", addr)
} else { } else {
conn, err = net.Dial("tcp", addr) conn, err = net.Dial("tcp", addr)
@@ -163,7 +163,7 @@ func sendWithSSL(config SMTPConfig, addr string, toList []string, msg string, tr
func sendWithStartTLS(config SMTPConfig, addr string, toList []string, msg string, transport *http.Transport) error { func sendWithStartTLS(config SMTPConfig, addr string, toList []string, msg string, transport *http.Transport) error {
var err error var err error
var conn net.Conn var conn net.Conn
if transport != nil { if transport != nil && transport.DialContext != nil {
conn, err = transport.DialContext(context.Background(), "tcp", addr) conn, err = transport.DialContext(context.Background(), "tcp", addr)
} else { } else {
conn, err = net.Dial("tcp", addr) conn, err = net.Dial("tcp", addr)
@@ -192,7 +192,7 @@ func sendWithStartTLS(config SMTPConfig, addr string, toList []string, msg strin
func sendPlaintext(config SMTPConfig, addr string, toList []string, msg string, transport *http.Transport) error { func sendPlaintext(config SMTPConfig, addr string, toList []string, msg string, transport *http.Transport) error {
var err error var err error
var conn net.Conn var conn net.Conn
if transport != nil { if transport != nil && transport.DialContext != nil {
conn, err = transport.DialContext(context.Background(), "tcp", addr) conn, err = transport.DialContext(context.Background(), "tcp", addr)
} else { } else {
conn, err = net.Dial("tcp", addr) conn, err = net.Dial("tcp", addr)
+60
View File
@@ -101,6 +101,66 @@ func countLines(path string) (int, error) {
} }
} }
func TailFromEnd(filename string, lines int) ([]string, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
stat, err := file.Stat()
if err != nil {
return nil, err
}
fileSize := stat.Size()
bufSize := int64(4096)
var result []string
var leftover string
for offset := fileSize; offset > 0 && len(result) < lines; {
readSize := bufSize
if offset < bufSize {
readSize = offset
}
offset -= readSize
buf := make([]byte, readSize)
_, err := file.ReadAt(buf, offset)
if err != nil && err != io.EOF {
return nil, err
}
data := string(buf) + leftover
linesInChunk := strings.Split(data, "\n")
if offset > 0 {
leftover = linesInChunk[0]
linesInChunk = linesInChunk[1:]
} else {
leftover = ""
}
for i := len(linesInChunk) - 1; i >= 0; i-- {
if len(result) < lines {
if !(i == len(linesInChunk)-1 && linesInChunk[i] == "" && len(result) == 0) {
result = append([]string{linesInChunk[i]}, result...)
}
}
}
}
if leftover != "" && len(result) < lines {
result = append([]string{leftover}, result...)
}
if len(result) > lines {
result = result[len(result)-lines:]
}
return result, nil
}
func ReadFileByLine(filename string, page, pageSize int, latest bool) (lines []string, isEndOfFile bool, total int, err error) { func ReadFileByLine(filename string, page, pageSize int, latest bool) (lines []string, isEndOfFile bool, total int, err error) {
if !NewFileOp().Stat(filename) { if !NewFileOp().Stat(filename) {
return return
+3 -13
View File
@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"regexp" "regexp"
"strings" "strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/model" "github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/global"
@@ -38,20 +39,9 @@ func NewIptables() (*Iptables, error) {
return iptables, nil return iptables, nil
} }
func (iptables *Iptables) run(rule string) error {
stdout, err := cmd.RunDefaultWithStdoutBashCf("%s iptables -t nat %s", iptables.CmdStr, rule)
if err != nil {
return fmt.Errorf("%s, %s", err, stdout)
}
if stdout != "" {
return fmt.Errorf("iptables error: %s", stdout)
}
return nil
}
func (iptables *Iptables) outf(tab, rule string, a ...any) (stdout string, err error) { func (iptables *Iptables) outf(tab, rule string, a ...any) (stdout string, err error) {
stdout, err = cmd.RunDefaultWithStdoutBashCf("%s iptables -t %s %s", iptables.CmdStr, tab, fmt.Sprintf(rule, a...)) cmdMgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1(), cmd.WithTimeout(20*time.Second))
stdout, err = cmdMgr.RunWithStdoutBashCf("%s iptables -t %s %s", iptables.CmdStr, tab, fmt.Sprintf(rule, a...))
if err != nil && stdout != "" { if err != nil && stdout != "" {
global.LOG.Errorf("iptables failed, err: %s", stdout) global.LOG.Errorf("iptables failed, err: %s", stdout)
} }
+4 -1
View File
@@ -5,6 +5,7 @@ import (
"strings" "strings"
"github.com/1Panel-dev/1Panel/agent/buserr" "github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd" "github.com/1Panel-dev/1Panel/agent/utils/cmd"
) )
@@ -99,7 +100,9 @@ func (f *Ufw) ListPort() ([]FireInfo, error) {
} }
func (f *Ufw) ListForward() ([]FireInfo, error) { func (f *Ufw) ListForward() ([]FireInfo, error) {
_ = f.EnableForward() if err := f.EnableForward(); err != nil {
global.LOG.Errorf("init port forward failed, err: %v", err)
}
iptables, err := NewIptables() iptables, err := NewIptables()
if err != nil { if err != nil {
return nil, err return nil, err
+13 -3
View File
@@ -234,7 +234,10 @@ func (r *Local) Backup(info BackupInfo) error {
dumpCmd = "mariadb-dump" dumpCmd = "mariadb-dump"
} }
global.LOG.Infof("start to %s | gzip > %s.gzip", dumpCmd, info.TargetDir+"/"+info.FileName) global.LOG.Infof("start to %s | gzip > %s.gzip", dumpCmd, info.TargetDir+"/"+info.FileName)
cmd := exec.Command("docker", "exec", r.ContainerName, dumpCmd, "--routines", "-uroot", "-p"+r.Password, "--default-character-set="+info.Format, info.Name)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(info.Timeout*uint(time.Second)))
defer cancel()
cmd := exec.CommandContext(ctx, "docker", "exec", r.ContainerName, dumpCmd, "--routines", "-uroot", "-p"+r.Password, "--default-character-set="+info.Format, info.Name)
var stderr bytes.Buffer var stderr bytes.Buffer
cmd.Stderr = &stderr cmd.Stderr = &stderr
@@ -253,7 +256,14 @@ func (r *Local) Backup(info BackupInfo) error {
func (r *Local) Recover(info RecoverInfo) error { func (r *Local) Recover(info RecoverInfo) error {
fi, _ := os.Open(info.SourceFile) fi, _ := os.Open(info.SourceFile)
defer fi.Close() defer fi.Close()
cmd := exec.Command("docker", "exec", "-i", r.ContainerName, r.Type, "-uroot", "-p"+r.Password, "--default-character-set="+info.Format, info.Name) mysqlCli := r.Type
if mysqlCli == "mysql-cluster" {
mysqlCli = "mysql"
}
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(info.Timeout*uint(time.Second)))
defer cancel()
cmd := exec.CommandContext(ctx, "docker", "exec", "-i", r.ContainerName, mysqlCli, "-uroot", "-p"+r.Password, "--default-character-set="+info.Format, info.Name)
if strings.HasSuffix(info.SourceFile, ".gz") { if strings.HasSuffix(info.SourceFile, ".gz") {
gzipFile, err := os.Open(info.SourceFile) gzipFile, err := os.Open(info.SourceFile)
if err != nil { if err != nil {
@@ -271,7 +281,7 @@ func (r *Local) Recover(info RecoverInfo) error {
} }
stdout, err := cmd.CombinedOutput() stdout, err := cmd.CombinedOutput()
stdStr := strings.ReplaceAll(string(stdout), "mysql: [Warning] Using a password on the command line interface can be insecure.\n", "") 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 errors.New(stdStr) return errors.New(stdStr)
} }
+15 -2
View File
@@ -136,7 +136,15 @@ func (r *Local) Backup(info BackupInfo) error {
} }
defer outfile.Close() defer outfile.Close()
global.LOG.Infof("start to pg_dump | gzip > %s.gzip", info.TargetDir+"/"+info.FileName) global.LOG.Infof("start to pg_dump | gzip > %s.gzip", info.TargetDir+"/"+info.FileName)
cmd := exec.Command("docker", "exec", "-e", fmt.Sprintf("PGPASSWORD=%s", r.Password), r.ContainerName, "pg_dump", "-F", "c", "-U", r.Username, "-d", info.Name)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(info.Timeout*uint(time.Second)))
defer cancel()
cmd := exec.CommandContext(
ctx,
"docker", "exec", "-i", r.ContainerName,
"sh", "-c",
fmt.Sprintf("PGPASSWORD=%s pg_dump -F c -U %s -d %s", r.Password, r.Username, info.Name),
)
var stderr bytes.Buffer var stderr bytes.Buffer
cmd.Stderr = &stderr cmd.Stderr = &stderr
@@ -155,7 +163,12 @@ func (r *Local) Backup(info BackupInfo) error {
func (r *Local) Recover(info RecoverInfo) error { func (r *Local) Recover(info RecoverInfo) error {
fi, _ := os.Open(info.SourceFile) fi, _ := os.Open(info.SourceFile)
defer fi.Close() defer fi.Close()
cmd := exec.Command("docker", "exec", "-i", r.ContainerName, "pg_restore", "-F", "c", "-c", "-U", r.Username, "-d", info.Name)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(info.Timeout*uint(time.Second)))
defer cancel()
cmd := exec.CommandContext(ctx, "docker", "exec", "-i", r.ContainerName, "sh", "-c",
fmt.Sprintf("PGPASSWORD=%s pg_restore -F c -c --if-exists --no-owner -U %s -d %s", r.Password, r.Username, info.Name),
)
if strings.HasSuffix(info.SourceFile, ".gz") { if strings.HasSuffix(info.SourceFile, ".gz") {
gzipFile, err := os.Open(info.SourceFile) gzipFile, err := os.Open(info.SourceFile)
if err != nil { if err != nil {
+1 -1
View File
@@ -177,7 +177,7 @@ func (r *Remote) Recover(info RecoverInfo) error {
}() }()
} }
recoverCommand := exec.Command("bash", "-c", recoverCommand := exec.Command("bash", "-c",
fmt.Sprintf("docker run --rm --net=host -i %s /bin/bash -c 'PGPASSWORD=\"%s\" pg_restore -h %s -p %d --verbose --clean --no-privileges --no-owner -Fc -U %s -d %s --role=%s' < %s", fmt.Sprintf("docker run --rm --net=host -i %s /bin/bash -c 'PGPASSWORD=\"%s\" pg_restore -h %s -p %d --verbose --clean --no-privileges --no-owner -Fc -c --if-exists --no-owner -U %s -d %s --role=%s' < %s",
imageTag, r.Password, r.Address, r.Port, r.User, info.Name, info.Username, fileName)) imageTag, r.Password, r.Address, r.Port, r.User, info.Name, info.Username, fileName))
pipe, _ := recoverCommand.StdoutPipe() pipe, _ := recoverCommand.StdoutPipe()
stderrPipe, _ := recoverCommand.StderrPipe() stderrPipe, _ := recoverCommand.StderrPipe()
+6 -3
View File
@@ -2,6 +2,7 @@ package redis
import ( import (
"fmt" "fmt"
"time"
"github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/global"
"github.com/go-redis/redis" "github.com/go-redis/redis"
@@ -11,13 +12,15 @@ type DBInfo struct {
Address string `json:"address"` Address string `json:"address"`
Port uint `json:"port"` Port uint `json:"port"`
Password string `json:"password"` Password string `json:"password"`
Timeout uint `json:"timeout"`
} }
func NewRedisClient(conn DBInfo) (*redis.Client, error) { func NewRedisClient(conn DBInfo) (*redis.Client, error) {
client := redis.NewClient(&redis.Options{ client := redis.NewClient(&redis.Options{
Addr: fmt.Sprintf("%s:%v", conn.Address, conn.Port), Addr: fmt.Sprintf("%s:%v", conn.Address, conn.Port),
Password: conn.Password, Password: conn.Password,
DB: 0, DB: 0,
DialTimeout: time.Duration(conn.Timeout) * time.Second,
}) })
if _, err := client.Ping().Result(); err != nil { if _, err := client.Ping().Result(); err != nil {
-4
View File
@@ -130,10 +130,6 @@ func loadVersion(isLatest bool, currentVersion, mode string) string {
return "" return ""
} }
num, _ := strconv.Atoi(versionPart[1]) num, _ := strconv.Atoi(versionPart[1])
if num == 0 {
global.LOG.Errorf("current version is error format: %s", currentVersion)
return ""
}
if num >= 10 { if num >= 10 {
if version, ok := versionMap[currentVersion[0:5]]; ok { if version, ok := versionMap[currentVersion[0:5]]; ok {
return checkVersion(version, currentVersion) return checkVersion(version, currentVersion)
+5
View File
@@ -15,6 +15,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/buserr" "github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd" "github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/gin-gonic/gin"
) )
func RemoveTamper(website string) {} func RemoveTamper(website string) {}
@@ -72,3 +73,7 @@ func LoadRequestTransport() *http.Transport {
IdleConnTimeout: 15 * time.Second, IdleConnTimeout: 15 * time.Second,
} }
} }
func ValidateCertificate(c *gin.Context) bool {
return true
}
-2
View File
@@ -116,8 +116,6 @@ func (u *GroupService) Delete(id uint) error {
if err := xpack.UpdateGroup("node", id, defaultGroup.ID); err != nil { if err := xpack.UpdateGroup("node", id, defaultGroup.ID); err != nil {
return err return err
} }
default:
return buserr.New("ErrNotSupportType")
} }
if err != nil { if err != nil {
return err return err
+15 -10
View File
@@ -189,7 +189,11 @@ func (u *SettingService) UpdateBindInfo(req dto.BindInfo) error {
} }
func (u *SettingService) UpdateProxy(req dto.ProxyUpdate) error { func (u *SettingService) UpdateProxy(req dto.ProxyUpdate) error {
if err := settingRepo.Update("ProxyUrl", req.ProxyUrl); err != nil { proxyUrl := req.ProxyUrl
if req.ProxyType == "https" || req.ProxyType == "http" {
proxyUrl = req.ProxyType + "://" + req.ProxyUrl
}
if err := settingRepo.Update("ProxyUrl", proxyUrl); err != nil {
return err return err
} }
if err := settingRepo.Update("ProxyType", req.ProxyType); err != nil { if err := settingRepo.Update("ProxyType", req.ProxyType); err != nil {
@@ -675,16 +679,17 @@ func (u *SettingService) GetAppstoreConfig() (*dto.AppstoreConfig, error) {
} }
func loadDockerProxy(req dto.ProxyUpdate) string { func loadDockerProxy(req dto.ProxyUpdate) string {
if len(req.ProxyType) == 0 || req.ProxyType == "close" || !req.ProxyDocker { if req.ProxyType == "" || req.ProxyType == "close" || !req.ProxyDocker {
return "" return ""
} }
proxyPasswd := "" var account string
if len(req.ProxyUser) != 0 { if req.ProxyUser != "" {
proxyPasswd = req.ProxyPasswd + "@" account = req.ProxyUser
if req.ProxyPasswd != "" {
account += ":" + req.ProxyPasswd
}
account += "@"
} }
proxyUrl := req.ProxyType + "://" + req.ProxyUser + ":" + proxyPasswd + req.ProxyUrl + ":" + req.ProxyPort
if req.ProxyType == "http" || req.ProxyType == "https" { return fmt.Sprintf("%s://%s%s:%s", req.ProxyType, account, req.ProxyUrl, req.ProxyPort)
req.ProxyUrl = req.ProxyType + "://" + req.ProxyUrl
}
return proxyUrl
} }
+1
View File
@@ -22,6 +22,7 @@ func Init() {
migrations.UpdateOnedrive, migrations.UpdateOnedrive,
migrations.AddClusterMenu, migrations.AddClusterMenu,
migrations.DeleteXpackHideMenu, migrations.DeleteXpackHideMenu,
migrations.AddCronjobGroup,
}) })
if err := m.Migrate(); err != nil { if err := m.Migrate(); err != nil {
global.LOG.Error(err) global.LOG.Error(err)
+10
View File
@@ -531,3 +531,13 @@ var DeleteXpackHideMenu = &gormigrate.Migration{
return tx.Model(&model.Setting{}).Where("key = ?", "HideMenu").Update("value", string(updatedJSON)).Error return tx.Model(&model.Setting{}).Where("key = ?", "HideMenu").Update("value", string(updatedJSON)).Error
}, },
} }
var AddCronjobGroup = &gormigrate.Migration{
ID: "20250729-add-cronjob-group",
Migrate: func(tx *gorm.DB) error {
if err := tx.Create(&model.Group{Name: "Default", Type: "cronjob", IsDefault: true}).Error; err != nil {
return err
}
return nil
},
}
+1 -1
View File
@@ -138,7 +138,7 @@ func HandleUnTar(sourceFile, targetDir string, secret string) error {
commands = fmt.Sprintf("%s tar -zxvf - -C %s", extraCmd, targetDir+" > /dev/null 2>&1") commands = fmt.Sprintf("%s tar -zxvf - -C %s", extraCmd, targetDir+" > /dev/null 2>&1")
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" '%s' ", secret), " ****** ")) global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" '%s' ", secret), " ****** "))
} else { } else {
commands = fmt.Sprintf("tar zxvfC %s %s", sourceFile, targetDir) commands = fmt.Sprintf("tar zxvf '%s' -C '%s'", sourceFile, targetDir)
global.LOG.Debug(commands) global.LOG.Debug(commands)
} }
+22 -1
View File
@@ -2,6 +2,7 @@ package ssh
import ( import (
"fmt" "fmt"
"net"
"strings" "strings"
"time" "time"
@@ -51,11 +52,14 @@ func NewClient(c ConnInfo) (*SSHClient, error) {
if strings.Contains(c.Addr, ":") { if strings.Contains(c.Addr, ":") {
proto = "tcp6" proto = "tcp6"
} }
client, err := gossh.Dial(proto, addr, config) client, err := DialWithTimeout(proto, addr, config)
if nil != err { if nil != err {
return nil, err return nil, err
} }
sshClient := &SSHClient{Client: client} sshClient := &SSHClient{Client: client}
if c.User == "root" {
return sshClient, nil
}
if _, err := sshClient.Run("sudo -n ls"); err == nil { if _, err := sshClient.Run("sudo -n ls"); err == nil {
sshClient.SudoItem = "sudo" sshClient.SudoItem = "sudo"
} }
@@ -204,3 +208,20 @@ func (c *SSHClient) RunWithStreamOutput(command string, outputCallback func(stri
return err return err
} }
func DialWithTimeout(network, addr string, config *gossh.ClientConfig) (*gossh.Client, error) {
conn, err := net.DialTimeout(network, addr, config.Timeout)
if err != nil {
return nil, err
}
_ = conn.SetDeadline(time.Now().Add(config.Timeout))
c, chans, reqs, err := gossh.NewClientConn(conn, addr, config)
if err != nil {
return nil, err
}
if err := conn.SetDeadline(time.Time{}); err != nil {
conn.Close()
return nil, fmt.Errorf("clear deadline failed: %v", err)
}
return gossh.NewClient(c, chans, reqs), nil
}
+20 -10
View File
@@ -2,14 +2,24 @@
root = true root = true
[*] # 表示所有文件适用 # 表示所有文件适用
charset = utf-8 # 设置文件字符集为 utf-8 [*]
end_of_line = lf # 控制换行类型(lf | cr | crlf) # 设置文件字符集为 utf-8
insert_final_newline = true # 始终在文件末尾插入一个新行 charset = utf-8
indent_style = space # 缩进风格(tab | space) # 控制换行类型(lf | cr | crlf)
indent_size = 4 # 缩进大小 end_of_line = lf
max_line_length = 120 # 最大行长度 # 始终在文件末尾插入一个新行
insert_final_newline = true
# 缩进风格(tab | space)
indent_style = space
# 缩进大小
indent_size = 4
# 最大行长度
max_line_length = 120
[*.md] # 表示仅 md 文件适用以下规则 # 表示仅 md 文件适用以下规则
max_line_length = off # 关闭最大行长度限制 [*.md]
trim_trailing_whitespace = false # 关闭末尾空格修剪 # 关闭最大行长度限制
max_line_length = off
# 关闭末尾空格修剪
trim_trailing_whitespace = false
+2 -1
View File
@@ -7,6 +7,7 @@ import { GlobalStore } from '@/store';
import { MsgError } from '@/utils/message'; import { MsgError } from '@/utils/message';
import { Base64 } from 'js-base64'; import { Base64 } from 'js-base64';
import i18n from '@/lang'; import i18n from '@/lang';
import { changeToLocal } from '@/utils/node';
const globalStore = GlobalStore(); const globalStore = GlobalStore();
@@ -66,7 +67,7 @@ class RequestHttp {
return Promise.reject(data); return Promise.reject(data);
} }
if (data.code == ResultEnum.NodeUnBind) { if (data.code == ResultEnum.NodeUnBind) {
globalStore.currentNode = 'local'; changeToLocal();
window.location.reload(); window.location.reload();
return; return;
} }
+2
View File
@@ -137,6 +137,8 @@ export namespace AI {
hostIP: string; hostIP: string;
protocol: string; protocol: string;
url: string; url: string;
outputTransport: string;
streamableHttpPath: string;
} }
export interface McpServerSearch extends ReqPage { export interface McpServerSearch extends ReqPage {
+1 -1
View File
@@ -282,7 +282,7 @@ export namespace App {
name: string; name: string;
detailID: number; detailID: number;
version: string; version: string;
icon: string; scope: string;
} }
export interface AppUpdateVersionReq { export interface AppUpdateVersionReq {
+54
View File
@@ -1,10 +1,17 @@
import { ReqPage } from '.'; import { ReqPage } from '.';
export namespace Cronjob { export namespace Cronjob {
export interface Search extends ReqPage {
info: string;
groupIDs: Array<number>;
orderBy?: string;
order?: string;
}
export interface CronjobInfo { export interface CronjobInfo {
id: number; id: number;
name: string; name: string;
type: string; type: string;
groupID: number;
specCustom: boolean; specCustom: boolean;
spec: string; spec: string;
specs: Array<string>; specs: Array<string>;
@@ -64,6 +71,7 @@ export namespace Cronjob {
export interface CronjobOperate { export interface CronjobOperate {
id: number; id: number;
name: string; name: string;
groupID: number;
type: string; type: string;
specCustom: boolean; specCustom: boolean;
spec: string; spec: string;
@@ -101,6 +109,52 @@ export namespace Cronjob {
alertTitle: string; alertTitle: string;
alertMethod: string; alertMethod: string;
} }
export interface CronjobTrans {
name: string;
type: string;
specCustom: boolean;
spec: string;
group: string;
executor: string;
scriptMode: string;
script: string;
command: string;
containerName: string;
user: string;
url: string;
scriptName: string;
apps: Array<TransHelper>;
websites: Array<string>;
dbType: string;
dbNames: Array<TransHelper>;
exclusionRules: string;
isDir: boolean;
sourceDir: string;
retainCopies: number;
retryTimes: number;
timeout: number;
ignoreErr: boolean;
snapshotRule: string;
secret: string;
sourceAccounts: Array<string>;
downloadAccount: string;
alertCount: number;
}
export interface TransHelper {
name: string;
detailName: string;
}
export interface snapshotTransHelper {
withImage: boolean;
ignoreApps: Array<TransHelper>;
}
export interface snapshotRule { export interface snapshotRule {
withImage: boolean; withImage: boolean;
ignoreAppIDs: Array<Number>; ignoreAppIDs: Array<Number>;
+4
View File
@@ -221,6 +221,7 @@ export namespace Database {
// redis // redis
export interface RedisConfUpdate { export interface RedisConfUpdate {
dbType: string;
database: string; database: string;
timeout: string; timeout: string;
maxclients: string; maxclients: string;
@@ -283,6 +284,7 @@ export namespace Database {
clientCert: string; clientCert: string;
skipVerify: boolean; skipVerify: boolean;
timeout: number;
description: string; description: string;
} }
export interface SearchDatabasePage { export interface SearchDatabasePage {
@@ -322,6 +324,7 @@ export namespace Database {
clientCert: string; clientCert: string;
skipVerify: boolean; skipVerify: boolean;
timeout: number;
description: string; description: string;
} }
export interface DatabaseUpdate { export interface DatabaseUpdate {
@@ -338,6 +341,7 @@ export namespace Database {
clientCert: string; clientCert: string;
skipVerify: boolean; skipVerify: boolean;
timeout: number;
description: string; description: string;
} }
export interface DatabaseDelete { export interface DatabaseDelete {
+1
View File
@@ -43,6 +43,7 @@ export namespace HostTool {
numprocs: string; numprocs: string;
status?: ProcessStatus[]; status?: ProcessStatus[];
autoRestart: string; autoRestart: string;
autoStart: string;
} }
export interface ProcessStatus { export interface ProcessStatus {
+18 -2
View File
@@ -161,15 +161,31 @@ export namespace Host {
primaryKey: string; primaryKey: string;
permitRootLogin: string; permitRootLogin: string;
useDNS: string; useDNS: string;
currentUser: string;
} }
export interface SSHUpdate { export interface SSHUpdate {
key: string; key: string;
oldValue: string; oldValue: string;
newValue: string; newValue: string;
} }
export interface SSHGenerate { export interface RootCert {
name: string;
mode: string;
encryptionMode: string; encryptionMode: string;
password: string; passPhrase: string;
privateKey: string;
publicKey: string;
description: string;
}
export interface RootCertInfo {
id: number;
createAt: Date;
name: string;
encryptionMode: string;
passPhrase: string;
description: string;
publicKey: string;
privateKey: string;
} }
export interface searchSSHLog extends ReqPage { export interface searchSSHLog extends ReqPage {
info: string; info: string;
+8
View File
@@ -50,4 +50,12 @@ export namespace Nginx {
export interface NginxModuleUpdate extends NginxModule { export interface NginxModuleUpdate extends NginxModule {
operate: string; operate: string;
} }
export interface NginxHttpsStatus {
https: boolean;
}
export interface NginxOperateReq {
operate: string;
}
} }
+2 -2
View File
@@ -53,7 +53,7 @@ export const containerOperator = (params: Container.ContainerOperate) => {
return http.post(`/containers/operate`, params); return http.post(`/containers/operate`, params);
}; };
export const containerPrune = (params: Container.ContainerPrune) => { export const containerPrune = (params: Container.ContainerPrune) => {
return http.post<Container.ContainerPruneReport>(`/containers/prune`, params); return http.post(`/containers/prune`, params);
}; };
export const inspect = (params: Container.ContainerInspect) => { export const inspect = (params: Container.ContainerInspect) => {
return http.post<string>(`/containers/inspect`, params); return http.post<string>(`/containers/inspect`, params);
@@ -95,7 +95,7 @@ export const imageTag = (params: Container.ImageTag) => {
return http.post(`/containers/image/tag`, params); return http.post(`/containers/image/tag`, params);
}; };
export const imageRemove = (params: Container.BatchDelete) => { export const imageRemove = (params: Container.BatchDelete) => {
return http.post<Container.ContainerPruneReport>(`/containers/image/remove`, params); return http.post(`/containers/image/remove`, params);
}; };
// network // network

Some files were not shown because too many files have changed in this diff Show More