mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 08:00:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f26412585 | ||
|
|
ac99d77fbb | ||
|
|
4f6f7632f5 | ||
|
|
f74248f0d7 | ||
|
|
bb8a15ca56 | ||
|
|
52432002c7 | ||
|
|
3fade48f8f | ||
|
|
fc81801c4c | ||
|
|
c23c5b2859 | ||
|
|
ccec57e32d | ||
|
|
56ae5fbf70 | ||
|
|
735b44856e | ||
|
|
1f6ea9f494 | ||
|
|
afa643519f | ||
|
|
1767b84026 | ||
|
|
e855e7692e | ||
|
|
94d37eb68a | ||
|
|
e10b19bcde | ||
|
|
e9192e75ea | ||
|
|
2fa4f68b1e | ||
|
|
08f53a4a37 | ||
|
|
c522295c56 | ||
|
|
7a45548387 | ||
|
|
810ea9c421 | ||
|
|
ba6a4fe6d7 | ||
|
|
4d144f7a8e | ||
|
|
45c41184c1 | ||
|
|
5c29c5e7dd | ||
|
|
4da9a84363 | ||
|
|
22d8fb7867 | ||
|
|
c7058cb3e6 | ||
|
|
e6092b2658 | ||
|
|
ae19b72e61 | ||
|
|
da117582d6 | ||
|
|
c7ca1ccbd7 | ||
|
|
911b5c406d | ||
|
|
71ed4b946a | ||
|
|
4ef9fcde59 | ||
|
|
7889b5ad69 | ||
|
|
be6d3ea337 | ||
|
|
64fd715973 | ||
|
|
315f99a03a | ||
|
|
7e1351c543 | ||
|
|
bd3c829653 | ||
|
|
4f6dde02d2 | ||
|
|
8cc7d1f67f | ||
|
|
2bbba9d755 | ||
|
|
e9e4755348 | ||
|
|
9fbb63df95 | ||
|
|
5b5a649589 | ||
|
|
79556affbd | ||
|
|
9cba19d4db | ||
|
|
4f377c04b8 | ||
|
|
763df5606b | ||
|
|
e1d20e3ec9 | ||
|
|
215c082a04 | ||
|
|
984985e650 | ||
|
|
249b3c3245 | ||
|
|
725786d6aa | ||
|
|
2edddaf487 | ||
|
|
49cbab08c1 | ||
|
|
a75df66151 | ||
|
|
43e9c13c5e | ||
|
|
5ad06f121b | ||
|
|
0eaaf7aacb | ||
|
|
dd44fdd1f2 | ||
|
|
669855de70 | ||
|
|
193ae2b7d6 | ||
|
|
af4b23e0e0 | ||
|
|
c35991315d | ||
|
|
9aa852370c | ||
|
|
8f18497551 | ||
|
|
4c59951e8c | ||
|
|
eb2a80d216 | ||
|
|
41d0175a6d | ||
|
|
2f6aa0f45b | ||
|
|
17c685bc4c | ||
|
|
cf230ca6e7 | ||
|
|
bb99853000 | ||
|
|
0c64c8ff52 | ||
|
|
e3e2006c19 | ||
|
|
eddc1b6a8d | ||
|
|
83e1db723e | ||
|
|
76c608c703 | ||
|
|
d61a77bec7 | ||
|
|
a6a2d9991c | ||
|
|
060dd08167 | ||
|
|
dd1d93e096 | ||
|
|
e4b24f51fc | ||
|
|
e472269a4e | ||
|
|
4032276556 | ||
|
|
420537f092 | ||
|
|
3867e536b2 | ||
|
|
4bc7b96ae4 | ||
|
|
9f0338382f | ||
|
|
692c77eb1a | ||
|
|
0778e3e81d | ||
|
|
80e217e839 | ||
|
|
47547ad4e1 | ||
|
|
8b22acce27 | ||
|
|
754ac64bf6 | ||
|
|
3b37d5ae72 | ||
|
|
4e2f24a8f0 | ||
|
|
23a1fbdd4b | ||
|
|
795223e7fa | ||
|
|
10039c6173 | ||
|
|
146b628cc1 | ||
|
|
0c31fcc8a3 | ||
|
|
2c290dbbf7 | ||
|
|
0df8a33b9e | ||
|
|
1278e910f5 | ||
|
|
429e2c6cbe | ||
|
|
00a5cc654a | ||
|
|
d68a0b076b | ||
|
|
99ef8e9310 | ||
|
|
5e549fdb71 | ||
|
|
8f7b026f0e | ||
|
|
fabf33a83b | ||
|
|
211c956c4b | ||
|
|
55d966a52e | ||
|
|
a0d5319cd9 | ||
|
|
d9358b87c4 | ||
|
|
89526d9075 | ||
|
|
b1799d8c06 | ||
|
|
185eaf1211 | ||
|
|
4aa7dbfa9f | ||
|
|
daef59b568 | ||
|
|
be2c30c9e0 | ||
|
|
3b166801d6 | ||
|
|
c289a5f14f | ||
|
|
5dfb4def56 | ||
|
|
c6d70f4dc8 | ||
|
|
254a6e6dbf | ||
|
|
62c010ce13 | ||
|
|
272536a3b5 | ||
|
|
13dc94aca5 | ||
|
|
b9a80f15e6 | ||
|
|
5cab80d842 | ||
|
|
8a4ff3c809 | ||
|
|
5b9e13f35b | ||
|
|
fd5437139b | ||
|
|
26852b6676 | ||
|
|
b8a02cc094 | ||
|
|
f7fa6cf567 | ||
|
|
64e74e87c4 | ||
|
|
f843438baf | ||
|
|
4528f1feaf | ||
|
|
1eae519e1a | ||
|
|
1416fb0f6e | ||
|
|
e9257735ba | ||
|
|
81caf126f7 |
@@ -35,6 +35,29 @@ func (b *BaseApi) SearchContainer(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Load container users
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithName true "request"
|
||||
// @Produce json
|
||||
// @Success 200 {array} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/users [post]
|
||||
func (b *BaseApi) LoadContainerUsers(c *gin.Context) {
|
||||
var req dto.OperationWithName
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
list, err := containerService.LoadUsers(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary List containers
|
||||
// @Accept json
|
||||
|
||||
@@ -296,7 +296,7 @@ func (b *BaseApi) SaveContent(c *gin.Context) {
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /files/upload [post]
|
||||
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"上传文件 [path]","formatEN":"Upload file [path]"}
|
||||
// @x-panel-log {"bodyKeys":["path", "file"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"上传文件 [path]/[file]","formatEN":"Upload file [path]/[file]"}
|
||||
func (b *BaseApi) UploadFiles(c *gin.Context) {
|
||||
form, err := c.MultipartForm()
|
||||
if err != nil {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// @Success 200 {object} response.HostToolRes
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool [post]
|
||||
// @Router /hosts/tool [post]
|
||||
func (b *BaseApi) GetToolStatus(c *gin.Context) {
|
||||
var req request.HostToolReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -35,7 +35,7 @@ func (b *BaseApi) GetToolStatus(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/create [post]
|
||||
// @Router /hosts/tool/create [post]
|
||||
// @x-panel-log {"bodyKeys":["type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建 [type] 配置","formatEN":"create [type] config"}
|
||||
func (b *BaseApi) InitToolConfig(c *gin.Context) {
|
||||
var req request.HostToolCreate
|
||||
@@ -57,7 +57,7 @@ func (b *BaseApi) InitToolConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/operate [post]
|
||||
// @Router /hosts/tool/operate [post]
|
||||
// @x-panel-log {"bodyKeys":["operate","type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] [type] ","formatEN":"[operate] [type]"}
|
||||
func (b *BaseApi) OperateTool(c *gin.Context) {
|
||||
var req request.HostToolReq
|
||||
@@ -79,7 +79,7 @@ func (b *BaseApi) OperateTool(c *gin.Context) {
|
||||
// @Success 200 {object} response.HostToolConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/config [post]
|
||||
// @Router /hosts/tool/config [post]
|
||||
// @x-panel-log {"bodyKeys":["operate"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] 主机工具配置文件 ","formatEN":"[operate] tool config"}
|
||||
func (b *BaseApi) OperateToolConfig(c *gin.Context) {
|
||||
var req request.HostToolConfig
|
||||
@@ -102,7 +102,7 @@ func (b *BaseApi) OperateToolConfig(c *gin.Context) {
|
||||
// @Success 200 {string} logContent
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/log [post]
|
||||
// @Router /hosts/tool/log [post]
|
||||
func (b *BaseApi) GetToolLog(c *gin.Context) {
|
||||
var req request.HostToolLogReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -124,7 +124,7 @@ func (b *BaseApi) GetToolLog(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/supervisor/process [post]
|
||||
// @Router /hosts/tool/supervisor/process [post]
|
||||
// @x-panel-log {"bodyKeys":["operate"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] 守护进程 ","formatEN":"[operate] process"}
|
||||
func (b *BaseApi) OperateProcess(c *gin.Context) {
|
||||
var req request.SupervisorProcessConfig
|
||||
@@ -146,7 +146,7 @@ func (b *BaseApi) OperateProcess(c *gin.Context) {
|
||||
// @Success 200 {object} response.SupervisorProcessConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/supervisor/process [get]
|
||||
// @Router /hosts/tool/supervisor/process [get]
|
||||
func (b *BaseApi) GetProcess(c *gin.Context) {
|
||||
configs, err := hostToolService.GetSupervisorProcessConfig()
|
||||
if err != nil {
|
||||
@@ -163,7 +163,7 @@ func (b *BaseApi) GetProcess(c *gin.Context) {
|
||||
// @Success 200 {string} content
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/supervisor/process/file [post]
|
||||
// @Router /hosts/tool/supervisor/process/file [post]
|
||||
// @x-panel-log {"bodyKeys":["operate"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] Supervisor 进程文件 ","formatEN":"[operate] Supervisor Process Config file"}
|
||||
func (b *BaseApi) GetProcessFile(c *gin.Context) {
|
||||
var req request.SupervisorProcessFileReq
|
||||
|
||||
@@ -98,20 +98,20 @@ func (b *BaseApi) CreateRepo(c *gin.Context) {
|
||||
// @Tags Container Image-repo
|
||||
// @Summary Delete image repo
|
||||
// @Accept json
|
||||
// @Param request body dto.ImageRepoDelete true "request"
|
||||
// @Param request body dto.OperateByID true "request"
|
||||
// @Produce json
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/repo/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"image_repos","output_column":"name","output_value":"names"}],"formatZH":"删除镜像仓库 [names]","formatEN":"delete image repo [names]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"image_repos","output_column":"name","output_value":"name"}],"formatZH":"删除镜像仓库 [name]","formatEN":"delete image repo [name]"}
|
||||
func (b *BaseApi) DeleteRepo(c *gin.Context) {
|
||||
var req dto.ImageRepoDelete
|
||||
var req dto.OperateByID
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := imageRepoService.BatchDelete(req); err != nil {
|
||||
if err := imageRepoService.Delete(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -535,3 +535,23 @@ func (b *BaseApi) GetPHPContainerConfig(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Runtime
|
||||
// @Summary Update runtime remark
|
||||
// @Accept json
|
||||
// @Param request body request.RuntimeRemarkUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /runtimes/remark [post]
|
||||
func (b *BaseApi) UpdateRuntimeRemark(c *gin.Context) {
|
||||
var req request.RuntimeRemark
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := runtimeService.UpdateRemark(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
// @Success 200 {object} dto.SSHInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/search [post]
|
||||
// @Router /hosts/ssh/search [post]
|
||||
func (b *BaseApi) GetSSHInfo(c *gin.Context) {
|
||||
info, err := sshService.GetSSHInfo()
|
||||
if err != nil {
|
||||
@@ -28,7 +28,7 @@ func (b *BaseApi) GetSSHInfo(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/operate [post]
|
||||
// @Router /hosts/ssh/operate [post]
|
||||
// @x-panel-log {"bodyKeys":["operation"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operation] SSH ","formatEN":"[operation] SSH"}
|
||||
func (b *BaseApi) OperateSSH(c *gin.Context) {
|
||||
var req dto.Operate
|
||||
@@ -50,7 +50,7 @@ func (b *BaseApi) OperateSSH(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/update [post]
|
||||
// @Router /hosts/ssh/update [post]
|
||||
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 SSH 配置 [key] => [value]","formatEN":"update SSH setting [key] => [value]"}
|
||||
func (b *BaseApi) UpdateSSH(c *gin.Context) {
|
||||
var req dto.SSHUpdate
|
||||
@@ -72,7 +72,7 @@ func (b *BaseApi) UpdateSSH(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/conffile/update [post]
|
||||
// @Router /hosts/conffile/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 SSH 配置文件","formatEN":"update SSH conf"}
|
||||
func (b *BaseApi) UpdateSSHByfile(c *gin.Context) {
|
||||
var req dto.SSHConf
|
||||
@@ -94,7 +94,7 @@ func (b *BaseApi) UpdateSSHByfile(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/generate [post]
|
||||
// @Router /hosts/ssh/generate [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"生成 SSH 密钥 ","formatEN":"generate SSH secret"}
|
||||
func (b *BaseApi) GenerateSSH(c *gin.Context) {
|
||||
var req dto.GenerateSSH
|
||||
@@ -116,7 +116,7 @@ func (b *BaseApi) GenerateSSH(c *gin.Context) {
|
||||
// @Success 200 {string} secret
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/secret [post]
|
||||
// @Router /hosts/ssh/secret [post]
|
||||
func (b *BaseApi) LoadSSHSecret(c *gin.Context) {
|
||||
var req dto.GenerateLoad
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -138,7 +138,7 @@ func (b *BaseApi) LoadSSHSecret(c *gin.Context) {
|
||||
// @Success 200 {object} dto.SSHLog
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/log [post]
|
||||
// @Router /hosts/ssh/log [post]
|
||||
func (b *BaseApi) LoadSSHLogs(c *gin.Context) {
|
||||
var req dto.SearchSSHLog
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -158,7 +158,7 @@ func (b *BaseApi) LoadSSHLogs(c *gin.Context) {
|
||||
// @Success 200 {string} conf
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/conf [get]
|
||||
// @Router /hosts/ssh/conf [get]
|
||||
func (b *BaseApi) LoadSSHConf(c *gin.Context) {
|
||||
data, err := sshService.LoadSSHConf()
|
||||
if err != nil {
|
||||
|
||||
+30
-21
@@ -29,27 +29,34 @@ type CronjobOperate struct {
|
||||
ContainerName string `json:"containerName"`
|
||||
User string `json:"user"`
|
||||
|
||||
ScriptID uint `json:"scriptID"`
|
||||
AppID string `json:"appID"`
|
||||
Website string `json:"website"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
DBType string `json:"dbType"`
|
||||
DBName string `json:"dbName"`
|
||||
URL string `json:"url"`
|
||||
IsDir bool `json:"isDir"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
ScriptID uint `json:"scriptID"`
|
||||
AppID string `json:"appID"`
|
||||
Website string `json:"website"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
DBType string `json:"dbType"`
|
||||
DBName string `json:"dbName"`
|
||||
URL string `json:"url"`
|
||||
IsDir bool `json:"isDir"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
SnapshotRule SnapshotRule `json:"snapshotRule"`
|
||||
|
||||
SourceAccountIDs string `json:"sourceAccountIDs"`
|
||||
DownloadAccountID uint `json:"downloadAccountID"`
|
||||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||
RetryTimes int `json:"retryTimes" validate:"number,min=0"`
|
||||
Timeout uint `json:"timeout" validate:"number,min=1"`
|
||||
IgnoreErr bool `json:"ignoreErr"`
|
||||
Secret string `json:"secret"`
|
||||
|
||||
AlertCount uint `json:"alertCount"`
|
||||
AlertTitle string `json:"alertTitle"`
|
||||
}
|
||||
|
||||
type SnapshotRule struct {
|
||||
WithImage bool `json:"withImage"`
|
||||
IgnoreAppIDs []uint `json:"ignoreAppIDs"`
|
||||
}
|
||||
|
||||
type CronjobUpdateStatus struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Status string `json:"status" validate:"required"`
|
||||
@@ -87,18 +94,20 @@ type CronjobInfo struct {
|
||||
ContainerName string `json:"containerName"`
|
||||
User string `json:"user"`
|
||||
|
||||
ScriptID uint `json:"scriptID"`
|
||||
AppID string `json:"appID"`
|
||||
Website string `json:"website"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
DBType string `json:"dbType"`
|
||||
DBName string `json:"dbName"`
|
||||
URL string `json:"url"`
|
||||
IsDir bool `json:"isDir"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
RetainCopies int `json:"retainCopies"`
|
||||
RetryTimes int `json:"retryTimes"`
|
||||
Timeout uint `json:"timeout"`
|
||||
ScriptID uint `json:"scriptID"`
|
||||
AppID string `json:"appID"`
|
||||
Website string `json:"website"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
DBType string `json:"dbType"`
|
||||
DBName string `json:"dbName"`
|
||||
URL string `json:"url"`
|
||||
IsDir bool `json:"isDir"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
RetainCopies int `json:"retainCopies"`
|
||||
RetryTimes int `json:"retryTimes"`
|
||||
Timeout uint `json:"timeout"`
|
||||
IgnoreErr bool `json:"ignoreErr"`
|
||||
SnapshotRule SnapshotRule `json:"snapshotRule"`
|
||||
|
||||
SourceAccounts []string `json:"sourceAccounts"`
|
||||
DownloadAccount string `json:"downloadAccount"`
|
||||
|
||||
@@ -6,8 +6,8 @@ type ImageRepoCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
DownloadUrl string `json:"downloadUrl"`
|
||||
Protocol string `json:"protocol"`
|
||||
Username string `json:"username" validate:"max=256"`
|
||||
Password string `json:"password" validate:"max=256"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Auth bool `json:"auth"`
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ type ImageRepoUpdate struct {
|
||||
ID uint `json:"id"`
|
||||
DownloadUrl string `json:"downloadUrl"`
|
||||
Protocol string `json:"protocol"`
|
||||
Username string `json:"username" validate:"max=256"`
|
||||
Password string `json:"password" validate:"max=256"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Auth bool `json:"auth"`
|
||||
}
|
||||
|
||||
|
||||
@@ -92,11 +92,12 @@ type FileWget struct {
|
||||
}
|
||||
|
||||
type FileMove struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
OldPaths []string `json:"oldPaths" validate:"required"`
|
||||
NewPath string `json:"newPath" validate:"required"`
|
||||
Name string `json:"name"`
|
||||
Cover bool `json:"cover"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
OldPaths []string `json:"oldPaths" validate:"required"`
|
||||
NewPath string `json:"newPath" validate:"required"`
|
||||
Name string `json:"name"`
|
||||
Cover bool `json:"cover"`
|
||||
CoverPaths []string `json:"coverPaths"`
|
||||
}
|
||||
|
||||
type FileDownload struct {
|
||||
|
||||
@@ -12,15 +12,17 @@ type RuntimeSearch struct {
|
||||
}
|
||||
|
||||
type RuntimeCreate struct {
|
||||
AppDetailID uint `json:"appDetailId"`
|
||||
Name string `json:"name"`
|
||||
Params map[string]interface{} `json:"params"`
|
||||
Resource string `json:"resource"`
|
||||
Image string `json:"image"`
|
||||
Type string `json:"type"`
|
||||
Version string `json:"version"`
|
||||
Source string `json:"source"`
|
||||
CodeDir string `json:"codeDir"`
|
||||
AppDetailID uint `json:"appDetailId"`
|
||||
Name string `json:"name"`
|
||||
Resource string `json:"resource"`
|
||||
Image string `json:"image"`
|
||||
Type string `json:"type"`
|
||||
Version string `json:"version"`
|
||||
Source string `json:"source"`
|
||||
CodeDir string `json:"codeDir"`
|
||||
Remark string `json:"remark"`
|
||||
|
||||
Params map[string]interface{} `json:"params"`
|
||||
NodeConfig
|
||||
}
|
||||
|
||||
@@ -53,14 +55,16 @@ type RuntimeDelete struct {
|
||||
}
|
||||
|
||||
type RuntimeUpdate struct {
|
||||
Name string `json:"name"`
|
||||
ID uint `json:"id"`
|
||||
Params map[string]interface{} `json:"params"`
|
||||
Image string `json:"image"`
|
||||
Version string `json:"version"`
|
||||
Rebuild bool `json:"rebuild"`
|
||||
Source string `json:"source"`
|
||||
CodeDir string `json:"codeDir"`
|
||||
Name string `json:"name"`
|
||||
ID uint `json:"id"`
|
||||
Image string `json:"image"`
|
||||
Version string `json:"version"`
|
||||
Rebuild bool `json:"rebuild"`
|
||||
Source string `json:"source"`
|
||||
CodeDir string `json:"codeDir"`
|
||||
Remark string `json:"remark"`
|
||||
|
||||
Params map[string]interface{} `json:"params"`
|
||||
NodeConfig
|
||||
}
|
||||
|
||||
@@ -131,3 +135,8 @@ type PHPContainerConfig struct {
|
||||
Environments []Environment `json:"environments"`
|
||||
Volumes []Volume `json:"volumes"`
|
||||
}
|
||||
|
||||
type RuntimeRemark struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ type WebsiteCreate struct {
|
||||
TaskID string `json:"taskID"`
|
||||
ParentWebsiteID uint `json:"parentWebsiteID"`
|
||||
|
||||
SiteDir string `json:"siteDir"`
|
||||
|
||||
RuntimeConfig
|
||||
FtpConfig
|
||||
DataBaseConfig
|
||||
|
||||
@@ -53,6 +53,7 @@ type ExistFileInfo struct {
|
||||
Path string `json:"path"`
|
||||
Size int64 `json:"size"`
|
||||
ModTime time.Time `json:"modTime"`
|
||||
IsDir bool `json:"isDir"`
|
||||
}
|
||||
|
||||
type UserInfo struct {
|
||||
|
||||
@@ -30,6 +30,7 @@ type RuntimeDTO struct {
|
||||
Volumes []request.Volume `json:"volumes"`
|
||||
ContainerStatus string `json:"containerStatus"`
|
||||
Container string `json:"container"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type PackageScripts struct {
|
||||
@@ -53,6 +54,7 @@ func NewRuntimeDTO(runtime model.Runtime) RuntimeDTO {
|
||||
Port: runtime.Port,
|
||||
Path: runtime.GetPath(),
|
||||
Container: runtime.ContainerName,
|
||||
Remark: runtime.Remark,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,6 @@ type SettingInfo struct {
|
||||
AppStoreSyncStatus string `json:"appStoreSyncStatus"`
|
||||
|
||||
FileRecycleBin string `json:"fileRecycleBin"`
|
||||
|
||||
SnapshotIgnore string `json:"snapshotIgnore"`
|
||||
}
|
||||
|
||||
type SettingUpdate struct {
|
||||
|
||||
@@ -23,11 +23,14 @@ type SnapshotCreate struct {
|
||||
BackupData []DataTree `json:"backupData"`
|
||||
PanelData []DataTree `json:"panelData"`
|
||||
|
||||
WithDockerConf bool `json:"withDockerConf"`
|
||||
WithMonitorData bool `json:"withMonitorData"`
|
||||
WithLoginLog bool `json:"withLoginLog"`
|
||||
WithOperationLog bool `json:"withOperationLog"`
|
||||
WithSystemLog bool `json:"withSystemLog"`
|
||||
WithTaskLog bool `json:"withTaskLog"`
|
||||
|
||||
IgnoreFiles []string `json:"ignoreFiles"`
|
||||
}
|
||||
|
||||
type SnapshotData struct {
|
||||
@@ -35,11 +38,13 @@ type SnapshotData struct {
|
||||
BackupData []DataTree `json:"backupData"`
|
||||
PanelData []DataTree `json:"panelData"`
|
||||
|
||||
WithMonitorData bool `json:"withMonitorData"`
|
||||
WithLoginLog bool `json:"withLoginLog"`
|
||||
WithOperationLog bool `json:"withOperationLog"`
|
||||
WithSystemLog bool `json:"withSystemLog"`
|
||||
WithTaskLog bool `json:"withTaskLog"`
|
||||
WithDockerConf bool `json:"withDockerConf"`
|
||||
WithMonitorData bool `json:"withMonitorData"`
|
||||
WithLoginLog bool `json:"withLoginLog"`
|
||||
WithOperationLog bool `json:"withOperationLog"`
|
||||
WithSystemLog bool `json:"withSystemLog"`
|
||||
WithTaskLog bool `json:"withTaskLog"`
|
||||
IgnoreFiles []string `json:"ignoreFiles"`
|
||||
}
|
||||
type DataTree struct {
|
||||
ID string `json:"id"`
|
||||
|
||||
@@ -29,12 +29,14 @@ type Cronjob struct {
|
||||
URL string `json:"url"`
|
||||
IsDir bool `json:"isDir"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
SnapshotRule string `json:"snapshotRule"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
|
||||
SourceAccountIDs string `json:"sourceAccountIDs"`
|
||||
DownloadAccountID uint `json:"downloadAccountID"`
|
||||
RetryTimes uint `json:"retryTimes"`
|
||||
Timeout uint `json:"timeout"`
|
||||
IgnoreErr bool `json:"ignoreErr"`
|
||||
RetainCopies uint64 `json:"retainCopies"`
|
||||
|
||||
Status string `json:"status"`
|
||||
|
||||
@@ -23,6 +23,7 @@ type Runtime struct {
|
||||
Message string `json:"message"`
|
||||
CodeDir string `json:"codeDir"`
|
||||
ContainerName string `json:"containerName"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
func (r *Runtime) GetComposePath() string {
|
||||
|
||||
@@ -15,14 +15,17 @@ type Snapshot struct {
|
||||
TaskRecoverID string `json:"taskRecoverID"`
|
||||
TaskRollbackID string `json:"taskRollbackID"`
|
||||
|
||||
AppData string `json:"appData"`
|
||||
PanelData string `json:"panelData"`
|
||||
BackupData string `json:"backupData"`
|
||||
AppData string `json:"appData"`
|
||||
PanelData string `json:"panelData"`
|
||||
BackupData string `json:"backupData"`
|
||||
|
||||
WithDockerConf bool `json:"withDockerConf"`
|
||||
WithMonitorData bool `json:"withMonitorData"`
|
||||
WithLoginLog bool `json:"withLoginLog"`
|
||||
WithOperationLog bool `json:"withOperationLog"`
|
||||
WithSystemLog bool `json:"withSystemLog"`
|
||||
WithTaskLog bool `json:"withTaskLog"`
|
||||
IgnoreFiles string `json:"ignoreFiles"`
|
||||
|
||||
InterruptStep string `json:"interruptStep"`
|
||||
RecoverStatus string `json:"recoverStatus"`
|
||||
|
||||
@@ -108,7 +108,7 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
|
||||
lang := strings.ToLower(common.GetLang(ctx))
|
||||
for _, ap := range apps {
|
||||
if req.Type == "php" {
|
||||
if ap.RequiredPanelVersion == 0 || !common.CompareAppVersion(fmt.Sprintf("%f", ap.RequiredPanelVersion), common.GetSystemVersion(info.SystemVersion)) {
|
||||
if ap.RequiredPanelVersion == 0 || !common.CompareAppVersion(common.GetSystemVersion(info.SystemVersion), fmt.Sprintf("%f", ap.RequiredPanelVersion)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,29 +14,25 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
|
||||
"github.com/docker/docker/api/types"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/env"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
|
||||
"github.com/joho/godotenv"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/env"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type AppInstallService struct {
|
||||
@@ -124,7 +120,7 @@ func (a *AppInstallService) Page(req request.AppInstalledSearch) (int64, []respo
|
||||
}
|
||||
}
|
||||
|
||||
installDTOs, err := handleInstalled(installs, req.Update, req.Sync)
|
||||
installDTOs, _ := handleInstalled(installs, req.Update, req.Sync)
|
||||
if req.Update {
|
||||
total = int64(len(installDTOs))
|
||||
}
|
||||
@@ -635,10 +631,14 @@ func (a *AppInstallService) DeleteCheck(installID uint) ([]dto.AppResource, erro
|
||||
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(appInstall.ID), repo.WithByFrom(constant.AppResourceLocal))
|
||||
for _, resource := range resources {
|
||||
linkInstall, _ := appInstallRepo.GetFirst(repo.WithByID(resource.AppInstallId))
|
||||
res = append(res, dto.AppResource{
|
||||
Type: "app",
|
||||
Name: linkInstall.Name,
|
||||
})
|
||||
if linkInstall.ID > 0 {
|
||||
res = append(res, dto.AppResource{
|
||||
Type: "app",
|
||||
Name: linkInstall.Name,
|
||||
})
|
||||
} else {
|
||||
_ = appInstallResourceRepo.DeleteBy(context.Background(), appInstallResourceRepo.WithAppInstallId(resource.AppInstallId))
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
@@ -703,6 +703,7 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
||||
Rule: form.Rule,
|
||||
Type: form.Type,
|
||||
Multiple: form.Multiple,
|
||||
Required: form.Required,
|
||||
}
|
||||
if form.Edit {
|
||||
appParam.Edit = true
|
||||
@@ -761,6 +762,7 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
||||
Value: form.Default,
|
||||
Values: form.Values,
|
||||
Multiple: form.Multiple,
|
||||
Required: form.Required,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -789,15 +791,15 @@ func syncAppInstallStatus(appInstall *model.AppInstall, force bool) error {
|
||||
defer cli.Close()
|
||||
|
||||
var (
|
||||
containers []types.Container
|
||||
containersMap map[string]types.Container
|
||||
containers []container.Summary
|
||||
containersMap map[string]container.Summary
|
||||
containerNames = strings.Split(appInstall.ContainerName, ",")
|
||||
)
|
||||
containers, err = cli.ListContainersByName(containerNames)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
containersMap = make(map[string]types.Container)
|
||||
containersMap = make(map[string]container.Summary)
|
||||
for _, con := range containers {
|
||||
containersMap[con.Names[0]] = con
|
||||
}
|
||||
|
||||
+79
-109
@@ -18,41 +18,33 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/subosito/gotenv"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/env"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
composeV2 "github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/env"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/subosito/gotenv"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type DatabaseOp string
|
||||
@@ -387,21 +379,25 @@ func deleteAppInstall(deleteReq request.AppInstallDelete) error {
|
||||
}
|
||||
|
||||
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||
if deleteReq.DeleteDB && len(resources) > 0 {
|
||||
del := dto.DelAppLink{
|
||||
Ctx: ctx,
|
||||
Install: &install,
|
||||
ForceDelete: deleteReq.ForceDelete,
|
||||
Task: uninstallTask,
|
||||
}
|
||||
t.LogWithOps(task.TaskDelete, i18n.GetMsgByKey("Database"))
|
||||
if err = deleteLink(del); err != nil {
|
||||
t.LogFailedWithOps(task.TaskDelete, i18n.GetMsgByKey("Database"), err)
|
||||
if !deleteReq.ForceDelete {
|
||||
return err
|
||||
if len(resources) > 0 {
|
||||
if deleteReq.DeleteDB {
|
||||
del := dto.DelAppLink{
|
||||
Ctx: ctx,
|
||||
Install: &install,
|
||||
ForceDelete: deleteReq.ForceDelete,
|
||||
Task: uninstallTask,
|
||||
}
|
||||
t.LogWithOps(task.TaskDelete, i18n.GetMsgByKey("Database"))
|
||||
if err = deleteLink(del); err != nil {
|
||||
t.LogFailedWithOps(task.TaskDelete, i18n.GetMsgByKey("Database"), err)
|
||||
if !deleteReq.ForceDelete {
|
||||
return err
|
||||
}
|
||||
}
|
||||
t.LogSuccessWithOps(task.TaskDelete, i18n.GetMsgByKey("Database"))
|
||||
} else {
|
||||
_ = appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||
}
|
||||
t.LogSuccessWithOps(task.TaskDelete, i18n.GetMsgByKey("Database"))
|
||||
}
|
||||
|
||||
if DatabaseKeys[install.App.Key] > 0 {
|
||||
@@ -490,17 +486,14 @@ func deleteLink(del dto.DelAppLink) error {
|
||||
return appInstallResourceRepo.DeleteBy(del.Ctx, appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||
}
|
||||
|
||||
func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string, error) {
|
||||
if detail.DockerCompose == "" {
|
||||
return "", nil
|
||||
}
|
||||
func handleUpgradeCompose(install model.AppInstall, detail model.AppDetail) (map[string]interface{}, error) {
|
||||
composeMap := make(map[string]interface{})
|
||||
if err := yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
value, ok := composeMap["services"]
|
||||
if !ok || value == nil {
|
||||
return "", buserr.New("ErrFileParse")
|
||||
return nil, buserr.New("ErrFileParse")
|
||||
}
|
||||
servicesMap := value.(map[string]interface{})
|
||||
if len(servicesMap) == 1 {
|
||||
@@ -518,6 +511,34 @@ func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string
|
||||
delete(servicesMap, oldServiceName)
|
||||
}
|
||||
}
|
||||
serviceValue := servicesMap[install.ServiceName].(map[string]interface{})
|
||||
|
||||
oldComposeMap := make(map[string]interface{})
|
||||
if err := yaml.Unmarshal([]byte(install.DockerCompose), &oldComposeMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
oldValue, ok := oldComposeMap["services"]
|
||||
if !ok || oldValue == nil {
|
||||
return nil, buserr.New("ErrFileParse")
|
||||
}
|
||||
oldValueMap := oldValue.(map[string]interface{})
|
||||
oldServiceValue := oldValueMap[install.ServiceName].(map[string]interface{})
|
||||
if oldServiceValue["deploy"] != nil {
|
||||
serviceValue["deploy"] = oldServiceValue["deploy"]
|
||||
}
|
||||
servicesMap[install.ServiceName] = serviceValue
|
||||
composeMap["services"] = servicesMap
|
||||
return composeMap, nil
|
||||
}
|
||||
|
||||
func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string, error) {
|
||||
if detail.DockerCompose == "" {
|
||||
return "", nil
|
||||
}
|
||||
composeMap, err := handleUpgradeCompose(install, detail)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
envs := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(install.Env), &envs); err != nil {
|
||||
return "", err
|
||||
@@ -618,7 +639,7 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dockerCLi, err := docker.NewClient()
|
||||
dockerCLi, _ := docker.NewClient()
|
||||
if req.PullImage {
|
||||
images, err := composeV2.GetDockerComposeImagesV2(content, []byte(detail.DockerCompose))
|
||||
if err != nil {
|
||||
@@ -675,63 +696,6 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
||||
}
|
||||
envParams := make(map[string]string, len(envs))
|
||||
handleMap(envs, envParams)
|
||||
if install.App.Key == "openresty" && install.App.Resource == "remote" && !common.CompareVersion(install.Version, "1.25.3.2-0-1") {
|
||||
t.Log(i18n.GetMsgByKey("MoveSiteDir"))
|
||||
siteDir := path.Join(global.Dir.DataDir, "www")
|
||||
envParams["WEBSITE_DIR"] = siteDir
|
||||
oldSiteDir := path.Join(install.GetPath(), "www")
|
||||
t.Log(i18n.GetWithName("MoveSiteToDir", siteDir))
|
||||
if err := fileOp.CopyDir(oldSiteDir, global.Dir.DataDir); err != nil {
|
||||
t.Log(i18n.GetMsgByKey("ErrMoveSiteDir"))
|
||||
return err
|
||||
}
|
||||
newConfDir := path.Join(global.Dir.DataDir, "www", "conf.d")
|
||||
_ = fileOp.CreateDir(newConfDir, constant.DirPerm)
|
||||
oldConfDir := path.Join(install.GetPath(), "conf/conf.d")
|
||||
items, err := os.ReadDir(oldConfDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, item := range items {
|
||||
itemPath := path.Join(oldConfDir, item.Name())
|
||||
if item.IsDir() {
|
||||
_ = fileOp.Mv(itemPath, newConfDir)
|
||||
}
|
||||
if item.Name() != "default.conf" && item.Name() != "00.default.conf" {
|
||||
_ = fileOp.Mv(itemPath, newConfDir)
|
||||
}
|
||||
}
|
||||
nginxConfPath := path.Join(install.GetPath(), "conf", "nginx.conf")
|
||||
parse, err := parser.NewParser(nginxConfPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config, err := parse.Parse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.FilePath = nginxConfPath
|
||||
httpDirective := config.FindHttp()
|
||||
httpDirective.UpdateDirective("include", []string{"/usr/local/openresty/nginx/conf/default/*.conf"})
|
||||
|
||||
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
return buserr.WithErr("ErrUpdateBuWebsite", err)
|
||||
}
|
||||
t.Log(i18n.GetMsgByKey("DeleteRuntimePHP"))
|
||||
_ = fileOp.DeleteDir(path.Join(global.Dir.RuntimeDir, "php"))
|
||||
websites, _ := websiteRepo.List(repo.WithByType("runtime"))
|
||||
for _, website := range websites {
|
||||
runtime, _ := runtimeRepo.GetFirst(context.Background(), repo.WithByID(website.RuntimeID))
|
||||
if runtime != nil && runtime.Type == "php" {
|
||||
website.Type = constant.Static
|
||||
website.RuntimeID = 0
|
||||
_ = websiteRepo.SaveWithoutCtx(&website)
|
||||
}
|
||||
}
|
||||
_ = runtimeRepo.DeleteBy(repo.WithByType("php"))
|
||||
t.Log(i18n.GetMsgByKey("MoveSiteDirSuccess"))
|
||||
}
|
||||
|
||||
if err = env.Write(envParams, install.GetEnvPath()); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -786,6 +750,15 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
||||
return appInstallRepo.Save(context.Background(), &install)
|
||||
}
|
||||
|
||||
func skipCheckStatus(service types.ServiceConfig) bool {
|
||||
for key := range service.Labels {
|
||||
if key == "skipStatusCheck" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getContainerNames(install model.AppInstall) ([]string, error) {
|
||||
envStr, err := coverEnvJsonToStr(install.Env)
|
||||
if err != nil {
|
||||
@@ -797,7 +770,7 @@ func getContainerNames(install model.AppInstall) ([]string, error) {
|
||||
}
|
||||
containerMap := make(map[string]struct{})
|
||||
for _, service := range project.AllServices() {
|
||||
if service.ContainerName == "${CONTAINER_NAME}" || service.ContainerName == "" {
|
||||
if service.ContainerName == "${CONTAINER_NAME}" || service.ContainerName == "" || skipCheckStatus(service) {
|
||||
continue
|
||||
}
|
||||
containerMap[service.ContainerName] = struct{}{}
|
||||
@@ -1363,7 +1336,7 @@ func doNotNeedSync(installed model.AppInstall) bool {
|
||||
installed.Status == constant.StatusSyncing || installed.Status == constant.StatusUninstalling || installed.Status == constant.StatusInstallErr
|
||||
}
|
||||
|
||||
func synAppInstall(containers map[string]types.Container, appInstall *model.AppInstall, force bool) {
|
||||
func synAppInstall(containers map[string]container.Summary, appInstall *model.AppInstall, force bool) {
|
||||
oldStatus := appInstall.Status
|
||||
containerNames := strings.Split(appInstall.ContainerName, ",")
|
||||
if len(containers) == 0 {
|
||||
@@ -1437,7 +1410,7 @@ func synAppInstall(containers map[string]types.Container, appInstall *model.AppI
|
||||
func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool) ([]response.AppInstallDTO, error) {
|
||||
var (
|
||||
res []response.AppInstallDTO
|
||||
containersMap map[string]types.Container
|
||||
containersMap map[string]container.Summary
|
||||
)
|
||||
if sync {
|
||||
cli, err := docker.NewClient()
|
||||
@@ -1445,7 +1418,7 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool)
|
||||
defer cli.Close()
|
||||
containers, err := cli.ListAllContainers()
|
||||
if err == nil {
|
||||
containersMap = make(map[string]types.Container, len(containers))
|
||||
containersMap = make(map[string]container.Summary, len(containers))
|
||||
for _, contain := range containers {
|
||||
containersMap[contain.Names[0]] = contain
|
||||
}
|
||||
@@ -1794,10 +1767,7 @@ func ignoreUpdate(installed model.AppInstall) bool {
|
||||
return true
|
||||
}
|
||||
ignores, _ := appIgnoreUpgradeRepo.List(appDetailRepo.WithAppId(installed.AppId), appIgnoreUpgradeRepo.WithScope("all"))
|
||||
if len(ignores) > 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return len(ignores) > 0
|
||||
}
|
||||
|
||||
func RequestDownloadCallBack(downloadCallBackUrl string) {
|
||||
|
||||
@@ -290,8 +290,6 @@ func (u *BackupService) RefreshToken(req dto.OperateByID) error {
|
||||
switch backup.Type {
|
||||
case constant.OneDrive:
|
||||
refreshToken, err = client.RefreshToken("refresh_token", "refreshToken", varMap)
|
||||
case constant.GoogleDrive:
|
||||
refreshToken, err = client.RefreshGoogleToken("refresh_token", "refreshToken", varMap)
|
||||
case constant.ALIYUN:
|
||||
refreshToken, err = client.RefreshALIToken(varMap)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
@@ -194,7 +193,7 @@ func (u *BackupRecordService) ListFiles(req dto.OperateByID) []string {
|
||||
var datas []string
|
||||
for _, file := range files {
|
||||
fileName := path.Base(file)
|
||||
if len(file) != 0 && (strings.HasPrefix(fileName, "1panel-v2.") || strings.HasPrefix(fileName, "snapshot-1panel-v2.")) {
|
||||
if len(file) != 0 && checkSnapshotIsOk(fileName) {
|
||||
datas = append(datas, path.Base(file))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -639,10 +639,7 @@ func handleAlert(stdout, clamName string, clamId uint) {
|
||||
EntryID: clamId,
|
||||
Param: strconv.Itoa(infectedFiles),
|
||||
}
|
||||
err := xpack.PushAlert(pushAlert)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("clamdscan push failed, err: %v", err)
|
||||
}
|
||||
_ = xpack.PushAlert(pushAlert)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
+110
-46
@@ -33,7 +33,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/build"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
@@ -83,6 +83,8 @@ type IContainerService interface {
|
||||
ComposeUpdate(req dto.ComposeUpdate) error
|
||||
Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error)
|
||||
|
||||
LoadUsers(req dto.OperationWithName) ([]string, error)
|
||||
|
||||
StreamLogs(ctx *gin.Context, params dto.StreamLog)
|
||||
}
|
||||
|
||||
@@ -92,8 +94,8 @@ func NewIContainerService() IContainerService {
|
||||
|
||||
func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, error) {
|
||||
var (
|
||||
records []types.Container
|
||||
list []types.Container
|
||||
records []container.Summary
|
||||
list []container.Summary
|
||||
)
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
@@ -163,7 +165,7 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
|
||||
total, start, end := len(list), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||
if start > total {
|
||||
records = make([]types.Container, 0)
|
||||
records = make([]container.Summary, 0)
|
||||
} else {
|
||||
if end >= total {
|
||||
end = total
|
||||
@@ -305,7 +307,7 @@ func (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(list))
|
||||
for i := 0; i < len(list); i++ {
|
||||
go func(item types.Container) {
|
||||
go func(item container.Summary) {
|
||||
datas = append(datas, loadCpuAndMem(client, item.ID))
|
||||
wg.Done()
|
||||
}(list[i])
|
||||
@@ -464,7 +466,7 @@ func (u *ContainerService) Prune(req dto.ContainerPrune) (dto.ContainerPruneRepo
|
||||
report.DeletedNumber = len(rep.VolumesDeleted)
|
||||
report.SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||
case "buildcache":
|
||||
opts := types.BuildCachePruneOptions{}
|
||||
opts := build.CachePruneOptions{}
|
||||
opts.All = true
|
||||
rep, err := client.BuildCachePrune(context.Background(), opts)
|
||||
if err != nil {
|
||||
@@ -882,16 +884,20 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
|
||||
func (u *ContainerService) StreamLogs(ctx *gin.Context, params dto.StreamLog) {
|
||||
messageChan := make(chan string, 1024)
|
||||
errorChan := make(chan error, 1)
|
||||
doneChan := make(chan struct{})
|
||||
|
||||
go collectLogs(params, messageChan, errorChan)
|
||||
|
||||
go func() {
|
||||
<-ctx.Request.Context().Done()
|
||||
close(doneChan)
|
||||
}()
|
||||
go collectLogs(doneChan, params, messageChan, errorChan)
|
||||
ctx.Stream(func(w io.Writer) bool {
|
||||
select {
|
||||
case msg, ok := <-messageChan:
|
||||
if !ok {
|
||||
return msg == ""
|
||||
return false
|
||||
}
|
||||
_, err := fmt.Fprintf(w, "data: %v\n\n", msg)
|
||||
_, err := fmt.Fprintf(w, "data: %s\n\n", msg)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -900,21 +906,15 @@ func (u *ContainerService) StreamLogs(ctx *gin.Context, params dto.StreamLog) {
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(w, "event: error\ndata: %v\n\n", err.Error())
|
||||
}
|
||||
return true
|
||||
case <-ctx.Request.Context().Done():
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func collectLogs(params dto.StreamLog, messageChan chan<- string, errorChan chan<- error) {
|
||||
func collectLogs(done <-chan struct{}, params dto.StreamLog, messageChan chan<- string, errorChan chan<- error) {
|
||||
defer close(messageChan)
|
||||
defer close(errorChan)
|
||||
|
||||
var cmdArgs []string
|
||||
if params.Type == "compose" {
|
||||
cmdArgs = []string{"compose", "-f", params.Compose}
|
||||
}
|
||||
cmdArgs = append(cmdArgs, "logs")
|
||||
if params.Follow {
|
||||
cmdArgs = append(cmdArgs, "-f")
|
||||
@@ -928,36 +928,85 @@ func collectLogs(params dto.StreamLog, messageChan chan<- string, errorChan chan
|
||||
if params.Container != "" {
|
||||
cmdArgs = append(cmdArgs, params.Container)
|
||||
}
|
||||
dockerCmd := exec.Command("docker", cmdArgs...)
|
||||
|
||||
var dockerCmd *exec.Cmd
|
||||
if params.Type == "compose" {
|
||||
dockerComposCmd := common.GetDockerComposeCommand()
|
||||
if dockerComposCmd == "docker-compose" {
|
||||
newCmdArgs := append([]string{"-f", params.Compose}, cmdArgs...)
|
||||
dockerCmd = exec.Command(dockerComposCmd, newCmdArgs...)
|
||||
} else {
|
||||
newCmdArgs := append([]string{"compose", "-f", params.Compose}, cmdArgs...)
|
||||
dockerCmd = exec.Command("docker", newCmdArgs...)
|
||||
}
|
||||
} else {
|
||||
dockerCmd = exec.Command("docker", cmdArgs...)
|
||||
}
|
||||
|
||||
dockerCmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
|
||||
stdout, err := dockerCmd.StdoutPipe()
|
||||
if err != nil {
|
||||
errorChan <- fmt.Errorf("failed to get stdout pipe: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
dockerCmd.Stderr = dockerCmd.Stdout
|
||||
|
||||
if err = dockerCmd.Start(); err != nil {
|
||||
errorChan <- fmt.Errorf("failed to start command: %v", err)
|
||||
errorChan <- fmt.Errorf("failed to start docker logs command: %v", err)
|
||||
return
|
||||
}
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
for scanner.Scan() {
|
||||
message := scanner.Text()
|
||||
|
||||
defer func() {
|
||||
if dockerCmd.Process != nil {
|
||||
if pgid, err := syscall.Getpgid(dockerCmd.Process.Pid); err == nil {
|
||||
_ = syscall.Kill(-pgid, syscall.SIGKILL)
|
||||
}
|
||||
_ = dockerCmd.Process.Kill()
|
||||
_ = dockerCmd.Wait()
|
||||
}
|
||||
}()
|
||||
|
||||
reader := bufio.NewReader(stdout)
|
||||
|
||||
processKilled := false
|
||||
go func() {
|
||||
<-done
|
||||
if !processKilled && dockerCmd.Process != nil {
|
||||
processKilled = true
|
||||
if pgid, err := syscall.Getpgid(dockerCmd.Process.Pid); err == nil {
|
||||
_ = syscall.Kill(-pgid, syscall.SIGKILL)
|
||||
}
|
||||
_ = dockerCmd.Process.Kill()
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
line, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
if len(line) > 0 {
|
||||
line = strings.TrimSuffix(line, "\n")
|
||||
select {
|
||||
case messageChan <- line:
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
errorChan <- fmt.Errorf("reader error: %v", err)
|
||||
return
|
||||
}
|
||||
line = strings.TrimSuffix(line, "\n")
|
||||
select {
|
||||
case messageChan <- message:
|
||||
case <-time.After(5 * time.Second):
|
||||
errorChan <- fmt.Errorf("message channel blocked")
|
||||
case messageChan <- line:
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
if err = scanner.Err(); err != nil {
|
||||
errorChan <- fmt.Errorf("scanner error: %v", err)
|
||||
return
|
||||
}
|
||||
if err = dockerCmd.Wait(); err != nil {
|
||||
errorChan <- fmt.Errorf("%v", err)
|
||||
return
|
||||
}
|
||||
_ = dockerCmd.Wait()
|
||||
}
|
||||
|
||||
func (u *ContainerService) DownloadContainerLogs(containerType, container, since, tail string, c *gin.Context) error {
|
||||
@@ -1065,6 +1114,21 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) LoadUsers(req dto.OperationWithName) ([]string, error) {
|
||||
std, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s cat /etc/passwd", req.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var users []string
|
||||
lines := strings.Split(string(std), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, ":") {
|
||||
users = append(users, strings.Split(line, ":")[0])
|
||||
}
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func stringsToMap(list []string) map[string]string {
|
||||
var labelMap = make(map[string]string)
|
||||
for _, label := range list {
|
||||
@@ -1257,7 +1321,7 @@ func checkPortStats(ports []dto.PortHelper) (nat.PortMap, error) {
|
||||
return portMap, nil
|
||||
}
|
||||
|
||||
func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types.ContainerJSON) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error) {
|
||||
func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *container.InspectResponse) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error) {
|
||||
var config container.Config
|
||||
var hostConf container.HostConfig
|
||||
if !isCreate {
|
||||
@@ -1340,7 +1404,7 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
|
||||
return &config, &hostConf, &networkConf, nil
|
||||
}
|
||||
|
||||
func reCreateAfterUpdate(name string, client *client.Client, config *container.Config, hostConf *container.HostConfig, networkConf *types.NetworkSettings) {
|
||||
func reCreateAfterUpdate(name string, client *client.Client, config *container.Config, hostConf *container.HostConfig, networkConf *container.NetworkSettings) {
|
||||
ctx := context.Background()
|
||||
|
||||
var oldNetworkConf network.NetworkingConfig
|
||||
@@ -1362,7 +1426,7 @@ func reCreateAfterUpdate(name string, client *client.Client, config *container.C
|
||||
global.LOG.Info("recreate after container update successful")
|
||||
}
|
||||
|
||||
func loadVolumeBinds(binds []types.MountPoint) []dto.VolumeHelper {
|
||||
func loadVolumeBinds(binds []container.MountPoint) []dto.VolumeHelper {
|
||||
var datas []dto.VolumeHelper
|
||||
for _, bind := range binds {
|
||||
var volumeItem dto.VolumeHelper
|
||||
@@ -1382,13 +1446,13 @@ func loadVolumeBinds(binds []types.MountPoint) []dto.VolumeHelper {
|
||||
return datas
|
||||
}
|
||||
|
||||
func loadPortByInspect(id string, client *client.Client) ([]types.Port, error) {
|
||||
container, err := client.ContainerInspect(context.Background(), id)
|
||||
func loadPortByInspect(id string, client *client.Client) ([]container.Port, error) {
|
||||
containerItem, err := client.ContainerInspect(context.Background(), id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var itemPorts []types.Port
|
||||
for key, val := range container.ContainerJSONBase.HostConfig.PortBindings {
|
||||
var itemPorts []container.Port
|
||||
for key, val := range containerItem.ContainerJSONBase.HostConfig.PortBindings {
|
||||
if !strings.Contains(string(key), "/") {
|
||||
continue
|
||||
}
|
||||
@@ -1397,15 +1461,15 @@ func loadPortByInspect(id string, client *client.Client) ([]types.Port, error) {
|
||||
|
||||
for _, itemVal := range val {
|
||||
publicPort, _ := strconv.ParseUint(itemVal.HostPort, 10, 16)
|
||||
itemPorts = append(itemPorts, types.Port{PrivatePort: uint16(itemPort), Type: item[1], PublicPort: uint16(publicPort), IP: itemVal.HostIP})
|
||||
itemPorts = append(itemPorts, container.Port{PrivatePort: uint16(itemPort), Type: item[1], PublicPort: uint16(publicPort), IP: itemVal.HostIP})
|
||||
}
|
||||
}
|
||||
return itemPorts, nil
|
||||
}
|
||||
func transPortToStr(ports []types.Port) []string {
|
||||
func transPortToStr(ports []container.Port) []string {
|
||||
var (
|
||||
ipv4Ports []types.Port
|
||||
ipv6Ports []types.Port
|
||||
ipv4Ports []container.Port
|
||||
ipv6Ports []container.Port
|
||||
)
|
||||
for _, port := range ports {
|
||||
if strings.Contains(port.IP, ":") {
|
||||
@@ -1418,7 +1482,7 @@ func transPortToStr(ports []types.Port) []string {
|
||||
list2 := simplifyPort(ipv6Ports)
|
||||
return append(list1, list2...)
|
||||
}
|
||||
func simplifyPort(ports []types.Port) []string {
|
||||
func simplifyPort(ports []container.Port) []string {
|
||||
var datas []string
|
||||
if len(ports) == 0 {
|
||||
return datas
|
||||
@@ -1514,7 +1578,7 @@ func loadComposeCount(client *client.Client) int {
|
||||
|
||||
return len(composeMap)
|
||||
}
|
||||
func loadContainerPortForInfo(itemPorts []types.Port) []dto.PortHelper {
|
||||
func loadContainerPortForInfo(itemPorts []container.Port) []dto.PortHelper {
|
||||
var exposedPorts []dto.PortHelper
|
||||
samePortMap := make(map[string]dto.PortHelper)
|
||||
ports := transPortToStr(itemPorts)
|
||||
|
||||
@@ -73,6 +73,9 @@ func (u *CronjobService) SearchWithPage(search dto.PageCronjob) (int64, interfac
|
||||
} else {
|
||||
item.AlertCount = 0
|
||||
}
|
||||
if cronjob.Type == "snapshot" && len(cronjob.SnapshotRule) != 0 {
|
||||
_ = json.Unmarshal([]byte(cronjob.SnapshotRule), &item.SnapshotRule)
|
||||
}
|
||||
dtoCronjobs = append(dtoCronjobs, item)
|
||||
}
|
||||
return total, dtoCronjobs, err
|
||||
@@ -94,6 +97,9 @@ func (u *CronjobService) LoadInfo(req dto.OperateByID) (*dto.CronjobOperate, err
|
||||
} else {
|
||||
item.AlertCount = 0
|
||||
}
|
||||
if cronjob.Type == "snapshot" && len(cronjob.SnapshotRule) != 0 {
|
||||
_ = json.Unmarshal([]byte(cronjob.SnapshotRule), &item.SnapshotRule)
|
||||
}
|
||||
return &item, err
|
||||
}
|
||||
|
||||
@@ -279,6 +285,13 @@ func (u *CronjobService) Create(req dto.CronjobOperate) error {
|
||||
if err := copier.Copy(&cronjob, &req); err != nil {
|
||||
return buserr.WithDetail("ErrStructTransform", err.Error(), nil)
|
||||
}
|
||||
if cronjob.Type == "snapshot" {
|
||||
rule, err := json.Marshal(req.SnapshotRule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cronjob.SnapshotRule = string(rule)
|
||||
}
|
||||
if cronjob.Type == "cutWebsiteLog" {
|
||||
backupAccount, err := backupRepo.Get(repo.WithByType(constant.Local))
|
||||
if backupAccount.ID == 0 {
|
||||
@@ -372,6 +385,13 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate) error {
|
||||
if err := copier.Copy(&cronjob, &req); err != nil {
|
||||
return buserr.WithDetail("ErrStructTransform", err.Error(), nil)
|
||||
}
|
||||
if req.Type == "snapshot" {
|
||||
itemRule, err := json.Marshal(req.SnapshotRule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cronjob.SnapshotRule = string(itemRule)
|
||||
}
|
||||
cronModel, err := cronjobRepo.Get(repo.WithByID(id))
|
||||
if err != nil {
|
||||
return buserr.New("ErrRecordNotFound")
|
||||
@@ -412,12 +432,14 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate) error {
|
||||
upMap["db_name"] = req.DBName
|
||||
upMap["url"] = req.URL
|
||||
upMap["source_dir"] = req.SourceDir
|
||||
upMap["snapshot_rule"] = cronjob.SnapshotRule
|
||||
|
||||
upMap["source_account_ids"] = req.SourceAccountIDs
|
||||
upMap["download_account_id"] = req.DownloadAccountID
|
||||
upMap["retain_copies"] = req.RetainCopies
|
||||
upMap["retry_times"] = req.RetryTimes
|
||||
upMap["timeout"] = req.Timeout
|
||||
upMap["ignore_err"] = req.IgnoreErr
|
||||
upMap["secret"] = req.Secret
|
||||
err = cronjobRepo.Update(id, upMap)
|
||||
if err != nil {
|
||||
|
||||
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
@@ -47,6 +48,7 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, t
|
||||
return err
|
||||
}
|
||||
for _, app := range apps {
|
||||
retry := 0
|
||||
taskItem.AddSubTaskWithOps(task.GetTaskName(app.Name, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
|
||||
var record model.BackupRecord
|
||||
record.From = "cronjob"
|
||||
@@ -58,11 +60,22 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, t
|
||||
backupDir := path.Join(global.Dir.TmpDir, fmt.Sprintf("app/%s/%s", app.App.Key, app.Name))
|
||||
record.FileName = fmt.Sprintf("app_%s_%s.tar.gz", app.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||
if err := doAppBackup(&app, task, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
|
||||
return err
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
} else {
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, record.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
}
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreUploadErr", err.Error()))
|
||||
return nil
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
@@ -86,6 +99,7 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
|
||||
return err
|
||||
}
|
||||
for _, web := range webs {
|
||||
retry := 0
|
||||
taskItem.AddSubTaskWithOps(task.GetTaskName(web.Alias, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
|
||||
var record model.BackupRecord
|
||||
record.From = "cronjob"
|
||||
@@ -98,12 +112,23 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
|
||||
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", web.Alias, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||
|
||||
if err := doWebsiteBackup(&web, taskItem, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
|
||||
return err
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
} else {
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, record.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
}
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreUploadErr", err.Error()))
|
||||
return nil
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
@@ -127,6 +152,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
|
||||
return err
|
||||
}
|
||||
for _, dbInfo := range dbs {
|
||||
retry := 0
|
||||
itemName := fmt.Sprintf("%s[%s] - %s", dbInfo.Database, dbInfo.DBType, dbInfo.Name)
|
||||
taskItem.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
|
||||
var record model.BackupRecord
|
||||
@@ -141,17 +167,34 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
|
||||
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
|
||||
if err := doMysqlBackup(dbInfo, backupDir, record.FileName); err != nil {
|
||||
return err
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
} else {
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := doPostgresqlgBackup(dbInfo, backupDir, record.FileName); err != nil {
|
||||
return err
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
} else {
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, record.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
}
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreUploadErr", err.Error()))
|
||||
return nil
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
@@ -193,7 +236,7 @@ func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.T
|
||||
record.CronjobID = cronjob.ID
|
||||
record.Name = cronjob.Name
|
||||
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, fileName))
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, fileName))
|
||||
if err != nil {
|
||||
taskItem.LogFailedWithErr("Upload backup file", err)
|
||||
return err
|
||||
@@ -228,7 +271,7 @@ func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.T
|
||||
record.CronjobID = cronjob.ID
|
||||
record.Name = cronjob.Name
|
||||
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(path.Dir(backupDir), fileName))
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(path.Dir(backupDir), fileName))
|
||||
if err != nil {
|
||||
taskItem.LogFailedWithErr("Upload backup file", err)
|
||||
return err
|
||||
@@ -250,10 +293,6 @@ func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.J
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
itemData, err := NewISnapshotService().LoadSnapshotData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var record model.BackupRecord
|
||||
record.From = "cronjob"
|
||||
@@ -268,6 +307,11 @@ func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.J
|
||||
if !global.IsMaster {
|
||||
scope = "agent"
|
||||
}
|
||||
|
||||
itemData, err := loadSnapWithRule(cronjob)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req := dto.SnapshotCreate{
|
||||
Name: fmt.Sprintf("snapshot-1panel-%s-%s-linux-%s-%s", scope, versionItem.Value, loadOs(), jobRecord.StartTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)),
|
||||
Secret: cronjob.Secret,
|
||||
@@ -278,11 +322,13 @@ func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.J
|
||||
AppData: itemData.AppData,
|
||||
PanelData: itemData.PanelData,
|
||||
BackupData: itemData.BackupData,
|
||||
WithDockerConf: true,
|
||||
WithMonitorData: true,
|
||||
WithLoginLog: true,
|
||||
WithOperationLog: true,
|
||||
WithSystemLog: true,
|
||||
WithTaskLog: true,
|
||||
IgnoreFiles: strings.Split(cronjob.ExclusionRules, ","),
|
||||
}
|
||||
|
||||
if err := NewISnapshotService().SnapshotCreate(taskItem, req, jobRecord.ID, cronjob.RetryTimes, cronjob.Timeout); err != nil {
|
||||
@@ -315,7 +361,7 @@ func loadDbsForJob(cronjob model.Cronjob) []DatabaseHelper {
|
||||
dbs = append(dbs, DatabaseHelper{
|
||||
ID: mysql.ID,
|
||||
DBType: cronjob.DBType,
|
||||
Database: mysql.Name,
|
||||
Database: mysql.Database,
|
||||
Name: mysql.Name,
|
||||
})
|
||||
}
|
||||
@@ -451,3 +497,52 @@ func handleBackupLogs(taskItem *task.Task, targetDir, fileName string, secret st
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadSnapWithRule(cronjob model.Cronjob) (dto.SnapshotData, error) {
|
||||
itemData, err := NewISnapshotService().LoadSnapshotData()
|
||||
if err != nil {
|
||||
return itemData, err
|
||||
}
|
||||
|
||||
if len(cronjob.SnapshotRule) == 0 {
|
||||
return itemData, nil
|
||||
}
|
||||
var snapRule dto.SnapshotRule
|
||||
if err := json.Unmarshal([]byte(cronjob.SnapshotRule), &snapRule); err != nil {
|
||||
return itemData, err
|
||||
}
|
||||
if len(snapRule.IgnoreAppIDs) == 0 && !snapRule.WithImage {
|
||||
return itemData, nil
|
||||
}
|
||||
|
||||
var ignoreApps []model.AppInstall
|
||||
if len(snapRule.IgnoreAppIDs) != 0 {
|
||||
ignoreApps, _ = appInstallRepo.ListBy(context.Background(), repo.WithByIDs(snapRule.IgnoreAppIDs))
|
||||
}
|
||||
if len(ignoreApps) == 0 && !snapRule.WithImage {
|
||||
return itemData, nil
|
||||
}
|
||||
for i := 0; i < len(itemData.AppData); i++ {
|
||||
isIgnore := false
|
||||
for _, ignore := range ignoreApps {
|
||||
if ignore.App.Key == itemData.AppData[i].Key && ignore.Name == itemData.AppData[i].Name {
|
||||
isIgnore = true
|
||||
itemData.AppData[i].IsCheck = false
|
||||
for j := 0; j < len(itemData.AppData[i].Children); j++ {
|
||||
if itemData.AppData[i].Children[j].Label == "appData" {
|
||||
itemData.AppData[i].Children[j].IsCheck = false
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if snapRule.WithImage && !isIgnore {
|
||||
for j := 0; j < len(itemData.AppData[i].Children); j++ {
|
||||
if itemData.AppData[i].Children[j].Label == "appImage" {
|
||||
itemData.AppData[i].Children[j].IsCheck = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return itemData, nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
pathUtils "path"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -78,17 +79,21 @@ func (u *CronjobService) loadTask(cronjob *model.Cronjob, record *model.JobRecor
|
||||
return fmt.Errorf("the script content is empty and is skipped")
|
||||
}
|
||||
u.handleShell(*cronjob, taskItem)
|
||||
u.removeExpiredLog(*cronjob)
|
||||
case "curl":
|
||||
if len(cronjob.URL) == 0 {
|
||||
return fmt.Errorf("the url is empty and is skipped")
|
||||
}
|
||||
u.handleCurl(*cronjob, taskItem)
|
||||
u.removeExpiredLog(*cronjob)
|
||||
case "ntp":
|
||||
u.handleNtpSync(*cronjob, taskItem)
|
||||
u.removeExpiredLog(*cronjob)
|
||||
case "cutWebsiteLog":
|
||||
err = u.handleCutWebsiteLog(cronjob, record.StartTime, taskItem)
|
||||
case "clean":
|
||||
u.handleSystemClean(*cronjob, taskItem)
|
||||
u.removeExpiredLog(*cronjob)
|
||||
case "website":
|
||||
err = u.handleWebsite(*cronjob, record.StartTime, taskItem)
|
||||
case "app":
|
||||
@@ -113,11 +118,21 @@ func (u *CronjobService) handleShell(cronjob model.Cronjob, taskItem *task.Task)
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem))
|
||||
taskItem.AddSubTaskWithOps(i18n.GetWithName("HandleShell", cronjob.Name), func(t *task.Task) error {
|
||||
if len(cronjob.ContainerName) != 0 {
|
||||
scriptItem := cronjob.Script
|
||||
if cronjob.ScriptMode == "select" {
|
||||
scriptItem = path.Join("/tmp", path.Base(cronjob.Script))
|
||||
if err := cmdMgr.Run("docker", "cp", cronjob.Script, cronjob.ContainerName+":"+scriptItem); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
command := "sh"
|
||||
if len(cronjob.Command) != 0 {
|
||||
command = cronjob.Command
|
||||
}
|
||||
return cmdMgr.Run("docker", "exec", cronjob.ContainerName, command, "-c", strings.ReplaceAll(cronjob.Script, "\"", "\\\""))
|
||||
if len(cronjob.User) != 0 {
|
||||
return cmdMgr.Run("docker", "exec", "-u", cronjob.User, cronjob.ContainerName, command, "-c", strings.ReplaceAll(scriptItem, "\"", "\\\""))
|
||||
}
|
||||
return cmdMgr.Run("docker", "exec", cronjob.ContainerName, command, "-c", strings.ReplaceAll(scriptItem, "\"", "\\\""))
|
||||
}
|
||||
if len(cronjob.Executor) == 0 {
|
||||
cronjob.Executor = "bash"
|
||||
@@ -247,23 +262,32 @@ func (u *CronjobService) handleSystemClean(cronjob model.Cronjob, taskItem *task
|
||||
taskItem.AddSubTaskWithOps(i18n.GetMsgByKey("HandleSystemClean"), cleanTask, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
|
||||
}
|
||||
|
||||
func (u *CronjobService) uploadCronjobBackFile(cronjob model.Cronjob, accountMap map[string]backupClientHelper, file string) (string, error) {
|
||||
func (u *CronjobService) uploadCronjobBackFile(cronjob model.Cronjob, task *task.Task, accountMap map[string]backupClientHelper, file string) (string, error) {
|
||||
defer func() {
|
||||
_ = os.Remove(file)
|
||||
}()
|
||||
var errItem error
|
||||
accounts := strings.Split(cronjob.SourceAccountIDs, ",")
|
||||
cloudSrc := strings.TrimPrefix(file, global.Dir.TmpDir+"/")
|
||||
for _, account := range accounts {
|
||||
if len(account) != 0 {
|
||||
global.LOG.Debugf("start upload file to %s, dir: %s", accountMap[account].name, pathUtils.Join(accountMap[account].backupPath, cloudSrc))
|
||||
if _, err := accountMap[account].client.Upload(file, pathUtils.Join(accountMap[account].backupPath, cloudSrc)); err != nil {
|
||||
global.LOG.Errorf("upload file to %s failed, err: %v", accountMap[account].name, err)
|
||||
task.LogStart(i18n.GetMsgWithMap("UploadFile", map[string]interface{}{
|
||||
"file": pathUtils.Join(accountMap[account].backupPath, cloudSrc),
|
||||
"backup": accountMap[account].name,
|
||||
}))
|
||||
_, err := accountMap[account].client.Upload(file, pathUtils.Join(accountMap[account].backupPath, cloudSrc))
|
||||
task.LogWithStatus(
|
||||
i18n.GetMsgWithMap("UploadFile", map[string]interface{}{
|
||||
"file": pathUtils.Join(accountMap[account].backupPath, cloudSrc),
|
||||
"backup": accountMap[account].name,
|
||||
}), err)
|
||||
if err != nil {
|
||||
errItem = err
|
||||
continue
|
||||
}
|
||||
global.LOG.Debugf("upload successful!")
|
||||
}
|
||||
}
|
||||
return cloudSrc, nil
|
||||
return cloudSrc, errItem
|
||||
}
|
||||
|
||||
func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap map[string]backupClientHelper, record model.BackupRecord) {
|
||||
@@ -306,6 +330,24 @@ func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap m
|
||||
}
|
||||
}
|
||||
|
||||
func (u *CronjobService) removeExpiredLog(cronjob model.Cronjob) {
|
||||
records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), repo.WithOrderBy("created_at desc"))
|
||||
if len(records) <= int(cronjob.RetainCopies) {
|
||||
return
|
||||
}
|
||||
for i := int(cronjob.RetainCopies); i < len(records); i++ {
|
||||
if len(records[i].File) != 0 {
|
||||
files := strings.Split(records[i].File, ",")
|
||||
for _, file := range files {
|
||||
_ = os.Remove(file)
|
||||
}
|
||||
}
|
||||
_ = cronjobRepo.DeleteRecord(repo.WithByID(uint(records[i].ID)))
|
||||
_ = taskRepo.Delete(taskRepo.WithByID(records[i].TaskID))
|
||||
_ = os.Remove(path.Join(global.CONF.Base.InstallDir, "1panel/log/task/Cronjob", records[i].TaskID+".log"))
|
||||
}
|
||||
}
|
||||
|
||||
func hasBackup(cronjobType string) bool {
|
||||
return cronjobType == "app" || cronjobType == "database" || cronjobType == "website" || cronjobType == "directory" || cronjobType == "snapshot" || cronjobType == "log" || cronjobType == "cutWebsiteLog"
|
||||
}
|
||||
@@ -317,9 +359,5 @@ func handleCronJobAlert(cronjob *model.Cronjob) {
|
||||
EntryID: cronjob.ID,
|
||||
Param: cronjob.Type,
|
||||
}
|
||||
err := xpack.PushAlert(pushAlert)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("cronjob alert push failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
_ = xpack.PushAlert(pushAlert)
|
||||
}
|
||||
|
||||
@@ -390,10 +390,10 @@ type diskInfo struct {
|
||||
func loadDiskInfo() []dto.DiskInfo {
|
||||
var datas []dto.DiskInfo
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(2 * time.Second))
|
||||
stdout, err := cmdMgr.RunWithStdoutBashC("df -hT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev")
|
||||
stdout, err := cmdMgr.RunWithStdoutBashC("timeout 2 df -hT -P | awk 'NR>1 && !/tmpfs|snap\\/core|udev/ {print}'")
|
||||
if err != nil {
|
||||
cmdMgr2 := cmd.NewCommandMgr(cmd.WithTimeout(1 * time.Second))
|
||||
stdout, err = cmdMgr2.RunWithStdoutBashC("df -lhT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev")
|
||||
stdout, err = cmdMgr2.RunWithStdoutBashC("timeout 1 df -lhT -P | awk 'NR>1 && !/tmpfs|snap\\/core|udev/ {print}'")
|
||||
if err != nil {
|
||||
return datas
|
||||
}
|
||||
|
||||
@@ -246,18 +246,23 @@ func (u *DeviceService) UpdateSwap(req dto.SwapHelper) error {
|
||||
}
|
||||
return operateSwapWithFile(true, req)
|
||||
}
|
||||
std1, err := cmd.RunDefaultWithStdoutBashCf("%s dd if=/dev/zero of=%s bs=1024 count=%d", cmd.SudoHandleCmd(), req.Path, req.Size)
|
||||
stdDD, err := cmd.RunDefaultWithStdoutBashCf("%s dd if=/dev/zero of=%s bs=1024 count=%d", cmd.SudoHandleCmd(), req.Path, req.Size)
|
||||
if err != nil {
|
||||
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std1)
|
||||
return fmt.Errorf("handle dd %s failed, std: %s, err: %s", req.Path, stdDD, err)
|
||||
}
|
||||
std2, err := cmd.RunDefaultWithStdoutBashCf("%s mkswap -f %s", cmd.SudoHandleCmd(), req.Path)
|
||||
stdChmod, err := cmd.RunDefaultWithStdoutBashCf("%s chmod 0600 %s", cmd.SudoHandleCmd(), req.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std2)
|
||||
return fmt.Errorf("handle chmod 0600 %s failed,std: %s, err: %s", req.Path, stdChmod, err)
|
||||
}
|
||||
std3, err := cmd.RunDefaultWithStdoutBashCf("%s swapon %s", cmd.SudoHandleCmd(), req.Path)
|
||||
stdMkswap, err := cmd.RunDefaultWithStdoutBashCf("%s mkswap -f %s", cmd.SudoHandleCmd(), req.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("handle mkswap -f %s failed, std: %s, err: %s", req.Path, stdMkswap, err)
|
||||
}
|
||||
|
||||
stdSwapon, err := cmd.RunDefaultWithStdoutBashCf("%s swapon %s", cmd.SudoHandleCmd(), req.Path)
|
||||
if err != nil {
|
||||
_, _ = cmd.RunDefaultWithStdoutBashCf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
|
||||
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std3)
|
||||
return fmt.Errorf("handle swapoff %s failed,std: %s, err: %s", req.Path, stdSwapon, err)
|
||||
}
|
||||
return operateSwapWithFile(false, req)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/build"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
@@ -50,7 +52,7 @@ func (u *DeviceService) Scan() dto.CleanData {
|
||||
ID: uuid.NewString(),
|
||||
Label: "1panel_original",
|
||||
Size: uint64(originalSize),
|
||||
IsCheck: true,
|
||||
IsCheck: originalSize > 0,
|
||||
IsRecommend: true,
|
||||
Type: "1panel_original",
|
||||
Children: loadTreeWithDir(true, "1panel_original", originalPath, fileOp),
|
||||
@@ -85,7 +87,7 @@ func (u *DeviceService) Scan() dto.CleanData {
|
||||
ID: uuid.NewString(),
|
||||
Label: "snapshot",
|
||||
Size: snapSize,
|
||||
IsCheck: true,
|
||||
IsCheck: snapSize > 0,
|
||||
IsRecommend: true,
|
||||
Type: "snapshot",
|
||||
Children: snapTree,
|
||||
@@ -100,7 +102,7 @@ func (u *DeviceService) Scan() dto.CleanData {
|
||||
ID: uuid.NewString(),
|
||||
Label: "rollback",
|
||||
Size: rollbackSize,
|
||||
IsCheck: true,
|
||||
IsCheck: rollbackSize > 0,
|
||||
IsRecommend: true,
|
||||
Type: "rollback",
|
||||
Children: rollBackTree,
|
||||
@@ -126,7 +128,7 @@ func (u *DeviceService) Scan() dto.CleanData {
|
||||
ID: uuid.NewString(),
|
||||
Label: "unused",
|
||||
Size: unusedSize,
|
||||
IsCheck: true,
|
||||
IsCheck: unusedSize > 0,
|
||||
IsRecommend: true,
|
||||
Type: "unused",
|
||||
Children: unusedTree,
|
||||
@@ -338,7 +340,7 @@ func doSystemClean(taskItem *task.Task) func(t *task.Task) error {
|
||||
}
|
||||
|
||||
dropWithTask(path.Join(global.Dir.BaseDir, tmpUploadPath), taskItem, &size, &fileCount)
|
||||
dropWithTask(path.Join(global.Dir.BaseDir, uploadPath), taskItem, &size, &fileCount)
|
||||
dropWithExclude(path.Join(global.Dir.BaseDir, uploadPath), []string{"theme"}, taskItem, &size, &fileCount)
|
||||
dropWithTask(path.Join(global.Dir.BaseDir, downloadPath), taskItem, &size, &fileCount)
|
||||
|
||||
logFiles, _ := os.ReadDir(global.Dir.LogDir)
|
||||
@@ -662,7 +664,7 @@ func dropBuildCache() {
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do not get docker client")
|
||||
}
|
||||
opts := types.BuildCachePruneOptions{}
|
||||
opts := build.CachePruneOptions{}
|
||||
opts.All = true
|
||||
_, err = client.BuildCachePrune(context.Background(), opts)
|
||||
if err != nil {
|
||||
@@ -714,6 +716,30 @@ func dropVolumes() {
|
||||
}
|
||||
}
|
||||
|
||||
func dropWithExclude(pathToDelete string, excludeSubDirs []string, taskItem *task.Task, size *int64, count *int) {
|
||||
entries, err := os.ReadDir(pathToDelete)
|
||||
if err != nil {
|
||||
taskItem.LogFailed(fmt.Sprintf("read dir %s failed: %v", pathToDelete, err))
|
||||
return
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
name := entry.Name()
|
||||
fullPath := filepath.Join(pathToDelete, name)
|
||||
excluded := false
|
||||
for _, ex := range excludeSubDirs {
|
||||
if name == ex {
|
||||
excluded = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if excluded {
|
||||
continue
|
||||
}
|
||||
dropWithTask(fullPath, taskItem, size, count)
|
||||
}
|
||||
}
|
||||
|
||||
func dropWithTask(itemPath string, taskItem *task.Task, size *int64, count *int) {
|
||||
itemSize := int64(0)
|
||||
itemCount := 0
|
||||
|
||||
@@ -220,16 +220,6 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate, withRestart bool) erro
|
||||
daemonMap["proxies"] = proxies
|
||||
}
|
||||
}
|
||||
if len(daemonMap) == 0 {
|
||||
if len(file) == 0 {
|
||||
return nil
|
||||
}
|
||||
_ = os.Remove(constant.DaemonJsonPath)
|
||||
if withRestart {
|
||||
return restartDocker()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
newJson, err := json.MarshalIndent(daemonMap, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -277,11 +267,6 @@ func (u *DockerService) UpdateLogOption(req dto.LogOption) error {
|
||||
_ = json.Unmarshal(file, &daemonMap)
|
||||
|
||||
changeLogOption(daemonMap, req.LogMaxFile, req.LogMaxSize)
|
||||
if len(daemonMap) == 0 {
|
||||
_ = os.Remove(constant.DaemonJsonPath)
|
||||
_ = restartDocker()
|
||||
return nil
|
||||
}
|
||||
newJson, err := json.MarshalIndent(daemonMap, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -321,11 +306,6 @@ func (u *DockerService) UpdateIpv6Option(req dto.Ipv6Option) error {
|
||||
if req.Experimental {
|
||||
daemonMap["experimental"] = req.Experimental
|
||||
}
|
||||
if len(daemonMap) == 0 {
|
||||
_ = os.Remove(constant.DaemonJsonPath)
|
||||
_ = restartDocker()
|
||||
return nil
|
||||
}
|
||||
newJson, err := json.MarshalIndent(daemonMap, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -345,13 +325,6 @@ func (u *DockerService) UpdateIpv6Option(req dto.Ipv6Option) error {
|
||||
}
|
||||
|
||||
func (u *DockerService) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error {
|
||||
if len(req.File) == 0 {
|
||||
_ = os.Remove(constant.DaemonJsonPath)
|
||||
if err := restartDocker(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
err := createIfNotExistDaemonJsonFile()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
+27
-14
@@ -4,7 +4,10 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/jinzhu/copier"
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
@@ -15,9 +18,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"golang.org/x/net/html/charset"
|
||||
"golang.org/x/sys/unix"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/transform"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
@@ -335,17 +334,22 @@ func (f *FileService) GetContent(op request.FileContentReq) (response.FileInfo,
|
||||
if len(content) > 1024 {
|
||||
content = content[:1024]
|
||||
}
|
||||
if !utf8.Valid(content) {
|
||||
_, decodeName, _ := charset.DetermineEncoding(content, "")
|
||||
_, decodeName, _ := charset.DetermineEncoding(content, "")
|
||||
decoder := files.GetDecoderByName(decodeName)
|
||||
if decoder != nil {
|
||||
reader := strings.NewReader(info.Content)
|
||||
var dec *encoding.Decoder
|
||||
if decodeName == "windows-1252" {
|
||||
reader := strings.NewReader(info.Content)
|
||||
item := transform.NewReader(reader, simplifiedchinese.GBK.NewDecoder())
|
||||
contents, err := io.ReadAll(item)
|
||||
if err != nil {
|
||||
return response.FileInfo{}, err
|
||||
}
|
||||
info.Content = string(contents)
|
||||
dec = simplifiedchinese.GBK.NewDecoder()
|
||||
} else {
|
||||
dec = decoder.NewDecoder()
|
||||
}
|
||||
decodedReader := transform.NewReader(reader, dec)
|
||||
contents, err := io.ReadAll(decodedReader)
|
||||
if err != nil {
|
||||
return response.FileInfo{}, err
|
||||
}
|
||||
info.Content = string(contents)
|
||||
}
|
||||
return response.FileInfo{FileInfo: *info}, nil
|
||||
}
|
||||
@@ -390,10 +394,18 @@ func (f *FileService) MvFile(m request.FileMove) error {
|
||||
return buserr.New("ErrMovePathFailed")
|
||||
}
|
||||
}
|
||||
var errs []error
|
||||
if m.Type == "cut" {
|
||||
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("cut copy file [%s] to [%s] failed, err: %s", src, m.NewPath, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
return fo.Cut(m.OldPaths, m.NewPath, m.Name, m.Cover)
|
||||
}
|
||||
var errs []error
|
||||
if m.Type == "copy" {
|
||||
for _, src := range m.OldPaths {
|
||||
if err := fo.CopyAndReName(src, m.NewPath, m.Name, m.Cover); err != nil {
|
||||
@@ -565,6 +577,7 @@ func (f *FileService) BatchCheckFiles(req request.FilePathsCheck) []response.Exi
|
||||
Name: info.Name(),
|
||||
Path: filePath,
|
||||
ModTime: info.ModTime(),
|
||||
IsDir: info.IsDir(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,6 +182,7 @@ func (u *FirewallService) OperateFirewall(operation string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
needRestartDocker := false
|
||||
switch operation {
|
||||
case "start":
|
||||
if err := client.Start(); err != nil {
|
||||
@@ -191,26 +192,30 @@ func (u *FirewallService) OperateFirewall(operation string) error {
|
||||
_ = client.Stop()
|
||||
return err
|
||||
}
|
||||
_, _ = cmd.RunDefaultWithStdoutBashC("systemctl restart docker")
|
||||
return nil
|
||||
needRestartDocker = true
|
||||
case "stop":
|
||||
if err := client.Stop(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = cmd.RunDefaultWithStdoutBashC("systemctl restart docker")
|
||||
return nil
|
||||
needRestartDocker = true
|
||||
case "restart":
|
||||
if err := client.Restart(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = cmd.RunDefaultWithStdoutBashC("systemctl restart docker")
|
||||
return nil
|
||||
needRestartDocker = true
|
||||
case "disablePing":
|
||||
return u.updatePingStatus("0")
|
||||
case "enablePing":
|
||||
return u.updatePingStatus("1")
|
||||
default:
|
||||
return fmt.Errorf("not supported operation: %s", operation)
|
||||
}
|
||||
return fmt.Errorf("not support such operation: %s", operation)
|
||||
if needRestartDocker {
|
||||
if err := restartDocker(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate, reload bool) error {
|
||||
|
||||
+13
-11
@@ -105,21 +105,23 @@ func (f *FtpService) Sync() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentData := make(map[string]model.Ftp)
|
||||
for _, item := range listsInDB {
|
||||
currentData[item.User] = item
|
||||
}
|
||||
sameData := make(map[string]struct{})
|
||||
for _, item := range lists {
|
||||
for _, itemInDB := range listsInDB {
|
||||
if item.User == itemInDB.User {
|
||||
sameData[item.User] = struct{}{}
|
||||
if item.Path != itemInDB.Path || item.Status != itemInDB.Status {
|
||||
_ = ftpRepo.Update(itemInDB.ID, map[string]interface{}{"path": item.Path, "status": item.Status})
|
||||
if itemInDB, ok := currentData[item.User]; ok {
|
||||
sameData[item.User] = struct{}{}
|
||||
if item.Path != itemInDB.Path || item.Status != itemInDB.Status {
|
||||
if err := ftpRepo.Update(itemInDB.ID, map[string]interface{}{"path": item.Path, "status": item.Status}); err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, item := range lists {
|
||||
if _, ok := sameData[item.User]; !ok {
|
||||
_ = ftpRepo.Create(&model.Ftp{User: item.User, Path: item.Path, Status: item.Status})
|
||||
} else {
|
||||
if err := ftpRepo.Create(&model.Ftp{User: item.User, Path: item.Path, Status: item.Status}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, item := range listsInDB {
|
||||
|
||||
@@ -602,7 +602,7 @@ func getProcessStatus(config *response.SupervisorProcessConfig, containerName st
|
||||
out, err = exec.Command("supervisorctl", processNames...).Output()
|
||||
output = string(out)
|
||||
}
|
||||
if containerName == "" && err != nil {
|
||||
if output == "" && err != nil {
|
||||
return err
|
||||
}
|
||||
lines := strings.Split(output, "\n")
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/build"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
@@ -189,7 +189,7 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) error {
|
||||
return err
|
||||
}
|
||||
|
||||
opts := types.ImageBuildOptions{
|
||||
opts := build.ImageBuildOptions{
|
||||
Dockerfile: fileName,
|
||||
Tags: []string{req.Name},
|
||||
Remove: true,
|
||||
@@ -280,7 +280,7 @@ func (u *ImageService) ImageLoad(req dto.ImageLoad) error {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
res, err := client.ImageLoad(context.TODO(), file, true)
|
||||
res, err := client.ImageLoad(context.TODO(), file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -432,7 +432,7 @@ func formatFileSize(fileSize int64) (size string) {
|
||||
}
|
||||
}
|
||||
|
||||
func checkUsed(imageID string, containers []types.Container) bool {
|
||||
func checkUsed(imageID string, containers []container.Summary) bool {
|
||||
for _, container := range containers {
|
||||
if container.ImageID == imageID {
|
||||
return true
|
||||
|
||||
@@ -28,7 +28,7 @@ type IImageRepoService interface {
|
||||
Login(req dto.OperateByID) error
|
||||
Create(req dto.ImageRepoCreate) error
|
||||
Update(req dto.ImageRepoUpdate) error
|
||||
BatchDelete(req dto.ImageRepoDelete) error
|
||||
Delete(req dto.OperateByID) error
|
||||
}
|
||||
|
||||
func NewIImageRepoService() IImageRepoService {
|
||||
@@ -91,29 +91,7 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
|
||||
if err := u.handleRegistries(req.DownloadUrl, "", "create"); err != nil {
|
||||
return fmt.Errorf("create registry %s failed, err: %v", req.DownloadUrl, err)
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashC("systemctl restart docker")
|
||||
if err != nil {
|
||||
return errors.New(string(stdout))
|
||||
}
|
||||
ticker := time.NewTicker(3 * time.Second)
|
||||
defer ticker.Stop()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
if err := func() error {
|
||||
for range ticker.C {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
cancel()
|
||||
return errors.New("the docker service cannot be restarted")
|
||||
default:
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashC("systemctl is-active docker")
|
||||
if string(stdout) == "active\n" && err == nil {
|
||||
global.LOG.Info("docker restart with new conf successful!")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}(); err != nil {
|
||||
if err := stopBeforeUpdateRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -132,15 +110,32 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
|
||||
return imageRepoRepo.Create(&imageRepo)
|
||||
}
|
||||
|
||||
func (u *ImageRepoService) BatchDelete(req dto.ImageRepoDelete) error {
|
||||
for _, id := range req.Ids {
|
||||
if id == 1 {
|
||||
return errors.New("The default value cannot be edit !")
|
||||
}
|
||||
func (u *ImageRepoService) Delete(req dto.OperateByID) error {
|
||||
if req.ID == 1 {
|
||||
return errors.New("The default value cannot be edit !")
|
||||
}
|
||||
if err := imageRepoRepo.Delete(repo.WithByIDs(req.Ids)); err != nil {
|
||||
itemRepo, _ := imageRepoRepo.Get(repo.WithByID(req.ID))
|
||||
if itemRepo.ID == 0 {
|
||||
return buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
if itemRepo.Auth {
|
||||
_, _ = cmd.NewCommandMgr().RunWithStdout("docker", "logout", "-i", itemRepo.DownloadUrl)
|
||||
}
|
||||
if itemRepo.Protocol == "https" {
|
||||
return imageRepoRepo.Delete(repo.WithByID(req.ID))
|
||||
}
|
||||
if err := u.handleRegistries("", itemRepo.DownloadUrl, "delete"); err != nil {
|
||||
return fmt.Errorf("delete registry %s failed, err: %v", itemRepo.DownloadUrl, err)
|
||||
}
|
||||
if err := validateDockerConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := imageRepoRepo.Delete(repo.WithByID(req.ID)); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
_ = restartDocker()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -155,38 +150,37 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
needRestart := false
|
||||
if repo.Protocol == "http" && req.Protocol == "https" {
|
||||
if err := u.handleRegistries("", repo.DownloadUrl, "delete"); err != nil {
|
||||
return fmt.Errorf("delete registry %s failed, err: %v", repo.DownloadUrl, err)
|
||||
}
|
||||
needRestart = true
|
||||
}
|
||||
if repo.Protocol == "http" && req.Protocol == "http" {
|
||||
if err := u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update"); err != nil {
|
||||
return fmt.Errorf("update registry %s => %s failed, err: %v", repo.DownloadUrl, req.DownloadUrl, err)
|
||||
}
|
||||
needRestart = repo.DownloadUrl == req.DownloadUrl
|
||||
}
|
||||
if repo.Protocol == "https" && req.Protocol == "http" {
|
||||
if err := u.handleRegistries(req.DownloadUrl, "", "create"); err != nil {
|
||||
return fmt.Errorf("create registry %s failed, err: %v", req.DownloadUrl, err)
|
||||
}
|
||||
needRestart = true
|
||||
}
|
||||
if repo.Auth != req.Auth || repo.DownloadUrl != req.DownloadUrl {
|
||||
if repo.Auth {
|
||||
cmdMgr := cmd.NewCommandMgr()
|
||||
_, _ = cmdMgr.RunWithStdout("docker", "logout", "-i", repo.DownloadUrl)
|
||||
}
|
||||
if req.Auth {
|
||||
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
|
||||
return err
|
||||
}
|
||||
if needRestart {
|
||||
if err := stopBeforeUpdateRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateDockerConfig(); err != nil {
|
||||
return err
|
||||
if repo.Auth {
|
||||
_, _ = cmd.NewCommandMgr().RunWithStdout("docker", "logout", "-i", repo.DownloadUrl)
|
||||
}
|
||||
if err := restartDocker(); err != nil {
|
||||
return err
|
||||
if req.Auth {
|
||||
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
upMap := make(map[string]interface{})
|
||||
@@ -261,3 +255,34 @@ func (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stopBeforeUpdateRepo() error {
|
||||
if err := validateDockerConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := restartDocker(); err != nil {
|
||||
return err
|
||||
}
|
||||
ticker := time.NewTicker(3 * time.Second)
|
||||
defer ticker.Stop()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||
if err := func() error {
|
||||
for range ticker.C {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
cancel()
|
||||
return errors.New("the docker service cannot be restarted")
|
||||
default:
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashC("systemctl is-active docker")
|
||||
if string(stdout) == "active\n" && err == nil {
|
||||
global.LOG.Info("docker restart with new conf successful!")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ type IRuntimeService interface {
|
||||
SyncForRestart() error
|
||||
SyncRuntimeStatus() error
|
||||
DeleteCheck(installID uint) ([]dto.AppResource, error)
|
||||
UpdateRemark(req request.RuntimeRemark) error
|
||||
|
||||
GetPHPExtensions(runtimeID uint) (response.PHPExtensionRes, error)
|
||||
InstallPHPExtension(req request.PHPExtensionInstallReq) error
|
||||
@@ -108,6 +109,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e
|
||||
Type: create.Type,
|
||||
Version: create.Version,
|
||||
Status: constant.StatusNormal,
|
||||
Remark: create.Remark,
|
||||
}
|
||||
return nil, runtimeRepo.Create(context.Background(), runtime)
|
||||
}
|
||||
@@ -168,6 +170,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e
|
||||
Version: create.Version,
|
||||
ContainerName: containerName.(string),
|
||||
Port: strings.Join(hostPorts, ","),
|
||||
Remark: create.Remark,
|
||||
}
|
||||
|
||||
switch create.Type {
|
||||
@@ -454,6 +457,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
||||
Params: req.Params,
|
||||
CodeDir: req.CodeDir,
|
||||
Version: req.Version,
|
||||
Remark: req.Remark,
|
||||
NodeConfig: request.NodeConfig{
|
||||
Install: true,
|
||||
ExposedPorts: req.ExposedPorts,
|
||||
@@ -465,6 +469,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runtime.Remark = req.Remark
|
||||
runtime.Env = string(envContent)
|
||||
runtime.DockerCompose = string(composeContent)
|
||||
|
||||
@@ -682,7 +687,7 @@ func (r *RuntimeService) GetPHPExtensions(runtimeID uint) (response.PHPExtension
|
||||
}
|
||||
}
|
||||
var phpExtensions []response.SupportExtension
|
||||
if err = json.Unmarshal(nginx_conf.PHPExtensionsJson, &phpExtensions); err != nil {
|
||||
if err = json.Unmarshal(nginx_conf.GetWebsiteFile("php_extensions.json"), &phpExtensions); err != nil {
|
||||
return res, err
|
||||
}
|
||||
for _, ext := range phpExtensions {
|
||||
@@ -719,25 +724,27 @@ func (r *RuntimeService) InstallPHPExtension(req request.PHPExtensionInstallReq)
|
||||
return err
|
||||
}
|
||||
client, err := docker.NewClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
if err == nil {
|
||||
oldImageID, err := client.GetImageIDByName(runtime.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = cmdMgr.RunBashCf("docker commit %s %s", runtime.ContainerName, runtime.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newImageID, err := client.GetImageIDByName(runtime.Image)
|
||||
if err == nil && newImageID != oldImageID {
|
||||
if err := client.DeleteImage(oldImageID); err != nil {
|
||||
t.Log(fmt.Sprintf("delete old image error %v", err))
|
||||
} else {
|
||||
t.Log("delete old image success")
|
||||
}
|
||||
oldImageID, err := client.GetImageIDByName(runtime.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = cmdMgr.RunBashCf("docker commit %s %s", runtime.ContainerName, runtime.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newImageID, err := client.GetImageIDByName(runtime.Image)
|
||||
if err == nil && newImageID != oldImageID {
|
||||
if err := client.DeleteImage(oldImageID); err != nil {
|
||||
t.Log(fmt.Sprintf("delete old image error %v", err))
|
||||
} else {
|
||||
t.Log("delete old image success")
|
||||
}
|
||||
}
|
||||
handlePHPDir(*runtime)
|
||||
if err = restartRuntime(runtime); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -887,7 +894,6 @@ func (r *RuntimeService) UpdatePHPConfig(req request.PHPConfigUpdate) (err error
|
||||
pattern := "^" + regexp.QuoteMeta("disable_functions") + "\\s*=\\s*.*$"
|
||||
if matched, _ := regexp.MatchString(pattern, line); matched {
|
||||
lines[i] = "disable_functions" + " = " + strings.Join(req.DisableFunctions, ",")
|
||||
break
|
||||
}
|
||||
case "upload_max_filesize":
|
||||
pattern := "^" + regexp.QuoteMeta("post_max_size") + "\\s*=\\s*.*$"
|
||||
@@ -1143,3 +1149,12 @@ func (r *RuntimeService) OperateSupervisorProcessFile(req request.PHPSupervisorP
|
||||
logFile := path.Join(supervisorDir, "log", fmt.Sprintf("%s.out.log", req.SupervisorProcessFileReq.Name))
|
||||
return handleSupervisorFile(req.SupervisorProcessFileReq, configDir, runtime.ContainerName, logFile)
|
||||
}
|
||||
|
||||
func (r *RuntimeService) UpdateRemark(req request.RuntimeRemark) error {
|
||||
runtime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runtime.Remark = req.Remark
|
||||
return runtimeRepo.Save(runtime)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
cmd2 "github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -17,6 +16,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
cmd2 "github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
|
||||
@@ -89,7 +90,7 @@ func handlePHP(create request.RuntimeCreate, runtime *model.Runtime, fileOp file
|
||||
version, ok := create.Params["PHP_VERSION"]
|
||||
if ok {
|
||||
extensionsDir := path.Join(projectDir, "extensions", getExtensionDir(version.(string)))
|
||||
_ = fileOp.CreateDir(extensionsDir, 0644)
|
||||
_ = fileOp.CreateDir(extensionsDir, 0755)
|
||||
}
|
||||
|
||||
composeContent, envContent, forms, err := handleParams(create, projectDir)
|
||||
@@ -141,11 +142,27 @@ func reCreateRuntime(runtime *model.Runtime) {
|
||||
}
|
||||
}
|
||||
|
||||
func runComposeCmdWithLog(operate string, composePath string, logPath string) error {
|
||||
cmd := exec.Command("docker", "compose", "-f", composePath, operate)
|
||||
if operate == "up" {
|
||||
cmd = exec.Command("docker", "compose", "-f", composePath, operate, "-d")
|
||||
func getComposeCmd(composePath, operate string) *exec.Cmd {
|
||||
dockerCommand := global.CONF.DockerConfig.Command
|
||||
var cmd *exec.Cmd
|
||||
if dockerCommand == "docker-compose" {
|
||||
if operate == "up" {
|
||||
cmd = exec.Command("docker-compose", "-f", composePath, operate, "-d")
|
||||
} else {
|
||||
cmd = exec.Command("docker-compose", "-f", composePath, operate)
|
||||
}
|
||||
} else {
|
||||
if operate == "up" {
|
||||
cmd = exec.Command("docker", "compose", "-f", composePath, operate, "-d")
|
||||
} else {
|
||||
cmd = exec.Command("docker", "compose", "-f", composePath, operate)
|
||||
}
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runComposeCmdWithLog(operate string, composePath string, logPath string) error {
|
||||
cmd := getComposeCmd(composePath, operate)
|
||||
logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, constant.FilePerm)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("Failed to open log file: %v", err)
|
||||
@@ -287,10 +304,22 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, oldEnv string, rebu
|
||||
_ = logFile.Close()
|
||||
}()
|
||||
|
||||
newPHPVersion := getRuntimeEnv(runtime.Env, "PHP_VERSION")
|
||||
oldPHPVersion := getRuntimeEnv(oldEnv, "PHP_VERSION")
|
||||
if newPHPVersion != oldPHPVersion {
|
||||
_ = os.Rename(path.Join(runtimePath, "extensions", getExtensionDir(oldPHPVersion)), path.Join(runtimePath, "extensions", getExtensionDir(newPHPVersion)))
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Hour)
|
||||
defer cancel()
|
||||
|
||||
cmd := exec.CommandContext(ctx, "docker", "compose", "-f", composePath, "build")
|
||||
dockerCommand := global.CONF.DockerConfig.Command
|
||||
var cmd *exec.Cmd
|
||||
if dockerCommand == "docker-compose" {
|
||||
cmd = exec.CommandContext(ctx, "docker-compose", "-f", composePath, "build")
|
||||
} else {
|
||||
cmd = exec.CommandContext(ctx, "docker", "compose", "-f", composePath, "build")
|
||||
}
|
||||
cmd.Stdout = logFile
|
||||
var stderrBuf bytes.Buffer
|
||||
multiWriterStderr := io.MultiWriter(&stderrBuf, logFile)
|
||||
@@ -373,6 +402,7 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, oldEnv string, rebu
|
||||
if deleteImageID != "" {
|
||||
deleteImageByID(deleteImageID, runtime.Image, client)
|
||||
}
|
||||
handlePHPDir(*runtime)
|
||||
if out, err := compose.DownAndUp(composePath); err != nil {
|
||||
runtime.Status = constant.StatusStartErr
|
||||
runtime.Message = out
|
||||
@@ -553,7 +583,7 @@ func handleCompose(env gotenv.Env, composeContent []byte, create request.Runtime
|
||||
serviceName = name
|
||||
serviceValue = service.(map[string]interface{})
|
||||
delete(serviceValue, "ports")
|
||||
if create.ExposedPorts != nil && len(create.ExposedPorts) > 0 {
|
||||
if len(create.ExposedPorts) > 0 {
|
||||
var ports []interface{}
|
||||
for i, port := range create.ExposedPorts {
|
||||
containerPortStr := fmt.Sprintf("CONTAINER_PORT_%d", i)
|
||||
@@ -567,12 +597,12 @@ func handleCompose(env gotenv.Env, composeContent []byte, create request.Runtime
|
||||
create.Params[hostIPStr] = port.HostIP
|
||||
}
|
||||
if create.Type == constant.RuntimePHP {
|
||||
ports = append(ports, fmt.Sprintf("127.0.0.1:${PANEL_APP_PORT_HTTP}:9000"))
|
||||
ports = append(ports, "127.0.0.1:${PANEL_APP_PORT_HTTP}:9000")
|
||||
}
|
||||
serviceValue["ports"] = ports
|
||||
} else {
|
||||
if create.Type == constant.RuntimePHP {
|
||||
serviceValue["ports"] = []interface{}{fmt.Sprintf("127.0.0.1:${PANEL_APP_PORT_HTTP}:9000")}
|
||||
serviceValue["ports"] = []interface{}{"127.0.0.1:${PANEL_APP_PORT_HTTP}:9000"}
|
||||
}
|
||||
}
|
||||
var environments []interface{}
|
||||
@@ -657,7 +687,7 @@ func unInstallPHPExtension(runtime *model.Runtime, delExtensions []string) error
|
||||
dir := runtime.GetPath()
|
||||
fileOP := files.NewFileOp()
|
||||
var phpExtensions []response.SupportExtension
|
||||
if err := json.Unmarshal(nginx_conf.PHPExtensionsJson, &phpExtensions); err != nil {
|
||||
if err := json.Unmarshal(nginx_conf.GetWebsiteFile("php_extensions.json"), &phpExtensions); err != nil {
|
||||
return err
|
||||
}
|
||||
phpVersion := getRuntimeEnv(runtime.Env, "PHP_VERSION")
|
||||
@@ -959,3 +989,43 @@ func handleRuntimeDTO(res *response.RuntimeDTO, runtime model.Runtime) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handlePHPDir(runtime model.Runtime) {
|
||||
fileOp := files.NewFileOp()
|
||||
dirs := []string{
|
||||
path.Join(runtime.GetPath(), "conf"),
|
||||
path.Join(runtime.GetPath(), "extensions"),
|
||||
path.Join(runtime.GetPath(), "composer"),
|
||||
path.Join(runtime.GetPath(), "log"),
|
||||
}
|
||||
for _, dir := range dirs {
|
||||
_ = fileOp.ChmodR(dir, 0755, true)
|
||||
_ = fileOp.ChownR(dir, strconv.Itoa(1000), strconv.Itoa(1000), true)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleOldPHPRuntime() {
|
||||
runtimes, _ := runtimeRepo.List(repo.WithByType(constant.RuntimePHP))
|
||||
if len(runtimes) == 0 {
|
||||
return
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
for _, runtime := range runtimes {
|
||||
composePtah := runtime.GetComposePath()
|
||||
composeBytes, _ := fileOp.GetContent(composePtah)
|
||||
composeContent := strings.ReplaceAll(string(composeBytes), "./conf:/usr/local/etc/php", "./conf/php.ini:/usr/local/etc/php/php.ini")
|
||||
composeContent = strings.ReplaceAll(composeContent, "./conf/php-fpm.conf:/usr/local/etc/php-fpm.d/www.conf", "./conf/php-fpm.conf:/usr/local/etc/php-fpm.conf")
|
||||
composeContent = strings.ReplaceAll(composeContent, "./extensions:${EXTENSION_DIR}", "./extensions:/usr/local/lib/php/extensions")
|
||||
_ = fileOp.WriteFile(composePtah, strings.NewReader(composeContent), constant.DirPerm)
|
||||
_ = fileOp.WriteFile(runtime.GetFPMPath(), bytes.NewReader(nginx_conf.GetWebsiteFile("php-fpm.conf")), constant.DirPerm)
|
||||
supervisorConfigPath := path.Join(runtime.GetPath(), "supervisor", "supervisor.d", "php-fpm.ini")
|
||||
supervisorConfigBytes, _ := fileOp.GetContent(supervisorConfigPath)
|
||||
if !strings.Contains(string(supervisorConfigBytes), "nodaemonize") {
|
||||
newConfigContent := strings.ReplaceAll(string(supervisorConfigBytes), "command=php-fpm", "command=php-fpm --nodaemonize")
|
||||
_ = fileOp.WriteFile(supervisorConfigPath, bytes.NewReader([]byte(newConfigContent)), constant.DirPerm)
|
||||
}
|
||||
go func() {
|
||||
_ = restartRuntime(&runtime)
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func (u *SnapshotService) SnapshotImport(req dto.SnapshotImport) error {
|
||||
}
|
||||
|
||||
for _, snapName := range req.Names {
|
||||
if !strings.HasPrefix(snapName, "1panel-v2.") && !strings.HasPrefix(snapName, "snapshot-1panel-v2.") {
|
||||
if !checkSnapshotIsOk(snapName) {
|
||||
return fmt.Errorf("incorrect snapshot name format of %s", snapName)
|
||||
}
|
||||
snap, _ := snapshotRepo.Get(repo.WithByName(strings.ReplaceAll(snapName, ".tar.gz", "")))
|
||||
@@ -77,11 +77,11 @@ func (u *SnapshotService) SnapshotImport(req dto.SnapshotImport) error {
|
||||
}
|
||||
}
|
||||
for _, snap := range req.Names {
|
||||
shortName := strings.TrimPrefix(snap, "snapshot_")
|
||||
nameItems := strings.Split(shortName, "-")
|
||||
if !strings.HasPrefix(shortName, "1panel-v") || !strings.HasSuffix(shortName, ".tar.gz") || len(nameItems) < 3 {
|
||||
return fmt.Errorf("incorrect snapshot name format of %s", shortName)
|
||||
}
|
||||
shortName := strings.ReplaceAll(snap, "snapshot-", "")
|
||||
shortName = strings.ReplaceAll(shortName, "1panel-", "")
|
||||
shortName = strings.ReplaceAll(shortName, "core-", "")
|
||||
shortName = strings.ReplaceAll(shortName, "agent-", "")
|
||||
nameItems := strings.Split(shortName, "-linux")
|
||||
if strings.HasSuffix(snap, ".tar.gz") {
|
||||
snap = strings.ReplaceAll(snap, ".tar.gz", "")
|
||||
}
|
||||
@@ -89,7 +89,7 @@ func (u *SnapshotService) SnapshotImport(req dto.SnapshotImport) error {
|
||||
Name: snap,
|
||||
SourceAccountIDs: fmt.Sprintf("%v", req.BackupAccountID),
|
||||
DownloadAccountID: req.BackupAccountID,
|
||||
Version: nameItems[1],
|
||||
Version: nameItems[0],
|
||||
Description: req.Description,
|
||||
Status: constant.StatusSuccess,
|
||||
}
|
||||
@@ -110,6 +110,7 @@ func (u *SnapshotService) LoadSnapshotData() (dto.SnapshotData, error) {
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
data.WithDockerConf = true
|
||||
data.PanelData, err = loadPanelFile(fileOp)
|
||||
if err != nil {
|
||||
return data, err
|
||||
@@ -326,7 +327,7 @@ func loadPanelFile(fileOp fileUtils.FileOp) ([]dto.DataTree, error) {
|
||||
switch itemData.Label {
|
||||
case "agent", "runtime", "docker", "task", "geo", "secret", "uploads":
|
||||
itemData.IsDisable = true
|
||||
case "clamav", "download", "resource":
|
||||
case "clamav", "download":
|
||||
panelPath := path.Join(global.Dir.DataDir, itemData.Label)
|
||||
itemData.Children, _ = loadFile(panelPath, 3, fileOp)
|
||||
case "apps", "backup", "log", "db", "tmp":
|
||||
@@ -403,3 +404,13 @@ func loadFile(pathItem string, index int, fileOp fileUtils.FileOp) ([]dto.DataTr
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func checkSnapshotIsOk(name string) bool {
|
||||
names := []string{"1panel-core-v2.", "1panel-agent-v2.", "1panel-v2.", "snapshot-1panel-core-v2.", "snapshot-1panel-agent-v2."}
|
||||
for _, item := range names {
|
||||
if strings.HasPrefix(name, item) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -34,7 +35,7 @@ func (u *SnapshotService) SnapshotCreate(parentTask *task.Task, req dto.Snapshot
|
||||
scope = "agent"
|
||||
}
|
||||
if jobID == 0 {
|
||||
req.Name = fmt.Sprintf("1panel-%s-%s-linux-%s-%s", versionItem.Value, scope, loadOs(), time.Now().Format(constant.DateTimeSlimLayout))
|
||||
req.Name = fmt.Sprintf("1panel-%s-%s-linux-%s-%s", scope, versionItem.Value, loadOs(), time.Now().Format(constant.DateTimeSlimLayout))
|
||||
}
|
||||
appItem, _ := json.Marshal(req.AppData)
|
||||
panelItem, _ := json.Marshal(req.PanelData)
|
||||
@@ -50,11 +51,13 @@ func (u *SnapshotService) SnapshotCreate(parentTask *task.Task, req dto.Snapshot
|
||||
AppData: string(appItem),
|
||||
PanelData: string(panelItem),
|
||||
BackupData: string(backupItem),
|
||||
WithDockerConf: req.WithDockerConf,
|
||||
WithMonitorData: req.WithMonitorData,
|
||||
WithLoginLog: req.WithLoginLog,
|
||||
WithOperationLog: req.WithOperationLog,
|
||||
WithTaskLog: req.WithTaskLog,
|
||||
WithSystemLog: req.WithSystemLog,
|
||||
IgnoreFiles: strings.Join(req.IgnoreFiles, ","),
|
||||
|
||||
Version: versionItem.Value,
|
||||
Status: constant.StatusWaiting,
|
||||
@@ -76,7 +79,7 @@ func (u *SnapshotService) SnapshotCreate(parentTask *task.Task, req dto.Snapshot
|
||||
}
|
||||
if jobID == 0 {
|
||||
go func() {
|
||||
_ = handleSnapshot(req, taskItem, jobID, 0, 0)
|
||||
_ = handleSnapshot(req, taskItem, jobID, 3, 0)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
@@ -106,13 +109,14 @@ func (u *SnapshotService) SnapshotReCreate(id uint) error {
|
||||
return err
|
||||
}
|
||||
req.TaskID = taskModel.ID
|
||||
taskItem, err := task.NewTaskWithOps(req.Name, task.TaskCreate, task.TaskScopeSnapshot, req.TaskID, req.ID)
|
||||
taskItem, err := task.ReNewTaskWithOps(req.Name, task.TaskCreate, task.TaskScopeSnapshot, req.TaskID, req.ID)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("new task for create snapshot failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
_ = snapshotRepo.Update(req.ID, map[string]interface{}{"status": constant.StatusWaiting, "message": ""})
|
||||
go func() {
|
||||
_ = handleSnapshot(req, taskItem, 0, 0, 0)
|
||||
_ = handleSnapshot(req, taskItem, 0, 3, 0)
|
||||
}()
|
||||
|
||||
return nil
|
||||
@@ -145,7 +149,7 @@ func handleSnapshot(req dto.SnapshotCreate, taskItem *task.Task, jobID, retry, t
|
||||
if len(req.InterruptStep) == 0 || req.InterruptStep == "SnapBaseInfo" {
|
||||
taskItem.AddSubTaskWithAliasAndOps(
|
||||
"SnapBaseInfo",
|
||||
func(t *task.Task) error { return snapBaseData(itemHelper, baseDir) },
|
||||
func(t *task.Task) error { return snapBaseData(itemHelper, baseDir, req.WithDockerConf) },
|
||||
nil, int(retry), time.Duration(timeout)*time.Second,
|
||||
)
|
||||
req.InterruptStep = ""
|
||||
@@ -233,6 +237,7 @@ func loadDbConn(snap *snapHelper, targetDir string, req dto.SnapshotCreate) erro
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = os.Remove(path.Join(targetDir, "db/session.db"))
|
||||
|
||||
agentDb, err := common.LoadDBConnByPathWithErr(path.Join(targetDir, "db/agent.db"), "agent.db")
|
||||
snap.Task.LogWithStatus(i18n.GetWithName("SnapNewDB", "agent"), err)
|
||||
@@ -284,7 +289,7 @@ func loadDbConn(snap *snapHelper, targetDir string, req dto.SnapshotCreate) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func snapBaseData(snap snapHelper, targetDir string) error {
|
||||
func snapBaseData(snap snapHelper, targetDir string, withDockerConf bool) error {
|
||||
snap.Task.Log("---------------------- 2 / 8 ----------------------")
|
||||
snap.Task.LogStart(i18n.GetMsgByKey("SnapBaseInfo"))
|
||||
|
||||
@@ -321,11 +326,13 @@ func snapBaseData(snap snapHelper, targetDir string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if snap.FileOp.Stat("/etc/docker/daemon.json") {
|
||||
err = snap.FileOp.CopyFile("/etc/docker/daemon.json", targetDir)
|
||||
snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/docker/daemon.json"), err)
|
||||
if err != nil {
|
||||
return err
|
||||
if withDockerConf {
|
||||
if snap.FileOp.Stat(constant.DaemonJsonPath) {
|
||||
err = snap.FileOp.CopyFile(constant.DaemonJsonPath, targetDir)
|
||||
snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", constant.DaemonJsonPath), err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,8 +384,8 @@ func snapAppImage(snap snapHelper, req dto.SnapshotCreate, targetDir string) err
|
||||
}
|
||||
}
|
||||
|
||||
snap.Task.Log(strings.Join(imageList, " "))
|
||||
if len(imageList) != 0 {
|
||||
snap.Task.Log(strings.Join(imageList, " "))
|
||||
snap.Task.Logf("docker save %s | gzip -c > %s", strings.Join(imageList, " "), path.Join(targetDir, "images.tar.gz"))
|
||||
std, err := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute)).RunWithStdoutBashCf("docker save %s | gzip -c > %s", strings.Join(imageList, " "), path.Join(targetDir, "images.tar.gz"))
|
||||
if err != nil {
|
||||
@@ -386,6 +393,8 @@ func snapAppImage(snap snapHelper, req dto.SnapshotCreate, targetDir string) err
|
||||
return errors.New(std)
|
||||
}
|
||||
snap.Task.LogSuccess(i18n.GetMsgByKey("SnapDockerSave"))
|
||||
} else {
|
||||
snap.Task.Log(i18n.GetMsgByKey("SnapInstallAppImageEmpty"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -395,6 +404,7 @@ func snapBackupData(snap snapHelper, req dto.SnapshotCreate, targetDir string) e
|
||||
snap.Task.LogStart(i18n.GetMsgByKey("SnapLocalBackup"))
|
||||
|
||||
excludes := loadBackupExcludes(snap, req.BackupData)
|
||||
excludes = append(excludes, req.IgnoreFiles...)
|
||||
excludes = append(excludes, "./system_snapshot")
|
||||
for _, item := range req.AppData {
|
||||
for _, itemApp := range item.Children {
|
||||
@@ -418,7 +428,8 @@ func loadBackupExcludes(snap snapHelper, req []dto.DataTree) []string {
|
||||
if err := snap.snapAgentDB.Where("file_dir = ? AND file_name = ?", strings.TrimPrefix(path.Dir(item.Path), global.Dir.LocalBackupDir+"/"), path.Base(item.Path)).Delete(&model.BackupRecord{}).Error; err != nil {
|
||||
snap.Task.LogWithStatus("delete backup file from database", err)
|
||||
}
|
||||
excludes = append(excludes, "."+strings.TrimPrefix(item.Path, global.Dir.LocalBackupDir))
|
||||
itemDir, _ := filepath.Rel(item.Path, global.Dir.LocalBackupDir)
|
||||
excludes = append(excludes, itemDir)
|
||||
} else {
|
||||
excludes = append(excludes, loadBackupExcludes(snap, item.Children)...)
|
||||
}
|
||||
@@ -430,7 +441,8 @@ func loadAppBackupExcludes(req []dto.DataTree) []string {
|
||||
for _, item := range req {
|
||||
if len(item.Children) == 0 {
|
||||
if !item.IsCheck {
|
||||
excludes = append(excludes, "."+strings.TrimPrefix(item.Path, path.Join(global.Dir.LocalBackupDir)))
|
||||
itemDir, _ := filepath.Rel(item.Path, global.Dir.LocalBackupDir)
|
||||
excludes = append(excludes, itemDir)
|
||||
}
|
||||
} else {
|
||||
excludes = append(excludes, loadAppBackupExcludes(item.Children)...)
|
||||
@@ -463,26 +475,21 @@ func snapPanelData(snap snapHelper, req dto.SnapshotCreate, targetDir string) er
|
||||
|
||||
rootDir := global.Dir.DataDir
|
||||
if strings.Contains(global.Dir.LocalBackupDir, rootDir) {
|
||||
excludes = append(excludes, "."+strings.ReplaceAll(global.Dir.LocalBackupDir, rootDir, ""))
|
||||
itemDir, _ := filepath.Rel(rootDir, global.Dir.LocalBackupDir)
|
||||
excludes = append(excludes, itemDir)
|
||||
}
|
||||
if len(snap.OpenrestyDir) != 0 && strings.Contains(snap.OpenrestyDir, rootDir) {
|
||||
excludes = append(excludes, "."+strings.ReplaceAll(snap.OpenrestyDir, rootDir, ""))
|
||||
}
|
||||
ignoreVal, _ := settingRepo.Get(settingRepo.WithByKey("SnapshotIgnore"))
|
||||
rules := strings.Split(ignoreVal.Value, ",")
|
||||
for _, ignore := range rules {
|
||||
if len(ignore) == 0 || cmd.CheckIllegal(ignore) {
|
||||
continue
|
||||
}
|
||||
excludes = append(excludes, "."+strings.ReplaceAll(ignore, rootDir, ""))
|
||||
itemDir, _ := filepath.Rel(rootDir, snap.OpenrestyDir)
|
||||
excludes = append(excludes, itemDir)
|
||||
}
|
||||
excludes = append(excludes, req.IgnoreFiles...)
|
||||
err := snap.FileOp.TarGzCompressPro(false, rootDir, path.Join(targetDir, "1panel_data.tar.gz"), "", strings.Join(excludes, ","))
|
||||
snap.Task.LogWithStatus(i18n.GetMsgByKey("SnapCompressPanel"), err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(snap.OpenrestyDir) != 0 {
|
||||
err := snap.FileOp.TarGzCompressPro(false, snap.OpenrestyDir, path.Join(targetDir, "website.tar.gz"), "", "")
|
||||
err := snap.FileOp.TarGzCompressPro(false, snap.OpenrestyDir, path.Join(targetDir, "website.tar.gz"), "", strings.Join(req.IgnoreFiles, ","))
|
||||
snap.Task.LogWithStatus(i18n.GetMsgByKey("SnapWebsite"), err)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -496,7 +503,8 @@ func loadPanelExcludes(req []dto.DataTree) []string {
|
||||
for _, item := range req {
|
||||
if len(item.Children) == 0 {
|
||||
if !item.IsCheck {
|
||||
excludes = append(excludes, "."+strings.TrimPrefix(item.Path, path.Join(global.Dir.BaseDir, "1panel")))
|
||||
itemDir, _ := filepath.Rel(item.Path, path.Join(global.Dir.BaseDir, "1panel"))
|
||||
excludes = append(excludes, itemDir)
|
||||
}
|
||||
} else {
|
||||
excludes = append(excludes, loadPanelExcludes(item.Children)...)
|
||||
|
||||
@@ -38,6 +38,18 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"recover_status": constant.StatusFailed, "recover_message": errInfo})
|
||||
return errors.New(errInfo)
|
||||
}
|
||||
if !strings.Contains(snap.Name, "-v2.") {
|
||||
return errors.New("snapshots are currently not supported for recovery across major versions")
|
||||
}
|
||||
if !strings.Contains(snap.Name, "-core") && !strings.Contains(snap.Name, "-agent") {
|
||||
return errors.New("the name of the snapshot file does not conform to the format")
|
||||
}
|
||||
if strings.Contains(snap.Name, "-core") && !global.IsMaster {
|
||||
return errors.New("the snapshot of the master node cannot be restored on the agent nodes")
|
||||
}
|
||||
if strings.Contains(snap.Name, "-agent") && global.IsMaster {
|
||||
return errors.New("the snapshot of the agent node cannot be restored on the master node")
|
||||
}
|
||||
if len(snap.RollbackStatus) != 0 && snap.RollbackStatus != constant.StatusSuccess {
|
||||
req.IsNew = true
|
||||
}
|
||||
@@ -55,7 +67,13 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
|
||||
} else {
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"task_recover_id": req.TaskID})
|
||||
}
|
||||
taskItem, err := task.NewTaskWithOps(snap.Name, task.TaskRecover, task.TaskScopeSnapshot, req.TaskID, snap.ID)
|
||||
|
||||
var taskItem *task.Task
|
||||
if req.IsNew {
|
||||
taskItem, err = task.NewTaskWithOps(snap.Name, task.TaskRecover, task.TaskScopeSnapshot, req.TaskID, snap.ID)
|
||||
} else {
|
||||
taskItem, err = task.ReNewTaskWithOps(snap.Name, task.TaskRecover, task.TaskScopeSnapshot, req.TaskID, snap.ID)
|
||||
}
|
||||
if err != nil {
|
||||
global.LOG.Errorf("new task for create snapshot failed, err: %v", err)
|
||||
return err
|
||||
@@ -84,7 +102,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
|
||||
taskItem.AddSubTaskWithAlias(
|
||||
"RecoverDecompress",
|
||||
func(t *task.Task) error {
|
||||
itemHelper.Task.Log("---------------------- 2 / 10 ----------------------")
|
||||
itemHelper.Task.Log("---------------------- 2 / 11 ----------------------")
|
||||
itemHelper.Task.LogStart(i18n.GetWithName("RecoverDecompress", snap.Name))
|
||||
err := itemHelper.FileOp.TarGzExtractPro(fmt.Sprintf("%s/%s.tar.gz", rootDir, snap.Name), rootDir, req.Secret)
|
||||
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("Decompress"), err)
|
||||
@@ -140,7 +158,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
|
||||
taskItem.AddSubTaskWithAlias(
|
||||
"RecoverBackups",
|
||||
func(t *task.Task) error {
|
||||
itemHelper.Task.Log("---------------------- 8 / 10 ----------------------")
|
||||
itemHelper.Task.Log("---------------------- 8 / 11 ----------------------")
|
||||
itemHelper.Task.LogStart(i18n.GetWithName("RecoverBackups", snap.Name))
|
||||
err := itemHelper.FileOp.TarGzExtractPro(path.Join(rootDir, snap.Name, "/1panel_backup.tar.gz"), snapJson.BackupDataDir, "")
|
||||
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("Decompress"), err)
|
||||
@@ -150,11 +168,29 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
|
||||
)
|
||||
req.IsNew = true
|
||||
}
|
||||
if req.IsNew || snap.InterruptStep == "RecoverWebsite" {
|
||||
taskItem.AddSubTaskWithAlias(
|
||||
"RecoverWebsite",
|
||||
func(t *task.Task) error {
|
||||
itemHelper.Task.Log("---------------------- 9 / 11 ----------------------")
|
||||
itemHelper.Task.LogStart(i18n.GetWithName("RecoverWebsite", snap.Name))
|
||||
webFile := path.Join(rootDir, snap.Name, "/website.tar.gz")
|
||||
var err error
|
||||
if itemHelper.FileOp.Stat(webFile) {
|
||||
err = itemHelper.FileOp.TarGzExtractPro(webFile, snapJson.OperestyDir, "")
|
||||
}
|
||||
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("Decompress"), err)
|
||||
return err
|
||||
},
|
||||
nil,
|
||||
)
|
||||
req.IsNew = true
|
||||
}
|
||||
if req.IsNew || snap.InterruptStep == "RecoverPanelData" {
|
||||
taskItem.AddSubTaskWithAlias(
|
||||
"RecoverPanelData",
|
||||
func(t *task.Task) error {
|
||||
itemHelper.Task.Log("---------------------- 9 / 10 ----------------------")
|
||||
itemHelper.Task.Log("---------------------- 10 / 11 ----------------------")
|
||||
itemHelper.Task.LogStart(i18n.GetWithName("RecoverPanelData", snap.Name))
|
||||
err := itemHelper.FileOp.TarGzExtractPro(path.Join(rootDir, snap.Name, "/1panel_data.tar.gz"), path.Join(snapJson.BaseDir, "1panel"), "")
|
||||
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("Decompress"), err)
|
||||
@@ -195,7 +231,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
|
||||
}
|
||||
|
||||
func handleDownloadSnapshot(itemHelper *snapRecoverHelper, snap model.Snapshot, targetDir string) error {
|
||||
itemHelper.Task.Log("---------------------- 1 / 10 ----------------------")
|
||||
itemHelper.Task.Log("---------------------- 1 / 11 ----------------------")
|
||||
itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverDownload"))
|
||||
|
||||
account, client, err := NewBackupClientWithID(snap.DownloadAccountID)
|
||||
@@ -214,7 +250,7 @@ func handleDownloadSnapshot(itemHelper *snapRecoverHelper, snap model.Snapshot,
|
||||
}
|
||||
|
||||
func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error {
|
||||
itemHelper.Task.Log("---------------------- 3 / 10 ----------------------")
|
||||
itemHelper.Task.Log("---------------------- 3 / 11 ----------------------")
|
||||
itemHelper.Task.LogStart(i18n.GetMsgByKey("BackupBeforeRecover"))
|
||||
|
||||
rootDir := fmt.Sprintf("%s/1panel_original/original_%s", global.Dir.BaseDir, name)
|
||||
@@ -273,9 +309,9 @@ func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if itemHelper.FileOp.Stat("/etc/docker/daemon.json") {
|
||||
err = itemHelper.FileOp.CopyFile("/etc/docker/daemon.json", baseDir)
|
||||
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/docker/daemon.json"), err)
|
||||
if itemHelper.FileOp.Stat(constant.DaemonJsonPath) {
|
||||
err = itemHelper.FileOp.CopyFile(constant.DaemonJsonPath, baseDir)
|
||||
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", constant.DaemonJsonPath), err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -284,7 +320,7 @@ func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error {
|
||||
}
|
||||
|
||||
func readFromJson(rootDir string, itemHelper *snapRecoverHelper) (SnapshotJson, error) {
|
||||
itemHelper.Task.Log("---------------------- 4 / 10 ----------------------")
|
||||
itemHelper.Task.Log("---------------------- 4 / 11 ----------------------")
|
||||
itemHelper.Task.LogStart(i18n.GetMsgByKey("Readjson"))
|
||||
|
||||
snapJsonPath := path.Join(rootDir, "base/snapshot.json")
|
||||
@@ -308,7 +344,7 @@ func readFromJson(rootDir string, itemHelper *snapRecoverHelper) (SnapshotJson,
|
||||
}
|
||||
|
||||
func recoverAppData(src string, itemHelper *snapRecoverHelper) error {
|
||||
itemHelper.Task.Log("---------------------- 5 / 10 ----------------------")
|
||||
itemHelper.Task.Log("---------------------- 5 / 11 ----------------------")
|
||||
itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverApp"))
|
||||
|
||||
if _, err := os.Stat(path.Join(src, "images.tar.gz")); err != nil {
|
||||
@@ -325,7 +361,7 @@ func recoverAppData(src string, itemHelper *snapRecoverHelper) error {
|
||||
}
|
||||
|
||||
func recoverBaseData(src string, itemHelper *snapRecoverHelper) error {
|
||||
itemHelper.Task.Log("---------------------- 6 / 10 ----------------------")
|
||||
itemHelper.Task.Log("---------------------- 6 / 11 ----------------------")
|
||||
itemHelper.Task.LogStart(i18n.GetMsgByKey("SnapBaseInfo"))
|
||||
|
||||
if global.IsMaster {
|
||||
@@ -356,27 +392,25 @@ func recoverBaseData(src string, itemHelper *snapRecoverHelper) error {
|
||||
return err
|
||||
}
|
||||
|
||||
daemonJsonPath := "/etc/docker/daemon.json"
|
||||
_, errSrc := os.Stat(path.Join(src, "docker/daemon.json"))
|
||||
_, errPath := os.Stat(daemonJsonPath)
|
||||
if os.IsNotExist(errSrc) && os.IsNotExist(errPath) {
|
||||
if !itemHelper.FileOp.Stat(path.Join(src, "daemon.json")) {
|
||||
itemHelper.Task.Log(i18n.GetMsgByKey("RecoverDaemonJsonEmpty"))
|
||||
return nil
|
||||
}
|
||||
if errSrc == nil {
|
||||
err = itemHelper.FileOp.CopyFile(path.Join(src, "docker/daemon.json"), "/etc/docker")
|
||||
} else {
|
||||
err = itemHelper.FileOp.CopyFile(path.Join(src, "daemon.json"), path.Dir(constant.DaemonJsonPath))
|
||||
itemHelper.Task.Log(i18n.GetMsgByKey("RecoverDaemonJson"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("recover docker daemon.json failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
_, _ = cmd.RunDefaultWithStdoutBashC("systemctl restart docker")
|
||||
if err := restartDocker(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func recoverDBData(src string, itemHelper *snapRecoverHelper) error {
|
||||
itemHelper.Task.Log("---------------------- 7 / 10 ----------------------")
|
||||
itemHelper.Task.Log("---------------------- 7 / 11 ----------------------")
|
||||
itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverDBData"))
|
||||
err := itemHelper.FileOp.CopyDirWithExclude(src, global.Dir.DataDir, nil)
|
||||
|
||||
@@ -385,7 +419,7 @@ func recoverDBData(src string, itemHelper *snapRecoverHelper) error {
|
||||
}
|
||||
|
||||
func restartCompose(composePath string, itemHelper *snapRecoverHelper) error {
|
||||
itemHelper.Task.Log("---------------------- 10 / 10 ----------------------")
|
||||
itemHelper.Task.Log("---------------------- 11 / 11 ----------------------")
|
||||
itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverCompose"))
|
||||
|
||||
composes, err := composeRepo.ListRecord()
|
||||
|
||||
@@ -76,9 +76,12 @@ func (u *SnapshotService) SnapshotRollback(req dto.SnapshotRecover) error {
|
||||
nil,
|
||||
)
|
||||
taskItem.AddSubTask(
|
||||
i18n.GetWithName("SnapCopy", "/etc/docker/daemon.json"),
|
||||
i18n.GetWithName("SnapCopy", constant.DaemonJsonPath),
|
||||
func(t *task.Task) error {
|
||||
return FileOp.CopyFile(path.Join(baseDir, "daemon.json"), "/etc/docker")
|
||||
if FileOp.Stat(path.Join(baseDir, "daemon.json")) {
|
||||
return FileOp.CopyFile(path.Join(baseDir, "daemon.json"), path.Dir(constant.DaemonJsonPath))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
nil,
|
||||
)
|
||||
|
||||
@@ -415,6 +415,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
||||
return err
|
||||
}
|
||||
website.ParentWebsiteID = parentWebsite.ID
|
||||
website.SiteDir = create.SiteDir
|
||||
}
|
||||
|
||||
configNginx := func(t *task.Task) error {
|
||||
@@ -469,7 +470,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
||||
WebsiteSSLID: websiteModel.ID,
|
||||
Type: "existed",
|
||||
HttpConfig: "HTTPToHTTPS",
|
||||
SSLProtocol: []string{"TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1"},
|
||||
SSLProtocol: []string{"TLSv1.3", "TLSv1.2"},
|
||||
Algorithm: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED",
|
||||
Hsts: true,
|
||||
}
|
||||
@@ -922,18 +923,12 @@ func (w WebsiteService) GetWebsiteHTTPS(websiteId uint) (response.WebsiteHTTPS,
|
||||
res response.WebsiteHTTPS
|
||||
httpsPorts []string
|
||||
)
|
||||
websiteDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(websiteId))
|
||||
for _, domain := range websiteDomains {
|
||||
if domain.SSL {
|
||||
httpsPorts = append(httpsPorts, strconv.Itoa(domain.Port))
|
||||
}
|
||||
}
|
||||
if len(httpsPorts) == 0 {
|
||||
nginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)
|
||||
res.HttpsPort = strconv.Itoa(nginxInstall.HttpsPort)
|
||||
} else {
|
||||
res.HttpsPort = strings.Join(httpsPorts, ",")
|
||||
|
||||
httpsPortsMap := getHttpsPort(websiteId)
|
||||
for port := range httpsPortsMap {
|
||||
httpsPorts = append(httpsPorts, strconv.Itoa(port))
|
||||
}
|
||||
res.HttpsPort = strings.Join(httpsPorts, ",")
|
||||
if website.WebsiteSSLID == 0 {
|
||||
res.Enable = false
|
||||
return res, nil
|
||||
@@ -981,11 +976,7 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
|
||||
res response.WebsiteHTTPS
|
||||
websiteSSL model.WebsiteSSL
|
||||
)
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = ChangeHSTSConfig(req.Hsts, website); err != nil {
|
||||
if err = ChangeHSTSConfig(req.Hsts, req.Http3, website); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.Enable = req.Enable
|
||||
@@ -995,24 +986,27 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
|
||||
website.Protocol = constant.ProtocolHTTP
|
||||
website.WebsiteSSLID = 0
|
||||
|
||||
httpsPorts, err := getHttpsPort(&website)
|
||||
websiteDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(website.ID))
|
||||
|
||||
ports := make(map[int]struct{})
|
||||
for _, domain := range websiteDomains {
|
||||
ports[domain.Port] = struct{}{}
|
||||
}
|
||||
for port := range ports {
|
||||
if err = removeSSLListen(website, []string{strconv.Itoa(port)}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(httpsPorts) == 1 && httpsPorts[0] == nginxInstall.HttpsPort {
|
||||
httpsPortStr := strconv.Itoa(httpsPorts[0])
|
||||
if _, ok := ports[nginxInstall.HttpsPort]; !ok {
|
||||
httpsPortStr := strconv.Itoa(nginxInstall.HttpsPort)
|
||||
if err = deleteListenAndServerName(website, []string{httpsPortStr, "[::]:" + httpsPortStr}, []string{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
for _, port := range httpsPorts {
|
||||
httpsPortStr := strconv.Itoa(port)
|
||||
if err = removeSSLListen(website, []string{httpsPortStr}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nginxParams := getNginxParamsFromStaticFile(dto.SSL, nil)
|
||||
nginxParams = append(nginxParams,
|
||||
dto.NginxParam{
|
||||
|
||||
@@ -436,14 +436,14 @@ func (w WebsiteCAService) DownloadFile(id uint) (*os.File, error) {
|
||||
if err = fileOp.CreateDir(dir, constant.DirPerm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = fileOp.WriteFile(path.Join(dir, "ca.csr"), strings.NewReader(ca.CSR), constant.DirPerm); err != nil {
|
||||
if err = fileOp.WriteFile(path.Join(dir, "ca.crt"), strings.NewReader(ca.CSR), constant.DirPerm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = fileOp.WriteFile(path.Join(dir, "private.key"), strings.NewReader(ca.PrivateKey), constant.DirPerm); err != nil {
|
||||
if err = fileOp.WriteFile(path.Join(dir, "ca.key"), strings.NewReader(ca.PrivateKey), constant.DirPerm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fileName := ca.Name + ".zip"
|
||||
if err = fileOp.Compress([]string{path.Join(dir, "ca.csr"), path.Join(dir, "private.key")}, dir, fileName, files.SdkZip, ""); err != nil {
|
||||
if err = fileOp.Compress([]string{path.Join(dir, "ca.crt"), path.Join(dir, "ca.key")}, dir, fileName, files.SdkZip, ""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return os.Open(path.Join(dir, fileName))
|
||||
|
||||
@@ -182,8 +182,8 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
|
||||
return err
|
||||
}
|
||||
configPath := GetSitePath(*website, SiteConf)
|
||||
nginxContent := string(nginx_conf.WebsiteDefault)
|
||||
config, err := parser.NewStringParser(nginxContent).Parse()
|
||||
nginxContent := nginx_conf.GetWebsiteFile("website_default.conf")
|
||||
config, err := parser.NewStringParser(string(nginxContent)).Parse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -651,22 +651,33 @@ func createPemFile(website model.Website, websiteSSL model.WebsiteSSL) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getHttpsPort(website *model.Website) ([]int, error) {
|
||||
websiteDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(website.ID))
|
||||
var httpsPorts []int
|
||||
for _, domain := range websiteDomains {
|
||||
if domain.SSL {
|
||||
httpsPorts = append(httpsPorts, domain.Port)
|
||||
func getHttpsPort(websiteID uint) map[int]struct{} {
|
||||
domains, err := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(websiteID))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
httpsPorts := make(map[int]struct{})
|
||||
nginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)
|
||||
hasDefaultPort := false
|
||||
for _, domain := range domains {
|
||||
if domain.Port == nginxInstall.HttpPort {
|
||||
hasDefaultPort = true
|
||||
}
|
||||
if domain.SSL {
|
||||
httpsPorts[domain.Port] = struct{}{}
|
||||
}
|
||||
}
|
||||
if hasDefaultPort {
|
||||
httpsPorts[nginxInstall.HttpsPort] = struct{}{}
|
||||
}
|
||||
if len(httpsPorts) == 0 {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
for _, domain := range domains {
|
||||
if !domain.SSL {
|
||||
httpsPorts[domain.Port] = struct{}{}
|
||||
}
|
||||
}
|
||||
httpsPorts = append(httpsPorts, nginxInstall.HttpsPort)
|
||||
}
|
||||
return httpsPorts, nil
|
||||
return httpsPorts
|
||||
}
|
||||
|
||||
func applySSL(website *model.Website, websiteSSL model.WebsiteSSL, req request.WebsiteHTTPSOp) error {
|
||||
@@ -678,63 +689,71 @@ func applySSL(website *model.Website, websiteSSL model.WebsiteSSL, req request.W
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
noDefaultPort := true
|
||||
httpPorts := make(map[int]struct{})
|
||||
httpsPorts := make(map[int]struct{})
|
||||
|
||||
hasDefaultPort := false
|
||||
for _, domain := range domains {
|
||||
if domain.Port == 80 {
|
||||
noDefaultPort = false
|
||||
if domain.Port == nginxFull.Install.HttpPort {
|
||||
hasDefaultPort = true
|
||||
}
|
||||
if domain.Port != 80 && !domain.SSL {
|
||||
if domain.SSL {
|
||||
httpsPorts[domain.Port] = struct{}{}
|
||||
} else {
|
||||
httpPorts[domain.Port] = struct{}{}
|
||||
}
|
||||
}
|
||||
if hasDefaultPort {
|
||||
httpsPorts[nginxFull.Install.HttpsPort] = struct{}{}
|
||||
}
|
||||
if len(httpsPorts) == 0 {
|
||||
for port := range httpPorts {
|
||||
httpsPorts[port] = struct{}{}
|
||||
}
|
||||
}
|
||||
config := nginxFull.SiteConfig.Config
|
||||
server := config.FindServers()[0]
|
||||
|
||||
httpPort := strconv.Itoa(nginxFull.Install.HttpPort)
|
||||
httpsPort, err := getHttpsPort(website)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpPortIPV6 := "[::]:" + httpPort
|
||||
defaultHttpPort := strconv.Itoa(nginxFull.Install.HttpPort)
|
||||
defaultHttpPortIPV6 := "[::]:" + defaultHttpPort
|
||||
|
||||
for _, port := range httpsPort {
|
||||
if _, ok := httpPorts[port]; !ok {
|
||||
server.DeleteListen(strconv.Itoa(port))
|
||||
}
|
||||
setListen(server, strconv.Itoa(port), website.IPV6, req.Http3, website.DefaultServer, true)
|
||||
for port := range httpsPorts {
|
||||
portStr := strconv.Itoa(port)
|
||||
server.RemoveListenByBind(portStr)
|
||||
server.RemoveListenByBind("[::]:" + portStr)
|
||||
setListen(server, portStr, website.IPV6, req.Http3, website.DefaultServer, true)
|
||||
}
|
||||
|
||||
server.UpdateDirective("http2", []string{"on"})
|
||||
|
||||
switch req.HttpConfig {
|
||||
case constant.HTTPSOnly:
|
||||
server.RemoveListenByBind(httpPort)
|
||||
server.RemoveListenByBind(httpPortIPV6)
|
||||
server.RemoveListenByBind(defaultHttpPort)
|
||||
server.RemoveListenByBind(defaultHttpPortIPV6)
|
||||
server.RemoveDirective("if", []string{"($scheme"})
|
||||
case constant.HTTPToHTTPS:
|
||||
if !noDefaultPort {
|
||||
server.UpdateListen(httpPort, website.DefaultServer)
|
||||
}
|
||||
if website.IPV6 {
|
||||
server.UpdateListen(httpPortIPV6, website.DefaultServer)
|
||||
if hasDefaultPort {
|
||||
server.UpdateListen(defaultHttpPort, website.DefaultServer)
|
||||
if website.IPV6 {
|
||||
server.UpdateListen(defaultHttpPortIPV6, website.DefaultServer)
|
||||
}
|
||||
}
|
||||
server.AddHTTP2HTTPS()
|
||||
case constant.HTTPAlso:
|
||||
if !noDefaultPort {
|
||||
server.UpdateListen(httpPort, website.DefaultServer)
|
||||
if hasDefaultPort {
|
||||
server.UpdateListen(defaultHttpPort, website.DefaultServer)
|
||||
if website.IPV6 {
|
||||
server.UpdateListen(defaultHttpPortIPV6, website.DefaultServer)
|
||||
}
|
||||
}
|
||||
server.RemoveDirective("if", []string{"($scheme"})
|
||||
if website.IPV6 {
|
||||
server.UpdateListen(httpPortIPV6, website.DefaultServer)
|
||||
}
|
||||
}
|
||||
|
||||
if !req.Hsts {
|
||||
server.RemoveDirective("add_header", []string{"Strict-Transport-Security", "\"max-age=31536000\""})
|
||||
}
|
||||
if !req.Http3 {
|
||||
for _, port := range httpsPort {
|
||||
for port := range httpsPorts {
|
||||
server.RemoveListen(strconv.Itoa(port), "quic")
|
||||
if website.IPV6 {
|
||||
httpsPortIPV6 := "[::]:" + strconv.Itoa(port)
|
||||
@@ -761,7 +780,7 @@ func applySSL(website *model.Website, websiteSSL model.WebsiteSSL, req request.W
|
||||
if param.Name == "ssl_protocols" {
|
||||
nginxParams[i].Params = req.SSLProtocol
|
||||
if len(req.SSLProtocol) == 0 {
|
||||
nginxParams[i].Params = []string{"TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1"}
|
||||
nginxParams[i].Params = []string{"TLSv1.3", "TLSv1.2"}
|
||||
}
|
||||
}
|
||||
if param.Name == "ssl_ciphers" {
|
||||
@@ -1078,7 +1097,7 @@ func getWebsiteDomains(domains []request.WebsiteDomain, defaultPort int, website
|
||||
err = buserr.WithMap("ErrPortExist", errMap, nil)
|
||||
return
|
||||
}
|
||||
if common.ScanPort(port) {
|
||||
if port != 443 && common.ScanPort(port) {
|
||||
err = buserr.WithDetail("ErrPortInUsed", port, nil)
|
||||
return
|
||||
}
|
||||
@@ -1117,7 +1136,6 @@ func saveCertificateFile(websiteSSL *model.WebsiteSSL, logger *log.Logger) {
|
||||
func GetSystemSSL() (bool, uint) {
|
||||
sslSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load service ssl from setting failed, err: %v", err)
|
||||
return false, 0
|
||||
}
|
||||
if sslSetting.Value == "enable" {
|
||||
@@ -1162,7 +1180,7 @@ func UpdateSSLConfig(websiteSSL model.WebsiteSSL) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ChangeHSTSConfig(enable bool, website model.Website) error {
|
||||
func ChangeHSTSConfig(enable bool, http3Enable bool, website model.Website) error {
|
||||
includeDir := GetSitePath(website, SiteProxyDir)
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(includeDir) {
|
||||
@@ -1193,6 +1211,11 @@ func ChangeHSTSConfig(enable bool, website model.Website) error {
|
||||
} else {
|
||||
location.RemoveDirective("add_header", []string{"Strict-Transport-Security", "\"max-age=31536000\""})
|
||||
}
|
||||
if http3Enable {
|
||||
location.UpdateDirective("add_header", []string{"Alt-Svc", "'h3=\":443\"; ma=2592000'"})
|
||||
} else {
|
||||
location.RemoveDirective("add_header", []string{"Alt-Svc", "'h3=\":443\"; ma=2592000'"})
|
||||
}
|
||||
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
return buserr.WithErr("ErrUpdateBuWebsite", err)
|
||||
}
|
||||
|
||||
+35
-1
@@ -142,6 +142,40 @@ func NewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task, e
|
||||
return task, nil
|
||||
}
|
||||
|
||||
func ReNewTaskWithOps(resourceName, operate, scope, taskID string, resourceID uint) (*Task, error) {
|
||||
return ReNewTask(GetTaskName(resourceName, operate, scope), operate, scope, taskID, resourceID)
|
||||
}
|
||||
func ReNewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task, error) {
|
||||
if taskID == "" {
|
||||
return NewTask(name, operate, taskScope, taskID, resourceID)
|
||||
}
|
||||
logDir := path.Join(global.Dir.TaskDir, taskScope)
|
||||
if _, err := os.Stat(logDir); err != nil {
|
||||
if err = os.MkdirAll(logDir, constant.DirPerm); err != nil {
|
||||
return nil, fmt.Errorf("failed to create log directory: %w", err)
|
||||
}
|
||||
}
|
||||
logPath := path.Join(global.Dir.TaskDir, taskScope, taskID+".log")
|
||||
file, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, constant.FilePerm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open log file: %w", err)
|
||||
}
|
||||
writer := bufio.NewWriter(file)
|
||||
logger := log.New(file, "", log.LstdFlags)
|
||||
taskModel := &model.Task{
|
||||
ID: taskID,
|
||||
Name: name,
|
||||
Type: taskScope,
|
||||
LogFile: logPath,
|
||||
Status: constant.StatusExecuting,
|
||||
ResourceID: resourceID,
|
||||
Operate: operate,
|
||||
}
|
||||
taskRepo := repo.NewITaskRepo()
|
||||
task := &Task{Name: name, logFile: file, Logger: logger, taskRepo: taskRepo, Task: taskModel, Writer: writer}
|
||||
return task, nil
|
||||
}
|
||||
|
||||
func (t *Task) AddSubTask(name string, action ActionFunc, rollback RollbackFunc) {
|
||||
subTask := &SubTask{RootTask: t, Name: name, Retry: 0, Timeout: 30 * time.Minute, Action: action, Rollback: rollback}
|
||||
t.SubTasks = append(t.SubTasks, subTask)
|
||||
@@ -158,7 +192,7 @@ func (t *Task) AddSubTaskWithOps(name string, action ActionFunc, rollback Rollba
|
||||
}
|
||||
|
||||
func (t *Task) AddSubTaskWithAliasAndOps(key string, action ActionFunc, rollback RollbackFunc, retry int, timeout time.Duration) {
|
||||
subTask := &SubTask{RootTask: t, Name: i18n.GetMsgByKey(key), Retry: retry, Timeout: timeout, Action: action, Rollback: rollback}
|
||||
subTask := &SubTask{RootTask: t, Name: i18n.GetMsgByKey(key), StepAlias: key, Retry: retry, Timeout: timeout, Action: action, Rollback: rollback}
|
||||
t.SubTasks = append(t.SubTasks, subTask)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
cpuProfile string
|
||||
memProfile string
|
||||
duration time.Duration
|
||||
showMemStats bool
|
||||
showGoroutine bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(debugCmd)
|
||||
|
||||
debugCmd.Flags().StringVar(&cpuProfile, "cpuprofile", "", "write cpu profile to file")
|
||||
debugCmd.Flags().StringVar(&memProfile, "memprofile", "", "write memory profile to file")
|
||||
debugCmd.Flags().DurationVar(&duration, "duration", 30*time.Second, "duration for cpu profiling")
|
||||
debugCmd.Flags().BoolVar(&showMemStats, "memstats", false, "show memory statistics")
|
||||
debugCmd.Flags().BoolVar(&showGoroutine, "goroutine", false, "show goroutine profile")
|
||||
}
|
||||
|
||||
var debugCmd = &cobra.Command{
|
||||
Use: "debug",
|
||||
Short: "Performance debugging with pprof",
|
||||
Long: `Debug command provides CPU and memory profiling capabilities using pprof.
|
||||
|
||||
Examples:
|
||||
# CPU profiling for 30 seconds
|
||||
1panel-agent debug --cpuprofile=cpu.prof --duration=30s
|
||||
|
||||
# Memory profiling
|
||||
1panel-agent debug --memprofile=mem.prof
|
||||
|
||||
# Show memory statistics
|
||||
1panel-agent debug --memstats
|
||||
|
||||
# Show goroutine information
|
||||
1panel-agent debug --goroutine
|
||||
|
||||
# Combined profiling
|
||||
1panel-agent debug --cpuprofile=cpu.prof --memprofile=mem.prof --memstats`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if cpuProfile != "" {
|
||||
if err := startCPUProfile(cpuProfile); err != nil {
|
||||
return fmt.Errorf("failed to start CPU profile: %v", err)
|
||||
}
|
||||
defer stopCPUProfile()
|
||||
time.Sleep(duration)
|
||||
fmt.Printf("CPU profile saved to: %s\n", cpuProfile)
|
||||
}
|
||||
|
||||
if memProfile != "" {
|
||||
if err := writeMemProfile(memProfile); err != nil {
|
||||
return fmt.Errorf("failed to write memory profile: %v", err)
|
||||
}
|
||||
fmt.Printf("Memory profile saved to: %s\n", memProfile)
|
||||
}
|
||||
|
||||
if showMemStats {
|
||||
printMemStats()
|
||||
}
|
||||
|
||||
if showGoroutine {
|
||||
printGoroutineInfo()
|
||||
}
|
||||
|
||||
if cpuProfile == "" && memProfile == "" && !showMemStats && !showGoroutine {
|
||||
printBasicInfo()
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func startCPUProfile(filename string) error {
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := pprof.StartCPUProfile(f); err != nil {
|
||||
f.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func stopCPUProfile() {
|
||||
pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
func writeMemProfile(filename string) error {
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
runtime.GC()
|
||||
|
||||
if err := pprof.WriteHeapProfile(f); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func printMemStats() {
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
|
||||
fmt.Println("\n=== Memory Statistics ===")
|
||||
fmt.Printf("Allocated memory: %d KB (%d MB)\n", m.Alloc/1024, m.Alloc/1024/1024)
|
||||
fmt.Printf("Total allocated: %d KB (%d MB)\n", m.TotalAlloc/1024, m.TotalAlloc/1024/1024)
|
||||
fmt.Printf("System memory: %d KB (%d MB)\n", m.Sys/1024, m.Sys/1024/1024)
|
||||
fmt.Printf("Heap allocated: %d KB (%d MB)\n", m.HeapAlloc/1024, m.HeapAlloc/1024/1024)
|
||||
fmt.Printf("Heap system: %d KB (%d MB)\n", m.HeapSys/1024, m.HeapSys/1024/1024)
|
||||
fmt.Printf("Heap idle: %d KB (%d MB)\n", m.HeapIdle/1024, m.HeapIdle/1024/1024)
|
||||
fmt.Printf("Heap in use: %d KB (%d MB)\n", m.HeapInuse/1024, m.HeapInuse/1024/1024)
|
||||
fmt.Printf("Heap released: %d KB (%d MB)\n", m.HeapReleased/1024, m.HeapReleased/1024/1024)
|
||||
fmt.Printf("Heap objects: %d\n", m.HeapObjects)
|
||||
fmt.Printf("GC runs: %d\n", m.NumGC)
|
||||
fmt.Printf("GC pause total: %d ns\n", m.PauseTotalNs)
|
||||
fmt.Printf("Next GC: %d KB (%d MB)\n", m.NextGC/1024, m.NextGC/1024/1024)
|
||||
fmt.Printf("Last GC: %s\n", time.Unix(0, int64(m.LastGC)).Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
func printGoroutineInfo() {
|
||||
fmt.Println("\n=== Goroutine Information ===")
|
||||
fmt.Printf("Number of goroutines: %d\n", runtime.NumGoroutine())
|
||||
fmt.Printf("Number of CPUs: %d\n", runtime.NumCPU())
|
||||
fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0))
|
||||
|
||||
buf := make([]byte, 1024*1024)
|
||||
stackSize := runtime.Stack(buf, true)
|
||||
fmt.Printf("\nGoroutine stack trace (first 2048 bytes):\n")
|
||||
if stackSize > 2048 {
|
||||
fmt.Printf("%s...\n", buf[:2048])
|
||||
fmt.Printf("(truncated, total size: %d bytes)\n", stackSize)
|
||||
} else {
|
||||
fmt.Printf("%s\n", buf[:stackSize])
|
||||
}
|
||||
}
|
||||
|
||||
func printBasicInfo() {
|
||||
fmt.Println("=== Performance Debug Info ===")
|
||||
fmt.Printf("Go version: %s\n", runtime.Version())
|
||||
fmt.Printf("OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
|
||||
fmt.Printf("CPUs: %d\n", runtime.NumCPU())
|
||||
fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0))
|
||||
fmt.Printf("Goroutines: %d\n", runtime.NumGoroutine())
|
||||
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
fmt.Printf("Memory allocated: %d KB\n", m.Alloc/1024)
|
||||
fmt.Printf("Total allocations: %d KB\n", m.TotalAlloc/1024)
|
||||
fmt.Printf("GC runs: %d\n", m.NumGC)
|
||||
|
||||
fmt.Println("\nUse --help to see available profiling options.")
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/server"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var RootCmd = &cobra.Command{
|
||||
Use: "1panel-agent",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
server.Start()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "net/http/pprof"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/server"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/agent/cmd/server/cmd"
|
||||
"os"
|
||||
)
|
||||
|
||||
// @title 1Panel
|
||||
@@ -15,5 +15,8 @@ import (
|
||||
// @host localhost
|
||||
// @BasePath /api/v2
|
||||
func main() {
|
||||
server.Start()
|
||||
if err := cmd.RootCmd.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,12 @@ package nginx_conf
|
||||
import (
|
||||
"embed"
|
||||
_ "embed"
|
||||
"io"
|
||||
)
|
||||
|
||||
//go:embed ssl.conf
|
||||
var SSL []byte
|
||||
|
||||
//go:embed website_default.conf
|
||||
var WebsiteDefault []byte
|
||||
|
||||
//go:embed index.html
|
||||
var Index []byte
|
||||
|
||||
@@ -44,8 +42,18 @@ var PathAuth []byte
|
||||
//go:embed upstream.conf
|
||||
var Upstream []byte
|
||||
|
||||
//go:embed php_extensions.json
|
||||
var PHPExtensionsJson []byte
|
||||
|
||||
//go:embed sse.conf
|
||||
var SSE []byte
|
||||
|
||||
//go:embed *.json *.conf
|
||||
var websitesFiles embed.FS
|
||||
|
||||
func GetWebsiteFile(filename string) []byte {
|
||||
file, err := websitesFiles.Open(filename)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer file.Close()
|
||||
res, _ := io.ReadAll(file)
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
[global]
|
||||
error_log = /var/log/php/fpm.error.log
|
||||
log_level = notice
|
||||
[www]
|
||||
user = www-data
|
||||
group = www-data
|
||||
listen = 0.0.0.0:9000
|
||||
pm = dynamic
|
||||
pm.max_children = 10
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
pm.status_path = /status
|
||||
slowlog = /var/log/php/fpm.slow.log
|
||||
request_slowlog_timeout = 3
|
||||
catch_workers_output = yes
|
||||
@@ -91,7 +91,7 @@
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"name": "Swoole",
|
||||
"name": "swoole",
|
||||
"check": "swoole",
|
||||
"file": "swoole.so",
|
||||
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
|
||||
@@ -362,5 +362,19 @@
|
||||
"file": "gd.so",
|
||||
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"name": "bcmath",
|
||||
"check": "bcmath",
|
||||
"file": "bcmath.so",
|
||||
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"name": "pcntl",
|
||||
"check": "pcntl",
|
||||
"file": "pcntl.so",
|
||||
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
|
||||
"installed": false
|
||||
}
|
||||
]
|
||||
|
||||
@@ -15,9 +15,16 @@ server {
|
||||
|
||||
access_log /www/sites/domain/log/access.log main;
|
||||
error_log /www/sites/domain/log/error.log;
|
||||
|
||||
|
||||
location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md){
|
||||
return 404;
|
||||
}
|
||||
location ^~ /.well-known{
|
||||
allow all;
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {
|
||||
return 403;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,6 @@ const (
|
||||
ComposeOpStop = "stop"
|
||||
ComposeOpRestart = "restart"
|
||||
ComposeOpRemove = "remove"
|
||||
|
||||
DaemonJsonPath = "/etc/docker/daemon.json"
|
||||
)
|
||||
|
||||
var DaemonJsonPath = "/etc/docker/daemon.json"
|
||||
|
||||
@@ -41,11 +41,11 @@ var RuntimeDefaultVolumes = map[string]string{
|
||||
|
||||
var PHPDefaultVolumes = map[string]string{
|
||||
"${PANEL_WEBSITE_DIR}": "/www/",
|
||||
"./conf": "/usr/local/etc/php",
|
||||
"./conf/php.ini": "/usr/local/etc/php/php.ini",
|
||||
"./conf/conf.d": "/usr/local/etc/php/conf.d",
|
||||
"./conf/php-fpm.conf": "/usr/local/etc/php-fpm.d/www.conf",
|
||||
"./conf/php-fpm.conf": "/usr/local/etc/php-fpm.conf",
|
||||
"./log": "/var/log/php",
|
||||
"./extensions": "${EXTENSION_DIR}",
|
||||
"./extensions": "/usr/local/lib/php/extensions",
|
||||
"./supervisor/supervisord.conf": "/etc/supervisord.conf",
|
||||
"./supervisor/supervisor.d/php-fpm.ini": "/etc/supervisor.d/php-fpm.ini",
|
||||
"./supervisor/supervisor.d": "/etc/supervisor.d",
|
||||
|
||||
@@ -18,7 +18,7 @@ func NewBackupJob() *backup {
|
||||
|
||||
func (b *backup) Run() {
|
||||
var backups []model.BackupAccount
|
||||
_ = global.DB.Where("`type` in (?) AND is_public = 0", []string{constant.OneDrive, constant.ALIYUN, constant.GoogleDrive}).Find(&backups)
|
||||
_ = global.DB.Where("`type` in (?) AND is_public = 0", []string{constant.OneDrive, constant.ALIYUN}).Find(&backups)
|
||||
if len(backups) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -39,8 +39,6 @@ func (b *backup) Run() {
|
||||
switch backupItem.Type {
|
||||
case constant.OneDrive:
|
||||
refreshToken, err = client.RefreshToken("refresh_token", "refreshToken", varMap)
|
||||
case constant.GoogleDrive:
|
||||
refreshToken, err = client.RefreshGoogleToken("refresh_token", "refreshToken", varMap)
|
||||
case constant.ALIYUN:
|
||||
refreshToken, err = client.RefreshALIToken(varMap)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package global
|
||||
|
||||
type ServerConfig struct {
|
||||
Base Base `mapstructure:"base"`
|
||||
RemoteURL RemoteURL `mapstructure:"remote_url"`
|
||||
Log LogConfig `mapstructure:"log"`
|
||||
Base Base `mapstructure:"base"`
|
||||
RemoteURL RemoteURL `mapstructure:"remote_url"`
|
||||
Log LogConfig `mapstructure:"log"`
|
||||
DockerConfig DockerConfig
|
||||
}
|
||||
|
||||
type Base struct {
|
||||
@@ -51,3 +52,7 @@ type LogConfig struct {
|
||||
LogSuffix string `mapstructure:"log_suffix"`
|
||||
MaxBackup int `mapstructure:"max_backup"`
|
||||
}
|
||||
|
||||
type DockerConfig struct {
|
||||
Command string
|
||||
}
|
||||
|
||||
+77
-98
@@ -1,23 +1,21 @@
|
||||
module github.com/1Panel-dev/1Panel/agent
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.3
|
||||
go 1.23.9
|
||||
|
||||
require (
|
||||
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
|
||||
github.com/aws/aws-sdk-go v1.55.0
|
||||
github.com/compose-spec/compose-go/v2 v2.1.4
|
||||
github.com/creack/pty v1.1.21
|
||||
github.com/docker/compose/v2 v2.29.0
|
||||
github.com/docker/docker v27.1.0+incompatible
|
||||
github.com/compose-spec/compose-go/v2 v2.6.4
|
||||
github.com/creack/pty v1.1.24
|
||||
github.com/docker/compose/v2 v2.37.0
|
||||
github.com/docker/docker v28.2.2+incompatible
|
||||
github.com/docker/go-connections v0.5.0
|
||||
github.com/fsnotify/fsnotify v1.8.0
|
||||
github.com/fsnotify/fsnotify v1.9.0
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/go-acme/lego/v4 v4.23.1
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.2
|
||||
github.com/go-playground/validator/v10 v10.22.0
|
||||
github.com/go-playground/validator/v10 v10.26.0
|
||||
github.com/go-redis/redis v6.15.9+incompatible
|
||||
github.com/go-resty/resty/v2 v2.16.5
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
@@ -28,11 +26,11 @@ require (
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/jinzhu/gorm v1.9.16
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/klauspost/compress v1.17.9
|
||||
github.com/klauspost/compress v1.18.0
|
||||
github.com/mholt/archiver/v4 v4.0.0-alpha.8
|
||||
github.com/minio/minio-go/v7 v7.0.74
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.0
|
||||
github.com/opencontainers/image-spec v1.1.0
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
@@ -42,69 +40,66 @@ require (
|
||||
github.com/shirou/gopsutil/v3 v3.24.5
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/afero v1.11.0
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/studio-b12/gowebdav v0.9.0
|
||||
github.com/subosito/gotenv v1.6.0
|
||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.54
|
||||
github.com/upyun/go-sdk v2.1.0+incompatible
|
||||
golang.org/x/crypto v0.36.0
|
||||
golang.org/x/net v0.37.0
|
||||
golang.org/x/oauth2 v0.28.0
|
||||
golang.org/x/sys v0.31.0
|
||||
golang.org/x/text v0.23.0
|
||||
golang.org/x/crypto v0.39.0
|
||||
golang.org/x/net v0.41.0
|
||||
golang.org/x/oauth2 v0.29.0
|
||||
golang.org/x/sys v0.33.0
|
||||
golang.org/x/text v0.26.0
|
||||
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gorm.io/gorm v1.25.11
|
||||
gorm.io/gorm v1.30.0
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.100 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/baidubce/bce-sdk-go v0.9.223 // indirect
|
||||
github.com/bodgit/plumbing v1.2.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.3.0 // indirect
|
||||
github.com/bodgit/windows v1.0.0 // indirect
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/clbanning/mxj v1.8.4 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.115.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/connesc/cipherio v0.2.1 // indirect
|
||||
github.com/containerd/console v1.0.4 // indirect
|
||||
github.com/containerd/containerd v1.7.19 // indirect
|
||||
github.com/containerd/containerd/api v1.7.19 // indirect
|
||||
github.com/containerd/continuity v0.4.3 // indirect
|
||||
github.com/containerd/errdefs v0.1.0 // indirect
|
||||
github.com/containerd/containerd/api v1.9.0 // indirect
|
||||
github.com/containerd/containerd/v2 v2.1.1 // indirect
|
||||
github.com/containerd/continuity v0.4.5 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/containerd/ttrpc v1.2.5 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.1.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/containerd/platforms v1.0.0-rc.1 // indirect
|
||||
github.com/containerd/ttrpc v1.2.7 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.2.3 // indirect
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/buildx v0.16.0 // indirect
|
||||
github.com/docker/cli v27.0.3+incompatible // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/docker/buildx v0.24.0 // indirect
|
||||
github.com/docker/cli v28.2.2+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.3 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dsnet/compress v0.0.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fvbommel/sortorder v1.0.2 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/fvbommel/sortorder v1.1.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||
github.com/glebarez/go-sqlite v1.22.0 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
@@ -112,26 +107,22 @@ require (
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/gofrs/flock v0.12.1 // indirect
|
||||
github.com/gogo/googleapis v1.4.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
|
||||
github.com/h2non/filetype v1.1.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.141 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/in-toto/in-toto-golang v0.5.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
@@ -146,29 +137,32 @@ require (
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/matishsiao/goInfo v0.0.0-20210923090445-da2e3fa8d45f // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/miekg/dns v1.1.64 // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/buildkit v0.15.0 // indirect
|
||||
github.com/moby/buildkit v0.22.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/go-archive v0.1.0 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||
github.com/moby/sys/signal v0.7.0 // indirect
|
||||
github.com/moby/sys/user v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/moby/sys/atomicwriter v0.1.0 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/moby/sys/signal v0.7.1 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/mozillazg/go-httpheader v0.2.1 // indirect
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/nrdcg/dnspod-go v0.4.0 // indirect
|
||||
github.com/nrdcg/freemyip v0.3.0 // indirect
|
||||
github.com/nrdcg/mailinabox v0.2.0 // indirect
|
||||
@@ -177,15 +171,12 @@ require (
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/prometheus/client_golang v1.17.0 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.44.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rs/xid v1.5.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
@@ -194,17 +185,15 @@ require (
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/cobra v1.8.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1128 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128 // indirect
|
||||
github.com/therootcompany/xz v1.0.1 // indirect
|
||||
github.com/theupdateframework/notary v0.7.0 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/tonistiigi/fsutil v0.0.0-20240424095704-91a3fc46842c // indirect
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144 // indirect
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 // indirect
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
|
||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
|
||||
@@ -215,47 +204,37 @@ require (
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.mongodb.org/mongo-driver v1.13.1 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
|
||||
go.opentelemetry.io/otel v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.34.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect
|
||||
golang.org/x/mod v0.23.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.30.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
|
||||
golang.org/x/tools v0.34.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
|
||||
google.golang.org/grpc v1.71.0 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/apimachinery v0.29.2 // indirect
|
||||
k8s.io/client-go v0.29.2 // indirect
|
||||
k8s.io/klog/v2 v2.110.1 // indirect
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
|
||||
modernc.org/libc v1.22.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/sqlite v1.23.1 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
google.golang.org/grpc v1.72.2 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
modernc.org/libc v1.66.1 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
modernc.org/sqlite v1.38.0 // indirect
|
||||
)
|
||||
|
||||
+207
-294
File diff suppressed because it is too large
Load Diff
@@ -92,6 +92,7 @@ ErrCustomApps: 'There is an installed application, please uninstall it first'
|
||||
ErrCustomRuntimes: 'There is an installed runtime environment, please delete it first'
|
||||
ErrAppVersionDeprecated: "The {{ .name }} application is not compatible with the current 1Panel version, skipped"
|
||||
ErrDockerFailed: "The state of Docker is abnormal, please check the service status"
|
||||
ErrDockerComposeCmdNotFound: "The Docker Compose command does not exist, please install this command on the host machine first"
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: 'This file does not support preview'
|
||||
@@ -107,6 +108,7 @@ ErrSourcePathNotFound: 'Source directory does not exist'
|
||||
ErrFavoriteExist: 'This path has already been favorited'
|
||||
ErrInvalidChar: 'Illegal characters are not allowed'
|
||||
ErrPathNotDelete: 'The selected directory cannot be deleted'
|
||||
ErrLogFileToLarge: "The log file exceeds 500MB and cannot be opened"
|
||||
|
||||
#website
|
||||
ErrAliasIsExist: 'Alias already exists'
|
||||
@@ -202,6 +204,9 @@ HandleSystemClean: 'System cache cleanup'
|
||||
SystemLog: 'System Log'
|
||||
CutWebsiteLog: 'Rotate Website Log'
|
||||
FileOrDir: 'Directory / File'
|
||||
UploadFile: 'Uploading backup file {{ .file }} to {{ .backup }}'
|
||||
IgnoreBackupErr: 'Backup failed, error: {{ .detail }}, ignoring this error...'
|
||||
IgnoreUploadErr: 'Upload failed, error: {{ .detail }}, ignoring this error...'
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: 'The current user does not exist, please modify and try again!'
|
||||
@@ -330,6 +335,7 @@ SnapDeleteLoginLog: 'Delete access log'
|
||||
SnapDeleteMonitor: 'Delete monitoring data'
|
||||
SnapRemoveSystemIP: 'Remove system IP'
|
||||
SnapBaseInfo: 'Write 1Panel basic information'
|
||||
SnapInstallAppImageEmpty: 'No application images selected, skipping...'
|
||||
SnapInstallApp: 'Backup 1Panel installed applications'
|
||||
SnapDockerSave: 'Compress installed applications'
|
||||
SnapLocalBackup: 'Backup 1Panel local backup directory'
|
||||
|
||||
@@ -92,6 +92,7 @@ ErrCustomApps: 'インストールされているアプリケーションがあ
|
||||
ErrCustomRuntimes: 'ランタイム環境がインストールされています。まずそれを削除してください'
|
||||
ErrAppVersionDeprecated: "{{ .name }} アプリケーションは現在の 1Panel バージョンと互換性がありません、スキップしました"
|
||||
ErrDockerFailed: "Docker の状態が異常です。サービス状態を確認してください"
|
||||
ErrDockerComposeCmdNotFound: "Docker Compose コマンドは存在しません。ホストマシンにこのコマンドを先にインストールしてください"
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: 'このファイルはプレビューをサポートしていません'
|
||||
@@ -107,6 +108,7 @@ ErrSourcePathNotFound: 'ソース ディレクトリが存在しません'
|
||||
ErrFavoriteExist: 'このパスはすでにお気に入りに登録されています'
|
||||
ErrInvalidChar: '不正な文字は許可されません'
|
||||
ErrPathNotDelete: '選択されたディレクトリは削除できません'
|
||||
ErrLogFileToLarge: "ログファイルが500MBを超えており、開くことができません"
|
||||
|
||||
#website
|
||||
ErrAliasIsExist: 'エイリアスがすでに存在します'
|
||||
@@ -202,6 +204,9 @@ HandleSystemClean: 'システム キャッシュのクリーンアップ'
|
||||
SystemLog: 'システムログ'
|
||||
CutWebsiteLog: 'ウェブサイトログのローテーション'
|
||||
FileOrDir: 'ディレクトリ / ファイル'
|
||||
UploadFile: 'バックアップファイル {{ .file }} を {{ .backup }} にアップロード中'
|
||||
IgnoreBackupErr: 'バックアップ失敗、エラー:{{ .detail }}、このエラーを無視します...'
|
||||
IgnoreUploadErr: 'アップロード失敗、エラー:{{ .detail }}、このエラーを無視します...'
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: '現在のユーザーは存在しません。変更してもう一度お試しください。'
|
||||
@@ -330,7 +335,8 @@ SnapDeleteLoginLog: 'アクセスログを削除する'
|
||||
SnapDeleteMonitor: '監視データを削除する'
|
||||
SnapRemoveSystemIP: 'システム IP を削除します'
|
||||
SnapBaseInfo: '1Panel の基本情報を書き込む'
|
||||
SnapInstallApp: '1Panel にインストールされたアプリケーションをバックアップする'
|
||||
SnapInstallAppImageEmpty: 'アプリケーションイメージが選択されていないため、スキップします...'
|
||||
SnapInstallApp: '1Panelインストール済みアプリケーションのバックアップ'
|
||||
SnapDockerSave: 'インストールされたアプリケーションを圧縮する'
|
||||
SnapLocalBackup: '1Panel ローカル バックアップ ディレクトリをバックアップします'
|
||||
SnapCompressBackup: 'ローカル バックアップ ディレクトリを圧縮する'
|
||||
|
||||
@@ -92,6 +92,7 @@ ErrCustomApps: '설치된 애플리케이션이 있습니다. 먼저 제거해
|
||||
ErrCustomRuntimes: '설치된 런타임 환경이 있습니다. 먼저 삭제해 주세요'
|
||||
ErrAppVersionDeprecated: "{{ .name }} 응용 프로그램은 현재 1Panel 버전과 호환되지 않아 건너뛰었습니다"
|
||||
ErrDockerFailed: "Docker의 상태가 비정상입니다. 서비스 상태를 확인하세요"
|
||||
ErrDockerComposeCmdNotFound: "Docker Compose 명령이 없습니다. 호스트 머신에 먼저 이 명령을 설치하세요"
|
||||
|
||||
#파일
|
||||
ErrFileCanNotRead: '이 파일은 미리보기를 지원하지 않습니다'
|
||||
@@ -107,6 +108,7 @@ ErrSourcePathNotFound: '소스 디렉토리가 존재하지 않습니다'
|
||||
ErrFavoriteExist: '이 경로는 이미 즐겨찾기되었습니다'
|
||||
ErrInvalidChar: '불법 문자는 허용되지 않습니다'
|
||||
ErrPathNotDelete: '선택한 디렉토리를 삭제할 수 없습니다'
|
||||
ErrLogFileToLarge: "로그 파일이 500MB를 초과하여 열 수 없습니다"
|
||||
|
||||
#웹사이트
|
||||
ErrAliasIsExist: '별칭이 이미 존재합니다'
|
||||
@@ -202,6 +204,9 @@ HandleSystemClean: '시스템 캐시 정리'
|
||||
SystemLog: '시스템 로그'
|
||||
CutWebsiteLog: '웹사이트 로그 회전'
|
||||
FileOrDir: '디렉터리 / 파일'
|
||||
UploadFile: '백업 파일 {{ .file }} 을(를) {{ .backup }}(으)로 업로드 중'
|
||||
IgnoreBackupErr: 'Sandaran gagal, ralat: {{ .detail }}, abaikan ralat ini...'
|
||||
IgnoreUploadErr: 'Muat naik gagal, ralat: {{ .detail }}, abaikan ralat ini...'
|
||||
|
||||
#도구상자
|
||||
ErrNotExistUser: '현재 사용자가 존재하지 않습니다. 수정한 후 다시 시도하세요!'
|
||||
@@ -330,6 +335,7 @@ SnapDeleteLoginLog: '액세스 로그 삭제'
|
||||
SnapDeleteMonitor: '모니터링 데이터 삭제'
|
||||
SnapRemoveSystemIP: '시스템 IP 제거'
|
||||
SnapBaseInfo: '1패널 기본 정보 쓰기'
|
||||
SnapInstallAppImageEmpty: '애플리케이션 이미지가 선택되지 않아 건너뜁니다...'
|
||||
SnapInstallApp: '1Panel 설치된 애플리케이션 백업'
|
||||
SnapDockerSave: '설치된 애플리케이션 압축'
|
||||
SnapLocalBackup: '1Panel 로컬 백업 디렉토리 백업'
|
||||
|
||||
@@ -92,6 +92,7 @@ ErrCustomApps: 'Ada aplikasi yang dipasang, sila nyahpasangnya dahulu'
|
||||
ErrCustomRuntimes: 'Terdapat persekitaran masa jalan yang dipasang, sila padamkannya dahulu'
|
||||
ErrAppVersionDeprecated: "Aplikasi {{ .name }} tidak sesuai dengan versi 1Panel saat ini, dilewati"
|
||||
ErrDockerFailed: "Keadaan Docker tidak normal, sila periksa status perkhidmatan"
|
||||
ErrDockerComposeCmdNotFound: "Perintah Docker Compose tidak wujud, sila pasang perintah ini di mesin tuan terlebih dahulu"
|
||||
|
||||
#fail
|
||||
ErrFileCanNotRead: 'Fail ini tidak menyokong pratonton'
|
||||
@@ -202,6 +203,9 @@ HandleSystemClean: 'Pembersihan cache sistem'
|
||||
SystemLog: 'Log Sistem'
|
||||
CutWebsiteLog: 'Putar Log Laman Web'
|
||||
FileOrDir: 'Direktori / Fail'
|
||||
UploadFile: 'Muat naik fail sandaran {{ .file }} ke {{ .backup }}'
|
||||
IgnoreBackupErr: 'Sandaran gagal, ralat: {{ .detail }}, abaikan ralat ini...'
|
||||
IgnoreUploadErr: 'Muat naik gagal, ralat: {{ .detail }}, abaikan ralat ini...'
|
||||
|
||||
#kotak alat
|
||||
ErrNotExistUser: 'Pengguna semasa tidak wujud, sila ubah suai dan cuba lagi!'
|
||||
@@ -330,7 +334,8 @@ SnapDeleteLoginLog: 'Padam log akses'
|
||||
SnapDeleteMonitor: 'Padamkan data pemantauan'
|
||||
SnapRemoveSystemIP: 'Alih keluar IP sistem'
|
||||
SnapBaseInfo: 'Tulis maklumat asas 1Panel'
|
||||
SnapInstallApp: 'Aplikasi yang dipasang sandaran 1Panel'
|
||||
SnapInstallAppImageEmpty: 'Tiada imej aplikasi dipilih, dilangkau...'
|
||||
SnapInstallApp: 'Sandaran aplikasi terpasang 1Panel'
|
||||
SnapDockerSave: 'Mampatkan aplikasi yang dipasang'
|
||||
SnapLocalBackup: 'Sandaran 1Panel direktori sandaran tempatan'
|
||||
SnapCompressBackup: 'Mampatkan direktori sandaran tempatan'
|
||||
|
||||
@@ -92,6 +92,7 @@ ErrCustomApps: 'Há um aplicativo instalado, desinstale-o primeiro'
|
||||
ErrCustomRuntimes: 'Há um ambiente de execução instalado, exclua-o primeiro'
|
||||
ErrAppVersionDeprecated: "O aplicativo {{ .name }} não é compatível com a versão atual do 1Panel, ignorado"
|
||||
ErrDockerFailed: "O estado do Docker está anormal, verifique o status do serviço"
|
||||
ErrDockerComposeCmdNotFound: "O comando Docker Compose não existe, por favor, instale este comando na máquina host primeiro"
|
||||
|
||||
#arquivo
|
||||
ErrFileCanNotRead: 'Este arquivo não suporta visualização'
|
||||
@@ -107,6 +108,7 @@ ErrSourcePathNotFound: 'O diretório de origem não existe'
|
||||
ErrFavoriteExist: 'Este caminho já foi favorito'
|
||||
ErrInvalidChar: 'Caracteres ilegais não são permitidos'
|
||||
ErrPathNotDelete: 'O diretório selecionado não pode ser excluído'
|
||||
ErrLogFileToLarge: "Fail log melebihi 500MB, tidak boleh dibuka"
|
||||
|
||||
#site
|
||||
ErrAliasIsExist: 'Alias já existe'
|
||||
@@ -202,6 +204,9 @@ HandleSystemClean: 'Limpeza de cache do sistema'
|
||||
SystemLog: 'Log do Sistema'
|
||||
CutWebsiteLog: 'Rotacionar Log do Website'
|
||||
FileOrDir: 'Diretório / Arquivo'
|
||||
UploadFile: 'Enviando arquivo de backup {{ .file }} para {{ .backup }}'
|
||||
IgnoreBackupErr: 'Backup falhou, erro: {{ .detail }}, ignorando este erro...'
|
||||
IgnoreUploadErr: 'Upload falhou, erro: {{ .detail }}, ignorando este erro...'
|
||||
|
||||
#caixa de ferramentas
|
||||
ErrNotExistUser: 'O usuário atual não existe, modifique e tente novamente!'
|
||||
@@ -330,7 +335,8 @@ SnapDeleteLoginLog: 'Excluir log de acesso'
|
||||
SnapDeleteMonitor: 'Excluir dados de monitoramento'
|
||||
SnapRemoveSystemIP: 'Remover IP do sistema'
|
||||
SnapBaseInfo: 'Escreva informações básicas do 1Panel'
|
||||
SnapInstallApp: 'Fazer backup dos aplicativos instalados do 1Panel'
|
||||
SnapInstallAppImageEmpty: 'Nenhuma imagem de aplicativo selecionada, pulando...'
|
||||
SnapInstallApp: 'Backup dos aplicativos instalados do 1Panel'
|
||||
SnapDockerSave: 'Compactar aplicativos instalados'
|
||||
SnapLocalBackup: 'Backup do diretório de backup local do 1Panel'
|
||||
SnapCompressBackup: 'Compactar diretório de backup local'
|
||||
|
||||
@@ -92,6 +92,7 @@ ErrCustomApps: 'Установлено приложение. Сначала уд
|
||||
ErrCustomRuntimes: 'Установлена среда выполнения. Сначала удалите ее'
|
||||
ErrAppVersionDeprecated: "Приложение {{ .name }} несовместимо с текущей версией 1Panel, пропущено"
|
||||
ErrDockerFailed: "Состояние Docker аномально, проверьте состояние сервиса"
|
||||
ErrDockerComposeCmdNotFound: "Команда Docker Compose отсутствует, пожалуйста, установите эту команду на хост-машине сначала"
|
||||
|
||||
#файл
|
||||
ErrFileCanNotRead: 'Этот файл не поддерживает предварительный просмотр'
|
||||
@@ -107,6 +108,7 @@ ErrSourcePathNotFound: 'Исходный каталог не существуе
|
||||
ErrFavoriteExist: 'Этот путь уже добавлен в избранное'
|
||||
ErrInvalidChar: 'Недопустимые символы не допускаются'
|
||||
ErrPathNotDelete: 'Выбранный каталог не может быть удален'
|
||||
ErrLogFileToLarge: "Файл журнала превышает 500MB и не может быть открыт"
|
||||
|
||||
#веб-сайт
|
||||
ErrAliasIsExist: 'Псевдоним уже существует'
|
||||
@@ -202,6 +204,9 @@ HandleSystemClean: 'Очистка системного кэша'
|
||||
SystemLog: 'Системный лог'
|
||||
CutWebsiteLog: 'Ротация логов сайта'
|
||||
FileOrDir: 'Каталог / Файл'
|
||||
UploadFile: 'Загрузка файла резервной копии {{ .file }} в {{ .backup }}'
|
||||
IgnoreBackupErr: 'Ошибка резервного копирования: {{ .detail }}, игнорируем эту ошибку...'
|
||||
IgnoreUploadErr: 'Ошибка загрузки: {{ .detail }}, игнорируем эту ошибку...'
|
||||
|
||||
#ящик для инструментов
|
||||
ErrNotExistUser: 'Текущий пользователь не существует, измените его и повторите попытку!'
|
||||
@@ -330,7 +335,8 @@ SnapDeleteLoginLog: 'Удалить журнал доступа'
|
||||
SnapDeleteMonitor: 'Удалить данные мониторинга'
|
||||
SnapRemoveSystemIP: 'Удалить системный IP'
|
||||
SnapBaseInfo: 'Записать основную информацию 1Panel'
|
||||
SnapInstallApp: 'Резервное копирование установленных приложений 1Panel'
|
||||
SnapInstallAppImageEmpty: 'Образы приложений не выбраны, пропускаем...'
|
||||
SnapInstallApp: 'Резервное копирование приложений, установленных в 1Panel'
|
||||
SnapDockerSave: 'Сжать установленные приложения'
|
||||
SnapLocalBackup: 'Резервное копирование локального каталога резервных копий 1Panel'
|
||||
SnapCompressBackup: 'Сжать локальный каталог резервных копий'
|
||||
|
||||
@@ -11,8 +11,8 @@ ErrNotSupportType: '系統暫不支援目前類型: {{ .name }}'
|
||||
ErrProxy: '請求錯誤,請檢查該節點狀態: {{ .detail }}'
|
||||
ErrApiConfigStatusInvalid: 'API 介面禁止存取: {{ .detail }}'
|
||||
ErrApiConfigKeyInvalid: 'API 介面金鑰錯誤: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: '呼叫API 介面IP 不在白名單: {{ .detail }}'
|
||||
ErrApiConfigDisable: '此介面禁止使用API 介面呼叫: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: '呼叫 API 介面 IP 不在白名單: {{ .detail }}'
|
||||
ErrApiConfigDisable: '此介面禁止使用 API 介面呼叫: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'API 介面時間戳記錯誤: {{ .detail }}'
|
||||
|
||||
#common
|
||||
@@ -20,13 +20,13 @@ ErrUsernameIsExist: '使用者名稱已存在'
|
||||
ErrNameIsExist: '名稱已存在'
|
||||
ErrDemoEnvironment: '示範伺服器,禁止此操作!'
|
||||
ErrCmdTimeout: '指令執行逾時!'
|
||||
ErrCmdIllegal: '執行指令中存在不合法字符,請修改後重試!'
|
||||
ErrCmdIllegal: '執行指令中存在不合法字元,請修改後重試!'
|
||||
ErrPortExist: '{{ .port }} 埠已被{{ .type }} [{{ .name }}] 佔用'
|
||||
TYPE_APP: '應用'
|
||||
TYPE_RUNTIME: '運作環境'
|
||||
TYPE_DOMAIN: '網域名稱'
|
||||
ErrTypePort: '埠{{ .name }} 格式錯誤'
|
||||
ErrTypePortRange: '連接埠範圍需要在1-65535 之間'
|
||||
ErrTypePortRange: '連接埠範圍需要在 1-65535 之間'
|
||||
Success: '成功'
|
||||
Failed: '失敗'
|
||||
SystemRestart: '系統重新啟動導致任務中斷'
|
||||
@@ -48,7 +48,7 @@ ErrDbUserNotValid: '存量資料庫,使用者名稱密碼不符!'
|
||||
ErrUpdateBuWebsite: '應用程式更新成功,但網站設定檔修改失敗,請檢查設定! '
|
||||
Err1PanelNetworkFailed: '預設容器網路建立失敗! {{ .detail }}'
|
||||
ErrFileParse: '應用docker-compose 檔案解析失敗!'
|
||||
ErrInstallDirNotFound: '安裝目錄不存在,如需卸載,請選擇強制卸載'
|
||||
ErrInstallDirNotFound: '安裝目錄不存在,如需移除,請選擇強制移除'
|
||||
AppStoreIsUpToDate: '應用程式商店已經是最新版本'
|
||||
LocalAppVersionNull: '{{ .name }} 應用程式未同步到版本!無法加入到應用程式列表'
|
||||
LocalAppVersionErr: '{{ .name }} 同步版本{{ .version }} 失敗! {{ .err }}'
|
||||
@@ -76,22 +76,22 @@ ErrAppUpgrade: '應用程式{{ .name }} 升級失敗{{ .err }}'
|
||||
AppRecover: '應用程式{{ .name }} 回滾'
|
||||
PullImageStart: '開始拉取鏡像{{ .name }}'
|
||||
PullImageSuccess: '鏡像拉取成功'
|
||||
AppStoreIsLastVersion: '應用程式商店已經是最新版本'
|
||||
AppStoreSyncSuccess: '應用程式商店同步成功'
|
||||
SyncAppDetail: '同步應用程式設定'
|
||||
AppVersionNotMatch: '{{ .name }} 應用程式需要更高的1Panel 版本,跳過同步'
|
||||
MoveSiteDir: '目前升級需要遷移OpenResty 網站目錄'
|
||||
AppVersionNotMatch: '{{ .name }} 應用程式需要更高的 1Panel 版本,跳過同步'
|
||||
MoveSiteDir: '目前升級需要遷移 OpenResty 網站目錄'
|
||||
MoveSiteToDir: '遷移網站目錄到{{ .name }}'
|
||||
ErrMoveSiteDir: '遷移網站目錄失敗'
|
||||
MoveSiteDirSuccess: '遷移網站目錄成功'
|
||||
DeleteRuntimePHP: '刪除PHP 運行環境'
|
||||
CustomAppStoreFileValid: '應用程式商店包需要.tar.gz 格式'
|
||||
DeleteRuntimePHP: '刪除PHP 執行環境'
|
||||
CustomAppStoreFileValid: '應用程式商店包需要 .tar.gz 格式'
|
||||
PullImageTimeout: '拉取鏡像逾時,請嘗試增加鏡像加速或更換其他鏡像加速'
|
||||
ErrAppIsDown: '{{ .name }} 應用程式狀態異常,請檢查'
|
||||
ErrCustomApps: '存在已安裝的應用程式,請先解除安裝'
|
||||
ErrCustomRuntimes: '存在已安裝的運作環境,請先刪除'
|
||||
ErrAppVersionDeprecated: "{{ .name }} 應用不適配當前 1Panel 版本,跳過"
|
||||
ErrAppVersionDeprecated: "{{ .name }} 應用不適配目前 1Panel 版本,跳過"
|
||||
ErrDockerFailed: "Docker 狀態異常,請檢查服務狀態"
|
||||
ErrDockerComposeCmdNotFound: "Docker Compose 命令不存在,請先在宿主機安裝此命令"
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: '此檔案不支援預覽'
|
||||
@@ -107,13 +107,14 @@ ErrSourcePathNotFound: '來源目錄不存在'
|
||||
ErrFavoriteExist: '已收藏此路徑'
|
||||
ErrInvalidChar: '禁止使用非法字元'
|
||||
ErrPathNotDelete: '所選目錄不可刪除'
|
||||
ErrLogFileToLarge: "日志文件超过 500M,無法打開"
|
||||
|
||||
#website
|
||||
ErrAliasIsExist: '代號已存在'
|
||||
ErrBackupMatch: '該備份檔案與目前網站部分資料不符{{ .detail }}'
|
||||
ErrBackupExist: '該備份檔案對應部分來源資料不存在{{ .detail }}'
|
||||
ErrPHPResource: '本地運行環境不支援切換! '
|
||||
ErrPathPermission: 'index 目錄下偵測到非1000:1000 權限資料夾,可能導致網站存取Access denied 錯誤,請點擊上方儲存按鈕'
|
||||
ErrPHPResource: '本機執行環境不支援切換! '
|
||||
ErrPathPermission: 'index 目錄下偵測到非 1000:1000 權限資料夾,可能導致網站存取 Access denied 錯誤,請點擊上方儲存按鈕'
|
||||
ErrDomainIsUsed: '網域名稱已被網站【{{ .name }}】使用'
|
||||
ErrDomainFormat: '{{ .name }} 網域格式不正確'
|
||||
ErrDefaultAlias: 'default 為保留代號,請使用其他代號'
|
||||
@@ -126,13 +127,13 @@ ErrSSLValid: '證書文件異常,請檢查證書狀態!'
|
||||
#ssl
|
||||
ErrSSLCannotDelete: '{{ .name }} 憑證正在被網站使用,無法刪除'
|
||||
ErrAccountCannotDelete: '帳號關聯證書,無法刪除'
|
||||
ErrSSLApply: '憑證續簽成功,openresty reload失敗,請檢查設定!'
|
||||
ErrEmailIsExist: '郵箱已存在'
|
||||
ErrSSLApply: '憑證續簽成功,openresty reload 失敗,請檢查設定!'
|
||||
ErrEmailIsExist: '信箱已存在'
|
||||
ErrSSLKeyNotFound: '私鑰檔案不存在'
|
||||
ErrSSLCertificateNotFound: '憑證檔案不存在'
|
||||
ErrSSLKeyFormat: '私鑰檔案校驗失敗'
|
||||
ErrSSLCertificateFormat: '憑證檔案格式錯誤,請使用pem 格式'
|
||||
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 或EabHmacKey 不能為空'
|
||||
ErrSSLCertificateFormat: '憑證檔案格式錯誤,請使用 pem 格式'
|
||||
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 或 EabHmacKey 不能為空'
|
||||
ErrOpenrestyNotFound: 'Http 模式需要先安裝Openresty'
|
||||
ApplySSLStart: '開始申請證書,網域名稱[{{ .domain }}] 申請方式[{{ .type }}] '
|
||||
dnsAccount: 'DNS 自動'
|
||||
@@ -163,7 +164,7 @@ ErrRemoteExist: '遠端資料庫已存在該名稱,請修改後重試'
|
||||
ErrLocalExist: '本機資料庫已存在該名稱,請修改後重試'
|
||||
|
||||
#redis
|
||||
ErrTypeOfRedis: '恢復檔案類型與目前持久化方式不符,請修改後重試'
|
||||
ErrTypeOfRedis: '復原檔案類型與目前持久化方式不符,請修改後重試'
|
||||
|
||||
#container
|
||||
ErrInUsed: '{{ .detail }} 正被使用,無法刪除'
|
||||
@@ -176,21 +177,21 @@ ErrPgImagePull: '鏡像拉取逾時,請配置鏡像加速或手動拉取{{ .na
|
||||
ErrFileNotExist: '{{ .detail }} 檔案不存在!請檢查來源檔案完整性!'
|
||||
ErrImageBuildErr: '鏡像build 失敗'
|
||||
ErrImageExist: '鏡像已存在!'
|
||||
ErrDelWithWebsite: '運行環境已經關聯網站,無法刪除'
|
||||
ErrDelWithWebsite: '執行環境已經關聯網站,無法刪除'
|
||||
ErrRuntimeStart: '啟動失敗'
|
||||
ErrPackageJsonNotFound: 'package.json 檔案不存在'
|
||||
ErrScriptsNotFound: '沒有在package.json 中找到scripts 設定項'
|
||||
ErrContainerNameNotFound: '無法取得容器名稱,請檢查.env 檔案'
|
||||
ErrNodeModulesNotFound: 'node_modules 資料夾不存在!請編輯運行環境或等待運行環境啟動成功'
|
||||
ErrScriptsNotFound: '沒有在 package.json 中找到 scripts 設定項'
|
||||
ErrContainerNameNotFound: '無法取得容器名稱,請檢查 .env 檔案'
|
||||
ErrNodeModulesNotFound: 'node_modules 資料夾不存在!請編輯執行環境或等待執行環境啟動成功'
|
||||
ErrContainerNameIsNull: '容器名稱不存在'
|
||||
ErrPHPPortIsDefault: "9000 端口為默認端口,請修改後重試"
|
||||
ErrPHPRuntimePortFailed: "{{ .name }} 端口已被當前運行環境使用,請修改後重試"
|
||||
ErrPHPPortIsDefault: "9000 埠為預設埠,請修改後重試"
|
||||
ErrPHPRuntimePortFailed: "{{ .name }} 埠已被目前執行環境使用,請修改後重試"
|
||||
|
||||
#tool
|
||||
ErrConfigNotFound: '設定檔不存在'
|
||||
ErrConfigParse: '設定檔格式有誤'
|
||||
ErrConfigIsNull: '設定檔不允許為空'
|
||||
ErrConfigDirNotFound: '運行目錄不存在'
|
||||
ErrConfigDirNotFound: '執行目錄不存在'
|
||||
ErrConfigAlreadyExist: '已存在同名設定檔'
|
||||
ErrUserFindErr: '使用者{{ .name }} 尋找失敗{{ .err }}'
|
||||
|
||||
@@ -202,6 +203,9 @@ HandleSystemClean: '系統快取清理'
|
||||
SystemLog: '系統日誌'
|
||||
CutWebsiteLog: '切割網站日誌'
|
||||
FileOrDir: '目錄 / 檔案'
|
||||
UploadFile: '上傳備份文件 {{ .file }} 到 {{ .backup }}'
|
||||
IgnoreBackupErr: '備份失敗,錯誤:{{ .detail }},忽略本次錯誤...'
|
||||
IgnoreUploadErr: '上傳失敗,錯誤:{{ .detail }},忽略本次錯誤...'
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: '目前使用者不存在,請修改後重試!'
|
||||
@@ -216,7 +220,7 @@ ErrRuleNotExist: '規則不存在'
|
||||
ErrParseIP: 'IP 格式錯誤'
|
||||
ErrDefaultIP: 'default 為保留名稱,請更換其他名稱'
|
||||
ErrGroupInUse: 'IP 群組被駭/白名單使用,無法刪除'
|
||||
ErrIPGroupAclUse: "IP 群組被網站 {{ .name }} 自定義規則使用,無法刪除"
|
||||
ErrIPGroupAclUse: "IP 群組被網站 {{ .name }} 自訂規則使用,無法刪除"
|
||||
ErrGroupExist: 'IP 群組名稱已存在'
|
||||
ErrIPRange: 'IP 範圍錯誤'
|
||||
ErrIPExist: 'IP 已存在'
|
||||
@@ -272,14 +276,14 @@ TaskRetry: '開始第{{ .name }} 次重試'
|
||||
SubTaskSuccess: '{{ .name }} 成功'
|
||||
SubTaskFailed: '{{ .name }} 失敗: {{ .err }}'
|
||||
TaskInstall: '安裝'
|
||||
TaskUninstall: '卸載'
|
||||
TaskUninstall: '移除'
|
||||
TaskCreate: '建立'
|
||||
TaskDelete: '刪除'
|
||||
TaskUpgrade: '升級'
|
||||
TaskUpdate: '更新'
|
||||
TaskRestart: '重啟'
|
||||
TaskBackup: '備份'
|
||||
TaskRecover: '恢復'
|
||||
TaskRecover: '復原'
|
||||
TaskRollback: '回滾'
|
||||
TaskPull: '拉取'
|
||||
TaskBuild: '建置'
|
||||
@@ -292,7 +296,7 @@ Database: '資料庫'
|
||||
ConfigFTP: '建立FTP 使用者{{ .name }}'
|
||||
ConfigOpenresty: '建立Openresty 設定檔'
|
||||
InstallAppSuccess: '應用程式{{ .name }} 安裝成功'
|
||||
ConfigRuntime: '設定運行環境'
|
||||
ConfigRuntime: '設定執行環境'
|
||||
ConfigApp: '設定應用程式'
|
||||
SuccessStatus: '{{ .name }} 成功'
|
||||
FailedStatus: '{{ .name }} 失敗{{ .err }}'
|
||||
@@ -310,15 +314,15 @@ AppLink: '關聯應用程式'
|
||||
EnableSSL: '開啟HTTPS'
|
||||
AppStore: '應用程式商店'
|
||||
TaskSync: '同步'
|
||||
LocalApp: '本地應用'
|
||||
LocalApp: '本機應用'
|
||||
SubTask: '子任務'
|
||||
RuntimeExtension: '運行環境擴充'
|
||||
RuntimeExtension: '執行環境擴充'
|
||||
TaskIsExecuting: '任務正在運作'
|
||||
CustomAppstore: '自訂應用程式倉庫'
|
||||
|
||||
# task - ai
|
||||
OllamaModelPull: '拉取Ollama 模型{{ .name }} '
|
||||
OllamaModelSize: '取得Ollama 模型{{ .name }} 大小'
|
||||
OllamaModelPull: '拉取 Ollama 模型{{ .name }} '
|
||||
OllamaModelSize: '取得 Ollama 模型{{ .name }} 大小'
|
||||
|
||||
# task - snapshot
|
||||
Snapshot: '快照'
|
||||
@@ -330,7 +334,8 @@ SnapDeleteLoginLog: '刪除存取日誌'
|
||||
SnapDeleteMonitor: '刪除監控資料'
|
||||
SnapRemoveSystemIP: '移除系統IP'
|
||||
SnapBaseInfo: '寫入1Panel 基本資料'
|
||||
SnapInstallApp: '備份1Panel 已安裝應用程式'
|
||||
SnapInstallAppImageEmpty: '當前未勾選應用鏡像,跳過...'
|
||||
SnapInstallApp: '備份 1Panel 已安裝應用'
|
||||
SnapDockerSave: '壓縮已安裝應用'
|
||||
SnapLocalBackup: '備份1Panel 本機備份目錄'
|
||||
SnapCompressBackup: '壓縮本機備份目錄'
|
||||
@@ -347,7 +352,7 @@ SnapLoadBackup: '取得備份帳號資訊'
|
||||
SnapUploadTo: '上傳快照檔案到{{ .name }}'
|
||||
SnapUploadRes: '上傳快照檔案到{{ .name }}'
|
||||
|
||||
SnapshotRecover: '快照恢復'
|
||||
SnapshotRecover: '快照復原'
|
||||
RecoverDownload: '下載快照檔案'
|
||||
Download: '下載'
|
||||
RecoverDownloadAccount: '取得快照下載備份帳號{{ .name }}'
|
||||
@@ -358,19 +363,19 @@ Readjson: '讀取快照內Json 檔案'
|
||||
ReadjsonPath: '取得快照內Json 檔案路徑'
|
||||
ReadjsonContent: '讀取Json 檔案'
|
||||
ReadjsonMarshal: 'Json 轉義處理'
|
||||
RecoverApp: '恢復已安裝應用程式'
|
||||
RecoverWebsite: '恢復網站目錄'
|
||||
RecoverAppImage: '恢復快照鏡像備份'
|
||||
RecoverCompose: '恢復其他編排內容'
|
||||
RecoverComposeList: '取得所有待恢復編排'
|
||||
RecoverComposeItem: '恢復編排{{ .name }}'
|
||||
RecoverApp: '復原已安裝應用程式'
|
||||
RecoverWebsite: '復原網站目錄'
|
||||
RecoverAppImage: '復原快照鏡像備份'
|
||||
RecoverCompose: '復原其他編排內容'
|
||||
RecoverComposeList: '取得所有待復原編排'
|
||||
RecoverComposeItem: '復原編排{{ .name }}'
|
||||
RecoverAppEmpty: '快照檔案中未發現應用程式鏡像備份'
|
||||
RecoverBaseData: '恢復基礎資料及檔案'
|
||||
RecoverBaseData: '復原基礎資料及檔案'
|
||||
RecoverDaemonJsonEmpty: '快照檔案及目前機器都不存在容器配置daemon.json 檔案'
|
||||
RecoverDaemonJson: '恢復容器配置daemon.json 檔案'
|
||||
RecoverDBData: '恢復資料庫資料'
|
||||
RecoverDaemonJson: '復原容器配置daemon.json 檔案'
|
||||
RecoverDBData: '復原資料庫資料'
|
||||
RecoverBackups: '還原本機備份目錄'
|
||||
RecoverPanelData: '恢復資料目錄'
|
||||
RecoverPanelData: '復原資料目錄'
|
||||
|
||||
# task - container
|
||||
ContainerNewCliet: '初始化Docker Client'
|
||||
@@ -378,7 +383,7 @@ ContainerImagePull: '拉取容器鏡像{{ .name }}'
|
||||
ContainerRemoveOld: '刪除原容器{{ .name }}'
|
||||
ContainerImageCheck: '檢查鏡像是否正常拉取'
|
||||
ContainerLoadInfo: '取得容器基本資訊'
|
||||
ContainerRecreate: '容器更新失敗,現在開始恢復原容器'
|
||||
ContainerRecreate: '容器更新失敗,現在開始復原原容器'
|
||||
ContainerCreate: '建立新容器{{ .name }}'
|
||||
ContainerCreateFailed: '容器建立失敗,刪除失敗容器'
|
||||
ContainerStartCheck: '檢查容器是否已啟動'
|
||||
@@ -405,8 +410,8 @@ DeCompressFile: '解壓縮檔案{{ .name }}'
|
||||
ErrCheckValid: '校驗備份檔失敗,{{ .name }}'
|
||||
Rollback: '回滾'
|
||||
websiteDir: '網站目錄'
|
||||
RecoverFailedStartRollBack: '恢復失敗,開始回溯'
|
||||
AppBackupFileIncomplete: '備份檔案不完整缺少app.json 或app.tar.gz 檔案'
|
||||
RecoverFailedStartRollBack: '復原失敗,開始回溯'
|
||||
AppBackupFileIncomplete: '備份檔案不完整缺少 app.json 或 app.tar.gz 檔案'
|
||||
AppAttributesNotMatch: '應用程式類型或名稱不一致'
|
||||
|
||||
#alert
|
||||
@@ -417,4 +422,4 @@ ErrAlertSync: '警告訊息同步錯誤,請檢查後重試!'
|
||||
ErrAlertRemote: '警告訊息遠端錯誤,請檢查後重試!'
|
||||
|
||||
#task - runtime
|
||||
ErrInstallExtension: "已有安裝任務正在進行,請等待任務結束"
|
||||
ErrInstallExtension: "已有安裝任務正在進行,請等待任務結束"
|
||||
|
||||
@@ -91,6 +91,7 @@ ErrCustomApps: "存在已经安装的应用,请先卸载"
|
||||
ErrCustomRuntimes: "存在已经安装的运行环境,请先删除"
|
||||
ErrAppVersionDeprecated: " {{ .name }} 应用不适配当前 1Panel 版本,跳过"
|
||||
ErrDockerFailed: "Docker 状态异常,请检查服务状态"
|
||||
ErrDockerComposeCmdNotFound: "Docker Compose 命令不存在,请先在宿主机安装此命令"
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: "此文件不支持预览"
|
||||
@@ -106,6 +107,7 @@ ErrSourcePathNotFound: "源目录不存在"
|
||||
ErrFavoriteExist: "已收藏此路径"
|
||||
ErrInvalidChar: "禁止使用非法字符"
|
||||
ErrPathNotDelete: "所选目录不可删除"
|
||||
ErrLogFileToLarge: "日志文件超过 500M,无法打开"
|
||||
|
||||
#website
|
||||
ErrAliasIsExist: "代号已存在"
|
||||
@@ -201,6 +203,9 @@ HandleSystemClean: "系统缓存清理"
|
||||
SystemLog: "系统日志"
|
||||
CutWebsiteLog: "切割网站日志"
|
||||
FileOrDir: "目录 / 文件"
|
||||
UploadFile: "上传备份文件 {{ .file }} 到 {{ .backup }}"
|
||||
IgnoreBackupErr: "备份失败,错误:{{ .detail }},忽略本次错误..."
|
||||
IgnoreUploadErr: "上传失败,错误:{{ .detail }},忽略本次错误..."
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: "当前用户不存在,请修改后重试!"
|
||||
@@ -329,6 +334,7 @@ SnapDeleteLoginLog: "删除访问日志"
|
||||
SnapDeleteMonitor: "删除监控数据"
|
||||
SnapRemoveSystemIP: "移除系统 IP"
|
||||
SnapBaseInfo: "写入 1Panel 基本信息"
|
||||
SnapInstallAppImageEmpty: "当前未勾选应用镜像,跳过..."
|
||||
SnapInstallApp: "备份 1Panel 已安装应用"
|
||||
SnapDockerSave: "压缩已安装应用"
|
||||
SnapLocalBackup: "备份 1Panel 本地备份目录"
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/service"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
@@ -15,6 +16,7 @@ func Init() {
|
||||
go syncSSL()
|
||||
go syncTask()
|
||||
go initAcmeAccount()
|
||||
go checkDockerCompose()
|
||||
}
|
||||
|
||||
func syncApp() {
|
||||
@@ -71,3 +73,16 @@ func initAcmeAccount() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkDockerCompose() {
|
||||
dockerComposCmd := common.GetDockerComposeCommand()
|
||||
if dockerComposCmd == "" {
|
||||
global.LOG.Errorf("Docker Compose command not found, please install Docker Compose Plugin")
|
||||
return
|
||||
}
|
||||
global.CONF.DockerConfig.Command = dockerComposCmd
|
||||
if err := service.NewISettingService().Update("DockerComposeCommand", dockerComposCmd); err != nil {
|
||||
global.LOG.Errorf("update docker compose command error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
+16
-5
@@ -2,12 +2,14 @@ package hook
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||
)
|
||||
|
||||
@@ -18,6 +20,8 @@ func Init() {
|
||||
handleOllamaModelStatus()
|
||||
|
||||
loadLocalDir()
|
||||
|
||||
initDockerConf()
|
||||
}
|
||||
|
||||
func initGlobalData() {
|
||||
@@ -92,11 +96,7 @@ func handleCronJobAlert(cronjob *model.Cronjob) {
|
||||
EntryID: cronjob.ID,
|
||||
Param: cronjob.Type,
|
||||
}
|
||||
err := xpack.PushAlert(pushAlert)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("cronjob alert push failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
_ = xpack.PushAlert(pushAlert)
|
||||
}
|
||||
|
||||
func loadLocalDir() {
|
||||
@@ -113,3 +113,14 @@ func loadLocalDir() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func initDockerConf() {
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashC("which docker")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
dockerPath := stdout
|
||||
if strings.Contains(dockerPath, "snap") {
|
||||
constant.DaemonJsonPath = "/var/snap/docker/current/config/daemon.json"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,11 @@ func InitAgentDB() {
|
||||
migrations.InitBackup,
|
||||
migrations.InitDefault,
|
||||
migrations.UpdateWebsiteExpireDate,
|
||||
migrations.UpdateRuntime,
|
||||
migrations.AddSnapshotRule,
|
||||
migrations.UpdatePHPRuntime,
|
||||
migrations.AddSnapshotIgnore,
|
||||
migrations.InitAppLauncher,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
||||
@@ -185,9 +185,6 @@ var InitSetting = &gormigrate.Migration{
|
||||
if err := tx.Create(&model.Setting{Key: "FileRecycleBin", Value: constant.StatusEnable}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "SnapshotIgnore", Value: "*.sock"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Create(&model.Setting{Key: "LocalSSHConn", Value: ""}).Error; err != nil {
|
||||
return err
|
||||
@@ -314,3 +311,52 @@ var UpdateWebsiteExpireDate = &gormigrate.Migration{
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var UpdateRuntime = &gormigrate.Migration{
|
||||
ID: "20250624-update-runtime",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.AutoMigrate(
|
||||
&model.Runtime{},
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
var AddSnapshotRule = &gormigrate.Migration{
|
||||
ID: "20250703-add-snapshot-rule",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.AutoMigrate(
|
||||
&model.Cronjob{},
|
||||
)
|
||||
},
|
||||
}
|
||||
var UpdatePHPRuntime = &gormigrate.Migration{
|
||||
ID: "20250702-update-php-runtime",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
service.HandleOldPHPRuntime()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
var AddSnapshotIgnore = &gormigrate.Migration{
|
||||
ID: "20250628-add-snapshot-ignore",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.AutoMigrate(
|
||||
&model.Snapshot{},
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
var InitAppLauncher = &gormigrate.Migration{
|
||||
ID: "20250702-init-app-launcher",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
launchers := []string{"openresty", "mysql", "halo", "redis", "maxkb", "wordpress"}
|
||||
for _, val := range launchers {
|
||||
var item model.AppLauncher
|
||||
_ = tx.Model(&model.AppLauncher{}).Where("key = ?", val).First(&item).Error
|
||||
if item.ID == 0 {
|
||||
item.Key = val
|
||||
_ = tx.Create(&item).Error
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var whiteUrlList = map[string]struct{}{
|
||||
"/api/v2/auth/login": {},
|
||||
"/api/v2/websites/config": {},
|
||||
"/api/v2/websites/waf/config": {},
|
||||
"/api/v2/files/loadfile": {},
|
||||
"/api/v2/files/size": {},
|
||||
"/api/v2/logs/operation": {},
|
||||
"/api/v2/logs/login": {},
|
||||
"/api/v2/auth/logout": {},
|
||||
"/api/v1/dashboard/current": {},
|
||||
|
||||
"/api/v2/apps/installed/loadport": {},
|
||||
"/api/v2/apps/installed/check": {},
|
||||
"/api/v2/apps/installed/conninfo": {},
|
||||
"/api/v2/databases/load/file": {},
|
||||
"/api/v2/databases/variables": {},
|
||||
"/api/v2/databases/status": {},
|
||||
"/api/v2/databases/baseinfo": {},
|
||||
|
||||
"/api/v2/waf/attack/stat": {},
|
||||
"/api/v2/waf/config/website": {},
|
||||
|
||||
"/api/v2/monitor/stat": {},
|
||||
"/api/v2/monitor/visitors": {},
|
||||
"/api/v2/monitor/visitors/loc": {},
|
||||
"/api/v2/monitor/qps": {},
|
||||
}
|
||||
|
||||
func DemoHandle() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if strings.Contains(c.Request.URL.Path, "search") || c.Request.Method == http.MethodGet {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
if _, ok := whiteUrlList[c.Request.URL.Path]; ok {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusInternalServerError, dto.Response{
|
||||
Code: http.StatusInternalServerError,
|
||||
Message: buserr.New("ErrDemoEnvironment").Error(),
|
||||
})
|
||||
c.Abort()
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,8 @@ func (s *ContainerRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
baRouter.POST("/operate", baseApi.ContainerOperation)
|
||||
baRouter.POST("/prune", baseApi.ContainerPrune)
|
||||
|
||||
baRouter.POST("/users", baseApi.LoadContainerUsers)
|
||||
|
||||
baRouter.GET("/repo", baseApi.ListRepo)
|
||||
baRouter.POST("/repo/status", baseApi.CheckRepoStatus)
|
||||
baRouter.POST("/repo/search", baseApi.SearchRepo)
|
||||
|
||||
@@ -48,5 +48,7 @@ func (r *RuntimeRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
groupRouter.GET("/supervisor/process/:id", baseApi.GetSupervisorProcess)
|
||||
groupRouter.POST("/supervisor/process", baseApi.OperateSupervisorProcess)
|
||||
groupRouter.POST("/supervisor/process/file", baseApi.OperateSupervisorProcessFile)
|
||||
|
||||
groupRouter.POST("/remark", baseApi.UpdateRuntimeRemark)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,6 +497,7 @@ func (a *aliClient) completeUpload(uploadID, fileID string) error {
|
||||
|
||||
type tokenResp struct {
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
|
||||
func loadToken(refresh_token string) (string, error) {
|
||||
@@ -524,7 +525,7 @@ func loadToken(refresh_token string) (string, error) {
|
||||
if err := json.Unmarshal(resp.Body(), &respItem); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return respItem.RefreshToken, nil
|
||||
return respItem.AccessToken, nil
|
||||
}
|
||||
|
||||
func RefreshALIToken(varMap map[string]interface{}) (string, error) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
cosSDK "github.com/tencentyun/cos-go-sdk-v5"
|
||||
)
|
||||
@@ -18,6 +19,7 @@ type cosClient struct {
|
||||
|
||||
func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
||||
region := loadParamFromVars("region", vars)
|
||||
endpoint := loadParamFromVars("endpoint", vars)
|
||||
accessKey := loadParamFromVars("accessKey", vars)
|
||||
secretKey := loadParamFromVars("secretKey", vars)
|
||||
bucket := loadParamFromVars("bucket", vars)
|
||||
@@ -26,7 +28,15 @@ func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
||||
scType = "Standard"
|
||||
}
|
||||
|
||||
u, _ := url.Parse(fmt.Sprintf("https://cos.%s.myqcloud.com", region))
|
||||
endpointType := "cos"
|
||||
if len(endpoint) != 0 {
|
||||
re := regexp.MustCompile(`.*cos-dualstack\..*`)
|
||||
if re.MatchString(endpoint) {
|
||||
endpointType = "cos-dualstack"
|
||||
}
|
||||
}
|
||||
|
||||
u, _ := url.Parse(fmt.Sprintf("https://%s.%s.myqcloud.com", endpointType, region))
|
||||
b := &cosSDK.BaseURL{BucketURL: u}
|
||||
client := cosSDK.NewClient(b, &http.Client{
|
||||
Transport: &cosSDK.AuthorizationTransport{
|
||||
@@ -36,7 +46,7 @@ func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
||||
})
|
||||
|
||||
if len(bucket) != 0 {
|
||||
u2, _ := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", bucket, region))
|
||||
u2, _ := url.Parse(fmt.Sprintf("https://%s.%s.%s.myqcloud.com", bucket, endpointType, region))
|
||||
b2 := &cosSDK.BaseURL{BucketURL: u2}
|
||||
clientWithBucket := cosSDK.NewClient(b2, &http.Client{
|
||||
Transport: &cosSDK.AuthorizationTransport{
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
@@ -58,11 +57,9 @@ func (g *googleDriveClient) Delete(pathItem string) (bool, error) {
|
||||
if len(fileInfo.ID) == 0 {
|
||||
return false, fmt.Errorf("no such file %s", pathItem)
|
||||
}
|
||||
res, err := g.googleRequest("https://www.googleapis.com/drive/v3/files/"+fileInfo.ID, http.MethodDelete, nil, nil)
|
||||
if err != nil {
|
||||
if _, err = g.googleRequest("https://www.googleapis.com/drive/v3/files/"+fileInfo.ID, http.MethodDelete, nil, nil); err != nil {
|
||||
return false, err
|
||||
}
|
||||
fmt.Println(string(res))
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -92,7 +89,6 @@ func (g *googleDriveClient) Upload(src, target string) (bool, error) {
|
||||
}
|
||||
urlItem := "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable&supportsAllDrives=true"
|
||||
client := resty.New()
|
||||
client.SetProxy("http://127.0.0.1:7890")
|
||||
resp, err := client.R().
|
||||
SetHeader("Authorization", "Bearer "+g.accessToken).
|
||||
SetBody(data).
|
||||
@@ -101,14 +97,51 @@ func (g *googleDriveClient) Upload(src, target string) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
uploadUrl := resp.Header().Get("location")
|
||||
if _, err := g.googleRequest(uploadUrl, http.MethodPut, func(req *resty.Request) {
|
||||
req.SetHeader("Content-Length", strconv.FormatInt(fileInfo.Size(), 10)).SetBody(file)
|
||||
}, nil); err != nil {
|
||||
return false, err
|
||||
|
||||
buf := make([]byte, 5*1024*1024)
|
||||
var offset int64 = 0
|
||||
for {
|
||||
n, err := file.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
return false, fmt.Errorf("read file failed, err: %v", err)
|
||||
}
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
chunk := buf[:n]
|
||||
start := offset
|
||||
end := offset + int64(n) - 1
|
||||
if err = uploadChunk(client, uploadUrl, chunk, start, end, fileInfo.Size()); err != nil {
|
||||
return false, err
|
||||
}
|
||||
offset += int64(n)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func uploadChunk(client *resty.Client, uploadURL string, chunk []byte, start, end, total int64) error {
|
||||
contentRange := fmt.Sprintf("bytes %d-%d/%d", start, end, total)
|
||||
|
||||
resp, err := client.R().
|
||||
SetHeader("Content-Length", strconv.Itoa(len(chunk))).
|
||||
SetHeader("Content-Range", contentRange).
|
||||
SetBody(chunk).
|
||||
Put(uploadURL)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("new request for upload chunk failed: %v", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode() != 200 && resp.StatusCode() != 308 {
|
||||
return fmt.Errorf("upload chunk failed: %s, %s", resp.Status(), string(resp.Body()))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *googleDriveClient) Download(src, target string) (bool, error) {
|
||||
src = path.Join("/root", src)
|
||||
fileInfo, err := g.loadFileWithName(src)
|
||||
@@ -220,12 +253,7 @@ func (g *googleDriveClient) handleDownload(urlItem string, target string) error
|
||||
return err
|
||||
}
|
||||
req.Header.Add("Authorization", "Bearer "+g.accessToken)
|
||||
proxyURL, _ := url.Parse("http://127.0.0.1:7890")
|
||||
client := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyURL(proxyURL),
|
||||
},
|
||||
}
|
||||
client := &http.Client{}
|
||||
defer client.CloseIdleConnections()
|
||||
response, err := client.Do(req)
|
||||
if err != nil {
|
||||
@@ -343,7 +371,6 @@ type reqCallback func(req *resty.Request)
|
||||
|
||||
func (g *googleDriveClient) googleRequest(urlItem, method string, callback reqCallback, resp interface{}) ([]byte, error) {
|
||||
client := resty.New()
|
||||
client.SetProxy("http://127.0.0.1:7890")
|
||||
req := client.R()
|
||||
req.SetHeader("Authorization", "Bearer "+g.accessToken)
|
||||
if callback != nil {
|
||||
@@ -374,7 +401,6 @@ type googleTokenRes struct {
|
||||
|
||||
func RefreshGoogleToken(grantType string, tokenType string, varMap map[string]interface{}) (string, error) {
|
||||
client := resty.New()
|
||||
client.SetProxy("http://127.0.0.1:7890")
|
||||
data := map[string]interface{}{
|
||||
"client_id": loadParamFromVars("client_id", varMap),
|
||||
"client_secret": loadParamFromVars("client_secret", varMap),
|
||||
@@ -402,7 +428,6 @@ func RefreshGoogleToken(grantType string, tokenType string, varMap map[string]in
|
||||
if err := json.Unmarshal(resp.Body(), &respItem); err != nil {
|
||||
return "", err
|
||||
}
|
||||
fmt.Println(respItem)
|
||||
if tokenType == "accessToken" {
|
||||
return respItem.AccessToken, nil
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ func NewCloudStorageClient(backupType string, vars map[string]interface{}) (Clou
|
||||
return client.NewUpClient(vars)
|
||||
case constant.ALIYUN:
|
||||
return client.NewALIClient(vars)
|
||||
case constant.GoogleDrive:
|
||||
return client.NewGoogleDriveClient(vars)
|
||||
default:
|
||||
return nil, buserr.WithName("ErrNotSupportType", backupType)
|
||||
}
|
||||
|
||||
+29
-19
@@ -9,6 +9,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
@@ -17,12 +18,13 @@ import (
|
||||
)
|
||||
|
||||
type CommandHelper struct {
|
||||
workDir string
|
||||
outputFile string
|
||||
scriptPath string
|
||||
timeout time.Duration
|
||||
taskItem *task.Task
|
||||
logger *log.Logger
|
||||
workDir string
|
||||
outputFile string
|
||||
scriptPath string
|
||||
timeout time.Duration
|
||||
taskItem *task.Task
|
||||
logger *log.Logger
|
||||
IgnoreExist1 bool
|
||||
}
|
||||
|
||||
type Option func(*CommandHelper)
|
||||
@@ -65,6 +67,7 @@ func (c *CommandHelper) RunBashCWithArgs(arg ...string) error {
|
||||
_, err := c.run("bash", arg...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *CommandHelper) RunBashC(command string) error {
|
||||
std, err := c.run("bash", "-c", command)
|
||||
if err != nil {
|
||||
@@ -120,9 +123,9 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
|
||||
cmd.Stdout = file
|
||||
cmd.Stderr = file
|
||||
} else if len(c.scriptPath) != 0 {
|
||||
cmd = exec.Command("bash", c.scriptPath)
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
cmd = exec.Command("bash", c.scriptPath)
|
||||
} else {
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
@@ -133,23 +136,17 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
|
||||
cmd.Dir = c.workDir
|
||||
}
|
||||
|
||||
err := cmd.Run()
|
||||
if c.taskItem != nil {
|
||||
customWriter.Flush()
|
||||
}
|
||||
if c.timeout != 0 {
|
||||
err := cmd.Run()
|
||||
if c.taskItem != nil {
|
||||
customWriter.Flush()
|
||||
}
|
||||
if ctx != nil && errors.Is(ctx.Err(), context.DeadlineExceeded) {
|
||||
return "", buserr.New("ErrCmdTimeout")
|
||||
}
|
||||
if err != nil {
|
||||
return handleErr(stdout, stderr, err)
|
||||
}
|
||||
return stdout.String(), nil
|
||||
}
|
||||
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return handleErr(stdout, stderr, err)
|
||||
return handleErr(stdout, stderr, c.IgnoreExist1, err)
|
||||
}
|
||||
return stdout.String(), nil
|
||||
}
|
||||
@@ -184,6 +181,11 @@ func WithScriptPath(scriptPath string) Option {
|
||||
s.scriptPath = scriptPath
|
||||
}
|
||||
}
|
||||
func WithIgnoreExist1() Option {
|
||||
return func(s *CommandHelper) {
|
||||
s.IgnoreExist1 = true
|
||||
}
|
||||
}
|
||||
|
||||
type CustomWriter struct {
|
||||
taskItem *task.Task
|
||||
@@ -209,7 +211,15 @@ func (cw *CustomWriter) Flush() {
|
||||
}
|
||||
}
|
||||
|
||||
func handleErr(stdout, stderr bytes.Buffer, err error) (string, error) {
|
||||
func handleErr(stdout, stderr bytes.Buffer, ignoreExist1 bool, err error) (string, error) {
|
||||
var exitError *exec.ExitError
|
||||
if ignoreExist1 && errors.As(err, &exitError) {
|
||||
if status, ok := exitError.Sys().(syscall.WaitStatus); ok {
|
||||
if status.ExitStatus() == 1 {
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
}
|
||||
errMsg := ""
|
||||
if len(stderr.String()) != 0 {
|
||||
errMsg = fmt.Sprintf("stderr: %s", stderr.String())
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
mathRand "math/rand"
|
||||
"net"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
@@ -409,3 +410,19 @@ func GetSystemVersion(versionString string) string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func isCommandAvailable(name string, args ...string) bool {
|
||||
execCmd := exec.Command(name, args...)
|
||||
err := execCmd.Run()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func GetDockerComposeCommand() string {
|
||||
if isCommandAvailable("docker", "compose", "version") {
|
||||
return "docker compose"
|
||||
}
|
||||
if isCommandAvailable("docker-compose", "version") {
|
||||
return "docker-compose"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -1,40 +1,72 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"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/common"
|
||||
"time"
|
||||
)
|
||||
|
||||
func checkCmd() error {
|
||||
if global.CONF.DockerConfig.Command == "" {
|
||||
dockerComposCmd := common.GetDockerComposeCommand()
|
||||
if dockerComposCmd == "" {
|
||||
return buserr.New("ErrDockerComposeCmdNotFound")
|
||||
}
|
||||
global.CONF.DockerConfig.Command = dockerComposCmd
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Up(filePath string) (string, error) {
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCfAndTimeOut("docker compose -f %s up -d", 20*time.Minute, filePath)
|
||||
if err := checkCmd(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCfAndTimeOut(global.CONF.DockerConfig.Command+" -f %s up -d", 20*time.Minute, filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func Down(filePath string) (string, error) {
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker compose -f %s down --remove-orphans", filePath)
|
||||
if err := checkCmd(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf(global.CONF.DockerConfig.Command+" -f %s down --remove-orphans", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func Stop(filePath string) (string, error) {
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker compose -f %s stop", filePath)
|
||||
if err := checkCmd(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf(global.CONF.DockerConfig.Command+" -f %s stop", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func Restart(filePath string) (string, error) {
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker compose -f %s restart", filePath)
|
||||
if err := checkCmd(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf(global.CONF.DockerConfig.Command+" -f %s restart", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func Operate(filePath, operation string) (string, error) {
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker compose -f %s %s", filePath, operation)
|
||||
if err := checkCmd(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf(global.CONF.DockerConfig.Command+" -f %s %s", filePath, operation)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func DownAndUp(filePath string) (string, error) {
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker compose -f %s down", filePath)
|
||||
if err := checkCmd(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf(global.CONF.DockerConfig.Command+" -f %s down", filePath)
|
||||
if err != nil {
|
||||
return stdout, err
|
||||
}
|
||||
stdout, err = cmd.RunDefaultWithStdoutBashCf("docker compose -f %s up -d", filePath)
|
||||
stdout, err = cmd.RunDefaultWithStdoutBashCf(global.CONF.DockerConfig.Command+" -f %s up -d", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
@@ -144,14 +144,14 @@ func (c Client) NetworkExist(name string) bool {
|
||||
func CreateDefaultDockerNetwork() error {
|
||||
cli, err := NewClient()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("init docker client error %s", err.Error())
|
||||
global.LOG.Warnf("init docker client error %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
defer cli.Close()
|
||||
if !cli.NetworkExist("1panel-network") {
|
||||
if err := cli.CreateNetwork("1panel-network"); err != nil {
|
||||
global.LOG.Errorf("create default docker network error %s", err.Error())
|
||||
global.LOG.Warnf("create default docker network error %s", err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,25 +329,30 @@ func (f FileOp) DownloadFile(url, dst string) error {
|
||||
}
|
||||
|
||||
func (f FileOp) Cut(oldPaths []string, dst, name string, cover bool) error {
|
||||
for _, p := range oldPaths {
|
||||
var dstPath string
|
||||
if name != "" {
|
||||
dstPath = filepath.Join(dst, name)
|
||||
if f.Stat(dstPath) {
|
||||
dstPath = dst
|
||||
}
|
||||
} else {
|
||||
base := filepath.Base(p)
|
||||
dstPath = filepath.Join(dst, base)
|
||||
if len(oldPaths) == 0 {
|
||||
return nil
|
||||
}
|
||||
var dstPath string
|
||||
coverFlag := ""
|
||||
if name != "" {
|
||||
dstPath = filepath.Join(dst, name)
|
||||
if f.Stat(dstPath) {
|
||||
dstPath = dst
|
||||
}
|
||||
coverFlag := ""
|
||||
if cover {
|
||||
coverFlag = "-f"
|
||||
}
|
||||
|
||||
if err := cmd.RunDefaultBashCf(`mv %s '%s' '%s'`, coverFlag, p, dstPath); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
dstPath = dst
|
||||
coverFlag = "-f"
|
||||
}
|
||||
var quotedPaths []string
|
||||
for _, p := range oldPaths {
|
||||
quotedPaths = append(quotedPaths, fmt.Sprintf("'%s'", p))
|
||||
}
|
||||
mvCommand := fmt.Sprintf("mv %s %s '%s'", coverFlag, strings.Join(quotedPaths, " "), dstPath)
|
||||
if err := cmd.RunDefaultBashCf(mvCommand); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -810,6 +815,9 @@ func (f FileOp) TarGzCompressPro(withDir bool, src, dst, secret, exclusionRules
|
||||
if len(exclude) == 0 {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(exclude, "/") {
|
||||
exclude, _ = filepath.Rel(src, exclude)
|
||||
}
|
||||
if _, ok := exMap[exclude]; ok {
|
||||
continue
|
||||
}
|
||||
@@ -817,19 +825,14 @@ func (f FileOp) TarGzCompressPro(withDir bool, src, dst, secret, exclusionRules
|
||||
exMap[exclude] = struct{}{}
|
||||
}
|
||||
|
||||
itemPrefix := filepath.Base(src)
|
||||
if itemPrefix == "/" {
|
||||
itemPrefix = ""
|
||||
}
|
||||
if len(secret) != 0 {
|
||||
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read %s --exclude-from=<(find %s -type s -printf '%s' | sed 's|^|%s/|') -zcf - %s | openssl enc -aes-256-cbc -salt -k '%s' -out %s", exStr, src, "%P\n", itemPrefix, srcItem, secret, dst)
|
||||
commands = fmt.Sprintf("tar %s -zcf - %s | openssl enc -aes-256-cbc -salt -k '%s' -out %s", exStr, srcItem, secret, dst)
|
||||
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" %s ", secret), "******"))
|
||||
} else {
|
||||
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read --exclude-from=<(find %s -type s -printf '%s' | sed 's|^|%s/|') -zcf %s %s %s", src, "%P\n", itemPrefix, dst, exStr, srcItem)
|
||||
commands = fmt.Sprintf("tar -zcf %s %s %s", dst, exStr, srcItem)
|
||||
global.LOG.Debug(commands)
|
||||
}
|
||||
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(workdir))
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(workdir), cmd.WithIgnoreExist1())
|
||||
return cmdMgr.RunBashC(commands)
|
||||
}
|
||||
|
||||
@@ -846,13 +849,14 @@ func (f FileOp) TarGzFilesWithCompressPro(list []string, dst, secret string) err
|
||||
}
|
||||
commands := ""
|
||||
if len(secret) != 0 {
|
||||
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read -zcf - %s | openssl enc -aes-256-cbc -salt -k '%s' -out %s", strings.Join(filelist, " "), secret, dst)
|
||||
commands = fmt.Sprintf("tar -zcf - %s | openssl enc -aes-256-cbc -salt -k '%s' -out %s", strings.Join(filelist, " "), secret, dst)
|
||||
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" %s ", secret), "******"))
|
||||
} else {
|
||||
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read -zcf %s %s", dst, strings.Join(filelist, " "))
|
||||
commands = fmt.Sprintf("tar -zcf %s %s", dst, strings.Join(filelist, " "))
|
||||
global.LOG.Debug(commands)
|
||||
}
|
||||
return cmd.RunDefaultBashC(commands)
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1())
|
||||
return cmdMgr.RunBashC(commands)
|
||||
}
|
||||
|
||||
func (f FileOp) TarGzExtractPro(src, dst string, secret string) error {
|
||||
@@ -870,7 +874,7 @@ func (f FileOp) TarGzExtractPro(src, dst string, secret string) error {
|
||||
commands = fmt.Sprintf("tar zxvf %s", src)
|
||||
global.LOG.Debug(commands)
|
||||
}
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(dst))
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(dst), cmd.WithIgnoreExist1())
|
||||
return cmdMgr.RunBashC(commands)
|
||||
}
|
||||
func CopyCustomAppFile(srcPath, dstPath string) error {
|
||||
|
||||
@@ -4,8 +4,16 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/charmap"
|
||||
"golang.org/x/text/encoding/japanese"
|
||||
"golang.org/x/text/encoding/korean"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/encoding/traditionalchinese"
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -103,6 +111,16 @@ func ReadFileByLine(filename string, page, pageSize int, latest bool) (lines []s
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
fi, err := file.Stat()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if fi.Size() > 500*1024*1024 {
|
||||
err = buserr.New("ErrLogFileToLarge")
|
||||
return
|
||||
}
|
||||
|
||||
totalLines, err := countLines(filename)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -190,3 +208,68 @@ func DownloadFileWithProxy(url, dst string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetDecoderByName(name string) encoding.Encoding {
|
||||
switch strings.ToLower(name) {
|
||||
case "gbk":
|
||||
return simplifiedchinese.GBK
|
||||
case "gb18030":
|
||||
return simplifiedchinese.GB18030
|
||||
case "big5":
|
||||
return traditionalchinese.Big5
|
||||
case "euc-jp":
|
||||
return japanese.EUCJP
|
||||
case "iso-2022-jp":
|
||||
return japanese.ISO2022JP
|
||||
case "shift_jis":
|
||||
return japanese.ShiftJIS
|
||||
case "euc-kr":
|
||||
return korean.EUCKR
|
||||
case "utf-16be":
|
||||
return unicode.UTF16(unicode.BigEndian, unicode.ExpectBOM)
|
||||
case "utf-16le":
|
||||
return unicode.UTF16(unicode.LittleEndian, unicode.ExpectBOM)
|
||||
case "windows-1250":
|
||||
return charmap.Windows1250
|
||||
case "windows-1251":
|
||||
return charmap.Windows1251
|
||||
case "windows-1252":
|
||||
return charmap.Windows1252
|
||||
case "windows-1253":
|
||||
return charmap.Windows1253
|
||||
case "windows-1254":
|
||||
return charmap.Windows1254
|
||||
case "windows-1255":
|
||||
return charmap.Windows1255
|
||||
case "windows-1256":
|
||||
return charmap.Windows1256
|
||||
case "windows-1257":
|
||||
return charmap.Windows1257
|
||||
case "windows-1258":
|
||||
return charmap.Windows1258
|
||||
case "iso-8859-1":
|
||||
return charmap.ISO8859_1
|
||||
case "iso-8859-2":
|
||||
return charmap.ISO8859_2
|
||||
case "iso-8859-3":
|
||||
return charmap.ISO8859_3
|
||||
case "iso-8859-4":
|
||||
return charmap.ISO8859_4
|
||||
case "iso-8859-5":
|
||||
return charmap.ISO8859_5
|
||||
case "iso-8859-6":
|
||||
return charmap.ISO8859_6
|
||||
case "iso-8859-7":
|
||||
return charmap.ISO8859_7
|
||||
case "iso-8859-8":
|
||||
return charmap.ISO8859_8
|
||||
case "iso-8859-9":
|
||||
return charmap.ISO8859_9
|
||||
case "iso-8859-13":
|
||||
return charmap.ISO8859_13
|
||||
case "iso-8859-15":
|
||||
return charmap.ISO8859_15
|
||||
default:
|
||||
return encoding.Nop
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,3 +33,12 @@ type IptablesNatInfo struct {
|
||||
SrcPort string `json:"srcPort"`
|
||||
DestPort string `json:"destPort"`
|
||||
}
|
||||
|
||||
type IptablesFilterInfo struct {
|
||||
Num string `json:"num"`
|
||||
Target string `json:"target"`
|
||||
Protocol string `json:"protocol"`
|
||||
Opt string `json:"opt"`
|
||||
Source string `json:"source"`
|
||||
Destination string `json:"destination"`
|
||||
}
|
||||
|
||||
@@ -10,9 +10,22 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
)
|
||||
|
||||
const (
|
||||
PreRoutingChain = "1PANEL_PREROUTING"
|
||||
PostRoutingChain = "1PANEL_POSTROUTING"
|
||||
ForwardChain = "1PANEL_FORWARD"
|
||||
)
|
||||
|
||||
const (
|
||||
FilterTab = "filter"
|
||||
NatTab = "nat"
|
||||
)
|
||||
|
||||
const NatChain = "1PANEL"
|
||||
|
||||
var NatListRegex = regexp.MustCompile(`^(\d+)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)(?:\s+(.+?) .+?:(\d{1,5}(?::\d+)?).+?[ :](.+-.+|(?:.+:)?\d{1,5}(?:-\d{1,5})?))?$`)
|
||||
var (
|
||||
natListRegex = regexp.MustCompile(`^(\d+)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)(?:\s+(.+?) .+?:(\d{1,5}(?::\d+)?).+?[ :](.+-.+|(?:.+:)?\d{1,5}(?:-\d{1,5})?))?$`)
|
||||
)
|
||||
|
||||
type Iptables struct {
|
||||
CmdStr string
|
||||
@@ -37,8 +50,24 @@ func (iptables *Iptables) run(rule string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iptables *Iptables) runf(rule string, a ...any) error {
|
||||
return iptables.run(fmt.Sprintf(rule, a...))
|
||||
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...))
|
||||
if err != nil && stdout != "" {
|
||||
global.LOG.Errorf("iptables failed, err: %s", stdout)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (iptables *Iptables) runf(tab, rule string, a ...any) error {
|
||||
stdout, err := iptables.outf(tab, rule, a...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s, %s", err, stdout)
|
||||
}
|
||||
if stdout != "" {
|
||||
return fmt.Errorf("iptables error: %s", stdout)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iptables *Iptables) Check() error {
|
||||
@@ -47,26 +76,30 @@ func (iptables *Iptables) Check() error {
|
||||
return fmt.Errorf("%s, %s", err, stdout)
|
||||
}
|
||||
if stdout == "0" {
|
||||
return fmt.Errorf("disable")
|
||||
return fmt.Errorf("ipv4 forward disable")
|
||||
}
|
||||
|
||||
chain, _ := iptables.outf(NatTab, "-L -n | grep 'Chain %s'", PreRoutingChain)
|
||||
if len(strings.ReplaceAll(chain, "\n", "")) != 0 {
|
||||
return fmt.Errorf("chain enabled")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iptables *Iptables) NatNewChain() error {
|
||||
return iptables.runf("-N %s", NatChain)
|
||||
func (iptables *Iptables) NewChain(tab, chain string) error {
|
||||
return iptables.runf(tab, "-N %s", chain)
|
||||
}
|
||||
|
||||
func (iptables *Iptables) NatAppendChain() error {
|
||||
return iptables.runf("-A PREROUTING -j %s", NatChain)
|
||||
func (iptables *Iptables) AppendChain(tab string, chain, chain1 string) error {
|
||||
return iptables.runf(tab, "-A %s -j %s", chain, chain1)
|
||||
}
|
||||
|
||||
func (iptables *Iptables) NatList(chain ...string) ([]IptablesNatInfo, error) {
|
||||
rule := fmt.Sprintf("%s iptables -t nat -nL %s --line", iptables.CmdStr, NatChain)
|
||||
if len(chain) == 1 {
|
||||
rule = fmt.Sprintf("%s iptables -t nat -nL %s --line", iptables.CmdStr, chain[0])
|
||||
if len(chain) == 0 {
|
||||
chain = append(chain, PreRoutingChain)
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashC(rule)
|
||||
stdout, err := iptables.outf(NatTab, "-nL %s --line", chain[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -76,8 +109,8 @@ func (iptables *Iptables) NatList(chain ...string) ([]IptablesNatInfo, error) {
|
||||
line = strings.TrimFunc(line, func(r rune) bool {
|
||||
return r <= 32
|
||||
})
|
||||
if NatListRegex.MatchString(line) {
|
||||
match := NatListRegex.FindStringSubmatch(line)
|
||||
if natListRegex.MatchString(line) {
|
||||
match := natListRegex.FindStringSubmatch(line)
|
||||
if !strings.Contains(match[9], ":") {
|
||||
match[9] = fmt.Sprintf(":%s", match[9])
|
||||
}
|
||||
@@ -97,43 +130,126 @@ func (iptables *Iptables) NatList(chain ...string) ([]IptablesNatInfo, error) {
|
||||
return forwardList, nil
|
||||
}
|
||||
|
||||
func (iptables *Iptables) NatAdd(protocol, src, destIp, destPort string, save bool) error {
|
||||
rule := fmt.Sprintf("-A %s -p %s --dport %s -j REDIRECT --to-port %s", NatChain, protocol, src, destPort)
|
||||
if destIp != "" && destIp != "127.0.0.1" && destIp != "localhost" {
|
||||
rule = fmt.Sprintf("-A %s -p %s --dport %s -j DNAT --to-destination %s:%s", NatChain, protocol, src, destIp, destPort)
|
||||
}
|
||||
if err := iptables.run(rule); err != nil {
|
||||
return err
|
||||
func (iptables *Iptables) NatAdd(protocol, srcPort, dest, destPort string, save bool) error {
|
||||
if dest != "" && dest != "127.0.0.1" && dest != "localhost" {
|
||||
if err := iptables.runf(NatTab, fmt.Sprintf(
|
||||
"-A %s -p %s --dport %s -j DNAT --to-destination %s:%s",
|
||||
PreRoutingChain,
|
||||
protocol,
|
||||
srcPort,
|
||||
dest,
|
||||
destPort,
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := iptables.runf(NatTab, fmt.Sprintf(
|
||||
"-A %s -d %s -p %s --dport %s -j MASQUERADE",
|
||||
PostRoutingChain,
|
||||
dest,
|
||||
protocol,
|
||||
destPort,
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := iptables.runf(FilterTab, fmt.Sprintf(
|
||||
"-A %s -d %s -p %s --dport %s -j ACCEPT",
|
||||
ForwardChain,
|
||||
dest,
|
||||
protocol,
|
||||
destPort,
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := iptables.runf(FilterTab, fmt.Sprintf(
|
||||
"-A %s -s %s -p %s --sport %s -j ACCEPT",
|
||||
ForwardChain,
|
||||
dest,
|
||||
protocol,
|
||||
destPort,
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := iptables.runf(NatTab, fmt.Sprintf(
|
||||
"-A %s -p %s --dport %s -j REDIRECT --to-port %s",
|
||||
PreRoutingChain,
|
||||
protocol,
|
||||
srcPort,
|
||||
destPort,
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if save {
|
||||
return global.DB.Save(&model.Forward{
|
||||
Protocol: protocol,
|
||||
Port: src,
|
||||
TargetIP: destIp,
|
||||
Port: srcPort,
|
||||
TargetIP: dest,
|
||||
TargetPort: destPort,
|
||||
}).Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iptables *Iptables) NatRemove(num string, protocol, src, destIp, destPort string) error {
|
||||
if err := iptables.runf("-D %s %s", NatChain, num); err != nil {
|
||||
func (iptables *Iptables) NatRemove(num string, protocol, srcPort, dest, destPort string) error {
|
||||
if err := iptables.runf(NatTab, "-D %s %s", PreRoutingChain, num); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if dest != "" && dest != "127.0.0.1" && dest != "localhost" {
|
||||
if err := iptables.runf(NatTab, fmt.Sprintf(
|
||||
"-D %s -d %s -p %s --dport %s -j MASQUERADE",
|
||||
PostRoutingChain,
|
||||
dest,
|
||||
protocol,
|
||||
destPort,
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := iptables.runf(FilterTab, fmt.Sprintf(
|
||||
"-D %s -d %s -p %s --dport %s -j ACCEPT",
|
||||
ForwardChain,
|
||||
dest,
|
||||
protocol,
|
||||
destPort,
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := iptables.runf(FilterTab, fmt.Sprintf(
|
||||
"-D %s -s %s -p %s --sport %s -j ACCEPT",
|
||||
ForwardChain,
|
||||
dest,
|
||||
protocol,
|
||||
destPort,
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
global.DB.Where(
|
||||
"protocol = ? AND port = ? AND target_ip = ? AND target_port = ?",
|
||||
protocol,
|
||||
src,
|
||||
destIp,
|
||||
srcPort,
|
||||
dest,
|
||||
destPort,
|
||||
).Delete(&model.Forward{})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iptables *Iptables) Reload() error {
|
||||
if err := iptables.runf("-F %s", NatChain); err != nil {
|
||||
if err := iptables.runf(NatTab, "-F %s", PreRoutingChain); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.runf(NatTab, "-F %s", PostRoutingChain); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.runf(FilterTab, "-F %s", ForwardChain); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
)
|
||||
|
||||
@@ -100,16 +99,11 @@ func (f *Ufw) ListPort() ([]FireInfo, error) {
|
||||
}
|
||||
|
||||
func (f *Ufw) ListForward() ([]FireInfo, error) {
|
||||
_ = f.EnableForward()
|
||||
iptables, err := NewIptables()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
panelChian, _ := cmd.RunDefaultWithStdoutBashCf("%s iptables -t nat -L -n | grep 'Chain 1PANEL'", iptables.CmdStr)
|
||||
if len(strings.ReplaceAll(panelChian, "\n", "")) == 0 {
|
||||
if err := f.EnableForward(); err != nil {
|
||||
global.LOG.Errorf("init port forward failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
rules, err := iptables.NatList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -304,21 +298,33 @@ func (f *Ufw) EnableForward() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = iptables.NatNewChain()
|
||||
if err = iptables.Check(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = iptables.NewChain(NatTab, PreRoutingChain)
|
||||
_ = iptables.NewChain(NatTab, PostRoutingChain)
|
||||
_ = iptables.NewChain(FilterTab, ForwardChain)
|
||||
|
||||
if err = f.enableChain(iptables); err != nil {
|
||||
return err
|
||||
}
|
||||
return iptables.Reload()
|
||||
}
|
||||
|
||||
func (f *Ufw) enableChain(iptables *Iptables) error {
|
||||
rules, err := iptables.NatList("PREROUTING")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, rule := range rules {
|
||||
if rule.Target == NatChain {
|
||||
goto reload
|
||||
if rule.Target == PreRoutingChain {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if err = iptables.NatAppendChain(); err != nil {
|
||||
return err
|
||||
}
|
||||
reload:
|
||||
return iptables.Reload()
|
||||
_ = iptables.AppendChain(NatTab, "PREROUTING", PreRoutingChain)
|
||||
_ = iptables.AppendChain(NatTab, "POSTROUTING", PostRoutingChain)
|
||||
_ = iptables.AppendChain(FilterTab, "FORWARD", ForwardChain)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func NewPostgresqlClient(conn client.DBInfo) (PostgresqlClient, error) {
|
||||
}
|
||||
escapedUsername := url.QueryEscape(conn.Username)
|
||||
escapedPassword := url.QueryEscape(conn.Password)
|
||||
connArgs := fmt.Sprintf("postgres://%s:%s@%s:%d/?sslmode=disable", escapedUsername, escapedPassword, conn.Address, conn.Port)
|
||||
connArgs := fmt.Sprintf("postgres://%s:%s@%s:%d/postgres?sslmode=disable", escapedUsername, escapedPassword, conn.Address, conn.Port)
|
||||
db, err := sql.Open("pgx", connArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
+148
-116
@@ -4,10 +4,12 @@ import (
|
||||
"encoding/json"
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/providers/dns/alidns"
|
||||
"github.com/go-acme/lego/v4/providers/dns/baiducloud"
|
||||
"github.com/go-acme/lego/v4/providers/dns/clouddns"
|
||||
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
|
||||
"github.com/go-acme/lego/v4/providers/dns/cloudns"
|
||||
"github.com/go-acme/lego/v4/providers/dns/dnspod"
|
||||
"github.com/go-acme/lego/v4/providers/dns/dynu"
|
||||
"github.com/go-acme/lego/v4/providers/dns/freemyip"
|
||||
"github.com/go-acme/lego/v4/providers/dns/godaddy"
|
||||
"github.com/go-acme/lego/v4/providers/dns/huaweicloud"
|
||||
@@ -15,6 +17,7 @@ import (
|
||||
"github.com/go-acme/lego/v4/providers/dns/namedotcom"
|
||||
"github.com/go-acme/lego/v4/providers/dns/namesilo"
|
||||
"github.com/go-acme/lego/v4/providers/dns/rainyun"
|
||||
"github.com/go-acme/lego/v4/providers/dns/regru"
|
||||
"github.com/go-acme/lego/v4/providers/dns/spaceship"
|
||||
"github.com/go-acme/lego/v4/providers/dns/tencentcloud"
|
||||
"github.com/go-acme/lego/v4/providers/dns/vercel"
|
||||
@@ -43,6 +46,9 @@ const (
|
||||
Spaceship DnsType = "Spaceship"
|
||||
WestCN DnsType = "WestCN"
|
||||
ClouDNS DnsType = "ClouDNS"
|
||||
RegRu DnsType = "RegRu"
|
||||
Dynu DnsType = "Dynu"
|
||||
BaiduCloud DnsType = "BaiduCloud"
|
||||
)
|
||||
|
||||
type DNSParam struct {
|
||||
@@ -83,139 +89,165 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
||||
}
|
||||
switch dnsType {
|
||||
case DnsPod:
|
||||
dnsPodConfig := dnspod.NewDefaultConfig()
|
||||
dnsPodConfig.LoginToken = param.ID + "," + param.Token
|
||||
dnsPodConfig.PropagationTimeout = propagationTimeout
|
||||
dnsPodConfig.PollingInterval = pollingInterval
|
||||
dnsPodConfig.TTL = ttl
|
||||
p, err = dnspod.NewDNSProviderConfig(dnsPodConfig)
|
||||
config := dnspod.NewDefaultConfig()
|
||||
config.LoginToken = param.ID + "," + param.Token
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = dnspod.NewDNSProviderConfig(config)
|
||||
case AliYun:
|
||||
alidnsConfig := alidns.NewDefaultConfig()
|
||||
alidnsConfig.SecretKey = param.SecretKey
|
||||
alidnsConfig.APIKey = param.AccessKey
|
||||
alidnsConfig.PropagationTimeout = propagationTimeout
|
||||
alidnsConfig.PollingInterval = pollingInterval
|
||||
alidnsConfig.TTL = ttl
|
||||
p, err = alidns.NewDNSProviderConfig(alidnsConfig)
|
||||
config := alidns.NewDefaultConfig()
|
||||
config.SecretKey = param.SecretKey
|
||||
config.APIKey = param.AccessKey
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = alidns.NewDNSProviderConfig(config)
|
||||
case CloudFlare:
|
||||
cloudflareConfig := cloudflare.NewDefaultConfig()
|
||||
cloudflareConfig.AuthEmail = param.Email
|
||||
cloudflareConfig.AuthToken = param.APIkey
|
||||
cloudflareConfig.PropagationTimeout = propagationTimeout
|
||||
cloudflareConfig.PollingInterval = pollingInterval
|
||||
cloudflareConfig.TTL = ttl
|
||||
p, err = cloudflare.NewDNSProviderConfig(cloudflareConfig)
|
||||
config := cloudflare.NewDefaultConfig()
|
||||
config.AuthEmail = param.Email
|
||||
config.AuthToken = param.APIkey
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = cloudflare.NewDNSProviderConfig(config)
|
||||
case CloudDns:
|
||||
clouddnsConfig := clouddns.NewDefaultConfig()
|
||||
clouddnsConfig.ClientID = param.ClientID
|
||||
clouddnsConfig.Email = param.Email
|
||||
clouddnsConfig.Password = param.Password
|
||||
clouddnsConfig.PropagationTimeout = propagationTimeout
|
||||
clouddnsConfig.PollingInterval = pollingInterval
|
||||
clouddnsConfig.TTL = ttl
|
||||
p, err = clouddns.NewDNSProviderConfig(clouddnsConfig)
|
||||
config := clouddns.NewDefaultConfig()
|
||||
config.ClientID = param.ClientID
|
||||
config.Email = param.Email
|
||||
config.Password = param.Password
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = clouddns.NewDNSProviderConfig(config)
|
||||
case NameCheap:
|
||||
namecheapConfig := namecheap.NewDefaultConfig()
|
||||
namecheapConfig.APIKey = param.APIkey
|
||||
namecheapConfig.APIUser = param.APIUser
|
||||
namecheapConfig.PropagationTimeout = propagationTimeout
|
||||
namecheapConfig.PollingInterval = pollingInterval
|
||||
namecheapConfig.TTL = ttl
|
||||
p, err = namecheap.NewDNSProviderConfig(namecheapConfig)
|
||||
config := namecheap.NewDefaultConfig()
|
||||
config.APIKey = param.APIkey
|
||||
config.APIUser = param.APIUser
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = namecheap.NewDNSProviderConfig(config)
|
||||
case NameSilo:
|
||||
nameSiloConfig := namesilo.NewDefaultConfig()
|
||||
nameSiloConfig.APIKey = param.APIkey
|
||||
nameSiloConfig.PropagationTimeout = propagationTimeout
|
||||
nameSiloConfig.PollingInterval = pollingInterval
|
||||
nameSiloConfig.TTL = ttl
|
||||
p, err = namesilo.NewDNSProviderConfig(nameSiloConfig)
|
||||
config := namesilo.NewDefaultConfig()
|
||||
config.APIKey = param.APIkey
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = namesilo.NewDNSProviderConfig(config)
|
||||
case Godaddy:
|
||||
godaddyConfig := godaddy.NewDefaultConfig()
|
||||
godaddyConfig.APIKey = param.APIkey
|
||||
godaddyConfig.APISecret = param.APISecret
|
||||
godaddyConfig.PropagationTimeout = propagationTimeout
|
||||
godaddyConfig.PollingInterval = pollingInterval
|
||||
godaddyConfig.TTL = ttl
|
||||
p, err = godaddy.NewDNSProviderConfig(godaddyConfig)
|
||||
config := godaddy.NewDefaultConfig()
|
||||
config.APIKey = param.APIkey
|
||||
config.APISecret = param.APISecret
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = godaddy.NewDNSProviderConfig(config)
|
||||
case NameCom:
|
||||
nameComConfig := namedotcom.NewDefaultConfig()
|
||||
nameComConfig.APIToken = param.Token
|
||||
nameComConfig.Username = param.APIUser
|
||||
nameComConfig.PropagationTimeout = propagationTimeout
|
||||
nameComConfig.PollingInterval = pollingInterval
|
||||
nameComConfig.TTL = ttl
|
||||
p, err = namedotcom.NewDNSProviderConfig(nameComConfig)
|
||||
config := namedotcom.NewDefaultConfig()
|
||||
config.APIToken = param.Token
|
||||
config.Username = param.APIUser
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = namedotcom.NewDNSProviderConfig(config)
|
||||
case TencentCloud:
|
||||
tencentCloudConfig := tencentcloud.NewDefaultConfig()
|
||||
tencentCloudConfig.SecretID = param.SecretID
|
||||
tencentCloudConfig.SecretKey = param.SecretKey
|
||||
tencentCloudConfig.PropagationTimeout = propagationTimeout
|
||||
tencentCloudConfig.PollingInterval = pollingInterval
|
||||
tencentCloudConfig.TTL = ttl
|
||||
p, err = tencentcloud.NewDNSProviderConfig(tencentCloudConfig)
|
||||
config := tencentcloud.NewDefaultConfig()
|
||||
config.SecretID = param.SecretID
|
||||
config.SecretKey = param.SecretKey
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = tencentcloud.NewDNSProviderConfig(config)
|
||||
case RainYun:
|
||||
rainyunConfig := rainyun.NewDefaultConfig()
|
||||
rainyunConfig.APIKey = param.APIkey
|
||||
rainyunConfig.PropagationTimeout = propagationTimeout
|
||||
rainyunConfig.PollingInterval = pollingInterval
|
||||
rainyunConfig.TTL = ttl
|
||||
p, err = rainyun.NewDNSProviderConfig(rainyunConfig)
|
||||
config := rainyun.NewDefaultConfig()
|
||||
config.APIKey = param.APIkey
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = rainyun.NewDNSProviderConfig(config)
|
||||
case Volcengine:
|
||||
volcConfig := volcengine.NewDefaultConfig()
|
||||
volcConfig.SecretKey = param.SecretKey
|
||||
volcConfig.AccessKey = param.AccessKey
|
||||
volcConfig.PropagationTimeout = propagationTimeout
|
||||
volcConfig.PollingInterval = pollingInterval
|
||||
volcConfig.TTL = ttl
|
||||
p, err = volcengine.NewDNSProviderConfig(volcConfig)
|
||||
config := volcengine.NewDefaultConfig()
|
||||
config.SecretKey = param.SecretKey
|
||||
config.AccessKey = param.AccessKey
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = volcengine.NewDNSProviderConfig(config)
|
||||
case HuaweiCloud:
|
||||
huaweiCloudConfig := huaweicloud.NewDefaultConfig()
|
||||
huaweiCloudConfig.AccessKeyID = param.AccessKey
|
||||
huaweiCloudConfig.SecretAccessKey = param.SecretKey
|
||||
huaweiCloudConfig.Region = param.Region
|
||||
huaweiCloudConfig.PropagationTimeout = propagationTimeout
|
||||
huaweiCloudConfig.PollingInterval = pollingInterval
|
||||
huaweiCloudConfig.TTL = int32(ttl)
|
||||
p, err = huaweicloud.NewDNSProviderConfig(huaweiCloudConfig)
|
||||
config := huaweicloud.NewDefaultConfig()
|
||||
config.AccessKeyID = param.AccessKey
|
||||
config.SecretAccessKey = param.SecretKey
|
||||
config.Region = param.Region
|
||||
if config.Region == "" {
|
||||
config.Region = "cn-north-1"
|
||||
}
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = int32(ttl)
|
||||
p, err = huaweicloud.NewDNSProviderConfig(config)
|
||||
case FreeMyIP:
|
||||
freeMyIpConfig := freemyip.NewDefaultConfig()
|
||||
freeMyIpConfig.Token = param.Token
|
||||
freeMyIpConfig.PropagationTimeout = propagationTimeout
|
||||
freeMyIpConfig.PollingInterval = pollingInterval
|
||||
p, err = freemyip.NewDNSProviderConfig(freeMyIpConfig)
|
||||
config := freemyip.NewDefaultConfig()
|
||||
config.Token = param.Token
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
p, err = freemyip.NewDNSProviderConfig(config)
|
||||
case Vercel:
|
||||
vercelConfig := vercel.NewDefaultConfig()
|
||||
vercelConfig.AuthToken = param.Token
|
||||
vercelConfig.PropagationTimeout = propagationTimeout
|
||||
vercelConfig.PollingInterval = pollingInterval
|
||||
p, err = vercel.NewDNSProviderConfig(vercelConfig)
|
||||
config := vercel.NewDefaultConfig()
|
||||
config.AuthToken = param.Token
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
p, err = vercel.NewDNSProviderConfig(config)
|
||||
case Spaceship:
|
||||
spaceshipConfig := spaceship.NewDefaultConfig()
|
||||
spaceshipConfig.APIKey = param.APIkey
|
||||
spaceshipConfig.APISecret = param.APISecret
|
||||
spaceshipConfig.PropagationTimeout = propagationTimeout
|
||||
spaceshipConfig.PollingInterval = pollingInterval
|
||||
spaceshipConfig.TTL = ttl
|
||||
p, err = spaceship.NewDNSProviderConfig(spaceshipConfig)
|
||||
config := spaceship.NewDefaultConfig()
|
||||
config.APIKey = param.APIkey
|
||||
config.APISecret = param.APISecret
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = spaceship.NewDNSProviderConfig(config)
|
||||
case WestCN:
|
||||
westcnConfig := westcn.NewDefaultConfig()
|
||||
westcnConfig.Username = param.Username
|
||||
westcnConfig.Password = param.Password
|
||||
westcnConfig.PropagationTimeout = propagationTimeout
|
||||
westcnConfig.PollingInterval = pollingInterval
|
||||
westcnConfig.TTL = ttl
|
||||
p, err = westcn.NewDNSProviderConfig(westcnConfig)
|
||||
|
||||
config := westcn.NewDefaultConfig()
|
||||
config.Username = param.Username
|
||||
config.Password = param.Password
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = westcn.NewDNSProviderConfig(config)
|
||||
case ClouDNS:
|
||||
cloudnsConfig := cloudns.NewDefaultConfig()
|
||||
cloudnsConfig.AuthID = param.AuthID
|
||||
cloudnsConfig.SubAuthID = param.SubAuthID
|
||||
cloudnsConfig.AuthPassword = param.AuthPassword
|
||||
cloudnsConfig.PropagationTimeout = propagationTimeout
|
||||
cloudnsConfig.PollingInterval = pollingInterval
|
||||
cloudnsConfig.TTL = ttl
|
||||
p, err = cloudns.NewDNSProviderConfig(cloudnsConfig)
|
||||
config := cloudns.NewDefaultConfig()
|
||||
config.AuthID = param.AuthID
|
||||
config.SubAuthID = param.SubAuthID
|
||||
config.AuthPassword = param.AuthPassword
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = cloudns.NewDNSProviderConfig(config)
|
||||
case RegRu:
|
||||
config := regru.NewDefaultConfig()
|
||||
config.Username = param.Username
|
||||
config.Password = param.Password
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = regru.NewDNSProviderConfig(config)
|
||||
case Dynu:
|
||||
config := dynu.NewDefaultConfig()
|
||||
config.APIKey = param.APIkey
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = dynu.NewDNSProviderConfig(config)
|
||||
case BaiduCloud:
|
||||
config := baiducloud.NewDefaultConfig()
|
||||
config.AccessKeyID = param.AccessKey
|
||||
config.SecretAccessKey = param.SecretKey
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = baiducloud.NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package systemctl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"os/exec"
|
||||
@@ -16,31 +17,65 @@ func RunSystemCtl(args ...string) (string, error) {
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
func isSnapService(serviceName string) bool {
|
||||
cmd := exec.Command("snap", "services")
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
if err := cmd.Run(); err != nil {
|
||||
return false
|
||||
}
|
||||
return strings.Contains(out.String(), serviceName)
|
||||
}
|
||||
|
||||
func isSnapServiceActive(serviceName string) bool {
|
||||
cmd := exec.Command("snap", "services")
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
if err := cmd.Run(); err != nil {
|
||||
return false
|
||||
}
|
||||
lines := strings.Split(out.String(), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, serviceName) && strings.Contains(line, "active") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsActive(serviceName string) (bool, error) {
|
||||
out, err := RunSystemCtl("is-active", serviceName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
if err == nil {
|
||||
return strings.TrimSpace(out) == "active", nil
|
||||
}
|
||||
return out == "active\n", nil
|
||||
|
||||
if isSnapServiceActive(serviceName) {
|
||||
return true, nil
|
||||
}
|
||||
return false, fmt.Errorf("service %s is not active: %v", serviceName, err)
|
||||
}
|
||||
|
||||
func IsEnable(serviceName string) (bool, error) {
|
||||
out, err := RunSystemCtl("is-enabled", serviceName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
if err == nil {
|
||||
return strings.TrimSpace(out) == "enabled", nil
|
||||
}
|
||||
return out == "enabled\n", nil
|
||||
|
||||
if isSnapServiceActive(serviceName) {
|
||||
return true, nil
|
||||
}
|
||||
return false, fmt.Errorf("failed to determine if service %s is enabled: %v", serviceName, err)
|
||||
}
|
||||
|
||||
func IsExist(serviceName string) (bool, error) {
|
||||
out, err := RunSystemCtl("is-enabled", serviceName)
|
||||
if err != nil {
|
||||
if strings.Contains(out, "disabled") {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
if err == nil || strings.Contains(out, "disabled") {
|
||||
return true, nil
|
||||
}
|
||||
return true, nil
|
||||
if isSnapService(serviceName) {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func handlerErr(out string, err error) error {
|
||||
@@ -55,10 +90,27 @@ func handlerErr(out string, err error) error {
|
||||
|
||||
func Restart(serviceName string) error {
|
||||
out, err := RunSystemCtl("restart", serviceName)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if isSnapService(serviceName) {
|
||||
cmd := exec.Command("snap", "restart", serviceName)
|
||||
output, snapErr := cmd.CombinedOutput()
|
||||
return handlerErr(string(output), snapErr)
|
||||
}
|
||||
return handlerErr(out, err)
|
||||
}
|
||||
|
||||
func Operate(operate, serviceName string) error {
|
||||
out, err := RunSystemCtl(operate, serviceName)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if isSnapService(serviceName) && (operate == "start" || operate == "stop" || operate == "restart") {
|
||||
cmd := exec.Command("snap", operate, serviceName)
|
||||
output, snapErr := cmd.CombinedOutput()
|
||||
return handlerErr(string(output), snapErr)
|
||||
}
|
||||
return handlerErr(out, err)
|
||||
}
|
||||
|
||||
+10
-1
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/app/model"
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/captcha"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -133,7 +134,15 @@ func (b *BaseApi) GetLoginSetting(c *gin.Context) {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, settingInfo)
|
||||
res := &dto.LoginSetting{
|
||||
IsDemo: global.CONF.Base.IsDemo,
|
||||
IsIntl: global.CONF.Base.IsIntl,
|
||||
Language: settingInfo.Language,
|
||||
MenuTabs: settingInfo.MenuTabs,
|
||||
PanelName: settingInfo.PanelName,
|
||||
Theme: settingInfo.Theme,
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
func saveLoginLogs(c *gin.Context, err error) {
|
||||
|
||||
@@ -3,7 +3,6 @@ package v2
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -185,26 +184,33 @@ func (b *BaseApi) RunScript(c *gin.Context) {
|
||||
tty.Start(quitChan)
|
||||
go slave.Wait(quitChan)
|
||||
} else {
|
||||
connInfo, installDir, err := xpack.LoadNodeInfo(currentNode)
|
||||
connInfo, _, err := xpack.LoadNodeInfo(currentNode)
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||
return
|
||||
}
|
||||
|
||||
fileDir := path.Join(installDir, "1panel/tmp/script")
|
||||
fileName := ""
|
||||
var translations = make(map[string]string)
|
||||
_ = json.Unmarshal([]byte(scriptItem.Name), &translations)
|
||||
if name, ok := translations["en"]; ok {
|
||||
fileName = path.Join(fileDir, strings.ReplaceAll(name, " ", "_"))
|
||||
fileName = strings.ReplaceAll(name, " ", "_")
|
||||
} else {
|
||||
fileName = path.Join(fileDir, strings.ReplaceAll(scriptItem.Name, " ", "_"))
|
||||
fileName = strings.ReplaceAll(scriptItem.Name, " ", "_")
|
||||
}
|
||||
initCmd := fmt.Sprintf("mkdir -p %s && cat > %s <<'MYMARKER'\n%s\nMYMARKER\n bash %s", fileDir, fileName, scriptItem.Script, fileName)
|
||||
client, err := ssh.NewClient(*connInfo)
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "failed to set up the connection. Please check the host information")) {
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "set up the connection failed. Please check the host information")) {
|
||||
return
|
||||
}
|
||||
defer client.Close()
|
||||
sudoItem := client.SudoHandleCmd()
|
||||
defer func() {
|
||||
_, _ = client.Runf("%s rm -rf %s", sudoItem, fileName)
|
||||
client.Close()
|
||||
}()
|
||||
std, err := client.Runf("%s touch %s && %s chmod 777 %s && %s cat > %s <<'MYMARKER'\n%s\nMYMARKER\n", sudoItem, fileName, sudoItem, fileName, sudoItem, fileName, scriptItem.Script)
|
||||
if wshandleError(wsConn, errors.WithMessage(err, fmt.Sprintf("touch script file failed, err: %s. Please check and retry", std))) {
|
||||
return
|
||||
}
|
||||
initCmd := fmt.Sprintf("%s bash %s", sudoItem, fileName)
|
||||
|
||||
sws, err := terminal.NewLogicSshWsSession(cols, rows, client.Client, wsConn, initCmd)
|
||||
if wshandleError(wsConn, err) {
|
||||
|
||||
@@ -219,3 +219,12 @@ type AppstoreConfig struct {
|
||||
UpgradeBackup string `json:"upgradeBackup"`
|
||||
UninstallDeleteBackup string `json:"uninstallDeleteBackup"`
|
||||
}
|
||||
|
||||
type LoginSetting struct {
|
||||
IsDemo bool `json:"isDemo"`
|
||||
IsIntl bool `json:"isIntl"`
|
||||
Language string `json:"language"`
|
||||
MenuTabs string `json:"menuTabs"`
|
||||
PanelName string `json:"panelName"`
|
||||
Theme string `json:"theme"`
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ type IUpgradeLogRepo interface {
|
||||
Delete(opts ...global.DBOption) error
|
||||
|
||||
WithByNodeID(nodeID uint) global.DBOption
|
||||
WithByUpgradeVersion(oldVersion, newVersion string) global.DBOption
|
||||
}
|
||||
|
||||
func NewIUpgradeLogRepo() IUpgradeLogRepo {
|
||||
@@ -79,3 +80,9 @@ func (c *UpgradeLogRepo) WithByNodeID(nodeID uint) global.DBOption {
|
||||
return g.Where("node_id = ?", nodeID)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *UpgradeLogRepo) WithByUpgradeVersion(oldVersion, newVersion string) global.DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("old_version = ? AND new_version = ?", oldVersion, newVersion)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*d
|
||||
return nil, "", buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
if nameSetting.Value != info.Name {
|
||||
return nil, "ErrAuth", nil
|
||||
return nil, "ErrAuth", buserr.New("ErrAuth")
|
||||
}
|
||||
if err = checkPassword(info.Password); err != nil {
|
||||
return nil, "ErrAuth", err
|
||||
@@ -47,7 +47,7 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*d
|
||||
return nil, "", err
|
||||
}
|
||||
if len(entranceSetting.Value) != 0 && entranceSetting.Value != entrance {
|
||||
return nil, "ErrEntrance", nil
|
||||
return nil, "ErrEntrance", buserr.New("ErrEntrance")
|
||||
}
|
||||
mfa, err := settingRepo.Get(repo.WithByKey("MFAStatus"))
|
||||
if err != nil {
|
||||
|
||||
@@ -268,8 +268,6 @@ func (u *BackupService) RefreshToken(req dto.OperateByName) error {
|
||||
switch backup.Type {
|
||||
case constant.OneDrive:
|
||||
refreshToken, err = client.RefreshToken("refresh_token", "refreshToken", varMap)
|
||||
case constant.GoogleDrive:
|
||||
refreshToken, err = client.RefreshGoogleToken("refresh_token", "refreshToken", varMap)
|
||||
case constant.ALIYUN:
|
||||
refreshToken, err = client.RefreshALIToken(varMap)
|
||||
}
|
||||
@@ -284,7 +282,15 @@ func (u *BackupService) RefreshToken(req dto.OperateByName) error {
|
||||
|
||||
varsItem, _ := json.Marshal(varMap)
|
||||
backup.Vars = string(varsItem)
|
||||
return backupRepo.Save(&backup)
|
||||
if err := backupRepo.Save(&backup); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
if err := xpack.Sync(constant.SyncBackupAccounts); err != nil {
|
||||
global.LOG.Errorf("sync backup account to node failed, err: %v", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error) {
|
||||
|
||||
@@ -11,7 +11,6 @@ var (
|
||||
groupRepo = repo.NewIGroupRepo()
|
||||
upgradeLogRepo = repo.NewIUpgradeLogRepo()
|
||||
|
||||
taskRepo = repo.NewITaskRepo()
|
||||
agentRepo = repo.NewIAgentRepo()
|
||||
scriptRepo = repo.NewIScriptRepo()
|
||||
)
|
||||
|
||||
@@ -188,34 +188,37 @@ func (u *ScriptService) Sync(req dto.OperateByTaskID) error {
|
||||
}
|
||||
var scriptSetting model.Setting
|
||||
_ = global.DB.Where("key = ?", "ScriptVersion").First(&scriptSetting).Error
|
||||
if scriptSetting.Value == string(versionRes) {
|
||||
syncTask.Logf("The local-%s and remote-%s versions are detected to be consistent. Skip...", scriptSetting.Value, versionRes)
|
||||
localVersion := strings.ReplaceAll(string(versionRes), "\n", "")
|
||||
remoteVersion := strings.ReplaceAll(scriptSetting.Value, "\n", "")
|
||||
|
||||
if localVersion == remoteVersion {
|
||||
syncTask.Log(i18n.GetMsgByKey("ScriptSyncSkip"))
|
||||
return nil
|
||||
}
|
||||
|
||||
syncTask.Log("start to download data.yaml")
|
||||
dataUrl := fmt.Sprintf("%s/scripts/data.yaml", global.CONF.RemoteURL.ResourceURL)
|
||||
_, dataRes, err := req_helper.HandleRequestWithProxy(dataUrl, http.MethodGet, constant.TimeOut20s)
|
||||
syncTask.LogWithStatus(i18n.GetMsgByKey("DownloadData"), err)
|
||||
if err != nil {
|
||||
return fmt.Errorf("load scripts data.yaml from remote failed, err: %v", err)
|
||||
}
|
||||
|
||||
syncTask.Log("download successful!")
|
||||
var scripts Scripts
|
||||
if err = yaml.Unmarshal(dataRes, &scripts); err != nil {
|
||||
return fmt.Errorf("the format of data.yaml is err: %v", err)
|
||||
}
|
||||
|
||||
syncTask.Log("start to download scripts.tar.gz")
|
||||
tmpDir := path.Join(global.CONF.Base.InstallDir, "1panel/tmp/script")
|
||||
if _, err := os.Stat(tmpDir); err != nil {
|
||||
_ = os.MkdirAll(tmpDir, 0755)
|
||||
}
|
||||
scriptsUrl := fmt.Sprintf("%s/scripts/scripts.tar.gz", global.CONF.RemoteURL.ResourceURL)
|
||||
if err := files.DownloadFileWithProxy(scriptsUrl, tmpDir+"/scripts.tar.gz"); err != nil {
|
||||
err = files.DownloadFileWithProxy(scriptsUrl, tmpDir+"/scripts.tar.gz")
|
||||
syncTask.LogWithStatus(i18n.GetMsgByKey("DownloadPackage"), err)
|
||||
if err != nil {
|
||||
return fmt.Errorf("download scripts.tar.gz failed, err: %v", err)
|
||||
}
|
||||
syncTask.Log("download successful! now start to decompress...")
|
||||
|
||||
if err := files.HandleUnTar(tmpDir+"/scripts.tar.gz", tmpDir, ""); err != nil {
|
||||
return fmt.Errorf("handle decompress scripts.tar.gz failed, err: %v", err)
|
||||
}
|
||||
@@ -234,7 +237,7 @@ func (u *ScriptService) Sync(req dto.OperateByTaskID) error {
|
||||
scriptsForDB = append(scriptsForDB, scriptItem)
|
||||
}
|
||||
|
||||
syncTask.Log("analytic completion! now start to refresh database...")
|
||||
syncTask.Log(i18n.GetMsgByKey("AnalyticCompletion"))
|
||||
if err := scriptRepo.SyncAll(scriptsForDB); err != nil {
|
||||
return fmt.Errorf("sync script with db failed, err: %v", err)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user