mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 16:00:29 +00:00
Compare commits
179
Commits
v1.9.0
...
release-1.9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fc26a3061 | ||
|
|
d8669b90bf | ||
|
|
9e980e8e0c | ||
|
|
140cd564da | ||
|
|
b40087c88c | ||
|
|
8e620172e6 | ||
|
|
9b8b9f505b | ||
|
|
1169648162 | ||
|
|
c169f211a0 | ||
|
|
eef445c1eb | ||
|
|
bedb8ce286 | ||
|
|
3652c46ac9 | ||
|
|
0449868d3b | ||
|
|
fdf9046430 | ||
|
|
b3852db39e | ||
|
|
2f7bb6d784 | ||
|
|
e5d3c1bb86 | ||
|
|
c758c1ce87 | ||
|
|
d82086ce4a | ||
|
|
edc3540e67 | ||
|
|
13a69fa1ed | ||
|
|
948653213e | ||
|
|
a4cfb587d6 | ||
|
|
6aa9a95971 | ||
|
|
e3666d17a3 | ||
|
|
7219566273 | ||
|
|
08a45e9723 | ||
|
|
be53945f0e | ||
|
|
725ad3f6d9 | ||
|
|
42828d0a48 | ||
|
|
3804942ae4 | ||
|
|
83dda25591 | ||
|
|
f91d7c6170 | ||
|
|
2cf3b27402 | ||
|
|
0e4f82c434 | ||
|
|
51d7a84062 | ||
|
|
70863f411d | ||
|
|
5c9315ff1c | ||
|
|
3846dcef86 | ||
|
|
8b0d73b5d5 | ||
|
|
e309b9c030 | ||
|
|
a5304b66e3 | ||
|
|
10eef260d4 | ||
|
|
045dbaef91 | ||
|
|
7a45db71d5 | ||
|
|
0ee942cbef | ||
|
|
aa8429a836 | ||
|
|
9f2227cf19 | ||
|
|
338ade095a | ||
|
|
d5432c8e59 | ||
|
|
4eb34f24d1 | ||
|
|
527c8143a1 | ||
|
|
a6891711b7 | ||
|
|
36a253ce03 | ||
|
|
bbe1161dc2 | ||
|
|
e85ed7427a | ||
|
|
97f8a872c0 | ||
|
|
48187b508d | ||
|
|
ca1710cabc | ||
|
|
df2a705713 | ||
|
|
d917008f47 | ||
|
|
61078a24fd | ||
|
|
e35998a4be | ||
|
|
d1fde34a14 | ||
|
|
45394598d9 | ||
|
|
edd6b52f05 | ||
|
|
0f53de56a7 | ||
|
|
4ce50328ba | ||
|
|
115b9c58b0 | ||
|
|
64def5bebd | ||
|
|
e46f931989 | ||
|
|
1a78aecfb1 | ||
|
|
e8c94450f4 | ||
|
|
6bc4cfc8ac | ||
|
|
24579edad6 | ||
|
|
6499d7f462 | ||
|
|
1e5cd23bbe | ||
|
|
fce0ac2c7d | ||
|
|
f5bf111203 | ||
|
|
5152e7b8d5 | ||
|
|
d30be34def | ||
|
|
1a9182bfde | ||
|
|
aeb0549a9a | ||
|
|
64f3bbb13b | ||
|
|
bc0fbc4461 | ||
|
|
1e5ea024ad | ||
|
|
f89365106a | ||
|
|
4891de0a8a | ||
|
|
be6b58b0a1 | ||
|
|
33910f7993 | ||
|
|
7036ba0d5c | ||
|
|
204a9fac87 | ||
|
|
6879b92db3 | ||
|
|
54242ae220 | ||
|
|
6f0f282b7e | ||
|
|
817fd123da | ||
|
|
ad4aac51d0 | ||
|
|
cb85860ff6 | ||
|
|
48713b51f1 | ||
|
|
ce3b841ca7 | ||
|
|
d95ec29de3 | ||
|
|
54d841787e | ||
|
|
714de55727 | ||
|
|
3c39415d84 | ||
|
|
2c04d24108 | ||
|
|
4591e44a62 | ||
|
|
3ffae2a9d4 | ||
|
|
009919f927 | ||
|
|
1c1bc872a8 | ||
|
|
3dca8e3a9c | ||
|
|
c23c3db150 | ||
|
|
4f482ad891 | ||
|
|
625189ce85 | ||
|
|
6d03042e26 | ||
|
|
45a58b3b4b | ||
|
|
c4b7dd70cb | ||
|
|
47524dc49b | ||
|
|
9a905750af | ||
|
|
51077b0597 | ||
|
|
3c2472ab6c | ||
|
|
39818b8424 | ||
|
|
2dc63e152b | ||
|
|
6130d4e026 | ||
|
|
9713b32ba8 | ||
|
|
040a0663c6 | ||
|
|
d626b2f4bd | ||
|
|
b8a89d86a0 | ||
|
|
a8e10d0d71 | ||
|
|
e9f876853b | ||
|
|
9561336934 | ||
|
|
4d19e3904b | ||
|
|
d9005bc937 | ||
|
|
b6948b6550 | ||
|
|
3acd449787 | ||
|
|
8d3fd42197 | ||
|
|
d05d7bbcc2 | ||
|
|
ebfaf77c8f | ||
|
|
9ae5592d2b | ||
|
|
46e7431c4c | ||
|
|
14e2ced3da | ||
|
|
9716e3754e | ||
|
|
aaafeb039e | ||
|
|
ee2fa70bb0 | ||
|
|
b547955965 | ||
|
|
a6c681a429 | ||
|
|
0b94524994 | ||
|
|
85336bc73e | ||
|
|
33484a9436 | ||
|
|
57f496fe12 | ||
|
|
326bcb5b3c | ||
|
|
74dd3ce6c3 | ||
|
|
411402f9cb | ||
|
|
733889407e | ||
|
|
f50972a0e3 | ||
|
|
d50fa7d928 | ||
|
|
df513c49e9 | ||
|
|
10bd640a9b | ||
|
|
6b491710c9 | ||
|
|
b9c5fee411 | ||
|
|
78f2b6f72d | ||
|
|
9aaa387b23 | ||
|
|
a6fb0f1e89 | ||
|
|
cbe1e9b4e1 | ||
|
|
0a4e48c057 | ||
|
|
3dc829a630 | ||
|
|
bcd88c6eca | ||
|
|
4d279a521e | ||
|
|
3fb5e523a8 | ||
|
|
b2926019cf | ||
|
|
a9d8018e78 | ||
|
|
471a245732 | ||
|
|
55c71df906 | ||
|
|
f91831a5ea | ||
|
|
18cbcb7f13 | ||
|
|
e8d3b5fe93 | ||
|
|
360cdf3f77 | ||
|
|
c3a67eed87 | ||
|
|
e8bc1310da | ||
|
|
c873c18a42 |
+2
-7
@@ -21,13 +21,6 @@ build/1panel
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
# NetBeans
|
||||
nbproject/private/
|
||||
build/
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
.nb-gradle/
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
@@ -44,6 +37,8 @@ cmd/server/web/monacoeditorwork
|
||||
cmd/server/web/index.html
|
||||
frontend/auto-imports.d.ts
|
||||
frontend/components.d.ts
|
||||
frontend/src/xpack
|
||||
backend/xpack
|
||||
|
||||
.history/
|
||||
dist/
|
||||
|
||||
@@ -63,7 +63,7 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2014-2023 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.
|
||||
Copyright (c) 2014-2024 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.
|
||||
|
||||
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
|
||||
|
||||
|
||||
@@ -49,6 +49,17 @@ func (b *BaseApi) CreateBackup(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Refresh OneDrive token
|
||||
// @Description 刷新 OneDrive token
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/refresh/onedrive [post]
|
||||
func (b *BaseApi) RefreshOneDriveToken(c *gin.Context) {
|
||||
backupService.Run()
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary List buckets
|
||||
// @Description 获取 bucket 列表
|
||||
@@ -91,16 +102,16 @@ func (b *BaseApi) ListBuckets(c *gin.Context) {
|
||||
// @Summary Load OneDrive info
|
||||
// @Description 获取 OneDrive 信息
|
||||
// @Accept json
|
||||
// @Success 200 string clientID
|
||||
// @Success 200 {object} dto.OneDriveInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/onedrive [get]
|
||||
func (b *BaseApi) LoadOneDriveInfo(c *gin.Context) {
|
||||
clientID, err := backupService.LoadOneDriveInfo()
|
||||
data, err := backupService.LoadOneDriveInfo()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, clientID)
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
@@ -151,6 +162,32 @@ func (b *BaseApi) SearchBackupRecords(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Page backup records by cronjob
|
||||
// @Description 通过计划任务获取备份记录列表分页
|
||||
// @Accept json
|
||||
// @Param request body dto.RecordSearchByCronjob true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/record/search/bycronjob [post]
|
||||
func (b *BaseApi) SearchBackupRecordsByCronjob(c *gin.Context) {
|
||||
var req dto.RecordSearchByCronjob
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
total, list, err := backupService.SearchRecordsByCronjobWithPage(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Download backup record
|
||||
// @Description 下载备份记录
|
||||
@@ -265,12 +302,7 @@ func (b *BaseApi) LoadFilesFromBackup(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := backupService.ListFiles(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
data := backupService.ListFiles(req)
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
@@ -281,7 +313,7 @@ func (b *BaseApi) LoadFilesFromBackup(c *gin.Context) {
|
||||
// @Param request body dto.CommonBackup true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/ [post]
|
||||
// @Router /settings/backup/backup [post]
|
||||
// @x-panel-log {"bodyKeys":["type","name","detailName"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"备份 [type] 数据 [name][detailName]","formatEN":"backup [type] data [name][detailName]"}
|
||||
func (b *BaseApi) Backup(c *gin.Context) {
|
||||
var req dto.CommonBackup
|
||||
@@ -300,6 +332,11 @@ func (b *BaseApi) Backup(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
case constant.AppPostgresql:
|
||||
if err := backupService.PostgresqlBackup(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
case "website":
|
||||
if err := backupService.WebsiteBackup(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
@@ -329,20 +366,23 @@ func (b *BaseApi) Recover(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if req.Source != "LOCAL" {
|
||||
downloadPath, err := backupService.DownloadRecord(dto.DownloadRecord{Source: req.Source, FileDir: path.Dir(req.File), FileName: path.Base(req.File)})
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, fmt.Errorf("download file failed, err: %v", err))
|
||||
return
|
||||
}
|
||||
req.File = downloadPath
|
||||
downloadPath, err := backupService.DownloadRecord(dto.DownloadRecord{Source: req.Source, FileDir: path.Dir(req.File), FileName: path.Base(req.File)})
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, fmt.Errorf("download file failed, err: %v", err))
|
||||
return
|
||||
}
|
||||
req.File = downloadPath
|
||||
switch req.Type {
|
||||
case "mysql", "mariadb":
|
||||
if err := backupService.MysqlRecover(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
case constant.AppPostgresql:
|
||||
if err := backupService.PostgresqlRecover(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
case "website":
|
||||
if err := backupService.WebsiteRecover(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
@@ -383,6 +423,11 @@ func (b *BaseApi) RecoverByUpload(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
case constant.AppPostgresql:
|
||||
if err := backupService.PostgresqlRecoverByUpload(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
case "app":
|
||||
if err := backupService.AppRecover(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
|
||||
@@ -95,7 +95,7 @@ func (b *BaseApi) SearchJobRecords(c *gin.Context) {
|
||||
// @Param request body dto.OperateByID true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /cronjob/record/log [post]
|
||||
// @Router /cronjobs/records/log [post]
|
||||
func (b *BaseApi) LoadRecordLog(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
|
||||
@@ -112,6 +112,27 @@ func (b *BaseApi) ListDatabase(c *gin.Context) {
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Database
|
||||
// @Summary List databases
|
||||
// @Description 获取数据库列表
|
||||
// @Success 200 {array} dto.DatabaseItem
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/db/item/:type [get]
|
||||
func (b *BaseApi) LoadDatabaseItems(c *gin.Context) {
|
||||
dbType, err := helper.GetStrParamByKey(c, "type")
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
list, err := databaseService.LoadItems(dbType)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Database
|
||||
// @Summary Get databases
|
||||
// @Description 获取远程数据库
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags Database Common
|
||||
// @Summary Load base info
|
||||
// @Description 获取数据库基础信息
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithNameAndType true "request"
|
||||
// @Success 200 {object} dto.DBBaseInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/common/info [post]
|
||||
func (b *BaseApi) LoadDBBaseInfo(c *gin.Context) {
|
||||
var req dto.OperationWithNameAndType
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := dbCommonService.LoadBaseInfo(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Database Common
|
||||
// @Summary Load Database conf
|
||||
// @Description 获取数据库配置文件
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithNameAndType true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/common/load/file [post]
|
||||
func (b *BaseApi) LoadDBFile(c *gin.Context) {
|
||||
var req dto.OperationWithNameAndType
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
content, err := dbCommonService.LoadDatabaseFile(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, content)
|
||||
}
|
||||
|
||||
// @Tags Database Common
|
||||
// @Summary Update conf by upload file
|
||||
// @Description 上传替换配置文件
|
||||
// @Accept json
|
||||
// @Param request body dto.DBConfUpdateByFile true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/common/update/conf [post]
|
||||
// @x-panel-log {"bodyKeys":["type","database"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 [type] 数据库 [database] 配置信息","formatEN":"update the [type] [database] database configuration information"}
|
||||
func (b *BaseApi) UpdateDBConfByFile(c *gin.Context) {
|
||||
var req dto.DBConfUpdateByFile
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := dbCommonService.UpdateConfByFile(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
@@ -41,6 +41,37 @@ func (b *BaseApi) CreateMysql(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Mysql
|
||||
// @Summary Bind user of mysql database
|
||||
// @Description 绑定 mysql 数据库用户
|
||||
// @Accept json
|
||||
// @Param request body dto.BindUser true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/bind [post]
|
||||
// @x-panel-log {"bodyKeys":["database", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"绑定 mysql 数据库名 [database] [username]","formatEN":"bind mysql database [database] [username]"}
|
||||
func (b *BaseApi) BindUser(c *gin.Context) {
|
||||
var req dto.BindUser
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Password) != 0 {
|
||||
password, err := base64.StdEncoding.DecodeString(req.Password)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.Password = string(password)
|
||||
}
|
||||
|
||||
if err := mysqlService.BindUser(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Mysql
|
||||
// @Summary Update mysql database description
|
||||
// @Description 更新 mysql 数据库库描述信息
|
||||
@@ -138,29 +169,6 @@ func (b *BaseApi) UpdateMysqlVariables(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Mysql
|
||||
// @Summary Update mysql conf by upload file
|
||||
// @Description 上传替换 mysql 配置文件
|
||||
// @Accept json
|
||||
// @Param request body dto.MysqlConfUpdateByFile true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/conffile/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 mysql 数据库配置信息","formatEN":"update the mysql database configuration information"}
|
||||
func (b *BaseApi) UpdateMysqlConfByFile(c *gin.Context) {
|
||||
var req dto.MysqlConfUpdateByFile
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := mysqlService.UpdateConfByFile(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Mysql
|
||||
// @Summary Page mysql databases
|
||||
// @Description 获取 mysql 数据库列表分页
|
||||
@@ -273,51 +281,6 @@ func (b *BaseApi) DeleteMysql(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Mysql
|
||||
// @Summary Load mysql base info
|
||||
// @Description 获取 mysql 基础信息
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithNameAndType true "request"
|
||||
// @Success 200 {object} dto.DBBaseInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/baseinfo [post]
|
||||
func (b *BaseApi) LoadBaseinfo(c *gin.Context) {
|
||||
var req dto.OperationWithNameAndType
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := mysqlService.LoadBaseInfo(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Database
|
||||
// @Summary Load Database file
|
||||
// @Description 获取数据库文件
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithNameAndType true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/load/file [post]
|
||||
func (b *BaseApi) LoadDatabaseFile(c *gin.Context) {
|
||||
var req dto.OperationWithNameAndType
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
content, err := mysqlService.LoadDatabaseFile(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, content)
|
||||
}
|
||||
|
||||
// @Tags Database Mysql
|
||||
// @Summary Load mysql remote access
|
||||
// @Description 获取 mysql 远程访问权限
|
||||
@@ -331,7 +294,6 @@ func (b *BaseApi) LoadRemoteAccess(c *gin.Context) {
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
isRemote, err := mysqlService.LoadRemoteAccess(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags Database Postgresql
|
||||
// @Summary Create postgresql database
|
||||
// @Description 创建 postgresql 数据库
|
||||
// @Accept json
|
||||
// @Param request body dto.PostgresqlDBCreate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建 postgresql 数据库 [name]","formatEN":"create postgresql database [name]"}
|
||||
func (b *BaseApi) CreatePostgresql(c *gin.Context) {
|
||||
var req dto.PostgresqlDBCreate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Password) != 0 {
|
||||
password, err := base64.StdEncoding.DecodeString(req.Password)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.Password = string(password)
|
||||
}
|
||||
|
||||
if _, err := postgresqlService.Create(context.Background(), req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Postgresql
|
||||
// @Summary Bind postgresql user
|
||||
// @Description 绑定 postgresql 数据库用户
|
||||
// @Accept json
|
||||
// @Param request body dto.PostgresqlBindUser true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg/bind [post]
|
||||
// @x-panel-log {"bodyKeys":["name", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"绑定 postgresql 数据库 [name] 用户 [username]","formatEN":"bind postgresql database [name] user [username]"}
|
||||
func (b *BaseApi) BindPostgresqlUser(c *gin.Context) {
|
||||
var req dto.PostgresqlBindUser
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := postgresqlService.BindUser(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Postgresql
|
||||
// @Summary Update postgresql database description
|
||||
// @Description 更新 postgresql 数据库库描述信息
|
||||
// @Accept json
|
||||
// @Param request body dto.UpdateDescription true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg/description [post]
|
||||
// @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_postgresqls","output_column":"name","output_value":"name"}],"formatZH":"postgresql 数据库 [name] 描述信息修改 [description]","formatEN":"The description of the postgresql database [name] is modified => [description]"}
|
||||
func (b *BaseApi) UpdatePostgresqlDescription(c *gin.Context) {
|
||||
var req dto.UpdateDescription
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := postgresqlService.UpdateDescription(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Postgresql
|
||||
// @Summary Change postgresql privileges
|
||||
// @Description 修改 postgresql 用户权限
|
||||
// @Accept json
|
||||
// @Param request body dto.ChangeDBInfo true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg/privileges [post]
|
||||
// @x-panel-log {"bodyKeys":["database", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新数据库 [database] 用户 [username] 权限","formatEN":"Update [user] privileges of database [database]"}
|
||||
func (b *BaseApi) ChangePostgresqlPrivileges(c *gin.Context) {
|
||||
var req dto.PostgresqlPrivileges
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := postgresqlService.ChangePrivileges(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Postgresql
|
||||
// @Summary Change postgresql password
|
||||
// @Description 修改 postgresql 密码
|
||||
// @Accept json
|
||||
// @Param request body dto.ChangeDBInfo true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg/password [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_postgresqls","output_column":"name","output_value":"name"}],"formatZH":"更新数据库 [name] 密码","formatEN":"Update database [name] password"}
|
||||
func (b *BaseApi) ChangePostgresqlPassword(c *gin.Context) {
|
||||
var req dto.ChangeDBInfo
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Value) != 0 {
|
||||
value, err := base64.StdEncoding.DecodeString(req.Value)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.Value = string(value)
|
||||
}
|
||||
|
||||
if err := postgresqlService.ChangePassword(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Postgresql
|
||||
// @Summary Page postgresql databases
|
||||
// @Description 获取 postgresql 数据库列表分页
|
||||
// @Accept json
|
||||
// @Param request body dto.PostgresqlDBSearch true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg/search [post]
|
||||
func (b *BaseApi) SearchPostgresql(c *gin.Context) {
|
||||
var req dto.PostgresqlDBSearch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
total, list, err := postgresqlService.SearchWithPage(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Database Postgresql
|
||||
// @Summary List postgresql database names
|
||||
// @Description 获取 postgresql 数据库列表
|
||||
// @Accept json
|
||||
// @Param request body dto.PageInfo true "request"
|
||||
// @Success 200 {array} dto.PostgresqlOption
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg/options [get]
|
||||
func (b *BaseApi) ListPostgresqlDBName(c *gin.Context) {
|
||||
list, err := postgresqlService.ListDBOption()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Database Postgresql
|
||||
// @Summary Load postgresql database from remote
|
||||
// @Description 从服务器获取
|
||||
// @Accept json
|
||||
// @Param request body dto.PostgresqlLoadDB true "request"
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg/:database/load [post]
|
||||
func (b *BaseApi) LoadPostgresqlDBFromRemote(c *gin.Context) {
|
||||
database, err := helper.GetStrParamByKey(c, "database")
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := postgresqlService.LoadFromRemote(database); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database Postgresql
|
||||
// @Summary Check before delete postgresql database
|
||||
// @Description Postgresql 数据库删除前检查
|
||||
// @Accept json
|
||||
// @Param request body dto.PostgresqlDBDeleteCheck true "request"
|
||||
// @Success 200 {array} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg/del/check [post]
|
||||
func (b *BaseApi) DeleteCheckPostgresql(c *gin.Context) {
|
||||
var req dto.PostgresqlDBDeleteCheck
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
apps, err := postgresqlService.DeleteCheck(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, apps)
|
||||
}
|
||||
|
||||
// @Tags Database Postgresql
|
||||
// @Summary Delete postgresql database
|
||||
// @Description 删除 postgresql 数据库
|
||||
// @Accept json
|
||||
// @Param request body dto.PostgresqlDBDelete true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/pg/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_postgresqls","output_column":"name","output_value":"name"}],"formatZH":"删除 postgresql 数据库 [name]","formatEN":"delete postgresql database [name]"}
|
||||
func (b *BaseApi) DeletePostgresql(c *gin.Context) {
|
||||
var req dto.PostgresqlDBDelete
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
tx, ctx := helper.GetTxAndContext()
|
||||
if err := postgresqlService.Delete(ctx, req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
tx.Commit()
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
@@ -1,15 +1,11 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -161,45 +157,3 @@ func (b *BaseApi) RedisBackupList(c *gin.Context) {
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Database Redis
|
||||
// @Summary Update redis conf by file
|
||||
// @Description 上传更新 redis 配置信息
|
||||
// @Accept json
|
||||
// @Param request body dto.RedisConfUpdateByFile true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/redis/conffile/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 redis 数据库配置信息","formatEN":"update the redis database configuration information"}
|
||||
func (b *BaseApi) UpdateRedisConfByFile(c *gin.Context) {
|
||||
var req dto.RedisConfUpdateByFile
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
redisInfo, err := redisService.LoadConf()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
path := fmt.Sprintf("%s/redis/%s/conf/redis.conf", constant.AppInstallDir, redisInfo.Name)
|
||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
write := bufio.NewWriter(file)
|
||||
_, _ = write.WriteString(req.File)
|
||||
write.Flush()
|
||||
|
||||
if req.RestartNow {
|
||||
composeDir := fmt.Sprintf("%s/redis/%s/docker-compose.yml", constant.AppInstallDir, redisInfo.Name)
|
||||
if _, err := compose.Restart(composeDir); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
// @Description 获取设备基础信息
|
||||
// @Success 200 {object} dto.DeviceBaseInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /toolbox/device/base [get]
|
||||
// @Router /toolbox/device/base [post]
|
||||
func (b *BaseApi) LoadDeviceBaseInfo(c *gin.Context) {
|
||||
data, err := deviceService.LoadBaseInfo()
|
||||
if err != nil {
|
||||
|
||||
@@ -21,9 +21,11 @@ var (
|
||||
imageService = service.NewIImageService()
|
||||
dockerService = service.NewIDockerService()
|
||||
|
||||
mysqlService = service.NewIMysqlService()
|
||||
databaseService = service.NewIDatabaseService()
|
||||
redisService = service.NewIRedisService()
|
||||
dbCommonService = service.NewIDBCommonService()
|
||||
mysqlService = service.NewIMysqlService()
|
||||
postgresqlService = service.NewIPostgresqlService()
|
||||
databaseService = service.NewIDatabaseService()
|
||||
redisService = service.NewIRedisService()
|
||||
|
||||
cronjobService = service.NewICronjobService()
|
||||
|
||||
@@ -52,8 +54,9 @@ var (
|
||||
snapshotService = service.NewISnapshotService()
|
||||
upgradeService = service.NewIUpgradeService()
|
||||
|
||||
runtimeService = service.NewRuntimeService()
|
||||
processService = service.NewIProcessService()
|
||||
runtimeService = service.NewRuntimeService()
|
||||
processService = service.NewIProcessService()
|
||||
phpExtensionsService = service.NewIPHPExtensionsService()
|
||||
|
||||
hostToolService = service.NewIHostToolService()
|
||||
|
||||
|
||||
@@ -422,11 +422,9 @@ func (b *BaseApi) MoveFile(c *gin.Context) {
|
||||
// @Summary Download file
|
||||
// @Description 下载文件
|
||||
// @Accept json
|
||||
// @Param request body request.FileDownload true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /files/download [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"下载文件 [name]","formatEN":"Download file [name]"}
|
||||
// @Router /files/download [get]
|
||||
func (b *BaseApi) Download(c *gin.Context) {
|
||||
filePath := c.Query("path")
|
||||
file, err := os.Open(filePath)
|
||||
@@ -689,7 +687,7 @@ func (b *BaseApi) Keys(c *gin.Context) {
|
||||
// @Param request body request.FileReadByLineReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /files/log/read [post]
|
||||
// @Router /files/read [post]
|
||||
func (b *BaseApi) ReadFileByLine(c *gin.Context) {
|
||||
var req request.FileReadByLineReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
|
||||
@@ -35,8 +35,24 @@ func (b *BaseApi) SearchImage(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags Container Image
|
||||
// @Summary List images
|
||||
// @Description 获取镜像列表
|
||||
// @Summary List all images
|
||||
// @Description 获取所有镜像列表
|
||||
// @Produce json
|
||||
// @Success 200 {array} dto.ImageInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/image/all [get]
|
||||
func (b *BaseApi) ListAllImage(c *gin.Context) {
|
||||
list, err := imageService.ListAll()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Container Image
|
||||
// @Summary load images options
|
||||
// @Description 获取镜像名称列表
|
||||
// @Produce json
|
||||
// @Success 200 {array} dto.Options
|
||||
// @Security ApiKeyAuth
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
// @Description 获取 OpenResty 配置信息
|
||||
// @Success 200 {object} response.FileInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /openResty [get]
|
||||
// @Router /openresty [get]
|
||||
func (b *BaseApi) GetNginx(c *gin.Context) {
|
||||
fileInfo, err := nginxService.GetNginxConfig()
|
||||
if err != nil {
|
||||
@@ -29,7 +29,7 @@ func (b *BaseApi) GetNginx(c *gin.Context) {
|
||||
// @Param request body request.NginxScopeReq true "request"
|
||||
// @Success 200 {array} response.NginxParam
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /openResty/scope [post]
|
||||
// @Router /openresty/scope [post]
|
||||
func (b *BaseApi) GetNginxConfigByScope(c *gin.Context) {
|
||||
var req request.NginxScopeReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -51,7 +51,7 @@ func (b *BaseApi) GetNginxConfigByScope(c *gin.Context) {
|
||||
// @Param request body request.NginxConfigUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /openResty/update [post]
|
||||
// @Router /openresty/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新 nginx 配置 [domain]","formatEN":"Update nginx conf [domain]"}
|
||||
func (b *BaseApi) UpdateNginxConfigByScope(c *gin.Context) {
|
||||
var req request.NginxConfigUpdate
|
||||
@@ -70,7 +70,7 @@ func (b *BaseApi) UpdateNginxConfigByScope(c *gin.Context) {
|
||||
// @Description 获取 OpenResty 状态信息
|
||||
// @Success 200 {object} response.NginxStatus
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /openResty/status [get]
|
||||
// @Router /openresty/status [get]
|
||||
func (b *BaseApi) GetNginxStatus(c *gin.Context) {
|
||||
res, err := nginxService.GetStatus()
|
||||
if err != nil {
|
||||
@@ -87,7 +87,7 @@ func (b *BaseApi) GetNginxStatus(c *gin.Context) {
|
||||
// @Param request body request.NginxConfigFileUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /openResty/file [post]
|
||||
// @Router /openresty/file [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 nginx 配置","formatEN":"Update nginx conf"}
|
||||
func (b *BaseApi) UpdateNginxFile(c *gin.Context) {
|
||||
var req request.NginxConfigFileUpdate
|
||||
@@ -101,3 +101,18 @@ func (b *BaseApi) UpdateNginxFile(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags OpenResty
|
||||
// @Summary Clear OpenResty proxy cache
|
||||
// @Description 清理 OpenResty 代理缓存
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /openresty/clear [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清理 Openresty 代理缓存","formatEN":"Clear nginx proxy cache"}
|
||||
func (b *BaseApi) ClearNginxProxyCache(c *gin.Context) {
|
||||
if err := nginxService.ClearProxyCache(); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags PHP Extensions
|
||||
// @Summary Page Extensions
|
||||
// @Description Page Extensions
|
||||
// @Accept json
|
||||
// @Param request body request.PHPExtensionsSearch true "request"
|
||||
// @Success 200 {array} response.PHPExtensionsDTO
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /runtimes/php/extensions/search [post]
|
||||
func (b *BaseApi) PagePHPExtensions(c *gin.Context) {
|
||||
var req request.PHPExtensionsSearch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if req.All {
|
||||
list, err := phpExtensionsService.List()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
} else {
|
||||
total, list, err := phpExtensionsService.Page(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Total: total,
|
||||
Items: list,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// @Tags PHP Extensions
|
||||
// @Summary Create Extensions
|
||||
// @Description Create Extensions
|
||||
// @Accept json
|
||||
// @Param request body request.PHPExtensionsCreate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /runtimes/php/extensions [post]
|
||||
func (b *BaseApi) CreatePHPExtensions(c *gin.Context) {
|
||||
var req request.PHPExtensionsCreate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := phpExtensionsService.Create(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags PHP Extensions
|
||||
// @Summary Update Extensions
|
||||
// @Description Update Extensions
|
||||
// @Accept json
|
||||
// @Param request body request.PHPExtensionsUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /runtimes/php/extensions/update [post]
|
||||
func (b *BaseApi) UpdatePHPExtensions(c *gin.Context) {
|
||||
var req request.PHPExtensionsUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := phpExtensionsService.Update(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags PHP Extensions
|
||||
// @Summary Delete Extensions
|
||||
// @Description Delete Extensions
|
||||
// @Accept json
|
||||
// @Param request body request.PHPExtensionsDelete true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /runtimes/php/extensions/del [post]
|
||||
func (b *BaseApi) DeletePHPExtensions(c *gin.Context) {
|
||||
var req request.PHPExtensionsDelete
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := phpExtensionsService.Delete(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
@@ -131,28 +131,6 @@ func (b *BaseApi) LoadSSHSecret(c *gin.Context) {
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Analysis host SSH logs
|
||||
// @Description 分析 SSH 登录日志
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchForAnalysis true "request"
|
||||
// @Success 200 {array} dto.SSHLogAnalysis
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /host/ssh/log/analysis [post]
|
||||
func (b *BaseApi) AnalysisLog(c *gin.Context) {
|
||||
var req dto.SearchForAnalysis
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := sshService.AnalysisLog(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Load host SSH logs
|
||||
// @Description 获取 SSH 登录日志
|
||||
|
||||
@@ -55,7 +55,6 @@ func (b *BaseApi) WsSsh(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
sws, err := terminal.NewLogicSshWsSession(cols, rows, true, connInfo.Client, wsConn)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
@@ -106,7 +105,7 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
||||
defer killBash(redisConf.ContainerName, commands, pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave)
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
@@ -174,7 +173,7 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
|
||||
defer killBash(containerID, command, pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave)
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, true)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ func (b *BaseApi) GetNotesByVersion(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/upgrade [post]
|
||||
// @x-panel-log {"bodyKeys":["version"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新系统 => [version]","formatEN":"upgrade service => [version]"}
|
||||
// @x-panel-log {"bodyKeys":["version"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新系统 => [version]","formatEN":"upgrade system => [version]"}
|
||||
func (b *BaseApi) Upgrade(c *gin.Context) {
|
||||
var req dto.Upgrade
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
|
||||
@@ -124,7 +124,7 @@ func (b *BaseApi) ObtainWebsiteCA(c *gin.Context) {
|
||||
// @Param request body request.WebsiteCAObtain true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/ca/obtain [post]
|
||||
// @Router /websites/ca/renew [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_cas","output_column":"name","output_value":"name"}],"formatZH":"自签 SSL 证书 [name]","formatEN":"Obtain SSL [name]"}
|
||||
func (b *BaseApi) RenewWebsiteCA(c *gin.Context) {
|
||||
var req request.WebsiteCARenew
|
||||
|
||||
@@ -21,18 +21,24 @@ type BackupInfo struct {
|
||||
Vars string `json:"vars"`
|
||||
}
|
||||
|
||||
type OneDriveInfo struct {
|
||||
ClientID string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
RedirectUri string `json:"redirect_uri"`
|
||||
}
|
||||
|
||||
type BackupSearchFile struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
}
|
||||
|
||||
type CommonBackup struct {
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website"`
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql"`
|
||||
Name string `json:"name"`
|
||||
DetailName string `json:"detailName"`
|
||||
}
|
||||
type CommonRecover struct {
|
||||
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive WebDAV"`
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website"`
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql"`
|
||||
Name string `json:"name"`
|
||||
DetailName string `json:"detailName"`
|
||||
File string `json:"file"`
|
||||
@@ -45,6 +51,11 @@ type RecordSearch struct {
|
||||
DetailName string `json:"detailName"`
|
||||
}
|
||||
|
||||
type RecordSearchByCronjob struct {
|
||||
PageInfo
|
||||
CronjobID uint `json:"cronjobID" validate:"required"`
|
||||
}
|
||||
|
||||
type BackupRecords struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
|
||||
@@ -2,6 +2,8 @@ package dto
|
||||
|
||||
type SearchCommandWithPage struct {
|
||||
SearchWithPage
|
||||
OrderBy string `json:"orderBy"`
|
||||
Order string `json:"order"`
|
||||
GroupID uint `json:"groupID"`
|
||||
Info string `json:"info"`
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ import "time"
|
||||
|
||||
type PageContainer struct {
|
||||
PageInfo
|
||||
Name string `json:"name"`
|
||||
State string `json:"state" validate:"required,oneof=all created running paused restarting removing exited dead"`
|
||||
OrderBy string `json:"orderBy"`
|
||||
Order string `json:"order"`
|
||||
Filters string `json:"filters"`
|
||||
Name string `json:"name"`
|
||||
State string `json:"state" validate:"required,oneof=all created running paused restarting removing exited dead"`
|
||||
OrderBy string `json:"orderBy"`
|
||||
Order string `json:"order"`
|
||||
Filters string `json:"filters"`
|
||||
ExcludeAppStore bool `json:"excludeAppStore"`
|
||||
}
|
||||
|
||||
type InspectReq struct {
|
||||
@@ -30,6 +31,10 @@ type ContainerInfo struct {
|
||||
|
||||
IsFromApp bool `json:"isFromApp"`
|
||||
IsFromCompose bool `json:"isFromCompose"`
|
||||
|
||||
AppName string `json:"appName"`
|
||||
AppInstallName string `json:"appInstallName"`
|
||||
Websites []string `json:"websites"`
|
||||
}
|
||||
|
||||
type ResourceLimit struct {
|
||||
|
||||
+33
-43
@@ -3,49 +3,43 @@ package dto
|
||||
import "time"
|
||||
|
||||
type CronjobCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
SpecType string `json:"specType" validate:"required"`
|
||||
Week int `json:"week" validate:"number,max=6,min=0"`
|
||||
Day int `json:"day" validate:"number"`
|
||||
Hour int `json:"hour" validate:"number"`
|
||||
Minute int `json:"minute" validate:"number"`
|
||||
Second int `json:"second" validate:"number"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
Spec string `json:"spec" validate:"required"`
|
||||
|
||||
Script string `json:"script"`
|
||||
ContainerName string `json:"containerName"`
|
||||
AppID string `json:"appID"`
|
||||
Website string `json:"website"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
DBType string `json:"dbType"`
|
||||
DBName string `json:"dbName"`
|
||||
URL string `json:"url"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
KeepLocal bool `json:"keepLocal"`
|
||||
TargetDirID int `json:"targetDirID"`
|
||||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||
|
||||
BackupAccounts string `json:"backupAccounts"`
|
||||
DefaultDownload string `json:"defaultDownload"`
|
||||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||
}
|
||||
|
||||
type CronjobUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
SpecType string `json:"specType" validate:"required"`
|
||||
Week int `json:"week" validate:"number,max=6,min=0"`
|
||||
Day int `json:"day" validate:"number"`
|
||||
Hour int `json:"hour" validate:"number"`
|
||||
Minute int `json:"minute" validate:"number"`
|
||||
Second int `json:"second" validate:"number"`
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Spec string `json:"spec" validate:"required"`
|
||||
|
||||
Script string `json:"script"`
|
||||
ContainerName string `json:"containerName"`
|
||||
AppID string `json:"appID"`
|
||||
Website string `json:"website"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
DBType string `json:"dbType"`
|
||||
DBName string `json:"dbName"`
|
||||
URL string `json:"url"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
KeepLocal bool `json:"keepLocal"`
|
||||
TargetDirID int `json:"targetDirID"`
|
||||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||
|
||||
BackupAccounts string `json:"backupAccounts"`
|
||||
DefaultDownload string `json:"defaultDownload"`
|
||||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||
}
|
||||
|
||||
type CronjobUpdateStatus struct {
|
||||
@@ -59,6 +53,7 @@ type CronjobDownload struct {
|
||||
}
|
||||
|
||||
type CronjobClean struct {
|
||||
IsDelete bool `json:"isDelete"`
|
||||
CleanData bool `json:"cleanData"`
|
||||
CronjobID uint `json:"cronjobID" validate:"required"`
|
||||
}
|
||||
@@ -69,28 +64,23 @@ type CronjobBatchDelete struct {
|
||||
}
|
||||
|
||||
type CronjobInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
SpecType string `json:"specType"`
|
||||
Week int `json:"week"`
|
||||
Day int `json:"day"`
|
||||
Hour int `json:"hour"`
|
||||
Minute int `json:"minute"`
|
||||
Second int `json:"second"`
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Spec string `json:"spec"`
|
||||
|
||||
Script string `json:"script"`
|
||||
ContainerName string `json:"containerName"`
|
||||
AppID string `json:"appID"`
|
||||
Website string `json:"website"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
DBName string `json:"dbName"`
|
||||
URL string `json:"url"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
KeepLocal bool `json:"keepLocal"`
|
||||
TargetDir string `json:"targetDir"`
|
||||
TargetDirID int `json:"targetDirID"`
|
||||
RetainCopies int `json:"retainCopies"`
|
||||
Script string `json:"script"`
|
||||
ContainerName string `json:"containerName"`
|
||||
AppID string `json:"appID"`
|
||||
Website string `json:"website"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
DBType string `json:"dbType"`
|
||||
DBName string `json:"dbName"`
|
||||
URL string `json:"url"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
BackupAccounts string `json:"backupAccounts"`
|
||||
DefaultDownload string `json:"defaultDownload"`
|
||||
RetainCopies int `json:"retainCopies"`
|
||||
|
||||
LastRecordTime string `json:"lastRecordTime"`
|
||||
Status string `json:"status"`
|
||||
|
||||
+36
-19
@@ -2,6 +2,27 @@ package dto
|
||||
|
||||
import "time"
|
||||
|
||||
// common
|
||||
type DBConfUpdateByFile struct {
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql redis"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
File string `json:"file"`
|
||||
}
|
||||
type ChangeDBInfo struct {
|
||||
ID uint `json:"id"`
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
Value string `json:"value" validate:"required"`
|
||||
}
|
||||
|
||||
type DBBaseInfo struct {
|
||||
Name string `json:"name"`
|
||||
ContainerName string `json:"containerName"`
|
||||
Port int64 `json:"port"`
|
||||
}
|
||||
|
||||
// mysql
|
||||
type MysqlDBSearch struct {
|
||||
PageInfo
|
||||
Info string `json:"info"`
|
||||
@@ -43,6 +64,14 @@ type MysqlDBCreate struct {
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type BindUser struct {
|
||||
Database string `json:"database" validate:"required"`
|
||||
DB string `json:"db" validate:"required"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Permission string `json:"permission" validate:"required"`
|
||||
}
|
||||
|
||||
type MysqlLoadDB struct {
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
|
||||
@@ -133,25 +162,6 @@ type MysqlVariablesUpdateHelper struct {
|
||||
Param string `json:"param"`
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
type MysqlConfUpdateByFile struct {
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
File string `json:"file"`
|
||||
}
|
||||
|
||||
type ChangeDBInfo struct {
|
||||
ID uint `json:"id"`
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
Value string `json:"value" validate:"required"`
|
||||
}
|
||||
|
||||
type DBBaseInfo struct {
|
||||
Name string `json:"name"`
|
||||
ContainerName string `json:"containerName"`
|
||||
Port int64 `json:"port"`
|
||||
}
|
||||
|
||||
// redis
|
||||
type ChangeRedisPass struct {
|
||||
@@ -256,6 +266,13 @@ type DatabaseOption struct {
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type DatabaseItem struct {
|
||||
ID uint `json:"id"`
|
||||
From string `json:"from"`
|
||||
Database string `json:"database"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type DatabaseCreate struct {
|
||||
Name string `json:"name" validate:"required,max=256"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package dto
|
||||
|
||||
import "time"
|
||||
|
||||
type PostgresqlDBSearch struct {
|
||||
PageInfo
|
||||
Info string `json:"info"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
OrderBy string `json:"orderBy"`
|
||||
Order string `json:"order"`
|
||||
}
|
||||
|
||||
type PostgresqlDBInfo struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Name string `json:"name"`
|
||||
From string `json:"from"`
|
||||
PostgresqlName string `json:"postgresqlName"`
|
||||
Format string `json:"format"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
SuperUser bool `json:"superUser"`
|
||||
BackupCount int `json:"backupCount"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type PostgresqlOption struct {
|
||||
ID uint `json:"id"`
|
||||
From string `json:"from"`
|
||||
Type string `json:"type"`
|
||||
Database string `json:"database"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type PostgresqlDBCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
Format string `json:"format"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
SuperUser bool `json:"superUser"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type PostgresqlBindUser struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
SuperUser bool `json:"superUser"`
|
||||
}
|
||||
|
||||
type PostgresqlPrivileges struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
SuperUser bool `json:"superUser"`
|
||||
}
|
||||
|
||||
type PostgresqlLoadDB struct {
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Type string `json:"type" validate:"required,oneof=postgresql"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
}
|
||||
|
||||
type PostgresqlDBDeleteCheck struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=postgresql"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
}
|
||||
|
||||
type PostgresqlDBDelete struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=postgresql"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
ForceDelete bool `json:"forceDelete"`
|
||||
DeleteBackup bool `json:"deleteBackup"`
|
||||
}
|
||||
|
||||
type PostgresqlConfUpdateByFile struct {
|
||||
Type string `json:"type" validate:"required,oneof=postgresql mariadb"`
|
||||
Database string `json:"database" validate:"required"`
|
||||
File string `json:"file"`
|
||||
}
|
||||
@@ -16,7 +16,7 @@ type RuleSearch struct {
|
||||
}
|
||||
|
||||
type FirewallOperation struct {
|
||||
Operation string `json:"operation" validate:"required,oneof=start stop disablePing enablePing"`
|
||||
Operation string `json:"operation" validate:"required,oneof=start stop restart disablePing enablePing"`
|
||||
}
|
||||
|
||||
type PortRuleOperate struct {
|
||||
|
||||
@@ -27,7 +27,6 @@ type ImagePull struct {
|
||||
}
|
||||
|
||||
type ImageTag struct {
|
||||
RepoID uint `json:"repoID"`
|
||||
SourceID string `json:"sourceID" validate:"required"`
|
||||
TargetName string `json:"targetName" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ type AppContainerConfig struct {
|
||||
EditCompose bool `json:"editCompose"`
|
||||
DockerCompose string `json:"dockerCompose"`
|
||||
HostMode bool `json:"hostMode"`
|
||||
PullImage bool `json:"pullImage"`
|
||||
}
|
||||
|
||||
type AppInstalledSearch struct {
|
||||
@@ -67,6 +68,7 @@ type AppInstalledOperate struct {
|
||||
DeleteBackup bool `json:"deleteBackup"`
|
||||
DeleteDB bool `json:"deleteDB"`
|
||||
Backup bool `json:"backup"`
|
||||
PullImage bool `json:"pullImage"`
|
||||
}
|
||||
|
||||
type AppInstalledUpdate struct {
|
||||
|
||||
@@ -77,9 +77,10 @@ type FilePathCheck struct {
|
||||
}
|
||||
|
||||
type FileWget struct {
|
||||
Url string `json:"url" validate:"required"`
|
||||
Path string `json:"path" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Url string `json:"url" validate:"required"`
|
||||
Path string `json:"path" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
IgnoreCertificate bool `json:"ignoreCertificate"`
|
||||
}
|
||||
|
||||
type FileMove struct {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package request
|
||||
|
||||
import "github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
|
||||
type PHPExtensionsSearch struct {
|
||||
dto.PageInfo
|
||||
All bool `json:"all"`
|
||||
}
|
||||
|
||||
type PHPExtensionsCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Extensions string `json:"extensions" validate:"required"`
|
||||
}
|
||||
|
||||
type PHPExtensionsUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Extensions string `json:"extensions" validate:"required"`
|
||||
}
|
||||
|
||||
type PHPExtensionsDelete struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
}
|
||||
@@ -32,7 +32,8 @@ type WebsiteSSLRenew struct {
|
||||
}
|
||||
|
||||
type WebsiteSSLApply struct {
|
||||
ID uint `json:"ID" validate:"required"`
|
||||
ID uint `json:"ID" validate:"required"`
|
||||
SkipDNSCheck bool `json:"SkipDNSCheck"`
|
||||
}
|
||||
|
||||
type WebsiteAcmeAccountCreate struct {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
)
|
||||
@@ -72,9 +73,11 @@ type AppInstalledDTO struct {
|
||||
}
|
||||
|
||||
type DatabaseConn struct {
|
||||
Password string `json:"password"`
|
||||
ServiceName string `json:"serviceName"`
|
||||
Port int64 `json:"port"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
ContainerName string `json:"containerName"`
|
||||
ServiceName string `json:"serviceName"`
|
||||
Port int64 `json:"port"`
|
||||
}
|
||||
|
||||
type AppService struct {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package response
|
||||
|
||||
import "github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
|
||||
type PHPExtensionsDTO struct {
|
||||
model.PHPExtensions
|
||||
}
|
||||
@@ -13,6 +13,12 @@ type WebsiteDTO struct {
|
||||
RuntimeName string `json:"runtimeName"`
|
||||
}
|
||||
|
||||
type WebsiteOption struct {
|
||||
ID uint `json:"id"`
|
||||
PrimaryDomain string `json:"primaryDomain"`
|
||||
Alias string `json:"alias"`
|
||||
}
|
||||
|
||||
type WebsitePreInstallCheck struct {
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
|
||||
+14
-12
@@ -60,7 +60,7 @@ type SettingUpdate struct {
|
||||
}
|
||||
|
||||
type SSLUpdate struct {
|
||||
SSLType string `json:"sslType" validate:"required,oneof=self select import-paste import-local"`
|
||||
SSLType string `json:"sslType" validate:"required,oneof=self select import import-paste import-local"`
|
||||
Domain string `json:"domain"`
|
||||
SSL string `json:"ssl" validate:"required,oneof=enable disable"`
|
||||
Cert string `json:"cert"`
|
||||
@@ -99,9 +99,10 @@ type SnapshotStatus struct {
|
||||
}
|
||||
|
||||
type SnapshotCreate struct {
|
||||
ID uint `json:"id"`
|
||||
From string `json:"from" validate:"required,oneof=OSS S3 SFTP MINIO COS KODO OneDrive WebDAV"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
ID uint `json:"id"`
|
||||
From string `json:"from" validate:"required"`
|
||||
DefaultDownload string `json:"defaultDownload" validate:"required"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
}
|
||||
type SnapshotRecover struct {
|
||||
IsNew bool `json:"isNew"`
|
||||
@@ -114,14 +115,15 @@ type SnapshotImport struct {
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
}
|
||||
type SnapshotInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
From string `json:"from"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Version string `json:"version"`
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
From string `json:"from"`
|
||||
DefaultDownload string `json:"defaultDownload"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Version string `json:"version"`
|
||||
|
||||
InterruptStep string `json:"interruptStep"`
|
||||
RecoverStatus string `json:"recoverStatus"`
|
||||
|
||||
@@ -44,26 +44,6 @@ type SSHLog struct {
|
||||
FailedCount int `json:"failedCount"`
|
||||
}
|
||||
|
||||
type SearchForAnalysis struct {
|
||||
PageInfo
|
||||
OrderBy string `json:"orderBy" validate:"required,oneof=Success Failed"`
|
||||
}
|
||||
|
||||
type AnalysisRes struct {
|
||||
Total int64 `json:"total"`
|
||||
Items []SSHLogAnalysis `json:"items"`
|
||||
SuccessfulCount int `json:"successfulCount"`
|
||||
FailedCount int `json:"failedCount"`
|
||||
}
|
||||
|
||||
type SSHLogAnalysis struct {
|
||||
Address string `json:"address"`
|
||||
Area string `json:"area"`
|
||||
SuccessfulCount int `json:"successfulCount"`
|
||||
FailedCount int `json:"failedCount"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type SSHHistory struct {
|
||||
Date time.Time `json:"date"`
|
||||
DateStr string `json:"dateStr"`
|
||||
|
||||
@@ -12,6 +12,8 @@ type BackupAccount struct {
|
||||
|
||||
type BackupRecord struct {
|
||||
BaseModel
|
||||
From string `gorm:"type:varchar(64)" json:"from"`
|
||||
CronjobID uint `gorm:"type:decimal" json:"cronjobID"`
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
Name string `gorm:"type:varchar(64);not null" json:"name"`
|
||||
DetailName string `gorm:"type:varchar(256)" json:"detailName"`
|
||||
|
||||
@@ -5,32 +5,31 @@ import "time"
|
||||
type Cronjob struct {
|
||||
BaseModel
|
||||
|
||||
Name string `gorm:"type:varchar(64);not null;unique" json:"name"`
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
SpecType string `gorm:"type:varchar(64);not null" json:"specType"`
|
||||
Spec string `gorm:"type:varchar(64);not null" json:"spec"`
|
||||
Week uint64 `gorm:"type:decimal" json:"week"`
|
||||
Day uint64 `gorm:"type:decimal" json:"day"`
|
||||
Hour uint64 `gorm:"type:decimal" json:"hour"`
|
||||
Minute uint64 `gorm:"type:decimal" json:"minute"`
|
||||
Second uint64 `gorm:"type:decimal" json:"second"`
|
||||
Name string `gorm:"type:varchar(64);not null" json:"name"`
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
Spec string `gorm:"type:varchar(64);not null" json:"spec"`
|
||||
|
||||
ContainerName string `gorm:"type:varchar(64)" json:"containerName"`
|
||||
Script string `gorm:"longtext" json:"script"`
|
||||
Website string `gorm:"type:varchar(64)" json:"website"`
|
||||
AppID string `gorm:"type:varchar(64)" json:"appID"`
|
||||
DBType string `gorm:"type:varchar(64)" json:"dbType"`
|
||||
DBName string `gorm:"type:varchar(64)" json:"dbName"`
|
||||
URL string `gorm:"type:varchar(256)" json:"url"`
|
||||
SourceDir string `gorm:"type:varchar(256)" json:"sourceDir"`
|
||||
ExclusionRules string `gorm:"longtext" json:"exclusionRules"`
|
||||
|
||||
KeepLocal bool `gorm:"type:varchar(64)" json:"keepLocal"`
|
||||
TargetDirID uint64 `gorm:"type:decimal" json:"targetDirID"`
|
||||
RetainCopies uint64 `gorm:"type:decimal" json:"retainCopies"`
|
||||
// 已废弃
|
||||
KeepLocal bool `gorm:"type:varchar(64)" json:"keepLocal"`
|
||||
TargetDirID uint64 `gorm:"type:decimal" json:"targetDirID"`
|
||||
|
||||
Status string `gorm:"type:varchar(64)" json:"status"`
|
||||
EntryID uint64 `gorm:"type:decimal" json:"entryID"`
|
||||
Records []JobRecords `json:"records"`
|
||||
BackupAccounts string `gorm:"type:varchar(64)" json:"backupAccounts"`
|
||||
DefaultDownload string `gorm:"type:varchar(64)" json:"defaultDownload"`
|
||||
RetainCopies uint64 `gorm:"type:decimal" json:"retainCopies"`
|
||||
|
||||
Status string `gorm:"type:varchar(64)" json:"status"`
|
||||
EntryIDs string `gorm:"type:varchar(64)" json:"entryIDs"`
|
||||
Records []JobRecords `json:"records"`
|
||||
}
|
||||
|
||||
type JobRecords struct {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package model
|
||||
|
||||
type DatabasePostgresql struct {
|
||||
BaseModel
|
||||
Name string `json:"name" gorm:"type:varchar(256);not null"`
|
||||
From string `json:"from" gorm:"type:varchar(256);not null;default:local"`
|
||||
PostgresqlName string `json:"postgresqlName" gorm:"type:varchar(64);not null"`
|
||||
Format string `json:"format" gorm:"type:varchar(64);not null"`
|
||||
Username string `json:"username" gorm:"type:varchar(256);not null"`
|
||||
Password string `json:"password" gorm:"type:varchar(256);not null"`
|
||||
SuperUser bool `json:"superUser" gorm:"type:varchar(64)" `
|
||||
Description string `json:"description" gorm:"type:varchar(256);"`
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package model
|
||||
|
||||
type PHPExtensions struct {
|
||||
BaseModel
|
||||
Name string ` json:"name" gorm:"not null"`
|
||||
Extensions string `json:"extensions" gorm:"not null"`
|
||||
}
|
||||
@@ -2,12 +2,13 @@ package model
|
||||
|
||||
type Snapshot struct {
|
||||
BaseModel
|
||||
Name string `json:"name" gorm:"type:varchar(64);not null;unique"`
|
||||
Description string `json:"description" gorm:"type:varchar(256)"`
|
||||
From string `json:"from"`
|
||||
Status string `json:"status" gorm:"type:varchar(64)"`
|
||||
Message string `json:"message" gorm:"type:varchar(256)"`
|
||||
Version string `json:"version" gorm:"type:varchar(256)"`
|
||||
Name string `json:"name" gorm:"type:varchar(64);not null;unique"`
|
||||
Description string `json:"description" gorm:"type:varchar(256)"`
|
||||
From string `json:"from"`
|
||||
DefaultDownload string `json:"defaultDownload" gorm:"type:varchar(64)"`
|
||||
Status string `json:"status" gorm:"type:varchar(64)"`
|
||||
Message string `json:"message" gorm:"type:varchar(256)"`
|
||||
Version string `json:"version" gorm:"type:varchar(256)"`
|
||||
|
||||
InterruptStep string `json:"interruptStep" gorm:"type:varchar(64)"`
|
||||
RecoverStatus string `json:"recoverStatus" gorm:"type:varchar(64)"`
|
||||
|
||||
@@ -17,6 +17,7 @@ type IAppRepo interface {
|
||||
OrderByRecommend() DBOption
|
||||
GetRecommend() DBOption
|
||||
WithResource(resource string) DBOption
|
||||
WithLikeName(name string) DBOption
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.App, error)
|
||||
GetFirst(opts ...DBOption) (model.App, error)
|
||||
GetBy(opts ...DBOption) ([]model.App, error)
|
||||
@@ -31,6 +32,15 @@ func NewIAppRepo() IAppRepo {
|
||||
return &AppRepo{}
|
||||
}
|
||||
|
||||
func (a AppRepo) WithLikeName(name string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(name) == 0 {
|
||||
return g
|
||||
}
|
||||
return g.Where("name like ? or short_desc_zh like ? or short_desc_en like ?", "%"+name+"%", "%"+name+"%", "%"+name+"%")
|
||||
}
|
||||
}
|
||||
|
||||
func (a AppRepo) WithKey(key string) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("key = ?", key)
|
||||
|
||||
@@ -3,6 +3,7 @@ package repo
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
|
||||
"gorm.io/gorm/clause"
|
||||
|
||||
@@ -170,6 +171,7 @@ type RootInfo struct {
|
||||
Env string `json:"env"`
|
||||
Key string `json:"key"`
|
||||
Version string `json:"version"`
|
||||
AppPath string `json:"app_path"`
|
||||
}
|
||||
|
||||
func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error) {
|
||||
@@ -205,7 +207,7 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
|
||||
if ok {
|
||||
info.Password = password
|
||||
}
|
||||
case "mongodb", "postgresql":
|
||||
case "mongodb", constant.AppPostgresql:
|
||||
user, ok := envMap["PANEL_DB_ROOT_USER"].(string)
|
||||
if ok {
|
||||
info.UserName = user
|
||||
@@ -230,5 +232,7 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
|
||||
info.Param = appInstall.Param
|
||||
info.Version = appInstall.Version
|
||||
info.Key = app.Key
|
||||
appInstall.App = app
|
||||
info.AppPath = appInstall.GetAppPath()
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"gorm.io/gorm"
|
||||
@@ -19,9 +20,11 @@ type IBackupRepo interface {
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
Delete(opts ...DBOption) error
|
||||
DeleteRecord(ctx context.Context, opts ...DBOption) error
|
||||
UpdateRecord(record *model.BackupRecord) error
|
||||
WithByDetailName(detailName string) DBOption
|
||||
WithByFileName(fileName string) DBOption
|
||||
WithByType(backupType string) DBOption
|
||||
WithByCronID(cronjobID uint) DBOption
|
||||
}
|
||||
|
||||
func NewIBackupRepo() IBackupRepo {
|
||||
@@ -105,6 +108,10 @@ func (u *BackupRepo) CreateRecord(record *model.BackupRecord) error {
|
||||
return global.DB.Create(record).Error
|
||||
}
|
||||
|
||||
func (u *BackupRepo) UpdateRecord(record *model.BackupRecord) error {
|
||||
return global.DB.Save(record).Error
|
||||
}
|
||||
|
||||
func (u *BackupRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.BackupAccount{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
@@ -120,3 +127,9 @@ func (u *BackupRepo) Delete(opts ...DBOption) error {
|
||||
func (u *BackupRepo) DeleteRecord(ctx context.Context, opts ...DBOption) error {
|
||||
return getTx(ctx, opts...).Delete(&model.BackupRecord{}).Error
|
||||
}
|
||||
|
||||
func (u *BackupRepo) WithByCronID(cronjobID uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("cronjob_id = ?", cronjobID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ type ICronjobRepo interface {
|
||||
Page(limit, offset int, opts ...DBOption) (int64, []model.Cronjob, error)
|
||||
Create(cronjob *model.Cronjob) error
|
||||
WithByJobID(id int) DBOption
|
||||
WithByBackupID(id uint) DBOption
|
||||
WithByDbName(name string) DBOption
|
||||
WithByDefaultDownload(account string) DBOption
|
||||
WithByRecordDropID(id int) DBOption
|
||||
WithByRecordFile(file string) DBOption
|
||||
Save(id uint, cronjob model.Cronjob) error
|
||||
@@ -28,6 +29,7 @@ type ICronjobRepo interface {
|
||||
Delete(opts ...DBOption) error
|
||||
DeleteRecord(opts ...DBOption) error
|
||||
StartRecords(cronjobID uint, fromLocal bool, targetPath string) model.JobRecords
|
||||
UpdateRecords(id uint, vars map[string]interface{}) error
|
||||
EndRecords(record model.JobRecords, status, message, records string)
|
||||
PageRecords(page, size int, opts ...DBOption) (int64, []model.JobRecords, error)
|
||||
}
|
||||
@@ -116,9 +118,15 @@ func (c *CronjobRepo) WithByJobID(id int) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CronjobRepo) WithByBackupID(id uint) DBOption {
|
||||
func (c *CronjobRepo) WithByDbName(name string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("target_dir_id = ?", id)
|
||||
return g.Where("db_name = ?", name)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CronjobRepo) WithByDefaultDownload(account string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("default_download = ?", account)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +172,10 @@ func (u *CronjobRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.Cronjob{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
|
||||
func (u *CronjobRepo) UpdateRecords(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.JobRecords{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
|
||||
func (u *CronjobRepo) Delete(opts ...DBOption) error {
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type PostgresqlRepo struct{}
|
||||
|
||||
type IPostgresqlRepo interface {
|
||||
Get(opts ...DBOption) (model.DatabasePostgresql, error)
|
||||
WithByPostgresqlName(postgresqlName string) DBOption
|
||||
WithByFrom(from string) DBOption
|
||||
List(opts ...DBOption) ([]model.DatabasePostgresql, error)
|
||||
Page(limit, offset int, opts ...DBOption) (int64, []model.DatabasePostgresql, error)
|
||||
Create(ctx context.Context, postgresql *model.DatabasePostgresql) error
|
||||
Delete(ctx context.Context, opts ...DBOption) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
DeleteLocal(ctx context.Context) error
|
||||
}
|
||||
|
||||
func NewIPostgresqlRepo() IPostgresqlRepo {
|
||||
return &PostgresqlRepo{}
|
||||
}
|
||||
|
||||
func (u *PostgresqlRepo) Get(opts ...DBOption) (model.DatabasePostgresql, error) {
|
||||
var postgresql model.DatabasePostgresql
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
if err := db.First(&postgresql).Error; err != nil {
|
||||
return postgresql, err
|
||||
}
|
||||
|
||||
pass, err := encrypt.StringDecrypt(postgresql.Password)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("decrypt database db %s password failed, err: %v", postgresql.Name, err)
|
||||
}
|
||||
postgresql.Password = pass
|
||||
return postgresql, err
|
||||
}
|
||||
|
||||
func (u *PostgresqlRepo) List(opts ...DBOption) ([]model.DatabasePostgresql, error) {
|
||||
var postgresqls []model.DatabasePostgresql
|
||||
db := global.DB.Model(&model.DatabasePostgresql{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
if err := db.Find(&postgresqls).Error; err != nil {
|
||||
return postgresqls, err
|
||||
}
|
||||
for i := 0; i < len(postgresqls); i++ {
|
||||
pass, err := encrypt.StringDecrypt(postgresqls[i].Password)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("decrypt database db %s password failed, err: %v", postgresqls[i].Name, err)
|
||||
}
|
||||
postgresqls[i].Password = pass
|
||||
}
|
||||
return postgresqls, nil
|
||||
}
|
||||
|
||||
func (u *PostgresqlRepo) Page(page, size int, opts ...DBOption) (int64, []model.DatabasePostgresql, error) {
|
||||
var postgresqls []model.DatabasePostgresql
|
||||
db := global.DB.Model(&model.DatabasePostgresql{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
if err := db.Limit(size).Offset(size * (page - 1)).Find(&postgresqls).Error; err != nil {
|
||||
return count, postgresqls, err
|
||||
}
|
||||
for i := 0; i < len(postgresqls); i++ {
|
||||
pass, err := encrypt.StringDecrypt(postgresqls[i].Password)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("decrypt database db %s password failed, err: %v", postgresqls[i].Name, err)
|
||||
}
|
||||
postgresqls[i].Password = pass
|
||||
}
|
||||
return count, postgresqls, nil
|
||||
}
|
||||
|
||||
func (u *PostgresqlRepo) Create(ctx context.Context, postgresql *model.DatabasePostgresql) error {
|
||||
pass, err := encrypt.StringEncrypt(postgresql.Password)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decrypt database db %s password failed, err: %v", postgresql.Name, err)
|
||||
}
|
||||
postgresql.Password = pass
|
||||
return getTx(ctx).Create(postgresql).Error
|
||||
}
|
||||
|
||||
func (u *PostgresqlRepo) Delete(ctx context.Context, opts ...DBOption) error {
|
||||
return getTx(ctx, opts...).Delete(&model.DatabasePostgresql{}).Error
|
||||
}
|
||||
|
||||
func (u *PostgresqlRepo) DeleteLocal(ctx context.Context) error {
|
||||
return getTx(ctx).Where("`from` = ?", "local").Delete(&model.DatabasePostgresql{}).Error
|
||||
}
|
||||
|
||||
func (u *PostgresqlRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.DatabasePostgresql{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
|
||||
func (u *PostgresqlRepo) WithByPostgresqlName(postgresqlName string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("postgresql_name = ?", postgresqlName)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *PostgresqlRepo) WithByFrom(from string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(from) != 0 {
|
||||
return g.Where("`from` = ?", from)
|
||||
}
|
||||
return g
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
)
|
||||
|
||||
type PHPExtensionsRepo struct {
|
||||
}
|
||||
|
||||
type IPHPExtensionsRepo interface {
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.PHPExtensions, error)
|
||||
Save(extension *model.PHPExtensions) error
|
||||
Create(extension *model.PHPExtensions) error
|
||||
GetFirst(opts ...DBOption) (model.PHPExtensions, error)
|
||||
DeleteBy(opts ...DBOption) error
|
||||
List() ([]model.PHPExtensions, error)
|
||||
}
|
||||
|
||||
func NewIPHPExtensionsRepo() IPHPExtensionsRepo {
|
||||
return &PHPExtensionsRepo{}
|
||||
}
|
||||
|
||||
func (p *PHPExtensionsRepo) Page(page, size int, opts ...DBOption) (int64, []model.PHPExtensions, error) {
|
||||
var (
|
||||
phpExtensions []model.PHPExtensions
|
||||
)
|
||||
db := getDb(opts...).Model(&model.PHPExtensions{})
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&phpExtensions).Error
|
||||
return count, phpExtensions, err
|
||||
}
|
||||
|
||||
func (p *PHPExtensionsRepo) List() ([]model.PHPExtensions, error) {
|
||||
var (
|
||||
phpExtensions []model.PHPExtensions
|
||||
)
|
||||
err := getDb().Model(&model.PHPExtensions{}).Find(&phpExtensions).Error
|
||||
return phpExtensions, err
|
||||
}
|
||||
|
||||
func (p *PHPExtensionsRepo) Save(extension *model.PHPExtensions) error {
|
||||
return getDb().Save(&extension).Error
|
||||
}
|
||||
|
||||
func (p *PHPExtensionsRepo) Create(extension *model.PHPExtensions) error {
|
||||
return getDb().Create(&extension).Error
|
||||
}
|
||||
|
||||
func (p *PHPExtensionsRepo) GetFirst(opts ...DBOption) (model.PHPExtensions, error) {
|
||||
var extension model.PHPExtensions
|
||||
db := getDb(opts...).Model(&model.PHPExtensions{})
|
||||
err := db.First(&extension).Error
|
||||
return extension, err
|
||||
}
|
||||
|
||||
func (p *PHPExtensionsRepo) DeleteBy(opts ...DBOption) error {
|
||||
return getDb(opts...).Delete(&model.PHPExtensions{}).Error
|
||||
}
|
||||
@@ -12,6 +12,7 @@ type IAcmeAccountRepo interface {
|
||||
Save(account model.WebsiteAcmeAccount) error
|
||||
DeleteBy(opts ...DBOption) error
|
||||
WithEmail(email string) DBOption
|
||||
WithType(acType string) DBOption
|
||||
}
|
||||
|
||||
func NewIAcmeAccountRepo() IAcmeAccountRepo {
|
||||
@@ -26,6 +27,11 @@ func (w *WebsiteAcmeAccountRepo) WithEmail(email string) DBOption {
|
||||
return db.Where("email = ?", email)
|
||||
}
|
||||
}
|
||||
func (w *WebsiteAcmeAccountRepo) WithType(acType string) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("type = ?", acType)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WebsiteAcmeAccountRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteAcmeAccount, error) {
|
||||
var accounts []model.WebsiteAcmeAccount
|
||||
|
||||
@@ -53,7 +53,7 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
|
||||
var opts []repo.DBOption
|
||||
opts = append(opts, appRepo.OrderByRecommend())
|
||||
if req.Name != "" {
|
||||
opts = append(opts, commonRepo.WithLikeName(req.Name))
|
||||
opts = append(opts, appRepo.WithLikeName(req.Name))
|
||||
}
|
||||
if req.Type != "" {
|
||||
opts = append(opts, appRepo.WithType(req.Type))
|
||||
@@ -397,7 +397,7 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
||||
}
|
||||
index++
|
||||
}
|
||||
if app.Limit == 0 && appInstall.Name != serviceName {
|
||||
if app.Limit == 0 && appInstall.Name != serviceName && len(servicesMap) == 1 {
|
||||
servicesMap[appInstall.Name] = servicesMap[serviceName]
|
||||
delete(servicesMap, serviceName)
|
||||
serviceName = appInstall.Name
|
||||
@@ -459,7 +459,7 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
||||
if err = runScript(appInstall, "init"); err != nil {
|
||||
return
|
||||
}
|
||||
upApp(appInstall)
|
||||
upApp(appInstall, req.PullImage)
|
||||
}()
|
||||
go updateToolApp(appInstall)
|
||||
return
|
||||
|
||||
@@ -184,9 +184,11 @@ func (a *AppInstallService) LoadConnInfo(req dto.OperationWithNameAndType) (resp
|
||||
if err != nil {
|
||||
return data, nil
|
||||
}
|
||||
data.Username = app.UserName
|
||||
data.Password = app.Password
|
||||
data.ServiceName = app.ServiceName
|
||||
data.Port = app.Port
|
||||
data.ContainerName = app.ContainerName
|
||||
return data, nil
|
||||
}
|
||||
|
||||
@@ -261,7 +263,7 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
|
||||
case constant.Sync:
|
||||
return syncByID(install.ID)
|
||||
case constant.Upgrade:
|
||||
return upgradeInstall(install.ID, req.DetailId, req.Backup)
|
||||
return upgradeInstall(install.ID, req.DetailId, req.Backup, req.PullImage)
|
||||
case constant.Reload:
|
||||
return opNginx(install.ContainerName, constant.NginxReload)
|
||||
default:
|
||||
@@ -431,6 +433,9 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
|
||||
func (a *AppInstallService) GetServices(key string) ([]response.AppService, error) {
|
||||
var res []response.AppService
|
||||
if DatabaseKeys[key] > 0 {
|
||||
if key == constant.AppPostgres {
|
||||
key = constant.AppPostgresql
|
||||
}
|
||||
dbs, _ := databaseRepo.GetList(commonRepo.WithByType(key))
|
||||
if len(dbs) == 0 {
|
||||
return res, nil
|
||||
@@ -781,7 +786,7 @@ func updateInstallInfoInDB(appKey, appName, param string, isRestart bool, value
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
envPath := fmt.Sprintf("%s/%s/%s/.env", constant.AppInstallDir, appKey, appInstall.Name)
|
||||
envPath := fmt.Sprintf("%s/%s/.env", appInstall.AppPath, appInstall.Name)
|
||||
lineBytes, err := os.ReadFile(envPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -790,7 +795,7 @@ func updateInstallInfoInDB(appKey, appName, param string, isRestart bool, value
|
||||
envKey := ""
|
||||
switch param {
|
||||
case "password":
|
||||
if appKey == "mysql" || appKey == "mariadb" {
|
||||
if appKey == "mysql" || appKey == "mariadb" || appKey == "postgresql" {
|
||||
envKey = "PANEL_DB_ROOT_PASSWORD="
|
||||
} else {
|
||||
envKey = "PANEL_REDIS_ROOT_PASSWORD="
|
||||
|
||||
@@ -110,12 +110,13 @@ func checkPortExist(port int) error {
|
||||
}
|
||||
|
||||
var DatabaseKeys = map[string]uint{
|
||||
"mysql": 3306,
|
||||
"mariadb": 3306,
|
||||
"postgresql": 5432,
|
||||
"mongodb": 27017,
|
||||
"redis": 6379,
|
||||
"memcached": 11211,
|
||||
constant.AppMysql: 3306,
|
||||
constant.AppMariaDB: 3306,
|
||||
constant.AppPostgresql: 5432,
|
||||
constant.AppPostgres: 5432,
|
||||
constant.AppMongodb: 27017,
|
||||
constant.AppRedis: 6379,
|
||||
constant.AppMemcached: 11211,
|
||||
}
|
||||
|
||||
var ToolKeys = map[string]uint{
|
||||
@@ -154,7 +155,7 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
|
||||
}
|
||||
|
||||
switch app.Key {
|
||||
case "mysql", "mariadb", "postgresql", "mongodb":
|
||||
case constant.AppMysql, constant.AppMariaDB, constant.AppPostgresql, constant.AppMongodb:
|
||||
if password, ok := params["PANEL_DB_ROOT_PASSWORD"]; ok {
|
||||
if password != "" {
|
||||
database.Password = password.(string)
|
||||
@@ -176,7 +177,7 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
|
||||
|
||||
}
|
||||
}
|
||||
case "redis":
|
||||
case constant.AppRedis:
|
||||
if password, ok := params["PANEL_REDIS_ROOT_PASSWORD"]; ok {
|
||||
if password != "" {
|
||||
authParam := dto.RedisAuthParam{
|
||||
@@ -233,28 +234,55 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
|
||||
}
|
||||
var resourceId uint
|
||||
if dbConfig.DbName != "" && dbConfig.DbUser != "" && dbConfig.Password != "" {
|
||||
iMysqlRepo := repo.NewIMysqlRepo()
|
||||
oldMysqlDb, _ := iMysqlRepo.Get(commonRepo.WithByName(dbConfig.DbName), iMysqlRepo.WithByFrom(constant.ResourceLocal))
|
||||
resourceId = oldMysqlDb.ID
|
||||
if oldMysqlDb.ID > 0 {
|
||||
if oldMysqlDb.Username != dbConfig.DbUser || oldMysqlDb.Password != dbConfig.Password {
|
||||
return buserr.New(constant.ErrDbUserNotValid)
|
||||
switch database.Type {
|
||||
case constant.AppPostgresql, constant.AppPostgres:
|
||||
iPostgresqlRepo := repo.NewIPostgresqlRepo()
|
||||
oldPostgresqlDb, _ := iPostgresqlRepo.Get(commonRepo.WithByName(dbConfig.DbName), iPostgresqlRepo.WithByFrom(constant.ResourceLocal))
|
||||
resourceId = oldPostgresqlDb.ID
|
||||
if oldPostgresqlDb.ID > 0 {
|
||||
if oldPostgresqlDb.Username != dbConfig.DbUser || oldPostgresqlDb.Password != dbConfig.Password {
|
||||
return buserr.New(constant.ErrDbUserNotValid)
|
||||
}
|
||||
} else {
|
||||
var createPostgresql dto.PostgresqlDBCreate
|
||||
createPostgresql.Name = dbConfig.DbName
|
||||
createPostgresql.Username = dbConfig.DbUser
|
||||
createPostgresql.Database = database.Name
|
||||
createPostgresql.Format = "UTF8"
|
||||
createPostgresql.Password = dbConfig.Password
|
||||
createPostgresql.From = database.From
|
||||
createPostgresql.SuperUser = true
|
||||
pgdb, err := NewIPostgresqlService().Create(ctx, createPostgresql)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resourceId = pgdb.ID
|
||||
}
|
||||
} else {
|
||||
var createMysql dto.MysqlDBCreate
|
||||
createMysql.Name = dbConfig.DbName
|
||||
createMysql.Username = dbConfig.DbUser
|
||||
createMysql.Database = database.Name
|
||||
createMysql.Format = "utf8mb4"
|
||||
createMysql.Permission = "%"
|
||||
createMysql.Password = dbConfig.Password
|
||||
createMysql.From = database.From
|
||||
mysqldb, err := NewIMysqlService().Create(ctx, createMysql)
|
||||
if err != nil {
|
||||
return err
|
||||
case constant.AppMysql, constant.AppMariaDB:
|
||||
iMysqlRepo := repo.NewIMysqlRepo()
|
||||
oldMysqlDb, _ := iMysqlRepo.Get(commonRepo.WithByName(dbConfig.DbName), iMysqlRepo.WithByFrom(constant.ResourceLocal))
|
||||
resourceId = oldMysqlDb.ID
|
||||
if oldMysqlDb.ID > 0 {
|
||||
if oldMysqlDb.Username != dbConfig.DbUser || oldMysqlDb.Password != dbConfig.Password {
|
||||
return buserr.New(constant.ErrDbUserNotValid)
|
||||
}
|
||||
} else {
|
||||
var createMysql dto.MysqlDBCreate
|
||||
createMysql.Name = dbConfig.DbName
|
||||
createMysql.Username = dbConfig.DbUser
|
||||
createMysql.Database = database.Name
|
||||
createMysql.Format = "utf8mb4"
|
||||
createMysql.Permission = "%"
|
||||
createMysql.Password = dbConfig.Password
|
||||
createMysql.From = database.From
|
||||
mysqldb, err := NewIMysqlService().Create(ctx, createMysql)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resourceId = mysqldb.ID
|
||||
}
|
||||
resourceId = mysqldb.ID
|
||||
}
|
||||
|
||||
}
|
||||
var installResource model.AppInstallResource
|
||||
installResource.ResourceId = resourceId
|
||||
@@ -338,6 +366,8 @@ func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete b
|
||||
_ = websiteDomainRepo.DeleteAll(ctx)
|
||||
case constant.AppMysql, constant.AppMariaDB:
|
||||
_ = mysqlRepo.Delete(ctx, mysqlRepo.WithByMysqlName(install.Name))
|
||||
case constant.AppPostgresql:
|
||||
_ = postgresqlRepo.Delete(ctx, postgresqlRepo.WithByPostgresqlName(install.Name))
|
||||
}
|
||||
|
||||
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType("app"), commonRepo.WithByName(install.App.Key), backupRepo.WithByDetailName(install.Name))
|
||||
@@ -364,32 +394,51 @@ func deleteLink(ctx context.Context, install *model.AppInstall, deleteDB bool, f
|
||||
return nil
|
||||
}
|
||||
for _, re := range resources {
|
||||
mysqlService := NewIMysqlService()
|
||||
if (re.Key == constant.AppMysql || re.Key == constant.AppMariaDB) && deleteDB {
|
||||
database, _ := mysqlRepo.Get(commonRepo.WithByID(re.ResourceId))
|
||||
if reflect.DeepEqual(database, model.DatabaseMysql{}) {
|
||||
continue
|
||||
}
|
||||
if err := mysqlService.Delete(ctx, dto.MysqlDBDelete{
|
||||
ID: database.ID,
|
||||
ForceDelete: forceDelete,
|
||||
DeleteBackup: deleteBackup,
|
||||
Type: re.Key,
|
||||
Database: database.MysqlName,
|
||||
}); err != nil && !forceDelete {
|
||||
return err
|
||||
if deleteDB {
|
||||
switch re.Key {
|
||||
case constant.AppMysql, constant.AppMariaDB:
|
||||
mysqlService := NewIMysqlService()
|
||||
database, _ := mysqlRepo.Get(commonRepo.WithByID(re.ResourceId))
|
||||
if reflect.DeepEqual(database, model.DatabaseMysql{}) {
|
||||
continue
|
||||
}
|
||||
if err := mysqlService.Delete(ctx, dto.MysqlDBDelete{
|
||||
ID: database.ID,
|
||||
ForceDelete: forceDelete,
|
||||
DeleteBackup: deleteBackup,
|
||||
Type: re.Key,
|
||||
Database: database.MysqlName,
|
||||
}); err != nil && !forceDelete {
|
||||
return err
|
||||
}
|
||||
case constant.AppPostgresql:
|
||||
pgsqlService := NewIPostgresqlService()
|
||||
database, _ := postgresqlRepo.Get(commonRepo.WithByID(re.ResourceId))
|
||||
if reflect.DeepEqual(database, model.DatabasePostgresql{}) {
|
||||
continue
|
||||
}
|
||||
if err := pgsqlService.Delete(ctx, dto.PostgresqlDBDelete{
|
||||
ID: database.ID,
|
||||
ForceDelete: forceDelete,
|
||||
DeleteBackup: deleteBackup,
|
||||
Type: re.Key,
|
||||
Database: database.PostgresqlName,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||
}
|
||||
|
||||
func upgradeInstall(installId uint, detailId uint, backup bool) error {
|
||||
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId))
|
||||
func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error {
|
||||
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
detail, err := appDetailRepo.GetFirst(commonRepo.WithByID(detailId))
|
||||
detail, err := appDetailRepo.GetFirst(commonRepo.WithByID(detailID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -443,23 +492,6 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
|
||||
detailDir = path.Join(constant.ResourceDir, "apps", "local", strings.TrimPrefix(install.App.Key, "local"), detail.Version)
|
||||
}
|
||||
|
||||
images, err := composeV2.GetDockerComposeImages([]byte(detail.DockerCompose))
|
||||
if err != nil {
|
||||
upErr = err
|
||||
return
|
||||
}
|
||||
dockerCli, err := composeV2.NewClient()
|
||||
if err != nil {
|
||||
upErr = err
|
||||
return
|
||||
}
|
||||
for _, image := range images {
|
||||
if err = dockerCli.PullImage(image, true); err != nil {
|
||||
upErr = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
command := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rn %s/* %s || true", detailDir, install.GetPath()))
|
||||
stdout, _ := command.CombinedOutput()
|
||||
if stdout != nil {
|
||||
@@ -524,7 +556,7 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
|
||||
|
||||
install.DockerCompose = string(composeByte)
|
||||
install.Version = detail.Version
|
||||
install.AppDetailId = detailId
|
||||
install.AppDetailId = detailID
|
||||
|
||||
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
@@ -543,6 +575,31 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
|
||||
return
|
||||
}
|
||||
|
||||
content, err := fileOp.GetContent(install.GetEnvPath())
|
||||
if err != nil {
|
||||
upErr = err
|
||||
return
|
||||
}
|
||||
|
||||
if pullImage {
|
||||
images, err := composeV2.GetDockerComposeImages(install.Name, content, []byte(detail.DockerCompose))
|
||||
if err != nil {
|
||||
upErr = err
|
||||
return
|
||||
}
|
||||
dockerCli, err := composeV2.NewClient()
|
||||
if err != nil {
|
||||
upErr = err
|
||||
return
|
||||
}
|
||||
for _, image := range images {
|
||||
if err = dockerCli.PullImage(image, true); err != nil {
|
||||
upErr = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if upErr = fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); upErr != nil {
|
||||
return
|
||||
}
|
||||
@@ -784,14 +841,14 @@ func checkContainerNameIsExist(containerName, appDir string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func upApp(appInstall *model.AppInstall) {
|
||||
func upApp(appInstall *model.AppInstall, pullImages bool) {
|
||||
upProject := func(appInstall *model.AppInstall) (err error) {
|
||||
if err == nil {
|
||||
var (
|
||||
out string
|
||||
errMsg string
|
||||
)
|
||||
if appInstall.App.Type != "php" {
|
||||
if pullImages && appInstall.App.Type != "php" {
|
||||
out, err = compose.Pull(appInstall.GetComposePath())
|
||||
if err != nil {
|
||||
if out != "" {
|
||||
@@ -806,6 +863,7 @@ func upApp(appInstall *model.AppInstall) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
out, err = compose.Up(appInstall.GetComposePath())
|
||||
if err != nil {
|
||||
if out != "" {
|
||||
@@ -1008,7 +1066,7 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]respons
|
||||
}
|
||||
var versions []string
|
||||
for _, detail := range details {
|
||||
if detail.IgnoreUpgrade {
|
||||
if detail.IgnoreUpgrade || installed.Version == "latest" {
|
||||
continue
|
||||
}
|
||||
if common.IsCrossVersion(installed.Version, detail.Version) && !app.CrossVersionUpdate {
|
||||
|
||||
@@ -109,6 +109,10 @@ func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
httpsSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lifeTime, err := strconv.Atoi(setting.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -129,7 +133,7 @@ func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) (
|
||||
sessionUser, err := global.SESSION.Get(sID)
|
||||
if err != nil {
|
||||
sID = uuid.New().String()
|
||||
c.SetCookie(constant.SessionName, sID, 0, "", "", false, false)
|
||||
c.SetCookie(constant.SessionName, sID, 0, "", "", httpsSetting.Value == "enable", false)
|
||||
err := global.SESSION.Set(sID, sessionUser, lifeTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -144,9 +148,13 @@ func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) (
|
||||
}
|
||||
|
||||
func (u *AuthService) LogOut(c *gin.Context) error {
|
||||
httpsSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sID, _ := c.Cookie(constant.SessionName)
|
||||
if sID != "" {
|
||||
c.SetCookie(constant.SessionName, sID, -1, "", "", false, false)
|
||||
c.SetCookie(constant.SessionName, sID, -1, "", "", httpsSetting.Value == "enable", false)
|
||||
err := global.SESSION.Delete(sID)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
+198
-103
@@ -6,12 +6,12 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage/client"
|
||||
fileUtils "github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
@@ -29,7 +30,8 @@ type BackupService struct{}
|
||||
type IBackupService interface {
|
||||
List() ([]dto.BackupInfo, error)
|
||||
SearchRecordsWithPage(search dto.RecordSearch) (int64, []dto.BackupRecords, error)
|
||||
LoadOneDriveInfo() (string, error)
|
||||
SearchRecordsByCronjobWithPage(search dto.RecordSearchByCronjob) (int64, []dto.BackupRecords, error)
|
||||
LoadOneDriveInfo() (dto.OneDriveInfo, error)
|
||||
DownloadRecord(info dto.DownloadRecord) (string, error)
|
||||
Create(backupDto dto.BackupOperate) error
|
||||
GetBuckets(backupDto dto.ForBuckets) ([]interface{}, error)
|
||||
@@ -38,11 +40,14 @@ type IBackupService interface {
|
||||
BatchDeleteRecord(ids []uint) error
|
||||
NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error)
|
||||
|
||||
ListFiles(req dto.BackupSearchFile) ([]string, error)
|
||||
ListFiles(req dto.BackupSearchFile) []string
|
||||
|
||||
MysqlBackup(db dto.CommonBackup) error
|
||||
PostgresqlBackup(db dto.CommonBackup) error
|
||||
MysqlRecover(db dto.CommonRecover) error
|
||||
PostgresqlRecover(db dto.CommonRecover) error
|
||||
MysqlRecoverByUpload(req dto.CommonRecover) error
|
||||
PostgresqlRecoverByUpload(req dto.CommonRecover) error
|
||||
|
||||
RedisBackup() error
|
||||
RedisRecover(db dto.CommonRecover) error
|
||||
@@ -52,6 +57,8 @@ type IBackupService interface {
|
||||
|
||||
AppBackup(db dto.CommonBackup) error
|
||||
AppRecover(req dto.CommonRecover) error
|
||||
|
||||
Run()
|
||||
}
|
||||
|
||||
func NewIBackupService() IBackupService {
|
||||
@@ -81,63 +88,72 @@ func (u *BackupService) SearchRecordsWithPage(search dto.RecordSearch) (int64, [
|
||||
commonRepo.WithByType(search.Type),
|
||||
backupRepo.WithByDetailName(search.DetailName),
|
||||
)
|
||||
|
||||
var datas []dto.BackupRecords
|
||||
clientMap := make(map[string]loadSizeHelper)
|
||||
for i := 0; i < len(records); i++ {
|
||||
var item dto.BackupRecords
|
||||
if err := copier.Copy(&item, &records[i]); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
itemPath := path.Join(records[i].FileDir, records[i].FileName)
|
||||
if records[i].Source == "LOCAL" {
|
||||
fileInfo, _ := os.Stat(itemPath)
|
||||
item.Size = fileInfo.Size()
|
||||
datas = append(datas, item)
|
||||
continue
|
||||
}
|
||||
if _, ok := clientMap[records[i].Source]; !ok {
|
||||
backup, err := backupRepo.Get(commonRepo.WithByType(records[i].Source))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load backup model %s from db failed, err: %v", records[i].Source, err)
|
||||
return total, datas, err
|
||||
}
|
||||
client, err := u.NewClient(&backup)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load backup client %s from db failed, err: %v", records[i].Source, err)
|
||||
return total, datas, err
|
||||
}
|
||||
item.Size, _ = client.Size(path.Join(strings.TrimLeft(backup.BackupPath, "/"), itemPath))
|
||||
datas = append(datas, item)
|
||||
clientMap[records[i].Source] = loadSizeHelper{backupPath: strings.TrimLeft(backup.BackupPath, "/"), client: client}
|
||||
continue
|
||||
}
|
||||
item.Size, _ = clientMap[records[i].Source].client.Size(path.Join(clientMap[records[i].Source].backupPath, itemPath))
|
||||
datas = append(datas, item)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
datas, err := u.loadRecordSize(records)
|
||||
sort.Slice(datas, func(i, j int) bool {
|
||||
return datas[i].CreatedAt.After(datas[j].CreatedAt)
|
||||
})
|
||||
return total, datas, err
|
||||
}
|
||||
|
||||
func (u *BackupService) SearchRecordsByCronjobWithPage(search dto.RecordSearchByCronjob) (int64, []dto.BackupRecords, error) {
|
||||
total, records, err := backupRepo.PageRecord(
|
||||
search.Page, search.PageSize,
|
||||
commonRepo.WithOrderBy("created_at desc"),
|
||||
backupRepo.WithByCronID(search.CronjobID),
|
||||
)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
datas, err := u.loadRecordSize(records)
|
||||
sort.Slice(datas, func(i, j int) bool {
|
||||
return datas[i].CreatedAt.After(datas[j].CreatedAt)
|
||||
})
|
||||
return total, datas, err
|
||||
}
|
||||
|
||||
type loadSizeHelper struct {
|
||||
isOk bool
|
||||
backupPath string
|
||||
client cloud_storage.CloudStorageClient
|
||||
}
|
||||
|
||||
func (u *BackupService) LoadOneDriveInfo() (string, error) {
|
||||
OneDriveID, err := settingRepo.Get(settingRepo.WithByKey("OneDriveID"))
|
||||
func (u *BackupService) LoadOneDriveInfo() (dto.OneDriveInfo, error) {
|
||||
var data dto.OneDriveInfo
|
||||
data.RedirectUri = constant.OneDriveRedirectURI
|
||||
clientID, err := settingRepo.Get(settingRepo.WithByKey("OneDriveID"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
return data, err
|
||||
}
|
||||
idItem, err := base64.StdEncoding.DecodeString(OneDriveID.Value)
|
||||
idItem, err := base64.StdEncoding.DecodeString(clientID.Value)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return data, err
|
||||
}
|
||||
return string(idItem), err
|
||||
data.ClientID = string(idItem)
|
||||
clientSecret, err := settingRepo.Get(settingRepo.WithByKey("OneDriveSc"))
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
secretItem, err := base64.StdEncoding.DecodeString(clientSecret.Value)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
data.ClientSecret = string(secretItem)
|
||||
|
||||
return data, err
|
||||
}
|
||||
|
||||
func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error) {
|
||||
if info.Source == "LOCAL" {
|
||||
return info.FileDir + "/" + info.FileName, nil
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path.Join(localDir, info.FileDir, info.FileName), nil
|
||||
}
|
||||
backup, _ := backupRepo.Get(commonRepo.WithByType(info.Source))
|
||||
if backup.ID == 0 {
|
||||
@@ -200,6 +216,9 @@ func (u *BackupService) Create(req dto.BackupOperate) error {
|
||||
return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err)
|
||||
}
|
||||
}
|
||||
if backup.Type == constant.OneDrive {
|
||||
StartRefreshOneDriveToken()
|
||||
}
|
||||
if err := backupRepo.Create(&backup); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -227,7 +246,14 @@ func (u *BackupService) GetBuckets(backupDto dto.ForBuckets) ([]interface{}, err
|
||||
}
|
||||
|
||||
func (u *BackupService) Delete(id uint) error {
|
||||
cronjobs, _ := cronjobRepo.List(cronjobRepo.WithByBackupID(id))
|
||||
backup, _ := backupRepo.Get(commonRepo.WithByID(id))
|
||||
if backup.ID == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
if backup.Type == constant.OneDrive {
|
||||
global.Cron.Remove(global.OneDriveCronID)
|
||||
}
|
||||
cronjobs, _ := cronjobRepo.List(cronjobRepo.WithByDefaultDownload(backup.Type))
|
||||
if len(cronjobs) != 0 {
|
||||
return buserr.New(constant.ErrBackupInUsed)
|
||||
}
|
||||
@@ -240,22 +266,16 @@ func (u *BackupService) BatchDeleteRecord(ids []uint) error {
|
||||
return err
|
||||
}
|
||||
for _, record := range records {
|
||||
if record.Source == "LOCAL" {
|
||||
if err := os.Remove(record.FileDir + "/" + record.FileName); err != nil {
|
||||
global.LOG.Errorf("remove file %s failed, err: %v", record.FileDir+record.FileName, err)
|
||||
}
|
||||
} else {
|
||||
backupAccount, err := backupRepo.Get(commonRepo.WithByType(record.Source))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := u.NewClient(&backupAccount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = client.Delete(record.FileDir + record.FileName); err != nil {
|
||||
global.LOG.Errorf("remove file %s from %s failed, err: %v", record.FileDir+record.FileName, record.Source, err)
|
||||
}
|
||||
backupAccount, err := backupRepo.Get(commonRepo.WithByType(record.Source))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := u.NewClient(&backupAccount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = client.Delete(path.Join(record.FileDir, record.FileName)); err != nil {
|
||||
global.LOG.Errorf("remove file %s from %s failed, err: %v", path.Join(record.FileDir, record.FileName), record.Source, err)
|
||||
}
|
||||
}
|
||||
return backupRepo.DeleteRecord(context.Background(), commonRepo.WithIdsIn(ids))
|
||||
@@ -308,27 +328,29 @@ func (u *BackupService) Update(req dto.BackupOperate) error {
|
||||
if backup.Type == "LOCAL" {
|
||||
if dir, ok := varMap["dir"]; ok {
|
||||
if dirStr, isStr := dir.(string); isStr {
|
||||
if strings.HasSuffix(dirStr, "/") {
|
||||
if strings.HasSuffix(dirStr, "/") && dirStr != "/" {
|
||||
dirStr = dirStr[:strings.LastIndex(dirStr, "/")]
|
||||
}
|
||||
if err := copyDir(oldDir, dirStr); err != nil {
|
||||
_ = backupRepo.Update(req.ID, (map[string]interface{}{"vars": oldVars}))
|
||||
return err
|
||||
}
|
||||
global.CONF.System.Backup = dirStr
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *BackupService) ListFiles(req dto.BackupSearchFile) ([]string, error) {
|
||||
func (u *BackupService) ListFiles(req dto.BackupSearchFile) []string {
|
||||
var datas []string
|
||||
backup, err := backupRepo.Get(backupRepo.WithByType(req.Type))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return datas
|
||||
}
|
||||
client, err := u.NewClient(&backup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return datas
|
||||
}
|
||||
prefix := "system_snapshot"
|
||||
if len(backup.BackupPath) != 0 {
|
||||
@@ -336,15 +358,15 @@ func (u *BackupService) ListFiles(req dto.BackupSearchFile) ([]string, error) {
|
||||
}
|
||||
files, err := client.ListObjects(prefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
global.LOG.Debugf("load files from %s failed, err: %v", req.Type, err)
|
||||
return datas
|
||||
}
|
||||
var datas []string
|
||||
for _, file := range files {
|
||||
if len(file) != 0 {
|
||||
datas = append(datas, path.Base(file))
|
||||
}
|
||||
}
|
||||
return datas, nil
|
||||
return datas
|
||||
}
|
||||
|
||||
func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error) {
|
||||
@@ -352,9 +374,6 @@ func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.Cl
|
||||
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if backup.Type == "LOCAL" {
|
||||
return nil, errors.New("not support")
|
||||
}
|
||||
varMap["bucket"] = backup.Bucket
|
||||
switch backup.Type {
|
||||
case constant.Sftp, constant.WebDAV:
|
||||
@@ -382,6 +401,15 @@ func (u *BackupService) loadByType(accountType string, accounts []model.BackupAc
|
||||
if err := copier.Copy(&item, &account); err != nil {
|
||||
global.LOG.Errorf("copy backup account to dto backup info failed, err: %v", err)
|
||||
}
|
||||
if account.Type == constant.OneDrive {
|
||||
varMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(item.Vars), &varMap); err != nil {
|
||||
return dto.BackupInfo{Type: accountType}
|
||||
}
|
||||
delete(varMap, "refresh_token")
|
||||
itemVars, _ := json.Marshal(varMap)
|
||||
item.Vars = string(itemVars)
|
||||
}
|
||||
return item
|
||||
}
|
||||
}
|
||||
@@ -393,48 +421,68 @@ func (u *BackupService) loadAccessToken(backup *model.BackupAccount) error {
|
||||
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
|
||||
return fmt.Errorf("unmarshal backup vars failed, err: %v", err)
|
||||
}
|
||||
|
||||
data := url.Values{}
|
||||
data.Set("client_id", global.CONF.System.OneDriveID)
|
||||
data.Set("client_secret", global.CONF.System.OneDriveSc)
|
||||
data.Set("grant_type", "authorization_code")
|
||||
data.Set("code", varMap["code"].(string))
|
||||
data.Set("redirect_uri", constant.OneDriveRedirectURI)
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest("POST", "https://login.microsoftonline.com/common/oauth2/v2.0/token", strings.NewReader(data.Encode()))
|
||||
token, refreshToken, err := client.RefreshToken("authorization_code", varMap)
|
||||
if err != nil {
|
||||
return fmt.Errorf("new http post client for access token failed, err: %v", err)
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("request for access token failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
delete(varMap, "code")
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read data from response body failed, err: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
token := map[string]interface{}{}
|
||||
if err := json.Unmarshal(respBody, &token); err != nil {
|
||||
return fmt.Errorf("unmarshal data from response body failed, err: %v", err)
|
||||
}
|
||||
accessToken, ok := token["refresh_token"].(string)
|
||||
if !ok {
|
||||
return errors.New("no such access token in response")
|
||||
}
|
||||
|
||||
backup.Credential = token
|
||||
varMap["refresh_status"] = constant.StatusSuccess
|
||||
varMap["refresh_time"] = time.Now().Format("2006-01-02 15:04:05")
|
||||
varMap["refresh_token"] = refreshToken
|
||||
itemVars, err := json.Marshal(varMap)
|
||||
if err != nil {
|
||||
return fmt.Errorf("json marshal var map failed, err: %v", err)
|
||||
}
|
||||
backup.Credential = accessToken
|
||||
backup.Vars = string(itemVars)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *BackupService) loadRecordSize(records []model.BackupRecord) ([]dto.BackupRecords, error) {
|
||||
var datas []dto.BackupRecords
|
||||
clientMap := make(map[string]loadSizeHelper)
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < len(records); i++ {
|
||||
var item dto.BackupRecords
|
||||
if err := copier.Copy(&item, &records[i]); err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
itemPath := path.Join(records[i].FileDir, records[i].FileName)
|
||||
if _, ok := clientMap[records[i].Source]; !ok {
|
||||
backup, err := backupRepo.Get(commonRepo.WithByType(records[i].Source))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load backup model %s from db failed, err: %v", records[i].Source, err)
|
||||
clientMap[records[i].Source] = loadSizeHelper{}
|
||||
datas = append(datas, item)
|
||||
continue
|
||||
}
|
||||
client, err := u.NewClient(&backup)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load backup client %s from db failed, err: %v", records[i].Source, err)
|
||||
clientMap[records[i].Source] = loadSizeHelper{}
|
||||
datas = append(datas, item)
|
||||
continue
|
||||
}
|
||||
item.Size, _ = client.Size(path.Join(strings.TrimLeft(backup.BackupPath, "/"), itemPath))
|
||||
datas = append(datas, item)
|
||||
clientMap[records[i].Source] = loadSizeHelper{backupPath: strings.TrimLeft(backup.BackupPath, "/"), client: client, isOk: true}
|
||||
continue
|
||||
}
|
||||
if clientMap[records[i].Source].isOk {
|
||||
wg.Add(1)
|
||||
go func(index int) {
|
||||
item.Size, _ = clientMap[records[index].Source].client.Size(path.Join(clientMap[records[index].Source].backupPath, itemPath))
|
||||
datas = append(datas, item)
|
||||
wg.Done()
|
||||
}(i)
|
||||
} else {
|
||||
datas = append(datas, item)
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
return datas, nil
|
||||
}
|
||||
|
||||
func loadLocalDir() (string, error) {
|
||||
backup, err := backupRepo.Get(commonRepo.WithByType("LOCAL"))
|
||||
if err != nil {
|
||||
@@ -516,3 +564,50 @@ func (u *BackupService) checkBackupConn(backup *model.BackupAccount) (bool, erro
|
||||
targetPath := strings.TrimPrefix(path.Join(backup.BackupPath, "test/1panel"), "/")
|
||||
return client.Upload(fileItem, targetPath)
|
||||
}
|
||||
|
||||
func StartRefreshOneDriveToken() {
|
||||
service := NewIBackupService()
|
||||
oneDriveCronID, err := global.Cron.AddJob("0 * * * *", service)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("can not add OneDrive corn job: %s", err.Error())
|
||||
return
|
||||
}
|
||||
global.OneDriveCronID = oneDriveCronID
|
||||
}
|
||||
|
||||
func (u *BackupService) Run() {
|
||||
var backupItem model.BackupAccount
|
||||
_ = global.DB.Where("`type` = ?", "OneDrive").First(&backupItem)
|
||||
if backupItem.ID == 0 {
|
||||
return
|
||||
}
|
||||
if len(backupItem.Credential) == 0 {
|
||||
global.LOG.Error("OneDrive configuration lacks token information, please rebind.")
|
||||
return
|
||||
}
|
||||
global.LOG.Info("start to refresh token of OneDrive ...")
|
||||
varMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(backupItem.Vars), &varMap); err != nil {
|
||||
global.LOG.Errorf("Failed to refresh OneDrive token, please retry, err: %v", err)
|
||||
return
|
||||
}
|
||||
token, refreshToken, err := client.RefreshToken("refresh_token", varMap)
|
||||
varMap["refresh_status"] = constant.StatusSuccess
|
||||
varMap["refresh_time"] = time.Now().Format("2006-01-02 15:04:05")
|
||||
if err != nil {
|
||||
varMap["refresh_status"] = constant.StatusFailed
|
||||
varMap["refresh_msg"] = err.Error()
|
||||
global.LOG.Errorf("Failed to refresh OneDrive token, please retry, err: %v", err)
|
||||
return
|
||||
}
|
||||
varMap["refresh_token"] = refreshToken
|
||||
|
||||
varsItem, _ := json.Marshal(varMap)
|
||||
_ = global.DB.Model(&model.BackupAccount{}).
|
||||
Where("id = ?", backupItem.ID).
|
||||
Updates(map[string]interface{}{
|
||||
"credential": token,
|
||||
"vars": varsItem,
|
||||
}).Error
|
||||
global.LOG.Info("Successfully refreshed OneDrive token.")
|
||||
}
|
||||
|
||||
@@ -10,10 +10,13 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/pkg/errors"
|
||||
@@ -33,10 +36,10 @@ func (u *BackupService) AppBackup(req dto.CommonBackup) error {
|
||||
return err
|
||||
}
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
itemDir := fmt.Sprintf("app/%s/%s", req.Name, req.DetailName)
|
||||
backupDir := path.Join(localDir, itemDir)
|
||||
|
||||
backupDir := path.Join(localDir, fmt.Sprintf("app/%s/%s", req.Name, req.DetailName))
|
||||
|
||||
fileName := fmt.Sprintf("%s_%s.tar.gz", req.DetailName, timeNow)
|
||||
fileName := fmt.Sprintf("%s_%s.tar.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
|
||||
if err := handleAppBackup(&install, backupDir, fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -47,7 +50,7 @@ func (u *BackupService) AppBackup(req dto.CommonBackup) error {
|
||||
DetailName: req.DetailName,
|
||||
Source: "LOCAL",
|
||||
BackupType: "LOCAL",
|
||||
FileDir: backupDir,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
}
|
||||
|
||||
@@ -70,7 +73,7 @@ func (u *BackupService) AppRecover(req dto.CommonRecover) error {
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(req.File) {
|
||||
return errors.New(fmt.Sprintf("%s file is not exist", req.File))
|
||||
return buserr.WithName("ErrFileNotFound", req.File)
|
||||
}
|
||||
if _, err := compose.Down(install.GetComposePath()); err != nil {
|
||||
return err
|
||||
@@ -106,7 +109,8 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
|
||||
|
||||
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||
for _, resource := range resources {
|
||||
if resource.Key == "mysql" || resource.Key == "mariadb" {
|
||||
switch resource.Key {
|
||||
case constant.AppMysql, constant.AppMariaDB:
|
||||
db, err := mysqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -114,6 +118,14 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
|
||||
if err := handleMysqlBackup(db.MysqlName, db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", install.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
case constant.AppPostgresql:
|
||||
db, err := postgresqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handlePostgresqlBackup(db.PostgresqlName, db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", install.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +202,21 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
return err
|
||||
}
|
||||
}
|
||||
if database.Type == "mysql" || database.Type == "mariadb" {
|
||||
switch database.Type {
|
||||
case constant.AppPostgresql:
|
||||
db, err := postgresqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handlePostgresqlRecover(dto.CommonRecover{
|
||||
Name: database.Name,
|
||||
DetailName: db.Name,
|
||||
File: fmt.Sprintf("%s/%s.sql.gz", tmpPath, install.Name),
|
||||
}, true); err != nil {
|
||||
global.LOG.Errorf("handle recover from sql.gz failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
case constant.AppMysql, constant.AppMariaDB:
|
||||
db, err := mysqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -234,7 +260,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
_ = fileOp.DeleteDir(backPath)
|
||||
|
||||
if len(newEnvFile) != 0 {
|
||||
envPath := fmt.Sprintf("%s/%s/%s/.env", constant.AppInstallDir, install.App.Key, install.Name)
|
||||
envPath := fmt.Sprintf("%s/%s/.env", install.GetAppPath(), install.Name)
|
||||
file, err := os.OpenFile(envPath, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -278,6 +304,10 @@ func reCreateDB(dbID uint, database model.Database, oldEnv string) (*model.Datab
|
||||
Password: oldPassword,
|
||||
Permission: "%",
|
||||
})
|
||||
cronjobs, _ := cronjobRepo.List(cronjobRepo.WithByDbName(fmt.Sprintf("%v", dbID)))
|
||||
for _, job := range cronjobs {
|
||||
_ = cronjobRepo.Update(job.ID, map[string]interface{}{"db_name": fmt.Sprintf("%v", createDB.ID)})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, envMap, err
|
||||
}
|
||||
|
||||
@@ -8,12 +8,14 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/mysql/client"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
|
||||
@@ -23,8 +25,9 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
|
||||
}
|
||||
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
targetDir := path.Join(localDir, fmt.Sprintf("database/%s/%s/%s", req.Type, req.Name, req.DetailName))
|
||||
fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow)
|
||||
itemDir := fmt.Sprintf("database/%s/%s/%s", req.Type, req.Name, req.DetailName)
|
||||
targetDir := path.Join(localDir, itemDir)
|
||||
fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
|
||||
|
||||
if err := handleMysqlBackup(req.Name, req.DetailName, targetDir, fileName); err != nil {
|
||||
return err
|
||||
@@ -36,7 +39,7 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
|
||||
DetailName: req.DetailName,
|
||||
Source: "LOCAL",
|
||||
BackupType: "LOCAL",
|
||||
FileDir: targetDir,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
@@ -125,7 +128,7 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
|
||||
isOk := false
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(req.File) {
|
||||
return errors.New(fmt.Sprintf("%s file is not exist", req.File))
|
||||
return buserr.WithName("ErrFileNotFound", req.File)
|
||||
}
|
||||
dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(req.DetailName), mysqlRepo.WithByMysqlName(req.Name))
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
pgclient "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
|
||||
)
|
||||
|
||||
func (u *BackupService) PostgresqlBackup(req dto.CommonBackup) error {
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
itemDir := fmt.Sprintf("database/%s/%s/%s", req.Type, req.Name, req.DetailName)
|
||||
targetDir := path.Join(localDir, itemDir)
|
||||
fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
|
||||
|
||||
if err := handlePostgresqlBackup(req.Name, req.DetailName, targetDir, fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
record := &model.BackupRecord{
|
||||
Type: req.Type,
|
||||
Name: req.Name,
|
||||
DetailName: req.DetailName,
|
||||
Source: "LOCAL",
|
||||
BackupType: "LOCAL",
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (u *BackupService) PostgresqlRecover(req dto.CommonRecover) error {
|
||||
if err := handlePostgresqlRecover(req, false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *BackupService) PostgresqlRecoverByUpload(req dto.CommonRecover) error {
|
||||
file := req.File
|
||||
fileName := path.Base(req.File)
|
||||
if strings.HasSuffix(fileName, ".tar.gz") {
|
||||
fileNameItem := time.Now().Format("20060102150405")
|
||||
dstDir := fmt.Sprintf("%s/%s", path.Dir(req.File), fileNameItem)
|
||||
if _, err := os.Stat(dstDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dstDir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("mkdir %s failed, err: %v", dstDir, err)
|
||||
}
|
||||
}
|
||||
if err := handleUnTar(req.File, dstDir); err != nil {
|
||||
_ = os.RemoveAll(dstDir)
|
||||
return err
|
||||
}
|
||||
global.LOG.Infof("decompress file %s successful, now start to check test.sql is exist", req.File)
|
||||
hasTestSql := false
|
||||
_ = filepath.Walk(dstDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if !info.IsDir() && info.Name() == "test.sql" {
|
||||
hasTestSql = true
|
||||
file = path
|
||||
fileName = "test.sql"
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if !hasTestSql {
|
||||
_ = os.RemoveAll(dstDir)
|
||||
return fmt.Errorf("no such file named test.sql in %s", fileName)
|
||||
}
|
||||
defer func() {
|
||||
_ = os.RemoveAll(dstDir)
|
||||
}()
|
||||
}
|
||||
|
||||
req.File = path.Dir(file) + "/" + fileName
|
||||
if err := handlePostgresqlRecover(req, false); err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Info("recover from uploads successful!")
|
||||
return nil
|
||||
}
|
||||
func handlePostgresqlBackup(database, dbName, targetDir, fileName string) error {
|
||||
cli, err := LoadPostgresqlClientByFrom(database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
backupInfo := pgclient.BackupInfo{
|
||||
Name: dbName,
|
||||
TargetDir: targetDir,
|
||||
FileName: fileName,
|
||||
|
||||
Timeout: 300,
|
||||
}
|
||||
if err := cli.Backup(backupInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handlePostgresqlRecover(req dto.CommonRecover, isRollback bool) error {
|
||||
isOk := false
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(req.File) {
|
||||
return buserr.WithName("ErrFileNotFound", req.File)
|
||||
}
|
||||
dbInfo, err := postgresqlRepo.Get(commonRepo.WithByName(req.DetailName), postgresqlRepo.WithByPostgresqlName(req.Name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cli, err := LoadPostgresqlClientByFrom(req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
if !isRollback {
|
||||
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("database/%s/%s_%s.sql.gz", req.Type, req.DetailName, time.Now().Format("20060102150405")))
|
||||
if err := cli.Backup(client.BackupInfo{
|
||||
Name: req.DetailName,
|
||||
TargetDir: path.Dir(rollbackFile),
|
||||
FileName: path.Base(rollbackFile),
|
||||
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("backup postgresql db %s for rollback before recover failed, err: %v", req.DetailName, err)
|
||||
}
|
||||
defer func() {
|
||||
if !isOk {
|
||||
global.LOG.Info("recover failed, start to rollback now")
|
||||
if err := cli.Recover(client.RecoverInfo{
|
||||
Name: req.DetailName,
|
||||
SourceFile: rollbackFile,
|
||||
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
global.LOG.Errorf("rollback postgresql db %s from %s failed, err: %v", req.DetailName, rollbackFile, err)
|
||||
}
|
||||
global.LOG.Infof("rollback postgresql db %s from %s successful", req.DetailName, rollbackFile)
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
} else {
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
}
|
||||
}()
|
||||
}
|
||||
if err := cli.Recover(client.RecoverInfo{
|
||||
Name: req.DetailName,
|
||||
SourceFile: req.File,
|
||||
Username: dbInfo.Username,
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
isOk = true
|
||||
return nil
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/pkg/errors"
|
||||
@@ -34,7 +35,7 @@ func (u *BackupService) RedisBackup() error {
|
||||
}
|
||||
global.LOG.Infof("appendonly in redis conf is %s", appendonly)
|
||||
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
timeNow := time.Now().Format("20060102150405") + common.RandStrAndNum(5)
|
||||
fileName := fmt.Sprintf("%s.rdb", timeNow)
|
||||
if appendonly == "yes" {
|
||||
if strings.HasPrefix(redisInfo.Version, "6.") {
|
||||
@@ -43,7 +44,8 @@ func (u *BackupService) RedisBackup() error {
|
||||
fileName = fmt.Sprintf("%s.tar.gz", timeNow)
|
||||
}
|
||||
}
|
||||
backupDir := path.Join(localDir, fmt.Sprintf("database/redis/%s", redisInfo.Name))
|
||||
itemDir := fmt.Sprintf("database/redis/%s", redisInfo.Name)
|
||||
backupDir := path.Join(localDir, itemDir)
|
||||
if err := handleRedisBackup(redisInfo, backupDir, fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -51,7 +53,7 @@ func (u *BackupService) RedisBackup() error {
|
||||
Type: "redis",
|
||||
Source: "LOCAL",
|
||||
BackupType: "LOCAL",
|
||||
FileDir: backupDir,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
@@ -111,7 +113,7 @@ func handleRedisBackup(redisInfo *repo.RootInfo, backupDir, fileName string) err
|
||||
func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback bool) error {
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(recoverFile) {
|
||||
return fmt.Errorf("%s file is not exist", recoverFile)
|
||||
return buserr.WithName("ErrFileNotFound", recoverFile)
|
||||
}
|
||||
|
||||
appendonly, err := configGetStr(redisInfo.ContainerName, redisInfo.Password, "appendonly")
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/pkg/errors"
|
||||
@@ -25,14 +26,15 @@ func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
website, err := websiteRepo.GetFirst(websiteRepo.WithDomain(req.Name))
|
||||
website, err := websiteRepo.GetFirst(websiteRepo.WithAlias(req.DetailName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
backupDir := path.Join(localDir, fmt.Sprintf("website/%s", req.Name))
|
||||
fileName := fmt.Sprintf("%s_%s.tar.gz", website.PrimaryDomain, timeNow)
|
||||
itemDir := fmt.Sprintf("website/%s", req.Name)
|
||||
backupDir := path.Join(localDir, itemDir)
|
||||
fileName := fmt.Sprintf("%s_%s.tar.gz", website.PrimaryDomain, timeNow+common.RandStrAndNum(5))
|
||||
if err := handleWebsiteBackup(&website, backupDir, fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -40,10 +42,10 @@ func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
|
||||
record := &model.BackupRecord{
|
||||
Type: "website",
|
||||
Name: website.PrimaryDomain,
|
||||
DetailName: "",
|
||||
DetailName: req.DetailName,
|
||||
Source: "LOCAL",
|
||||
BackupType: "LOCAL",
|
||||
FileDir: backupDir,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
@@ -54,13 +56,13 @@ func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
|
||||
}
|
||||
|
||||
func (u *BackupService) WebsiteRecover(req dto.CommonRecover) error {
|
||||
website, err := websiteRepo.GetFirst(websiteRepo.WithDomain(req.Name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(req.File) {
|
||||
return errors.New(fmt.Sprintf("%s file is not exist", req.File))
|
||||
return buserr.WithName("ErrFileNotFound", req.File)
|
||||
}
|
||||
website, err := websiteRepo.GetFirst(websiteRepo.WithAlias(req.DetailName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Infof("recover website %s from backup file %s", req.Name, req.File)
|
||||
if err := handleWebsiteRecover(&website, req.File, false); err != nil {
|
||||
@@ -79,15 +81,6 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
|
||||
_ = os.RemoveAll(tmpPath)
|
||||
}()
|
||||
|
||||
temPathWithName := tmpPath + "/" + website.Alias
|
||||
if !fileOp.Stat(tmpPath+"/website.json") || !fileOp.Stat(temPathWithName+".conf") || !fileOp.Stat(temPathWithName+".web.tar.gz") {
|
||||
return buserr.WithDetail(constant.ErrBackupExist, ".conf or .web.tar.gz", nil)
|
||||
}
|
||||
if website.Type == constant.Deployment {
|
||||
if !fileOp.Stat(temPathWithName + ".app.tar.gz") {
|
||||
return buserr.WithDetail(constant.ErrBackupExist, ".app.tar.gz", nil)
|
||||
}
|
||||
}
|
||||
var oldWebsite model.Website
|
||||
websiteJson, err := os.ReadFile(tmpPath + "/website.json")
|
||||
if err != nil {
|
||||
@@ -101,6 +94,16 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
|
||||
return err
|
||||
}
|
||||
|
||||
temPathWithName := tmpPath + "/" + website.Alias
|
||||
if !fileOp.Stat(tmpPath+"/website.json") || !fileOp.Stat(temPathWithName+".conf") || !fileOp.Stat(temPathWithName+".web.tar.gz") {
|
||||
return buserr.WithDetail(constant.ErrBackupExist, ".conf or .web.tar.gz", nil)
|
||||
}
|
||||
if website.Type == constant.Deployment {
|
||||
if !fileOp.Stat(temPathWithName + ".app.tar.gz") {
|
||||
return buserr.WithDetail(constant.ErrBackupExist, ".app.tar.gz", nil)
|
||||
}
|
||||
}
|
||||
|
||||
isOk := false
|
||||
if !isRollback {
|
||||
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("website/%s_%s.tar.gz", website.Alias, time.Now().Format("20060102150405")))
|
||||
|
||||
@@ -23,7 +23,7 @@ func NewICommandService() ICommandService {
|
||||
}
|
||||
|
||||
func (u *CommandService) List() ([]dto.CommandInfo, error) {
|
||||
commands, err := commandRepo.GetList()
|
||||
commands, err := commandRepo.GetList(commonRepo.WithOrderBy("name"))
|
||||
if err != nil {
|
||||
return nil, constant.ErrRecordNotFound
|
||||
}
|
||||
@@ -39,7 +39,7 @@ func (u *CommandService) List() ([]dto.CommandInfo, error) {
|
||||
}
|
||||
|
||||
func (u *CommandService) SearchForTree() ([]dto.CommandTree, error) {
|
||||
cmdList, err := commandRepo.GetList()
|
||||
cmdList, err := commandRepo.GetList(commonRepo.WithOrderBy("name"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -65,11 +65,11 @@ func (u *CommandService) SearchForTree() ([]dto.CommandTree, error) {
|
||||
}
|
||||
|
||||
func (u *CommandService) SearchWithPage(search dto.SearchCommandWithPage) (int64, interface{}, error) {
|
||||
total, commands, err := commandRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info), commonRepo.WithByGroupID(search.GroupID))
|
||||
total, commands, err := commandRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info), commonRepo.WithByGroupID(search.GroupID), commonRepo.WithOrderRuleBy(search.OrderBy, search.Order))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
groups, _ := groupRepo.GetList(commonRepo.WithByType("command"))
|
||||
groups, _ := groupRepo.GetList(commonRepo.WithByType("command"), commonRepo.WithOrderBy("name"))
|
||||
var dtoCommands []dto.CommandInfo
|
||||
for _, command := range commands {
|
||||
var item dto.CommandInfo
|
||||
|
||||
@@ -2,8 +2,10 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -14,6 +16,7 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
@@ -26,6 +29,7 @@ import (
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/gorilla/websocket"
|
||||
@@ -87,10 +91,21 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
options.Filters = filters.NewArgs()
|
||||
options.Filters.Add("label", req.Filters)
|
||||
}
|
||||
list, err = client.ContainerList(context.Background(), options)
|
||||
containers, err := client.ContainerList(context.Background(), options)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if req.ExcludeAppStore {
|
||||
for _, item := range containers {
|
||||
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
|
||||
continue
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
} else {
|
||||
list = containers
|
||||
}
|
||||
|
||||
if len(req.Name) != 0 {
|
||||
length, count := len(list), 0
|
||||
for count < length {
|
||||
@@ -160,7 +175,7 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
}
|
||||
|
||||
ports := loadContainerPort(item.Ports)
|
||||
backDatas[i] = dto.ContainerInfo{
|
||||
info := dto.ContainerInfo{
|
||||
ContainerID: item.ID,
|
||||
CreateTime: time.Unix(item.Created, 0).Format("2006-01-02 15:04:05"),
|
||||
Name: item.Names[0][1:],
|
||||
@@ -172,6 +187,16 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
IsFromApp: IsFromApp,
|
||||
IsFromCompose: IsFromCompose,
|
||||
}
|
||||
install, _ := appInstallRepo.GetFirst(appInstallRepo.WithContainerName(info.Name))
|
||||
if install.ID > 0 {
|
||||
info.AppInstallName = install.Name
|
||||
info.AppName = install.App.Name
|
||||
websites, _ := websiteRepo.GetBy(websiteRepo.WithAppInstallId(install.ID))
|
||||
for _, website := range websites {
|
||||
info.Websites = append(info.Websites, website.PrimaryDomain)
|
||||
}
|
||||
}
|
||||
backDatas[i] = info
|
||||
if item.NetworkSettings != nil && len(item.NetworkSettings.Networks) > 0 {
|
||||
networks := make([]string, 0, len(item.NetworkSettings.Networks))
|
||||
for key := range item.NetworkSettings.Networks {
|
||||
@@ -614,6 +639,9 @@ func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, containerType,
|
||||
cmd := exec.Command("bash", "-c", command)
|
||||
if !follow {
|
||||
stdout, _ := cmd.CombinedOutput()
|
||||
if !utf8.Valid(stdout) {
|
||||
return errors.New("invalid utf8")
|
||||
}
|
||||
if err := wsConn.WriteMessage(websocket.TextMessage, stdout); err != nil {
|
||||
global.LOG.Errorf("send message with log to ws failed, err: %v", err)
|
||||
}
|
||||
@@ -655,6 +683,10 @@ func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, containerType,
|
||||
global.LOG.Errorf("read bytes from log failed, err: %v", err)
|
||||
continue
|
||||
}
|
||||
// check if the bytes is valid utf8
|
||||
if !utf8.Valid(buffer[:n]) {
|
||||
continue
|
||||
}
|
||||
if err = wsConn.WriteMessage(websocket.TextMessage, buffer[:n]); err != nil {
|
||||
global.LOG.Errorf("send message with log to ws failed, err: %v", err)
|
||||
return err
|
||||
@@ -805,7 +837,25 @@ func checkImageExist(client *client.Client, image string) bool {
|
||||
}
|
||||
|
||||
func pullImages(ctx context.Context, client *client.Client, image string) error {
|
||||
out, err := client.ImagePull(ctx, image, types.ImagePullOptions{})
|
||||
options := types.ImagePullOptions{}
|
||||
repos, _ := imageRepoRepo.List()
|
||||
if len(repos) != 0 {
|
||||
for _, repo := range repos {
|
||||
if strings.HasPrefix(image, repo.DownloadUrl) && repo.Auth {
|
||||
authConfig := registry.AuthConfig{
|
||||
Username: repo.Username,
|
||||
Password: repo.Password,
|
||||
}
|
||||
encodedJSON, err := json.Marshal(authConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
|
||||
options.RegistryAuth = authStr
|
||||
}
|
||||
}
|
||||
}
|
||||
out, err := client.ImagePull(ctx, image, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
@@ -28,7 +30,7 @@ type ICronjobService interface {
|
||||
UpdateStatus(id uint, status string) error
|
||||
Delete(req dto.CronjobBatchDelete) error
|
||||
Download(down dto.CronjobDownload) (string, error)
|
||||
StartJob(cronjob *model.Cronjob) (int, error)
|
||||
StartJob(cronjob *model.Cronjob) (string, error)
|
||||
CleanRecord(req dto.CronjobClean) error
|
||||
|
||||
LoadRecordLog(req dto.OperateByID) (string, error)
|
||||
@@ -46,14 +48,6 @@ func (u *CronjobService) SearchWithPage(search dto.SearchWithPage) (int64, inter
|
||||
if err := copier.Copy(&item, &cronjob); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
if hasBackup(item.Type) {
|
||||
backup, _ := backupRepo.Get(commonRepo.WithByID(uint(item.TargetDirID)))
|
||||
if len(backup.Type) != 0 {
|
||||
item.TargetDir = backup.Type
|
||||
}
|
||||
} else {
|
||||
item.TargetDir = "-"
|
||||
}
|
||||
record, _ := cronjobRepo.RecordFirst(cronjob.ID)
|
||||
if record.ID != 0 {
|
||||
item.LastRecordTime = record.StartTime.Format("2006-01-02 15:04:05")
|
||||
@@ -103,24 +97,23 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req.CleanData && hasBackup(cronjob.Type) {
|
||||
cronjob.RetainCopies = 0
|
||||
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if backup.Type != "LOCAL" {
|
||||
localDir, err := loadLocalDir()
|
||||
if req.CleanData {
|
||||
if hasBackup(cronjob.Type) {
|
||||
accountMap, err := loadClientMap(cronjob.BackupAccounts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
|
||||
cronjob.RetainCopies = 0
|
||||
u.removeExpiredBackup(cronjob, accountMap, model.BackupRecord{})
|
||||
} else {
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, "", &cronjob, nil)
|
||||
u.removeExpiredLog(cronjob)
|
||||
}
|
||||
}
|
||||
if req.IsDelete {
|
||||
records, _ := backupRepo.ListRecord(backupRepo.WithByCronID(cronjob.ID))
|
||||
for _, records := range records {
|
||||
records.CronjobID = 0
|
||||
_ = backupRepo.UpdateRecord(&records)
|
||||
}
|
||||
}
|
||||
delRecords, err := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(req.CronjobID)))
|
||||
@@ -184,29 +177,40 @@ func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
cronjob.Status = constant.StatusEnable
|
||||
cronjob.Spec = loadSpec(cronjob)
|
||||
|
||||
global.LOG.Infof("create cronjob %s successful, spec: %s", cronjob.Name, cronjob.Spec)
|
||||
entryID, err := u.StartJob(&cronjob)
|
||||
spec := cronjob.Spec
|
||||
entryIDs, err := u.StartJob(&cronjob)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cronjob.EntryID = uint64(entryID)
|
||||
cronjob.Spec = spec
|
||||
cronjob.EntryIDs = entryIDs
|
||||
if err := cronjobRepo.Create(&cronjob); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) StartJob(cronjob *model.Cronjob) (int, error) {
|
||||
if cronjob.EntryID != 0 {
|
||||
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
|
||||
func (u *CronjobService) StartJob(cronjob *model.Cronjob) (string, error) {
|
||||
if len(cronjob.EntryIDs) != 0 {
|
||||
ids := strings.Split(cronjob.EntryIDs, ",")
|
||||
for _, id := range ids {
|
||||
idItem, _ := strconv.Atoi(id)
|
||||
global.Cron.Remove(cron.EntryID(idItem))
|
||||
}
|
||||
}
|
||||
entryID, err := u.AddCronJob(cronjob)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
specs := strings.Split(cronjob.Spec, ",")
|
||||
var ids []string
|
||||
for _, spec := range specs {
|
||||
cronjob.Spec = spec
|
||||
entryID, err := u.AddCronJob(cronjob)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ids = append(ids, fmt.Sprintf("%v", entryID))
|
||||
}
|
||||
return entryID, nil
|
||||
return strings.Join(ids, ","), nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error {
|
||||
@@ -215,9 +219,13 @@ func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error {
|
||||
if cronjob.ID == 0 {
|
||||
return errors.New("find cronjob in db failed")
|
||||
}
|
||||
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
|
||||
global.LOG.Infof("stop cronjob entryID: %d", cronjob.EntryID)
|
||||
if err := u.CleanRecord(dto.CronjobClean{CronjobID: id, CleanData: req.CleanData}); err != nil {
|
||||
ids := strings.Split(cronjob.EntryIDs, ",")
|
||||
for _, id := range ids {
|
||||
idItem, _ := strconv.Atoi(id)
|
||||
global.Cron.Remove(cron.EntryID(idItem))
|
||||
}
|
||||
global.LOG.Infof("stop cronjob entryID: %s", cronjob.EntryIDs)
|
||||
if err := u.CleanRecord(dto.CronjobClean{CronjobID: id, CleanData: req.CleanData, IsDelete: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cronjobRepo.Delete(commonRepo.WithByID(id)); err != nil {
|
||||
@@ -238,37 +246,37 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
upMap := make(map[string]interface{})
|
||||
cronjob.EntryID = cronModel.EntryID
|
||||
cronjob.EntryIDs = cronModel.EntryIDs
|
||||
cronjob.Type = cronModel.Type
|
||||
cronjob.Spec = loadSpec(cronjob)
|
||||
spec := cronjob.Spec
|
||||
if cronModel.Status == constant.StatusEnable {
|
||||
newEntryID, err := u.StartJob(&cronjob)
|
||||
newEntryIDs, err := u.StartJob(&cronjob)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
upMap["entry_id"] = newEntryID
|
||||
upMap["entry_ids"] = newEntryIDs
|
||||
} else {
|
||||
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
|
||||
ids := strings.Split(cronjob.EntryIDs, ",")
|
||||
for _, id := range ids {
|
||||
idItem, _ := strconv.Atoi(id)
|
||||
global.Cron.Remove(cron.EntryID(idItem))
|
||||
}
|
||||
}
|
||||
|
||||
upMap["name"] = req.Name
|
||||
upMap["spec"] = cronjob.Spec
|
||||
upMap["spec"] = spec
|
||||
upMap["script"] = req.Script
|
||||
upMap["container_name"] = req.ContainerName
|
||||
upMap["spec_type"] = req.SpecType
|
||||
upMap["week"] = req.Week
|
||||
upMap["day"] = req.Day
|
||||
upMap["hour"] = req.Hour
|
||||
upMap["minute"] = req.Minute
|
||||
upMap["second"] = req.Second
|
||||
upMap["app_id"] = req.AppID
|
||||
upMap["website"] = req.Website
|
||||
upMap["exclusion_rules"] = req.ExclusionRules
|
||||
upMap["db_type"] = req.DBType
|
||||
upMap["db_name"] = req.DBName
|
||||
upMap["url"] = req.URL
|
||||
upMap["source_dir"] = req.SourceDir
|
||||
upMap["keep_local"] = req.KeepLocal
|
||||
upMap["target_dir_id"] = req.TargetDirID
|
||||
|
||||
upMap["backup_accounts"] = req.BackupAccounts
|
||||
upMap["default_download"] = req.DefaultDownload
|
||||
upMap["retain_copies"] = req.RetainCopies
|
||||
return cronjobRepo.Update(id, upMap)
|
||||
}
|
||||
@@ -279,19 +287,23 @@ func (u *CronjobService) UpdateStatus(id uint, status string) error {
|
||||
return errors.WithMessage(constant.ErrRecordNotFound, "record not found")
|
||||
}
|
||||
var (
|
||||
entryID int
|
||||
err error
|
||||
entryIDs string
|
||||
err error
|
||||
)
|
||||
if status == constant.StatusEnable {
|
||||
entryID, err = u.StartJob(&cronjob)
|
||||
entryIDs, err = u.StartJob(&cronjob)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
|
||||
global.LOG.Infof("stop cronjob entryID: %d", cronjob.EntryID)
|
||||
ids := strings.Split(cronjob.EntryIDs, ",")
|
||||
for _, id := range ids {
|
||||
idItem, _ := strconv.Atoi(id)
|
||||
global.Cron.Remove(cron.EntryID(idItem))
|
||||
}
|
||||
global.LOG.Infof("stop cronjob entryID: %s", cronjob.EntryIDs)
|
||||
}
|
||||
return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"status": status, "entry_id": entryID})
|
||||
return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"status": status, "entry_ids": entryIDs})
|
||||
}
|
||||
|
||||
func (u *CronjobService) AddCronJob(cronjob *model.Cronjob) (int, error) {
|
||||
@@ -327,26 +339,3 @@ func mkdirAndWriteFile(cronjob *model.Cronjob, startTime time.Time, msg []byte)
|
||||
write.Flush()
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func loadSpec(cronjob model.Cronjob) string {
|
||||
switch cronjob.SpecType {
|
||||
case "perMonth":
|
||||
return fmt.Sprintf("%v %v %v * *", cronjob.Minute, cronjob.Hour, cronjob.Day)
|
||||
case "perWeek":
|
||||
return fmt.Sprintf("%v %v * * %v", cronjob.Minute, cronjob.Hour, cronjob.Week)
|
||||
case "perNDay":
|
||||
return fmt.Sprintf("%v %v */%v * *", cronjob.Minute, cronjob.Hour, cronjob.Day)
|
||||
case "perDay":
|
||||
return fmt.Sprintf("%v %v * * *", cronjob.Minute, cronjob.Hour)
|
||||
case "perNHour":
|
||||
return fmt.Sprintf("%v */%v * * *", cronjob.Minute, cronjob.Hour)
|
||||
case "perHour":
|
||||
return fmt.Sprintf("%v * * * *", cronjob.Minute)
|
||||
case "perNMinute":
|
||||
return fmt.Sprintf("@every %vm", cronjob.Minute)
|
||||
case "perNSecond":
|
||||
return fmt.Sprintf("@every %vs", cronjob.Second)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,386 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
)
|
||||
|
||||
func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time) error {
|
||||
var apps []model.AppInstall
|
||||
if cronjob.AppID == "all" {
|
||||
apps, _ = appInstallRepo.ListBy()
|
||||
} else {
|
||||
itemID, _ := (strconv.Atoi(cronjob.AppID))
|
||||
app, err := appInstallRepo.GetFirst(commonRepo.WithByID(uint(itemID)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
apps = append(apps, app)
|
||||
}
|
||||
accountMap, err := loadClientMap(cronjob.BackupAccounts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, app := range apps {
|
||||
var record model.BackupRecord
|
||||
record.From = "cronjob"
|
||||
record.Type = "app"
|
||||
record.CronjobID = cronjob.ID
|
||||
record.Name = app.App.Key
|
||||
record.DetailName = app.Name
|
||||
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
|
||||
backupDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("app/%s/%s", app.App.Key, app.Name))
|
||||
record.FileName = fmt.Sprintf("app_%s_%s.tar.gz", app.Name, startTime.Format("20060102150405")+common.RandStrAndNum(5))
|
||||
if err := handleAppBackup(&app, backupDir, record.FileName); err != nil {
|
||||
return err
|
||||
}
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
u.removeExpiredBackup(cronjob, accountMap, record)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Time) error {
|
||||
webs := loadWebsForJob(cronjob)
|
||||
accountMap, err := loadClientMap(cronjob.BackupAccounts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, web := range webs {
|
||||
var record model.BackupRecord
|
||||
record.From = "cronjob"
|
||||
record.Type = "website"
|
||||
record.CronjobID = cronjob.ID
|
||||
record.Name = web.PrimaryDomain
|
||||
record.DetailName = web.Alias
|
||||
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
|
||||
backupDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("website/%s", web.PrimaryDomain))
|
||||
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", web.PrimaryDomain, startTime.Format("20060102150405")+common.RandStrAndNum(5))
|
||||
if err := handleWebsiteBackup(&web, backupDir, record.FileName); err != nil {
|
||||
return err
|
||||
}
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
u.removeExpiredBackup(cronjob, accountMap, record)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Time) error {
|
||||
dbs := loadDbsForJob(cronjob)
|
||||
accountMap, err := loadClientMap(cronjob.BackupAccounts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, dbInfo := range dbs {
|
||||
var record model.BackupRecord
|
||||
record.From = "cronjob"
|
||||
record.Type = dbInfo.DBType
|
||||
record.CronjobID = cronjob.ID
|
||||
record.Name = dbInfo.Database
|
||||
record.DetailName = dbInfo.Name
|
||||
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
|
||||
|
||||
backupDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("database/%s/%s/%s", dbInfo.DBType, record.Name, dbInfo.Name))
|
||||
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format("20060102150405")+common.RandStrAndNum(5))
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
|
||||
if err := handleMysqlBackup(dbInfo.Database, dbInfo.Name, backupDir, record.FileName); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := handlePostgresqlBackup(dbInfo.Database, dbInfo.Name, backupDir, record.FileName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
u.removeExpiredBackup(cronjob, accountMap, record)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.Time) error {
|
||||
accountMap, err := loadClientMap(cronjob.BackupAccounts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileName := fmt.Sprintf("directory%s_%s.tar.gz", strings.ReplaceAll(cronjob.SourceDir, "/", "_"), startTime.Format("20060102150405")+common.RandStrAndNum(5))
|
||||
backupDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("%s/%s", cronjob.Type, cronjob.Name))
|
||||
if err := handleTar(cronjob.SourceDir, backupDir, fileName, cronjob.ExclusionRules); err != nil {
|
||||
return err
|
||||
}
|
||||
var record model.BackupRecord
|
||||
record.From = "cronjob"
|
||||
record.Type = "directory"
|
||||
record.CronjobID = cronjob.ID
|
||||
record.Name = cronjob.Name
|
||||
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, fileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
record.FileName = fileName
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
u.removeExpiredBackup(cronjob, accountMap, record)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.Time) error {
|
||||
accountMap, err := loadClientMap(cronjob.BackupAccounts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nameItem := startTime.Format("20060102150405") + common.RandStrAndNum(5)
|
||||
fileName := fmt.Sprintf("system_log_%s.tar.gz", nameItem)
|
||||
backupDir := path.Join(global.CONF.System.TmpDir, "log", nameItem)
|
||||
if err := handleBackupLogs(backupDir, fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
var record model.BackupRecord
|
||||
record.From = "cronjob"
|
||||
record.Type = "log"
|
||||
record.CronjobID = cronjob.ID
|
||||
record.Name = cronjob.Name
|
||||
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(path.Dir(backupDir), fileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
record.FileName = fileName
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
u.removeExpiredBackup(cronjob, accountMap, record)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, startTime time.Time, logPath string) error {
|
||||
accountMap, err := loadClientMap(cronjob.BackupAccounts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var record model.BackupRecord
|
||||
record.From = "cronjob"
|
||||
record.Type = "directory"
|
||||
record.CronjobID = cronjob.ID
|
||||
record.Name = cronjob.Name
|
||||
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
|
||||
record.FileDir = "system_snapshot"
|
||||
|
||||
req := dto.SnapshotCreate{
|
||||
From: record.BackupType,
|
||||
DefaultDownload: cronjob.DefaultDownload,
|
||||
}
|
||||
name, err := NewISnapshotService().HandleSnapshot(true, logPath, req, startTime.Format("20060102150405")+common.RandStrAndNum(5))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
record.FileName = name + ".tar.gz"
|
||||
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
u.removeExpiredBackup(cronjob, accountMap, record)
|
||||
return nil
|
||||
}
|
||||
|
||||
type databaseHelper struct {
|
||||
DBType string
|
||||
Database string
|
||||
Name string
|
||||
}
|
||||
|
||||
func loadDbsForJob(cronjob model.Cronjob) []databaseHelper {
|
||||
var dbs []databaseHelper
|
||||
if cronjob.DBName == "all" {
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
|
||||
mysqlItems, _ := mysqlRepo.List()
|
||||
for _, mysql := range mysqlItems {
|
||||
dbs = append(dbs, databaseHelper{
|
||||
DBType: cronjob.DBType,
|
||||
Database: mysql.MysqlName,
|
||||
Name: mysql.Name,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
pgItems, _ := postgresqlRepo.List()
|
||||
for _, pg := range pgItems {
|
||||
dbs = append(dbs, databaseHelper{
|
||||
DBType: cronjob.DBType,
|
||||
Database: pg.PostgresqlName,
|
||||
Name: pg.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
return dbs
|
||||
}
|
||||
itemID, _ := (strconv.Atoi(cronjob.DBName))
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
|
||||
mysqlItem, _ := mysqlRepo.Get(commonRepo.WithByID(uint(itemID)))
|
||||
dbs = append(dbs, databaseHelper{
|
||||
DBType: cronjob.DBType,
|
||||
Database: mysqlItem.MysqlName,
|
||||
Name: mysqlItem.Name,
|
||||
})
|
||||
} else {
|
||||
pgItem, _ := postgresqlRepo.Get(commonRepo.WithByID(uint(itemID)))
|
||||
dbs = append(dbs, databaseHelper{
|
||||
DBType: cronjob.DBType,
|
||||
Database: pgItem.PostgresqlName,
|
||||
Name: pgItem.Name,
|
||||
})
|
||||
}
|
||||
return dbs
|
||||
}
|
||||
|
||||
func loadWebsForJob(cronjob model.Cronjob) []model.Website {
|
||||
var weblist []model.Website
|
||||
if cronjob.Website == "all" {
|
||||
weblist, _ = websiteRepo.List()
|
||||
return weblist
|
||||
}
|
||||
itemID, _ := (strconv.Atoi(cronjob.Website))
|
||||
webItem, _ := websiteRepo.GetFirst(commonRepo.WithByID(uint(itemID)))
|
||||
if webItem.ID != 0 {
|
||||
weblist = append(weblist, webItem)
|
||||
}
|
||||
return weblist
|
||||
}
|
||||
|
||||
func loadRecordPath(cronjob model.Cronjob, accountMap map[string]cronjobUploadHelper) (string, string) {
|
||||
source := accountMap[fmt.Sprintf("%v", cronjob.DefaultDownload)].backType
|
||||
targets := strings.Split(cronjob.BackupAccounts, ",")
|
||||
var itemAccounts []string
|
||||
for _, target := range targets {
|
||||
if len(target) == 0 {
|
||||
continue
|
||||
}
|
||||
if len(accountMap[target].backType) != 0 {
|
||||
itemAccounts = append(itemAccounts, accountMap[target].backType)
|
||||
}
|
||||
}
|
||||
backupType := strings.Join(itemAccounts, ",")
|
||||
return source, backupType
|
||||
}
|
||||
|
||||
func handleBackupLogs(targetDir, fileName string) error {
|
||||
websites, err := websiteRepo.List()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(websites) != 0 {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
webItem := path.Join(nginxInstall.GetPath(), "www/sites")
|
||||
for _, website := range websites {
|
||||
dirItem := path.Join(targetDir, "website", website.Alias)
|
||||
if _, err := os.Stat(dirItem); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dirItem, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
itemDir := path.Join(webItem, website.Alias, "log")
|
||||
logFiles, _ := os.ReadDir(itemDir)
|
||||
if len(logFiles) != 0 {
|
||||
for i := 0; i < len(logFiles); i++ {
|
||||
if !logFiles[i].IsDir() {
|
||||
_ = cpBinary([]string{path.Join(itemDir, logFiles[i].Name())}, dirItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
itemDir2 := path.Join(global.CONF.System.Backup, "log/website", website.Alias)
|
||||
logFiles2, _ := os.ReadDir(itemDir2)
|
||||
if len(logFiles2) != 0 {
|
||||
for i := 0; i < len(logFiles2); i++ {
|
||||
if !logFiles2[i].IsDir() {
|
||||
_ = cpBinary([]string{path.Join(itemDir2, logFiles2[i].Name())}, dirItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
global.LOG.Debug("backup website log successful!")
|
||||
}
|
||||
|
||||
systemLogDir := path.Join(global.CONF.System.BaseDir, "1panel/log")
|
||||
systemDir := path.Join(targetDir, "system")
|
||||
if _, err := os.Stat(systemDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(systemDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
systemLogFiles, _ := os.ReadDir(systemLogDir)
|
||||
if len(systemLogFiles) != 0 {
|
||||
for i := 0; i < len(systemLogFiles); i++ {
|
||||
if !systemLogFiles[i].IsDir() {
|
||||
_ = cpBinary([]string{path.Join(systemLogDir, systemLogFiles[i].Name())}, systemDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
global.LOG.Debug("backup system log successful!")
|
||||
|
||||
loginLogFiles, _ := os.ReadDir("/var/log")
|
||||
loginDir := path.Join(targetDir, "login")
|
||||
if _, err := os.Stat(loginDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(loginDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(loginLogFiles) != 0 {
|
||||
for i := 0; i < len(loginLogFiles); i++ {
|
||||
if !loginLogFiles[i].IsDir() && (strings.HasPrefix(loginLogFiles[i].Name(), "secure") || strings.HasPrefix(loginLogFiles[i].Name(), "auth.log")) {
|
||||
_ = cpBinary([]string{path.Join("/var/log", loginLogFiles[i].Name())}, loginDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
global.LOG.Debug("backup ssh log successful!")
|
||||
|
||||
if err := handleTar(targetDir, path.Dir(targetDir), fileName, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = os.RemoveAll(targetDir)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
@@ -5,13 +5,14 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage"
|
||||
@@ -33,44 +34,51 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
if len(cronjob.Script) == 0 {
|
||||
return
|
||||
}
|
||||
record.Records = u.generateLogsPath(*cronjob, record.StartTime)
|
||||
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
|
||||
script := cronjob.Script
|
||||
if len(cronjob.ContainerName) != 0 {
|
||||
message, err = u.handleShell(cronjob.Type, cronjob.Name, fmt.Sprintf("docker exec %s %s", cronjob.ContainerName, cronjob.Script))
|
||||
} else {
|
||||
message, err = u.handleShell(cronjob.Type, cronjob.Name, cronjob.Script)
|
||||
script = fmt.Sprintf("docker exec %s %s", cronjob.ContainerName, cronjob.Script)
|
||||
}
|
||||
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
|
||||
case "snapshot":
|
||||
messageItem := ""
|
||||
messageItem, record.File, err = u.handleSnapshot(cronjob, record.StartTime)
|
||||
message = []byte(messageItem)
|
||||
err = u.handleShell(cronjob.Type, cronjob.Name, script, record.Records)
|
||||
u.removeExpiredLog(*cronjob)
|
||||
case "curl":
|
||||
if len(cronjob.URL) == 0 {
|
||||
return
|
||||
}
|
||||
message, err = u.handleShell(cronjob.Type, cronjob.Name, fmt.Sprintf("curl '%s'", cronjob.URL))
|
||||
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
|
||||
record.Records = u.generateLogsPath(*cronjob, record.StartTime)
|
||||
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
|
||||
err = u.handleShell(cronjob.Type, cronjob.Name, fmt.Sprintf("curl '%s'", cronjob.URL), record.Records)
|
||||
u.removeExpiredLog(*cronjob)
|
||||
case "ntp":
|
||||
err = u.handleNtpSync()
|
||||
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
|
||||
case "website", "database", "app":
|
||||
record.File, err = u.handleBackup(cronjob, record.StartTime)
|
||||
case "directory":
|
||||
if len(cronjob.SourceDir) == 0 {
|
||||
return
|
||||
}
|
||||
record.File, err = u.handleBackup(cronjob, record.StartTime)
|
||||
u.removeExpiredLog(*cronjob)
|
||||
case "cutWebsiteLog":
|
||||
record.File, err = u.handleCutWebsiteLog(cronjob, record.StartTime)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("cut website log file failed, err: %v", err)
|
||||
}
|
||||
var messageItem []string
|
||||
messageItem, record.File, err = u.handleCutWebsiteLog(cronjob, record.StartTime)
|
||||
message = []byte(strings.Join(messageItem, "\n"))
|
||||
case "clean":
|
||||
messageItem := ""
|
||||
messageItem, err = u.handleSystemClean()
|
||||
message = []byte(messageItem)
|
||||
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
|
||||
u.removeExpiredLog(*cronjob)
|
||||
case "website":
|
||||
err = u.handleWebsite(*cronjob, record.StartTime)
|
||||
case "app":
|
||||
err = u.handleApp(*cronjob, record.StartTime)
|
||||
case "database":
|
||||
err = u.handleDatabase(*cronjob, record.StartTime)
|
||||
case "directory":
|
||||
if len(cronjob.SourceDir) == 0 {
|
||||
return
|
||||
}
|
||||
err = u.handleDirectory(*cronjob, record.StartTime)
|
||||
case "log":
|
||||
record.File, err = u.handleSystemLog(*cronjob, record.StartTime)
|
||||
err = u.handleSystemLog(*cronjob, record.StartTime)
|
||||
case "snapshot":
|
||||
record.Records = u.generateLogsPath(*cronjob, record.StartTime)
|
||||
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
|
||||
err = u.handleSnapshot(*cronjob, record.StartTime, record.Records)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -87,18 +95,17 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
}()
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleShell(cronType, cornName, script string) ([]byte, error) {
|
||||
func (u *CronjobService) handleShell(cronType, cornName, script, logPath string) error {
|
||||
handleDir := fmt.Sprintf("%s/task/%s/%s", constant.DataDir, cronType, cornName)
|
||||
if _, err := os.Stat(handleDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(handleDir, os.ModePerm); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
}
|
||||
stdout, err := cmd.ExecCronjobWithTimeOut(script, handleDir, 24*time.Hour)
|
||||
if err != nil {
|
||||
return []byte(stdout), err
|
||||
if err := cmd.ExecCronjobWithTimeOut(script, handleDir, logPath, 24*time.Hour); err != nil {
|
||||
return err
|
||||
}
|
||||
return []byte(stdout), nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleNtpSync() error {
|
||||
@@ -116,98 +123,6 @@ func (u *CronjobService) handleNtpSync() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleBackup(cronjob *model.Cronjob, startTime time.Time) (string, error) {
|
||||
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
global.LOG.Infof("start to backup %s %s to %s", cronjob.Type, cronjob.Name, backup.Type)
|
||||
|
||||
switch cronjob.Type {
|
||||
case "database":
|
||||
paths, err := u.handleDatabase(*cronjob, backup, startTime)
|
||||
return strings.Join(paths, ","), err
|
||||
case "app":
|
||||
paths, err := u.handleApp(*cronjob, backup, startTime)
|
||||
return strings.Join(paths, ","), err
|
||||
case "website":
|
||||
paths, err := u.handleWebsite(*cronjob, backup, startTime)
|
||||
return strings.Join(paths, ","), err
|
||||
default:
|
||||
fileName := fmt.Sprintf("directory%s_%s.tar.gz", strings.ReplaceAll(cronjob.SourceDir, "/", "_"), startTime.Format("20060102150405"))
|
||||
backupDir := path.Join(localDir, fmt.Sprintf("%s/%s", cronjob.Type, cronjob.Name))
|
||||
itemFileDir := fmt.Sprintf("%s/%s", cronjob.Type, cronjob.Name)
|
||||
global.LOG.Infof("handle tar %s to %s", backupDir, fileName)
|
||||
if err := handleTar(cronjob.SourceDir, backupDir, fileName, cronjob.ExclusionRules); err != nil {
|
||||
return "", err
|
||||
}
|
||||
var client cloud_storage.CloudStorageClient
|
||||
if backup.Type != "LOCAL" {
|
||||
if !cronjob.KeepLocal {
|
||||
defer func() {
|
||||
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, fileName))
|
||||
}()
|
||||
}
|
||||
client, err = NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(backup.BackupPath) != 0 {
|
||||
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
|
||||
}
|
||||
if _, err = client.Upload(backupDir+"/"+fileName, itemFileDir+"/"+fileName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, cronjob, client)
|
||||
if backup.Type == "LOCAL" || cronjob.KeepLocal {
|
||||
return fmt.Sprintf("%s/%s", backupDir, fileName), nil
|
||||
} else {
|
||||
return fmt.Sprintf("%s/%s", itemFileDir, fileName), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *CronjobService) HandleRmExpired(backType, backupPath, localDir string, cronjob *model.Cronjob, backClient cloud_storage.CloudStorageClient) {
|
||||
global.LOG.Infof("start to handle remove expired, retain copies: %d", cronjob.RetainCopies)
|
||||
records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), commonRepo.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)
|
||||
_ = backupRepo.DeleteRecord(context.TODO(), backupRepo.WithByFileName(path.Base(file)))
|
||||
if backType == "LOCAL" {
|
||||
continue
|
||||
}
|
||||
|
||||
fileItem := file
|
||||
if cronjob.KeepLocal {
|
||||
if len(backupPath) != 0 {
|
||||
fileItem = path.Join(strings.TrimPrefix(backupPath, "/") + strings.TrimPrefix(file, localDir+"/"))
|
||||
} else {
|
||||
fileItem = strings.TrimPrefix(file, localDir+"/")
|
||||
}
|
||||
}
|
||||
|
||||
if cronjob.Type == "snapshot" {
|
||||
_ = snapshotRepo.Delete(commonRepo.WithByName(strings.TrimSuffix(path.Base(fileItem), ".tar.gz")))
|
||||
}
|
||||
_, _ = backClient.Delete(fileItem)
|
||||
}
|
||||
}
|
||||
_ = cronjobRepo.DeleteRecord(commonRepo.WithByID(uint(records[i].ID)))
|
||||
_ = os.Remove(records[i].Records)
|
||||
}
|
||||
}
|
||||
|
||||
func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
|
||||
@@ -265,433 +180,190 @@ func handleUnTar(sourceFile, targetDir string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleDatabase(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
|
||||
var paths []string
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
|
||||
var dbs []model.DatabaseMysql
|
||||
if cronjob.DBName == "all" {
|
||||
dbs, err = mysqlRepo.List()
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
} else {
|
||||
itemID, _ := (strconv.Atoi(cronjob.DBName))
|
||||
dbs, err = mysqlRepo.List(commonRepo.WithByID(uint(itemID)))
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
}
|
||||
|
||||
var client cloud_storage.CloudStorageClient
|
||||
if backup.Type != "LOCAL" {
|
||||
client, err = NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, dbInfo := range dbs {
|
||||
var record model.BackupRecord
|
||||
|
||||
database, _ := databaseRepo.Get(commonRepo.WithByName(dbInfo.MysqlName))
|
||||
record.Type = database.Type
|
||||
record.Source = "LOCAL"
|
||||
record.BackupType = backup.Type
|
||||
|
||||
record.Name = dbInfo.MysqlName
|
||||
backupDir := path.Join(localDir, fmt.Sprintf("database/%s/%s/%s", database.Type, record.Name, dbInfo.Name))
|
||||
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format("20060102150405"))
|
||||
if err = handleMysqlBackup(dbInfo.MysqlName, dbInfo.Name, backupDir, record.FileName); err != nil {
|
||||
return paths, err
|
||||
}
|
||||
|
||||
record.DetailName = dbInfo.Name
|
||||
record.FileDir = backupDir
|
||||
itemFileDir := strings.TrimPrefix(backupDir, localDir+"/")
|
||||
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
|
||||
record.Source = backup.Type
|
||||
record.FileDir = itemFileDir
|
||||
}
|
||||
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return paths, err
|
||||
}
|
||||
if backup.Type != "LOCAL" {
|
||||
if !cronjob.KeepLocal {
|
||||
defer func() {
|
||||
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, record.FileName))
|
||||
}()
|
||||
}
|
||||
if len(backup.BackupPath) != 0 {
|
||||
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
|
||||
}
|
||||
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
|
||||
return paths, err
|
||||
}
|
||||
}
|
||||
if backup.Type == "LOCAL" || cronjob.KeepLocal {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s", record.FileDir, record.FileName))
|
||||
} else {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s", itemFileDir, record.FileName))
|
||||
}
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime time.Time) (string, error) {
|
||||
func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime time.Time) ([]string, string, error) {
|
||||
var (
|
||||
websites []string
|
||||
err error
|
||||
filePaths []string
|
||||
msgs []string
|
||||
)
|
||||
if cronjob.Website == "all" {
|
||||
websites, _ = NewIWebsiteService().GetWebsiteOptions()
|
||||
if len(websites) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
} else {
|
||||
websites = append(websites, cronjob.Website)
|
||||
}
|
||||
|
||||
websites := loadWebsForJob(*cronjob)
|
||||
nginx, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
return msgs, "", nil
|
||||
}
|
||||
baseDir := path.Join(nginx.GetPath(), "www", "sites")
|
||||
fileOp := files.NewFileOp()
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(websites))
|
||||
for _, websiteName := range websites {
|
||||
name := websiteName
|
||||
go func() {
|
||||
website, _ := websiteRepo.GetFirst(websiteRepo.WithDomain(name))
|
||||
if website.ID == 0 {
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
websiteLogDir := path.Join(baseDir, website.Alias, "log")
|
||||
srcAccessLogPath := path.Join(websiteLogDir, "access.log")
|
||||
srcErrorLogPath := path.Join(websiteLogDir, "error.log")
|
||||
dstLogDir := path.Join(global.CONF.System.Backup, "log", "website", website.Alias)
|
||||
if !fileOp.Stat(dstLogDir) {
|
||||
_ = os.MkdirAll(dstLogDir, 0755)
|
||||
}
|
||||
for _, website := range websites {
|
||||
websiteLogDir := path.Join(baseDir, website.Alias, "log")
|
||||
srcAccessLogPath := path.Join(websiteLogDir, "access.log")
|
||||
srcErrorLogPath := path.Join(websiteLogDir, "error.log")
|
||||
dstLogDir := path.Join(global.CONF.System.Backup, "log", "website", website.Alias)
|
||||
if !fileOp.Stat(dstLogDir) {
|
||||
_ = os.MkdirAll(dstLogDir, 0755)
|
||||
}
|
||||
|
||||
dstName := fmt.Sprintf("%s_log_%s.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
|
||||
filePaths = append(filePaths, path.Join(dstLogDir, dstName))
|
||||
if err = fileOp.Compress([]string{srcAccessLogPath, srcErrorLogPath}, dstLogDir, dstName, files.Gz); err != nil {
|
||||
global.LOG.Errorf("There was an error in compressing the website[%s] access.log, err: %v", website.PrimaryDomain, err)
|
||||
} else {
|
||||
_ = fileOp.WriteFile(srcAccessLogPath, strings.NewReader(""), 0755)
|
||||
_ = fileOp.WriteFile(srcErrorLogPath, strings.NewReader(""), 0755)
|
||||
}
|
||||
global.LOG.Infof("The website[%s] log file was successfully rotated in the directory [%s]", website.PrimaryDomain, dstLogDir)
|
||||
var record model.BackupRecord
|
||||
record.Type = "cutWebsiteLog"
|
||||
record.Name = cronjob.Website
|
||||
record.Source = "LOCAL"
|
||||
record.BackupType = "LOCAL"
|
||||
record.FileDir = dstLogDir
|
||||
record.FileName = dstName
|
||||
if err = backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
|
||||
return strings.Join(filePaths, ","), nil
|
||||
}
|
||||
dstName := fmt.Sprintf("%s_log_%s.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
|
||||
dstFilePath := path.Join(dstLogDir, dstName)
|
||||
filePaths = append(filePaths, dstFilePath)
|
||||
|
||||
func (u *CronjobService) handleApp(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
|
||||
var paths []string
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
|
||||
var applist []model.AppInstall
|
||||
if cronjob.AppID == "all" {
|
||||
applist, err = appInstallRepo.ListBy()
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
} else {
|
||||
itemID, _ := (strconv.Atoi(cronjob.AppID))
|
||||
app, err := appInstallRepo.GetFirst(commonRepo.WithByID(uint(itemID)))
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
applist = append(applist, app)
|
||||
}
|
||||
|
||||
var client cloud_storage.CloudStorageClient
|
||||
if backup.Type != "LOCAL" {
|
||||
client, err = NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, app := range applist {
|
||||
var record model.BackupRecord
|
||||
record.Type = "app"
|
||||
record.Name = app.App.Key
|
||||
record.DetailName = app.Name
|
||||
record.Source = "LOCAL"
|
||||
record.BackupType = backup.Type
|
||||
backupDir := path.Join(localDir, fmt.Sprintf("app/%s/%s", app.App.Key, app.Name))
|
||||
record.FileDir = backupDir
|
||||
itemFileDir := strings.TrimPrefix(backupDir, localDir+"/")
|
||||
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
|
||||
record.Source = backup.Type
|
||||
record.FileDir = strings.TrimPrefix(backupDir, localDir+"/")
|
||||
}
|
||||
record.FileName = fmt.Sprintf("app_%s_%s.tar.gz", app.Name, startTime.Format("20060102150405"))
|
||||
if err := handleAppBackup(&app, backupDir, record.FileName); err != nil {
|
||||
return paths, err
|
||||
}
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return paths, err
|
||||
}
|
||||
if backup.Type != "LOCAL" {
|
||||
if !cronjob.KeepLocal {
|
||||
defer func() {
|
||||
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, record.FileName))
|
||||
}()
|
||||
}
|
||||
if len(backup.BackupPath) != 0 {
|
||||
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
|
||||
}
|
||||
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
|
||||
return paths, err
|
||||
}
|
||||
}
|
||||
if backup.Type == "LOCAL" || cronjob.KeepLocal {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s", record.FileDir, record.FileName))
|
||||
if err = backupLogFile(dstFilePath, websiteLogDir, fileOp); err != nil {
|
||||
websiteErr := buserr.WithNameAndErr("ErrCutWebsiteLog", website.PrimaryDomain, err)
|
||||
err = websiteErr
|
||||
msgs = append(msgs, websiteErr.Error())
|
||||
global.LOG.Error(websiteErr.Error())
|
||||
continue
|
||||
} else {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s", itemFileDir, record.FileName))
|
||||
_ = fileOp.WriteFile(srcAccessLogPath, strings.NewReader(""), 0755)
|
||||
_ = fileOp.WriteFile(srcErrorLogPath, strings.NewReader(""), 0755)
|
||||
}
|
||||
msg := i18n.GetMsgWithMap("CutWebsiteLogSuccess", map[string]interface{}{"name": website.PrimaryDomain, "path": dstFilePath})
|
||||
global.LOG.Infof(msg)
|
||||
msgs = append(msgs, msg)
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
|
||||
return paths, nil
|
||||
u.removeExpiredLog(*cronjob)
|
||||
return msgs, strings.Join(filePaths, ","), err
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
|
||||
var paths []string
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
|
||||
var weblist []string
|
||||
if cronjob.Website == "all" {
|
||||
weblist, err = NewIWebsiteService().GetWebsiteOptions()
|
||||
if err != nil {
|
||||
return paths, err
|
||||
func backupLogFile(dstFilePath, websiteLogDir string, fileOp files.FileOp) error {
|
||||
if err := cmd.ExecCmd(fmt.Sprintf("tar -czf %s -C %s %s", dstFilePath, websiteLogDir, strings.Join([]string{"access.log", "error.log"}, " "))); err != nil {
|
||||
dstDir := path.Dir(dstFilePath)
|
||||
if err = fileOp.Copy(path.Join(websiteLogDir, "access.log"), dstDir); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
weblist = append(weblist, cronjob.Website)
|
||||
}
|
||||
|
||||
var client cloud_storage.CloudStorageClient
|
||||
if backup.Type != "LOCAL" {
|
||||
client, err = NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return paths, err
|
||||
if err = fileOp.Copy(path.Join(websiteLogDir, "error.log"), dstDir); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, websiteItem := range weblist {
|
||||
var record model.BackupRecord
|
||||
record.Type = "website"
|
||||
record.Name = cronjob.Website
|
||||
record.Source = "LOCAL"
|
||||
record.BackupType = backup.Type
|
||||
website, err := websiteRepo.GetFirst(websiteRepo.WithDomain(websiteItem))
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
backupDir := path.Join(localDir, fmt.Sprintf("website/%s", website.PrimaryDomain))
|
||||
record.FileDir = backupDir
|
||||
itemFileDir := strings.TrimPrefix(backupDir, localDir+"/")
|
||||
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
|
||||
record.Source = backup.Type
|
||||
record.FileDir = strings.TrimPrefix(backupDir, localDir+"/")
|
||||
}
|
||||
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
|
||||
if err := handleWebsiteBackup(&website, backupDir, record.FileName); err != nil {
|
||||
return paths, err
|
||||
}
|
||||
record.Name = website.PrimaryDomain
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return paths, err
|
||||
}
|
||||
if backup.Type != "LOCAL" {
|
||||
if !cronjob.KeepLocal {
|
||||
defer func() {
|
||||
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, record.FileName))
|
||||
}()
|
||||
}
|
||||
if len(backup.BackupPath) != 0 {
|
||||
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
|
||||
}
|
||||
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
|
||||
return paths, err
|
||||
}
|
||||
}
|
||||
if backup.Type == "LOCAL" || cronjob.KeepLocal {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s", record.FileDir, record.FileName))
|
||||
} else {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s", itemFileDir, record.FileName))
|
||||
if err = cmd.ExecCmd(fmt.Sprintf("tar -czf %s -C %s %s", dstFilePath, dstDir, strings.Join([]string{"access.log", "error.log"}, " "))); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = fileOp.DeleteFile(path.Join(dstDir, "access.log"))
|
||||
_ = fileOp.DeleteFile(path.Join(dstDir, "error.log"))
|
||||
return nil
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleSnapshot(cronjob *model.Cronjob, startTime time.Time) (string, string, error) {
|
||||
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
client, err := NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
req := dto.SnapshotCreate{
|
||||
From: backup.Type,
|
||||
}
|
||||
message, name, err := NewISnapshotService().HandleSnapshot(true, req, startTime.Format("20060102150405"))
|
||||
if err != nil {
|
||||
return message, "", err
|
||||
}
|
||||
|
||||
path := path.Join(strings.TrimPrefix(backup.BackupPath, "/"), "system_snapshot", name+".tar.gz")
|
||||
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, "", cronjob, client)
|
||||
return message, path, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleSystemClean() (string, error) {
|
||||
return NewIDeviceService().CleanForCronjob()
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.Time) (string, error) {
|
||||
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
|
||||
func loadClientMap(backupAccounts string) (map[string]cronjobUploadHelper, error) {
|
||||
clients := make(map[string]cronjobUploadHelper)
|
||||
accounts, err := backupRepo.List()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pathItem := path.Join(global.CONF.System.BaseDir, "1panel/tmp/log", startTime.Format("20060102150405"))
|
||||
websites, err := websiteRepo.List()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(websites) != 0 {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return "", err
|
||||
targets := strings.Split(backupAccounts, ",")
|
||||
for _, target := range targets {
|
||||
if len(target) == 0 {
|
||||
continue
|
||||
}
|
||||
webItem := path.Join(nginxInstall.GetPath(), "www/sites")
|
||||
for _, website := range websites {
|
||||
dirItem := path.Join(pathItem, "website", website.Alias)
|
||||
if _, err := os.Stat(dirItem); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dirItem, os.ModePerm); err != nil {
|
||||
return "", err
|
||||
for _, account := range accounts {
|
||||
if target == account.Type {
|
||||
client, err := NewIBackupService().NewClient(&account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
itemDir := path.Join(webItem, website.Alias, "log")
|
||||
logFiles, _ := os.ReadDir(itemDir)
|
||||
if len(logFiles) != 0 {
|
||||
for i := 0; i < len(logFiles); i++ {
|
||||
if !logFiles[i].IsDir() {
|
||||
_ = cpBinary([]string{path.Join(itemDir, logFiles[i].Name())}, dirItem)
|
||||
}
|
||||
pathItem := account.BackupPath
|
||||
if account.BackupPath != "/" {
|
||||
pathItem = strings.TrimPrefix(account.BackupPath, "/")
|
||||
}
|
||||
}
|
||||
itemDir2 := path.Join(global.CONF.System.Backup, "log/website", website.Alias)
|
||||
logFiles2, _ := os.ReadDir(itemDir2)
|
||||
if len(logFiles2) != 0 {
|
||||
for i := 0; i < len(logFiles2); i++ {
|
||||
if !logFiles2[i].IsDir() {
|
||||
_ = cpBinary([]string{path.Join(itemDir2, logFiles2[i].Name())}, dirItem)
|
||||
}
|
||||
clients[target] = cronjobUploadHelper{
|
||||
client: client,
|
||||
backupPath: pathItem,
|
||||
backType: account.Type,
|
||||
}
|
||||
}
|
||||
}
|
||||
global.LOG.Debug("backup website log successful!")
|
||||
}
|
||||
return clients, nil
|
||||
}
|
||||
|
||||
systemLogDir := path.Join(global.CONF.System.BaseDir, "1panel/log")
|
||||
systemDir := path.Join(pathItem, "system")
|
||||
if _, err := os.Stat(systemDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(systemDir, os.ModePerm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
systemLogFiles, _ := os.ReadDir(systemLogDir)
|
||||
if len(systemLogFiles) != 0 {
|
||||
for i := 0; i < len(systemLogFiles); i++ {
|
||||
if !systemLogFiles[i].IsDir() {
|
||||
_ = cpBinary([]string{path.Join(systemLogDir, systemLogFiles[i].Name())}, systemDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
global.LOG.Debug("backup system log successful!")
|
||||
type cronjobUploadHelper struct {
|
||||
backupPath string
|
||||
backType string
|
||||
client cloud_storage.CloudStorageClient
|
||||
}
|
||||
|
||||
loginLogFiles, _ := os.ReadDir("/var/log")
|
||||
loginDir := path.Join(pathItem, "login")
|
||||
if _, err := os.Stat(loginDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(loginDir, os.ModePerm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if len(loginLogFiles) != 0 {
|
||||
for i := 0; i < len(loginLogFiles); i++ {
|
||||
if !loginLogFiles[i].IsDir() && (strings.HasPrefix(loginLogFiles[i].Name(), "secure") || strings.HasPrefix(loginLogFiles[i].Name(), "auth.log")) {
|
||||
_ = cpBinary([]string{path.Join("/var/log", loginLogFiles[i].Name())}, loginDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
global.LOG.Debug("backup ssh log successful!")
|
||||
|
||||
fileName := fmt.Sprintf("system_log_%s.tar.gz", startTime.Format("20060102150405"))
|
||||
targetDir := path.Dir(pathItem)
|
||||
if err := handleTar(pathItem, targetDir, fileName, ""); err != nil {
|
||||
return "", err
|
||||
}
|
||||
func (u *CronjobService) uploadCronjobBackFile(cronjob model.Cronjob, accountMap map[string]cronjobUploadHelper, file string) (string, error) {
|
||||
defer func() {
|
||||
os.RemoveAll(pathItem)
|
||||
os.RemoveAll(path.Join(targetDir, fileName))
|
||||
_ = os.Remove(file)
|
||||
}()
|
||||
accounts := strings.Split(cronjob.BackupAccounts, ",")
|
||||
cloudSrc := strings.TrimPrefix(file, global.CONF.System.TmpDir+"/")
|
||||
for _, account := range accounts {
|
||||
if len(account) != 0 {
|
||||
global.LOG.Debugf("start upload file to %s, dir: %s", account, path.Join(accountMap[account].backupPath, cloudSrc))
|
||||
if _, err := accountMap[account].client.Upload(file, path.Join(accountMap[account].backupPath, cloudSrc)); err != nil {
|
||||
return "", err
|
||||
}
|
||||
global.LOG.Debugf("upload successful!")
|
||||
}
|
||||
}
|
||||
return cloudSrc, nil
|
||||
}
|
||||
|
||||
client, err := NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return "", err
|
||||
func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap map[string]cronjobUploadHelper, record model.BackupRecord) {
|
||||
global.LOG.Infof("start to handle remove expired, retain copies: %d", cronjob.RetainCopies)
|
||||
var opts []repo.DBOption
|
||||
opts = append(opts, commonRepo.WithByFrom("cronjob"))
|
||||
opts = append(opts, backupRepo.WithByCronID(cronjob.ID))
|
||||
opts = append(opts, commonRepo.WithOrderBy("created_at desc"))
|
||||
if record.ID != 0 {
|
||||
opts = append(opts, backupRepo.WithByType(record.Type))
|
||||
opts = append(opts, commonRepo.WithByName(record.Name))
|
||||
opts = append(opts, backupRepo.WithByDetailName(record.DetailName))
|
||||
}
|
||||
records, _ := backupRepo.ListRecord(opts...)
|
||||
if len(records) <= int(cronjob.RetainCopies) {
|
||||
return
|
||||
}
|
||||
for i := int(cronjob.RetainCopies); i < len(records); i++ {
|
||||
accounts := strings.Split(cronjob.BackupAccounts, ",")
|
||||
if cronjob.Type == "snapshot" {
|
||||
for _, account := range accounts {
|
||||
if len(account) != 0 {
|
||||
_, _ = accountMap[account].client.Delete(path.Join(accountMap[account].backupPath, "system_snapshot", records[i].FileName))
|
||||
}
|
||||
}
|
||||
_ = snapshotRepo.Delete(commonRepo.WithByName(strings.TrimSuffix(records[i].FileName, ".tar.gz")))
|
||||
} else {
|
||||
for _, account := range accounts {
|
||||
if len(account) != 0 {
|
||||
_, _ = accountMap[account].client.Delete(path.Join(accountMap[account].backupPath, records[i].FileDir, records[i].FileName))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = backupRepo.DeleteRecord(context.Background(), commonRepo.WithByID(records[i].ID))
|
||||
}
|
||||
}
|
||||
|
||||
func (u *CronjobService) removeExpiredLog(cronjob model.Cronjob) {
|
||||
global.LOG.Infof("start to handle remove expired, retain copies: %d", cronjob.RetainCopies)
|
||||
records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), commonRepo.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(commonRepo.WithByID(uint(records[i].ID)))
|
||||
_ = os.Remove(records[i].Records)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *CronjobService) generateLogsPath(cronjob model.Cronjob, startTime time.Time) string {
|
||||
dir := fmt.Sprintf("%s/task/%s/%s", constant.DataDir, cronjob.Type, cronjob.Name)
|
||||
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
||||
_ = os.MkdirAll(dir, os.ModePerm)
|
||||
}
|
||||
|
||||
targetPath := "log/" + fileName
|
||||
if len(backup.BackupPath) != 0 {
|
||||
targetPath = strings.TrimPrefix(backup.BackupPath, "/") + "/log/" + fileName
|
||||
}
|
||||
|
||||
if _, err = client.Upload(path.Join(targetDir, fileName), targetPath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, "", &cronjob, client)
|
||||
return targetPath, nil
|
||||
path := fmt.Sprintf("%s/%s.log", dir, startTime.Format("20060102150405"))
|
||||
return path
|
||||
}
|
||||
|
||||
func hasBackup(cronjobType string) bool {
|
||||
|
||||
+133
-48
@@ -6,6 +6,9 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/postgresql"
|
||||
client2 "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
@@ -28,6 +31,7 @@ type IDatabaseService interface {
|
||||
DeleteCheck(id uint) ([]string, error)
|
||||
Delete(req dto.DatabaseDelete) error
|
||||
List(dbType string) ([]dto.DatabaseOption, error)
|
||||
LoadItems(dbType string) ([]dto.DatabaseItem, error)
|
||||
}
|
||||
|
||||
func NewIDatabaseService() IDatabaseService {
|
||||
@@ -77,24 +81,66 @@ func (u *DatabaseService) List(dbType string) ([]dto.DatabaseOption, error) {
|
||||
return datas, err
|
||||
}
|
||||
|
||||
func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
||||
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
|
||||
SSL: req.SSL,
|
||||
RootCert: req.RootCert,
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
Timeout: 6,
|
||||
}); err != nil {
|
||||
return false
|
||||
func (u *DatabaseService) LoadItems(dbType string) ([]dto.DatabaseItem, error) {
|
||||
dbs, err := databaseRepo.GetList(databaseRepo.WithTypeList(dbType))
|
||||
var datas []dto.DatabaseItem
|
||||
for _, db := range dbs {
|
||||
if dbType == "postgresql" {
|
||||
items, _ := postgresqlRepo.List(postgresqlRepo.WithByPostgresqlName(db.Name))
|
||||
for _, item := range items {
|
||||
var dItem dto.DatabaseItem
|
||||
if err := copier.Copy(&dItem, &item); err != nil {
|
||||
continue
|
||||
}
|
||||
dItem.Database = db.Name
|
||||
datas = append(datas, dItem)
|
||||
}
|
||||
} else {
|
||||
items, _ := mysqlRepo.List(mysqlRepo.WithByMysqlName(db.Name))
|
||||
for _, item := range items {
|
||||
var dItem dto.DatabaseItem
|
||||
if err := copier.Copy(&dItem, &item); err != nil {
|
||||
continue
|
||||
}
|
||||
dItem.Database = db.Name
|
||||
datas = append(datas, dItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
return datas, err
|
||||
}
|
||||
|
||||
func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
||||
switch req.Type {
|
||||
case constant.AppPostgresql:
|
||||
_, err := postgresql.NewPostgresqlClient(client2.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: 6,
|
||||
})
|
||||
return err == nil
|
||||
case "mysql", "mariadb":
|
||||
_, err := mysql.NewMysqlClient(client.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
|
||||
SSL: req.SSL,
|
||||
RootCert: req.RootCert,
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
Timeout: 6,
|
||||
})
|
||||
return err == nil
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
|
||||
@@ -105,22 +151,39 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
|
||||
}
|
||||
return constant.ErrRecordExist
|
||||
}
|
||||
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
switch req.Type {
|
||||
case constant.AppPostgresql:
|
||||
if _, err := postgresql.NewPostgresqlClient(client2.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: 6,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
case "mysql", "mariadb":
|
||||
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
|
||||
SSL: req.SSL,
|
||||
RootCert: req.RootCert,
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
Timeout: 6,
|
||||
}); err != nil {
|
||||
return err
|
||||
SSL: req.SSL,
|
||||
RootCert: req.RootCert,
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
Timeout: 6,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.New("database type not supported")
|
||||
}
|
||||
|
||||
if err := copier.Copy(&db, &req); err != nil {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
@@ -170,30 +233,51 @@ func (u *DatabaseService) Delete(req dto.DatabaseDelete) error {
|
||||
return err
|
||||
}
|
||||
if db.From != "local" {
|
||||
if err := mysqlRepo.Delete(context.Background(), mysqlRepo.WithByMysqlName(db.Name)); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
if db.Type == "mysql" || db.Type == "mariadb" {
|
||||
if err := mysqlRepo.Delete(context.Background(), mysqlRepo.WithByMysqlName(db.Name)); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := postgresqlRepo.Delete(context.Background(), postgresqlRepo.WithByPostgresqlName(db.Name)); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
||||
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
switch req.Type {
|
||||
case constant.AppPostgresql:
|
||||
if _, err := postgresql.NewPostgresqlClient(client2.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
case "mysql", "mariadb":
|
||||
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
|
||||
SSL: req.SSL,
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
RootCert: req.RootCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return err
|
||||
SSL: req.SSL,
|
||||
RootCert: req.RootCert,
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.New("database type not supported")
|
||||
}
|
||||
|
||||
pass, err := encrypt.StringEncrypt(req.Password)
|
||||
@@ -208,6 +292,7 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
||||
upMap["port"] = req.Port
|
||||
upMap["username"] = req.Username
|
||||
upMap["password"] = pass
|
||||
upMap["description"] = req.Description
|
||||
upMap["ssl"] = req.SSL
|
||||
upMap["client_key"] = req.ClientKey
|
||||
upMap["client_cert"] = req.ClientCert
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
)
|
||||
|
||||
type DBCommonService struct{}
|
||||
|
||||
type IDBCommonService interface {
|
||||
LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error)
|
||||
LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error)
|
||||
|
||||
UpdateConfByFile(req dto.DBConfUpdateByFile) error
|
||||
}
|
||||
|
||||
func NewIDBCommonService() IDBCommonService {
|
||||
return &DBCommonService{}
|
||||
}
|
||||
|
||||
func (u *DBCommonService) LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error) {
|
||||
var data dto.DBBaseInfo
|
||||
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data.ContainerName = app.ContainerName
|
||||
data.Name = app.Name
|
||||
data.Port = int64(app.Port)
|
||||
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func (u *DBCommonService) LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error) {
|
||||
filePath := ""
|
||||
switch req.Type {
|
||||
case "mysql-conf":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/conf/my.cnf", req.Name))
|
||||
case "mariadb-conf":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/conf/my.cnf", req.Name))
|
||||
case "postgresql-conf":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/postgresql/%s/data/postgresql.conf", req.Name))
|
||||
case "redis-conf":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/redis/%s/conf/redis.conf", req.Name))
|
||||
case "mysql-slow-logs":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
|
||||
case "mariadb-slow-logs":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/db/data/1Panel-slow.log", req.Name))
|
||||
}
|
||||
if _, err := os.Stat(filePath); err != nil {
|
||||
return "", buserr.New("ErrHttpReqNotFound")
|
||||
}
|
||||
content, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
func (u *DBCommonService) UpdateConfByFile(req dto.DBConfUpdateByFile) error {
|
||||
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := ""
|
||||
switch req.Type {
|
||||
case constant.AppMariaDB, constant.AppMysql:
|
||||
path = fmt.Sprintf("%s/%s/%s/conf/my.cnf", constant.AppInstallDir, req.Type, app.Name)
|
||||
case constant.AppPostgresql:
|
||||
path = fmt.Sprintf("%s/%s/%s/data/postgresql.conf", constant.AppInstallDir, req.Type, app.Name)
|
||||
case constant.AppRedis:
|
||||
path = fmt.Sprintf("%s/%s/%s/conf/redis.conf", constant.AppInstallDir, req.Type, app.Name)
|
||||
}
|
||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
write := bufio.NewWriter(file)
|
||||
_, _ = write.WriteString(req.File)
|
||||
write.Flush()
|
||||
if _, err := compose.Restart(fmt.Sprintf("%s/%s/%s/docker-compose.yml", constant.AppInstallDir, req.Type, app.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -35,21 +34,18 @@ type IMysqlService interface {
|
||||
SearchWithPage(search dto.MysqlDBSearch) (int64, interface{}, error)
|
||||
ListDBOption() ([]dto.MysqlOption, error)
|
||||
Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error)
|
||||
BindUser(req dto.BindUser) error
|
||||
LoadFromRemote(req dto.MysqlLoadDB) error
|
||||
ChangeAccess(info dto.ChangeDBInfo) error
|
||||
ChangePassword(info dto.ChangeDBInfo) error
|
||||
UpdateVariables(req dto.MysqlVariablesUpdate) error
|
||||
UpdateConfByFile(info dto.MysqlConfUpdateByFile) error
|
||||
UpdateDescription(req dto.UpdateDescription) error
|
||||
DeleteCheck(req dto.MysqlDBDeleteCheck) ([]string, error)
|
||||
Delete(ctx context.Context, req dto.MysqlDBDelete) error
|
||||
|
||||
LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlStatus, error)
|
||||
LoadVariables(req dto.OperationWithNameAndType) (*dto.MysqlVariables, error)
|
||||
LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error)
|
||||
LoadRemoteAccess(req dto.OperationWithNameAndType) (bool, error)
|
||||
|
||||
LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error)
|
||||
}
|
||||
|
||||
func NewIMysqlService() IMysqlService {
|
||||
@@ -144,6 +140,42 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
|
||||
return &createItem, nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) BindUser(req dto.BindUser) error {
|
||||
dbItem, err := mysqlRepo.Get(mysqlRepo.WithByMysqlName(req.Database), commonRepo.WithByName(req.DB))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cli, version, err := LoadMysqlClientByFrom(req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
if err := cli.CreateUser(client.CreateInfo{
|
||||
Name: dbItem.Name,
|
||||
Format: dbItem.Format,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Permission: req.Permission,
|
||||
Version: version,
|
||||
Timeout: 300,
|
||||
}, false); err != nil {
|
||||
return err
|
||||
}
|
||||
pass, err := encrypt.StringEncrypt(req.Password)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decrypt database db password failed, err: %v", err)
|
||||
}
|
||||
if err := mysqlRepo.Update(dbItem.ID, map[string]interface{}{
|
||||
"username": req.Username,
|
||||
"password": pass,
|
||||
"permission": req.Permission,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) LoadFromRemote(req dto.MysqlLoadDB) error {
|
||||
client, version, err := LoadMysqlClientByFrom(req.Database)
|
||||
if err != nil {
|
||||
@@ -369,26 +401,6 @@ func (u *MysqlService) ChangeAccess(req dto.ChangeDBInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) UpdateConfByFile(req dto.MysqlConfUpdateByFile) error {
|
||||
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := fmt.Sprintf("%s/%s/%s/conf/my.cnf", constant.AppInstallDir, req.Type, app.Name)
|
||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
write := bufio.NewWriter(file)
|
||||
_, _ = write.WriteString(req.File)
|
||||
write.Flush()
|
||||
if _, err := compose.Restart(fmt.Sprintf("%s/%s/%s/docker-compose.yml", constant.AppInstallDir, req.Type, app.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) UpdateVariables(req dto.MysqlVariablesUpdate) error {
|
||||
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
|
||||
if err != nil {
|
||||
@@ -434,19 +446,6 @@ func (u *MysqlService) UpdateVariables(req dto.MysqlVariablesUpdate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error) {
|
||||
var data dto.DBBaseInfo
|
||||
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data.ContainerName = app.ContainerName
|
||||
data.Name = app.Name
|
||||
data.Port = int64(app.Port)
|
||||
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) LoadRemoteAccess(req dto.OperationWithNameAndType) (bool, error) {
|
||||
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
|
||||
if err != nil {
|
||||
@@ -528,30 +527,6 @@ func (u *MysqlService) LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlS
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error) {
|
||||
filePath := ""
|
||||
switch req.Type {
|
||||
case "mysql-conf":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/conf/my.cnf", req.Name))
|
||||
case "mariadb-conf":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/conf/my.cnf", req.Name))
|
||||
case "redis-conf":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/redis/%s/conf/redis.conf", req.Name))
|
||||
case "mysql-slow-logs":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
|
||||
case "mariadb-slow-logs":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/db/data/1Panel-slow.log", req.Name))
|
||||
}
|
||||
if _, err := os.Stat(filePath); err != nil {
|
||||
return "", buserr.New("ErrHttpReqNotFound")
|
||||
}
|
||||
content, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
func executeSqlForMaps(containerName, password, command string) (map[string]string, error) {
|
||||
cmd := exec.Command("docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
|
||||
@@ -0,0 +1,413 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/postgresql"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
|
||||
_ "github.com/jackc/pgx/v5/stdlib"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type PostgresqlService struct{}
|
||||
|
||||
type IPostgresqlService interface {
|
||||
SearchWithPage(search dto.PostgresqlDBSearch) (int64, interface{}, error)
|
||||
ListDBOption() ([]dto.PostgresqlOption, error)
|
||||
BindUser(req dto.PostgresqlBindUser) error
|
||||
Create(ctx context.Context, req dto.PostgresqlDBCreate) (*model.DatabasePostgresql, error)
|
||||
LoadFromRemote(database string) error
|
||||
ChangePrivileges(req dto.PostgresqlPrivileges) error
|
||||
ChangePassword(info dto.ChangeDBInfo) error
|
||||
UpdateDescription(req dto.UpdateDescription) error
|
||||
DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]string, error)
|
||||
Delete(ctx context.Context, req dto.PostgresqlDBDelete) error
|
||||
}
|
||||
|
||||
func NewIPostgresqlService() IPostgresqlService {
|
||||
return &PostgresqlService{}
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) SearchWithPage(search dto.PostgresqlDBSearch) (int64, interface{}, error) {
|
||||
total, postgresqls, err := postgresqlRepo.Page(search.Page, search.PageSize,
|
||||
postgresqlRepo.WithByPostgresqlName(search.Database),
|
||||
commonRepo.WithLikeName(search.Info),
|
||||
commonRepo.WithOrderRuleBy(search.OrderBy, search.Order),
|
||||
)
|
||||
var dtoPostgresqls []dto.PostgresqlDBInfo
|
||||
for _, pg := range postgresqls {
|
||||
var item dto.PostgresqlDBInfo
|
||||
if err := copier.Copy(&item, &pg); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
dtoPostgresqls = append(dtoPostgresqls, item)
|
||||
}
|
||||
return total, dtoPostgresqls, err
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) ListDBOption() ([]dto.PostgresqlOption, error) {
|
||||
postgresqls, err := postgresqlRepo.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
databases, err := databaseRepo.GetList(databaseRepo.WithTypeList("postgresql,mariadb"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var dbs []dto.PostgresqlOption
|
||||
for _, pg := range postgresqls {
|
||||
var item dto.PostgresqlOption
|
||||
if err := copier.Copy(&item, &pg); err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
item.Database = pg.PostgresqlName
|
||||
for _, database := range databases {
|
||||
if database.Name == item.Database {
|
||||
item.Type = database.Type
|
||||
}
|
||||
}
|
||||
dbs = append(dbs, item)
|
||||
}
|
||||
return dbs, err
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) BindUser(req dto.PostgresqlBindUser) error {
|
||||
if cmd.CheckIllegal(req.Name, req.Username, req.Password) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
dbItem, err := postgresqlRepo.Get(postgresqlRepo.WithByPostgresqlName(req.Database), commonRepo.WithByName(req.Name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pgClient, err := LoadPostgresqlClientByFrom(req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := pgClient.CreateUser(client.CreateInfo{
|
||||
Name: req.Name,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
SuperUser: req.SuperUser,
|
||||
Timeout: 300,
|
||||
}, false); err != nil {
|
||||
return err
|
||||
}
|
||||
pass, err := encrypt.StringEncrypt(req.Password)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decrypt database db password failed, err: %v", err)
|
||||
}
|
||||
if err := postgresqlRepo.Update(dbItem.ID, map[string]interface{}{
|
||||
"username": req.Username,
|
||||
"password": pass,
|
||||
"super_user": req.SuperUser,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) Create(ctx context.Context, req dto.PostgresqlDBCreate) (*model.DatabasePostgresql, error) {
|
||||
if cmd.CheckIllegal(req.Name, req.Username, req.Password, req.Format) {
|
||||
return nil, buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
|
||||
pgsql, _ := postgresqlRepo.Get(commonRepo.WithByName(req.Name), postgresqlRepo.WithByPostgresqlName(req.Database), databaseRepo.WithByFrom(req.From))
|
||||
if pgsql.ID != 0 {
|
||||
return nil, constant.ErrRecordExist
|
||||
}
|
||||
|
||||
var createItem model.DatabasePostgresql
|
||||
if err := copier.Copy(&createItem, &req); err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
|
||||
if req.From == "local" && req.Username == "root" {
|
||||
return nil, errors.New("Cannot set root as user name")
|
||||
}
|
||||
|
||||
cli, err := LoadPostgresqlClientByFrom(req.Database)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
createItem.PostgresqlName = req.Database
|
||||
defer cli.Close()
|
||||
if err := cli.Create(client.CreateInfo{
|
||||
Name: req.Name,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
SuperUser: req.SuperUser,
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
global.LOG.Infof("create database %s successful!", req.Name)
|
||||
if err := postgresqlRepo.Create(ctx, &createItem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &createItem, nil
|
||||
}
|
||||
|
||||
func LoadPostgresqlClientByFrom(database string) (postgresql.PostgresqlClient, error) {
|
||||
var (
|
||||
dbInfo client.DBInfo
|
||||
err error
|
||||
)
|
||||
|
||||
dbInfo.Timeout = 300
|
||||
databaseItem, err := databaseRepo.Get(commonRepo.WithByName(database))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbInfo.From = databaseItem.From
|
||||
dbInfo.Database = database
|
||||
if dbInfo.From != "local" {
|
||||
dbInfo.Address = databaseItem.Address
|
||||
dbInfo.Port = databaseItem.Port
|
||||
dbInfo.Username = databaseItem.Username
|
||||
dbInfo.Password = databaseItem.Password
|
||||
} else {
|
||||
app, err := appInstallRepo.LoadBaseInfo(databaseItem.Type, database)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbInfo.From = "local"
|
||||
dbInfo.Address = app.ContainerName
|
||||
dbInfo.Username = app.UserName
|
||||
dbInfo.Password = app.Password
|
||||
dbInfo.Port = uint(app.Port)
|
||||
}
|
||||
|
||||
cli, err := postgresql.NewPostgresqlClient(dbInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cli, nil
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) LoadFromRemote(database string) error {
|
||||
client, err := LoadPostgresqlClientByFrom(database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
databases, err := postgresqlRepo.List(postgresqlRepo.WithByPostgresqlName(database))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
datas, err := client.SyncDB()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, data := range datas {
|
||||
hasOld := false
|
||||
for _, oldData := range databases {
|
||||
if strings.EqualFold(oldData.Name, data.Name) && strings.EqualFold(oldData.PostgresqlName, data.PostgresqlName) {
|
||||
hasOld = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasOld {
|
||||
var createItem model.DatabasePostgresql
|
||||
if err := copier.Copy(&createItem, &data); err != nil {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
if err := postgresqlRepo.Create(context.Background(), &createItem); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) UpdateDescription(req dto.UpdateDescription) error {
|
||||
return postgresqlRepo.Update(req.ID, map[string]interface{}{"description": req.Description})
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]string, error) {
|
||||
var appInUsed []string
|
||||
db, err := postgresqlRepo.Get(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return appInUsed, err
|
||||
}
|
||||
|
||||
if db.From == "local" {
|
||||
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
|
||||
if err != nil {
|
||||
return appInUsed, err
|
||||
}
|
||||
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(db.ID))
|
||||
for _, app := range apps {
|
||||
appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId))
|
||||
if appInstall.ID != 0 {
|
||||
appInUsed = append(appInUsed, appInstall.Name)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(db.ID))
|
||||
for _, app := range apps {
|
||||
appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId))
|
||||
if appInstall.ID != 0 {
|
||||
appInUsed = append(appInUsed, appInstall.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return appInUsed, nil
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) Delete(ctx context.Context, req dto.PostgresqlDBDelete) error {
|
||||
db, err := postgresqlRepo.Get(commonRepo.WithByID(req.ID))
|
||||
if err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
cli, err := LoadPostgresqlClientByFrom(req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
if err := cli.Delete(client.DeleteInfo{
|
||||
Name: db.Name,
|
||||
Username: db.Username,
|
||||
ForceDelete: req.ForceDelete,
|
||||
Timeout: 300,
|
||||
}); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
|
||||
if req.DeleteBackup {
|
||||
uploadDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/uploads/database/%s/%s/%s", req.Type, req.Database, db.Name))
|
||||
if _, err := os.Stat(uploadDir); err == nil {
|
||||
_ = os.RemoveAll(uploadDir)
|
||||
}
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
backupDir := path.Join(localDir, fmt.Sprintf("database/%s/%s/%s", req.Type, db.PostgresqlName, db.Name))
|
||||
if _, err := os.Stat(backupDir); err == nil {
|
||||
_ = os.RemoveAll(backupDir)
|
||||
}
|
||||
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType(req.Type), commonRepo.WithByName(req.Database), backupRepo.WithByDetailName(db.Name))
|
||||
global.LOG.Infof("delete database %s-%s backups successful", req.Database, db.Name)
|
||||
}
|
||||
|
||||
_ = postgresqlRepo.Delete(ctx, commonRepo.WithByID(db.ID))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) ChangePrivileges(req dto.PostgresqlPrivileges) error {
|
||||
if cmd.CheckIllegal(req.Database, req.Username) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
dbItem, err := postgresqlRepo.Get(postgresqlRepo.WithByPostgresqlName(req.Database), commonRepo.WithByName(req.Name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cli, err := LoadPostgresqlClientByFrom(req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
if err := cli.ChangePrivileges(client.Privileges{Username: req.Username, SuperUser: req.SuperUser, Timeout: 300}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := postgresqlRepo.Update(dbItem.ID, map[string]interface{}{
|
||||
"super_user": req.SuperUser,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *PostgresqlService) ChangePassword(req dto.ChangeDBInfo) error {
|
||||
if cmd.CheckIllegal(req.Value) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
cli, err := LoadPostgresqlClientByFrom(req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
var (
|
||||
postgresqlData model.DatabasePostgresql
|
||||
passwordInfo client.PasswordChangeInfo
|
||||
)
|
||||
passwordInfo.Password = req.Value
|
||||
passwordInfo.Timeout = 300
|
||||
|
||||
if req.ID != 0 {
|
||||
postgresqlData, err = postgresqlRepo.Get(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
passwordInfo.Username = postgresqlData.Username
|
||||
} else {
|
||||
dbItem, err := databaseRepo.Get(commonRepo.WithByType(req.Type), commonRepo.WithByFrom(req.From))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
passwordInfo.Username = dbItem.Username
|
||||
}
|
||||
if err := cli.ChangePassword(passwordInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if req.ID != 0 {
|
||||
var appRess []model.AppInstallResource
|
||||
if req.From == "local" {
|
||||
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appRess, _ = appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(postgresqlData.ID))
|
||||
} else {
|
||||
appRess, _ = appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(postgresqlData.ID))
|
||||
}
|
||||
for _, appRes := range appRess {
|
||||
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(appRes.AppInstallId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appModel, err := appRepo.GetFirst(commonRepo.WithByID(appInstall.AppId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
global.LOG.Infof("start to update postgresql password used by app %s-%s", appModel.Key, appInstall.Name)
|
||||
if err := updateInstallInfoInDB(appModel.Key, appInstall.Name, "user-password", true, req.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
global.LOG.Info("execute password change sql successful")
|
||||
pass, err := encrypt.StringEncrypt(req.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decrypt database db password failed, err: %v", err)
|
||||
}
|
||||
_ = postgresqlRepo.Update(postgresqlData.ID, map[string]interface{}{"password": pass})
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := updateInstallInfoInDB(req.Type, req.Database, "password", false, req.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
|
||||
confs = append(confs, redisConfig{key: "timeout", value: req.Timeout})
|
||||
confs = append(confs, redisConfig{key: "maxclients", value: req.Maxclients})
|
||||
confs = append(confs, redisConfig{key: "maxmemory", value: req.Maxmemory})
|
||||
if err := confSet(redisInfo.Name, confs); err != nil {
|
||||
if err := confSet(redisInfo.Name, "", confs); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := compose.Restart(fmt.Sprintf("%s/redis/%s/docker-compose.yml", constant.AppInstallDir, redisInfo.Name)); err != nil {
|
||||
@@ -78,7 +78,7 @@ func (u *RedisService) UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate)
|
||||
confs = append(confs, redisConfig{key: "appendonly", value: req.Appendonly})
|
||||
confs = append(confs, redisConfig{key: "appendfsync", value: req.Appendfsync})
|
||||
}
|
||||
if err := confSet(redisInfo.Name, confs); err != nil {
|
||||
if err := confSet(redisInfo.Name, req.Type, confs); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := compose.Restart(fmt.Sprintf("%s/redis/%s/docker-compose.yml", constant.AppInstallDir, redisInfo.Name)); err != nil {
|
||||
@@ -213,7 +213,7 @@ type redisConfig struct {
|
||||
value string
|
||||
}
|
||||
|
||||
func confSet(redisName string, changeConf []redisConfig) error {
|
||||
func confSet(redisName string, updateType string, changeConf []redisConfig) error {
|
||||
path := fmt.Sprintf("%s/redis/%s/conf/redis.conf", constant.AppInstallDir, redisName)
|
||||
lineBytes, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
@@ -221,19 +221,38 @@ func confSet(redisName string, changeConf []redisConfig) error {
|
||||
}
|
||||
files := strings.Split(string(lineBytes), "\n")
|
||||
|
||||
startIndex, endIndex := 0, 0
|
||||
startIndex, endIndex, emptyLine := 0, 0, 0
|
||||
var newFiles []string
|
||||
for i := 0; i < len(files); i++ {
|
||||
if files[i] == "# Redis configuration rewrite by 1Panel" {
|
||||
startIndex = i
|
||||
newFiles = append(newFiles, files[i])
|
||||
continue
|
||||
}
|
||||
if files[i] == "# End Redis configuration rewrite by 1Panel" {
|
||||
endIndex = i
|
||||
break
|
||||
}
|
||||
if startIndex == 0 && strings.HasPrefix(files[i], "save ") {
|
||||
newFiles = append(newFiles, "# "+files[i])
|
||||
continue
|
||||
}
|
||||
if startIndex != 0 && endIndex == 0 && (len(files[i]) == 0 || (updateType == "rbd" && strings.HasPrefix(files[i], "save "))) {
|
||||
emptyLine++
|
||||
continue
|
||||
}
|
||||
newFiles = append(newFiles, files[i])
|
||||
}
|
||||
endIndex = endIndex - emptyLine
|
||||
for _, item := range changeConf {
|
||||
if item.key == "save" {
|
||||
saveVal := strings.Split(item.value, ",")
|
||||
for i := 0; i < len(saveVal); i++ {
|
||||
newFiles = append(newFiles, "save "+saveVal[i])
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
isExist := false
|
||||
for i := startIndex; i < endIndex; i++ {
|
||||
if strings.HasPrefix(newFiles[i], item.key) || strings.HasPrefix(newFiles[i], "# "+item.key) {
|
||||
|
||||
@@ -55,10 +55,15 @@ func (u *DeviceService) LoadBaseInfo() (dto.DeviceBaseInfo, error) {
|
||||
baseInfo.Hosts = loadHosts()
|
||||
baseInfo.Hostname = loadHostname()
|
||||
baseInfo.User = loadUser()
|
||||
ntp, _ := settingRepo.Get(settingRepo.WithByKey("NtpSite"))
|
||||
baseInfo.Ntp = ntp.Value
|
||||
ntp, err := settingRepo.Get(settingRepo.WithByKey("NtpSite"))
|
||||
if err == nil {
|
||||
baseInfo.Ntp = ntp.Value
|
||||
}
|
||||
|
||||
swapInfo, _ := mem.SwapMemory()
|
||||
swapInfo, err := mem.SwapMemory()
|
||||
if err != nil {
|
||||
return baseInfo, err
|
||||
}
|
||||
baseInfo.SwapMemoryTotal = swapInfo.Total
|
||||
baseInfo.SwapMemoryAvailable = swapInfo.Free
|
||||
baseInfo.SwapMemoryUsed = swapInfo.Used
|
||||
@@ -348,7 +353,7 @@ func loadHosts() []dto.HostHelper {
|
||||
lines := strings.Split(string(hostConf), "\n")
|
||||
for _, line := range lines {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) < 2 {
|
||||
if len(parts) < 2 || strings.HasPrefix(strings.TrimPrefix(line, " "), "#") {
|
||||
continue
|
||||
}
|
||||
list = append(list, dto.HostHelper{IP: parts[0], Host: strings.Join(parts[1:], " ")})
|
||||
@@ -375,7 +380,7 @@ func loadUser() string {
|
||||
|
||||
func loadSwap() []dto.SwapHelper {
|
||||
var data []dto.SwapHelper
|
||||
std, err := cmd.Execf("%s swapon --show --summary", cmd.SudoHandleCmd())
|
||||
std, err := cmd.Execf("%s swapon --summary", cmd.SudoHandleCmd())
|
||||
if err != nil {
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -12,8 +12,9 @@ var (
|
||||
appInstallRepo = repo.NewIAppInstallRepo()
|
||||
appInstallResourceRepo = repo.NewIAppInstallResourceRpo()
|
||||
|
||||
mysqlRepo = repo.NewIMysqlRepo()
|
||||
databaseRepo = repo.NewIDatabaseRepo()
|
||||
mysqlRepo = repo.NewIMysqlRepo()
|
||||
postgresqlRepo = repo.NewIPostgresqlRepo()
|
||||
databaseRepo = repo.NewIDatabaseRepo()
|
||||
|
||||
imageRepoRepo = repo.NewIImageRepoRepo()
|
||||
composeRepo = repo.NewIComposeTemplateRepo()
|
||||
@@ -37,7 +38,8 @@ var (
|
||||
logRepo = repo.NewILogRepo()
|
||||
snapshotRepo = repo.NewISnapshotRepo()
|
||||
|
||||
runtimeRepo = repo.NewIRunTimeRepo()
|
||||
runtimeRepo = repo.NewIRunTimeRepo()
|
||||
phpExtensionsRepo = repo.NewIPHPExtensionsRepo()
|
||||
|
||||
favoriteRepo = repo.NewIFavoriteRepo()
|
||||
)
|
||||
|
||||
@@ -39,12 +39,16 @@ func (u *Fail2BanService) LoadBaseInfo() (dto.Fail2BanBaseInfo, error) {
|
||||
baseInfo.IsEnable, baseInfo.IsActive, baseInfo.IsExist = client.Status()
|
||||
if !baseInfo.IsActive {
|
||||
baseInfo.Version = "-"
|
||||
return baseInfo, nil
|
||||
} else {
|
||||
baseInfo.Version = client.Version()
|
||||
}
|
||||
baseInfo.Version = client.Version()
|
||||
conf, err := os.ReadFile(defaultFail2BanPath)
|
||||
if err != nil {
|
||||
return baseInfo, fmt.Errorf("read fail2ban conf of %s failed, err: %v", defaultFail2BanPath, err)
|
||||
if baseInfo.IsActive {
|
||||
return baseInfo, fmt.Errorf("read fail2ban conf of %s failed, err: %v", defaultFail2BanPath, err)
|
||||
} else {
|
||||
return baseInfo, nil
|
||||
}
|
||||
}
|
||||
lines := strings.Split(string(conf), "\n")
|
||||
|
||||
@@ -103,7 +107,7 @@ func (u *Fail2BanService) UpdateConf(req dto.Fail2BanUpdate) error {
|
||||
if req.Value == "firewallcmd-ipset" || req.Value == "ufw" {
|
||||
itemName := "ufw"
|
||||
if req.Value == "firewallcmd-ipset" {
|
||||
itemName = "firewallcmd"
|
||||
itemName = "firewalld"
|
||||
}
|
||||
client, err := firewall.NewFirewallClient()
|
||||
if err != nil {
|
||||
|
||||
@@ -243,7 +243,7 @@ func (f *FileService) ChangeName(req request.FileRename) error {
|
||||
func (f *FileService) Wget(w request.FileWget) (string, error) {
|
||||
fo := files.NewFileOp()
|
||||
key := "file-wget-" + common.GetUuid()
|
||||
return key, fo.DownloadFileWithProcess(w.Url, filepath.Join(w.Path, w.Name), key)
|
||||
return key, fo.DownloadFileWithProcess(w.Url, filepath.Join(w.Path, w.Name), key, w.IgnoreCertificate)
|
||||
}
|
||||
|
||||
func (f *FileService) MvFile(m request.FileMove) error {
|
||||
|
||||
@@ -196,6 +196,12 @@ func (u *FirewallService) OperateFirewall(operation string) error {
|
||||
}
|
||||
_, _ = cmd.Exec("systemctl restart docker")
|
||||
return nil
|
||||
case "restart":
|
||||
if err := client.Restart(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = cmd.Exec("systemctl restart docker")
|
||||
return nil
|
||||
case "disablePing":
|
||||
return u.updatePingStatus("0")
|
||||
case "enablePing":
|
||||
@@ -582,24 +588,6 @@ func (u *FirewallService) addAddressRecord(req dto.AddrRuleOperate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func listIpRules(strategy string) ([]string, error) {
|
||||
client, err := firewall.NewFirewallClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addrs, err := client.ListAddress()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var rules []string
|
||||
for _, addr := range addrs {
|
||||
if addr.Strategy == strategy {
|
||||
rules = append(rules, addr.Address)
|
||||
}
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func checkPortUsed(ports, proto string, apps []portOfApp) string {
|
||||
var portList []int
|
||||
if strings.Contains(ports, "-") || strings.Contains(ports, ",") {
|
||||
|
||||
@@ -28,6 +28,7 @@ type ImageService struct{}
|
||||
type IImageService interface {
|
||||
Page(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
List() ([]dto.Options, error)
|
||||
ListAll() ([]dto.ImageInfo, error)
|
||||
ImageBuild(req dto.ImageBuild) (string, error)
|
||||
ImagePull(req dto.ImagePull) (string, error)
|
||||
ImageLoad(req dto.ImageLoad) error
|
||||
@@ -97,6 +98,30 @@ func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
return int64(total), backDatas, nil
|
||||
}
|
||||
|
||||
func (u *ImageService) ListAll() ([]dto.ImageInfo, error) {
|
||||
var records []dto.ImageInfo
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list, err := client.ImageList(context.Background(), types.ImageListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containers, _ := client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
|
||||
for _, image := range list {
|
||||
size := formatFileSize(image.Size)
|
||||
records = append(records, dto.ImageInfo{
|
||||
ID: image.ID,
|
||||
Tags: image.RepoTags,
|
||||
IsUsed: checkUsed(image.ID, containers),
|
||||
CreatedAt: time.Unix(image.Created, 0),
|
||||
Size: size,
|
||||
})
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func (u *ImageService) List() ([]dto.Options, error) {
|
||||
var (
|
||||
list []types.ImageSummary
|
||||
|
||||
@@ -211,6 +211,6 @@ func StartMonitor(removeBefore bool, interval string) error {
|
||||
go service.saveIODataToDB(ctx, float64(intervalItem))
|
||||
go service.saveNetDataToDB(ctx, float64(intervalItem))
|
||||
|
||||
global.MonitorCronID = int(monitorID)
|
||||
global.MonitorCronID = monitorID
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -26,6 +27,7 @@ type INginxService interface {
|
||||
UpdateConfigByScope(req request.NginxConfigUpdate) error
|
||||
GetStatus() (response.NginxStatus, error)
|
||||
UpdateConfigFile(req request.NginxConfigFileUpdate) error
|
||||
ClearProxyCache() error
|
||||
}
|
||||
|
||||
func NewINginxService() INginxService {
|
||||
@@ -121,3 +123,22 @@ func (n NginxService) UpdateConfigFile(req request.NginxConfigFileUpdate) error
|
||||
}
|
||||
return nginxCheckAndReload(string(oldContent), filePath, nginxInstall.ContainerName)
|
||||
}
|
||||
|
||||
func (n NginxService) ClearProxyCache() error {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cacheDir := path.Join(nginxInstall.GetPath(), "www/common/proxy/proxy_cache_dir")
|
||||
fileOp := files.NewFileOp()
|
||||
if fileOp.Stat(cacheDir) {
|
||||
if err = fileOp.CleanDir(cacheDir); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = compose.Restart(nginxInstall.GetComposePath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
)
|
||||
|
||||
type PHPExtensionsService struct {
|
||||
}
|
||||
|
||||
type IPHPExtensionsService interface {
|
||||
Page(req request.PHPExtensionsSearch) (int64, []response.PHPExtensionsDTO, error)
|
||||
List() ([]response.PHPExtensionsDTO, error)
|
||||
Create(req request.PHPExtensionsCreate) error
|
||||
Update(req request.PHPExtensionsUpdate) error
|
||||
Delete(req request.PHPExtensionsDelete) error
|
||||
}
|
||||
|
||||
func NewIPHPExtensionsService() IPHPExtensionsService {
|
||||
return &PHPExtensionsService{}
|
||||
}
|
||||
|
||||
func (p PHPExtensionsService) Page(req request.PHPExtensionsSearch) (int64, []response.PHPExtensionsDTO, error) {
|
||||
var (
|
||||
total int64
|
||||
extensions []model.PHPExtensions
|
||||
err error
|
||||
result []response.PHPExtensionsDTO
|
||||
)
|
||||
total, extensions, err = phpExtensionsRepo.Page(req.Page, req.PageSize)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
for _, extension := range extensions {
|
||||
result = append(result, response.PHPExtensionsDTO{
|
||||
PHPExtensions: extension,
|
||||
})
|
||||
}
|
||||
return total, result, nil
|
||||
}
|
||||
|
||||
func (p PHPExtensionsService) List() ([]response.PHPExtensionsDTO, error) {
|
||||
var (
|
||||
extensions []model.PHPExtensions
|
||||
err error
|
||||
result []response.PHPExtensionsDTO
|
||||
)
|
||||
extensions, err = phpExtensionsRepo.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, extension := range extensions {
|
||||
result = append(result, response.PHPExtensionsDTO{
|
||||
PHPExtensions: extension,
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (p PHPExtensionsService) Create(req request.PHPExtensionsCreate) error {
|
||||
exist, _ := phpExtensionsRepo.GetFirst(commonRepo.WithByName(req.Name))
|
||||
if exist.ID > 0 {
|
||||
return buserr.New(constant.ErrNameIsExist)
|
||||
}
|
||||
extension := model.PHPExtensions{
|
||||
Name: req.Name,
|
||||
Extensions: req.Extensions,
|
||||
}
|
||||
return phpExtensionsRepo.Create(&extension)
|
||||
}
|
||||
|
||||
func (p PHPExtensionsService) Update(req request.PHPExtensionsUpdate) error {
|
||||
exist, err := phpExtensionsRepo.GetFirst(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
exist.Extensions = req.Extensions
|
||||
return phpExtensionsRepo.Save(&exist)
|
||||
}
|
||||
|
||||
func (p PHPExtensionsService) Delete(req request.PHPExtensionsDelete) error {
|
||||
return phpExtensionsRepo.DeleteBy(commonRepo.WithByID(req.ID))
|
||||
}
|
||||
@@ -2,6 +2,14 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||
@@ -9,12 +17,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RecycleBinService struct {
|
||||
@@ -193,6 +195,10 @@ func getRecycleBinDTOFromName(filename string) (*response.RecycleBinDTO, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if size < math.MinInt || size > math.MaxInt {
|
||||
return nil, fmt.Errorf("size out of int range")
|
||||
}
|
||||
|
||||
deleteTime, err := strconv.ParseInt(matches[3], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -198,6 +198,9 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
|
||||
}
|
||||
_ = os.Remove(path.Join(secretDir, "server.crt"))
|
||||
_ = os.Remove(path.Join(secretDir, "server.key"))
|
||||
sID, _ := c.Cookie(constant.SessionName)
|
||||
c.SetCookie(constant.SessionName, sID, 0, "", "", false, false)
|
||||
|
||||
go func() {
|
||||
_, err := cmd.Exec("systemctl restart 1panel.service")
|
||||
if err != nil {
|
||||
@@ -289,6 +292,9 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
|
||||
if err := settingRepo.Update("SSL", req.SSL); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sID, _ := c.Cookie(constant.SessionName)
|
||||
c.SetCookie(constant.SessionName, sID, 0, "", "", true, false)
|
||||
go func() {
|
||||
time.Sleep(1 * time.Second)
|
||||
_, err := cmd.Exec("systemctl restart 1panel.service")
|
||||
|
||||
@@ -41,7 +41,7 @@ type ISnapshotService interface {
|
||||
UpdateDescription(req dto.UpdateDescription) error
|
||||
readFromJson(path string) (SnapshotJson, error)
|
||||
|
||||
HandleSnapshot(isCronjob bool, req dto.SnapshotCreate, timeNow string) (string, string, error)
|
||||
HandleSnapshot(isCronjob bool, logPath string, req dto.SnapshotCreate, timeNow string) (string, error)
|
||||
}
|
||||
|
||||
func NewISnapshotService() ISnapshotService {
|
||||
@@ -77,23 +77,16 @@ func (u *SnapshotService) SnapshotImport(req dto.SnapshotImport) error {
|
||||
if !strings.HasPrefix(shortName, "1panel_v") || !strings.HasSuffix(shortName, ".tar.gz") || len(nameItems) != 3 {
|
||||
return fmt.Errorf("incorrect snapshot name format of %s", shortName)
|
||||
}
|
||||
formatTime, err := time.Parse("20060102150405", strings.ReplaceAll(nameItems[2], ".tar.gz", ""))
|
||||
if err != nil {
|
||||
return fmt.Errorf("incorrect snapshot name format of %s", shortName)
|
||||
}
|
||||
if strings.HasSuffix(snap, ".tar.gz") {
|
||||
snap = strings.ReplaceAll(snap, ".tar.gz", "")
|
||||
}
|
||||
itemSnap := model.Snapshot{
|
||||
Name: snap,
|
||||
From: req.From,
|
||||
Version: nameItems[1],
|
||||
Description: req.Description,
|
||||
Status: constant.StatusSuccess,
|
||||
BaseModel: model.BaseModel{
|
||||
CreatedAt: formatTime,
|
||||
UpdatedAt: formatTime,
|
||||
},
|
||||
Name: snap,
|
||||
From: req.From,
|
||||
DefaultDownload: req.From,
|
||||
Version: nameItems[1],
|
||||
Description: req.Description,
|
||||
Status: constant.StatusSuccess,
|
||||
}
|
||||
if err := snapshotRepo.Create(&itemSnap); err != nil {
|
||||
return err
|
||||
@@ -132,7 +125,7 @@ type SnapshotJson struct {
|
||||
}
|
||||
|
||||
func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
|
||||
if _, _, err := u.HandleSnapshot(false, req, time.Now().Format("20060102150405")); err != nil {
|
||||
if _, err := u.HandleSnapshot(false, "", req, time.Now().Format("20060102150405")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -152,7 +145,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
|
||||
if len(snap.InterruptStep) != 0 && !req.IsNew {
|
||||
isReTry = true
|
||||
}
|
||||
backup, err := backupRepo.Get(commonRepo.WithByType(snap.From))
|
||||
backup, err := backupRepo.Get(commonRepo.WithByType(snap.DefaultDownload))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -469,10 +462,10 @@ func (u *SnapshotService) readFromJson(path string) (SnapshotJson, error) {
|
||||
return snap, nil
|
||||
}
|
||||
|
||||
func (u *SnapshotService) HandleSnapshot(isCronjob bool, req dto.SnapshotCreate, timeNow string) (string, string, error) {
|
||||
func (u *SnapshotService) HandleSnapshot(isCronjob bool, logPath string, req dto.SnapshotCreate, timeNow string) (string, error) {
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return "", err
|
||||
}
|
||||
var (
|
||||
rootDir string
|
||||
@@ -489,11 +482,12 @@ func (u *SnapshotService) HandleSnapshot(isCronjob bool, req dto.SnapshotCreate,
|
||||
rootDir = path.Join(localDir, "system", name)
|
||||
|
||||
snap = model.Snapshot{
|
||||
Name: name,
|
||||
Description: req.Description,
|
||||
From: req.From,
|
||||
Version: versionItem.Value,
|
||||
Status: constant.StatusWaiting,
|
||||
Name: name,
|
||||
Description: req.Description,
|
||||
From: req.From,
|
||||
DefaultDownload: req.DefaultDownload,
|
||||
Version: versionItem.Value,
|
||||
Status: constant.StatusWaiting,
|
||||
}
|
||||
_ = snapshotRepo.Create(&snap)
|
||||
snapStatus.SnapID = snap.ID
|
||||
@@ -501,7 +495,7 @@ func (u *SnapshotService) HandleSnapshot(isCronjob bool, req dto.SnapshotCreate,
|
||||
} else {
|
||||
snap, err = snapshotRepo.Get(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return "", err
|
||||
}
|
||||
snapStatus, _ = snapshotRepo.GetStatus(snap.ID)
|
||||
if snapStatus.ID == 0 {
|
||||
@@ -523,7 +517,7 @@ func (u *SnapshotService) HandleSnapshot(isCronjob bool, req dto.SnapshotCreate,
|
||||
BackupDataDir: localDir,
|
||||
PanelDataDir: path.Join(global.CONF.System.BaseDir, "1panel"),
|
||||
}
|
||||
|
||||
loadLogByStatus(snapStatus, logPath)
|
||||
if snapStatus.PanelInfo != constant.StatusDone {
|
||||
wg.Add(1)
|
||||
go snapJson(itemHelper, jsonItem, rootDir)
|
||||
@@ -544,10 +538,6 @@ func (u *SnapshotService) HandleSnapshot(isCronjob bool, req dto.SnapshotCreate,
|
||||
wg.Add(1)
|
||||
go snapBackup(itemHelper, localDir, backupPanelDir)
|
||||
}
|
||||
if snapStatus.PanelData != constant.StatusDone {
|
||||
wg.Add(1)
|
||||
go snapPanelData(itemHelper, localDir, backupPanelDir)
|
||||
}
|
||||
|
||||
if !isCronjob {
|
||||
go func() {
|
||||
@@ -556,12 +546,16 @@ func (u *SnapshotService) HandleSnapshot(isCronjob bool, req dto.SnapshotCreate,
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
|
||||
return
|
||||
}
|
||||
snapPanelData(itemHelper, localDir, backupPanelDir)
|
||||
if snapStatus.PanelData != constant.StatusDone {
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
|
||||
return
|
||||
}
|
||||
snapCompress(itemHelper, rootDir)
|
||||
if snapStatus.Compress != constant.StatusDone {
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
|
||||
return
|
||||
}
|
||||
|
||||
snapUpload(itemHelper, req.From, fmt.Sprintf("%s.tar.gz", rootDir))
|
||||
if snapStatus.Upload != constant.StatusDone {
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
|
||||
@@ -569,25 +563,38 @@ func (u *SnapshotService) HandleSnapshot(isCronjob bool, req dto.SnapshotCreate,
|
||||
}
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusSuccess})
|
||||
}()
|
||||
return "", "", nil
|
||||
return "", nil
|
||||
}
|
||||
wg.Wait()
|
||||
if !checkIsAllDone(snap.ID) {
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
|
||||
return loadLogByStatus(snapStatus), snap.Name, fmt.Errorf("snapshot %s backup failed", snap.Name)
|
||||
loadLogByStatus(snapStatus, logPath)
|
||||
return snap.Name, fmt.Errorf("snapshot %s backup failed", snap.Name)
|
||||
}
|
||||
loadLogByStatus(snapStatus, logPath)
|
||||
snapPanelData(itemHelper, localDir, backupPanelDir)
|
||||
if snapStatus.PanelData != constant.StatusDone {
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
|
||||
loadLogByStatus(snapStatus, logPath)
|
||||
return snap.Name, fmt.Errorf("snapshot %s 1panel data failed", snap.Name)
|
||||
}
|
||||
loadLogByStatus(snapStatus, logPath)
|
||||
snapCompress(itemHelper, rootDir)
|
||||
if snapStatus.Compress != constant.StatusDone {
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
|
||||
return loadLogByStatus(snapStatus), snap.Name, fmt.Errorf("snapshot %s compress failed", snap.Name)
|
||||
loadLogByStatus(snapStatus, logPath)
|
||||
return snap.Name, fmt.Errorf("snapshot %s compress failed", snap.Name)
|
||||
}
|
||||
loadLogByStatus(snapStatus, logPath)
|
||||
snapUpload(itemHelper, req.From, fmt.Sprintf("%s.tar.gz", rootDir))
|
||||
if snapStatus.Upload != constant.StatusDone {
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
|
||||
return loadLogByStatus(snapStatus), snap.Name, fmt.Errorf("snapshot %s upload failed", snap.Name)
|
||||
loadLogByStatus(snapStatus, logPath)
|
||||
return snap.Name, fmt.Errorf("snapshot %s upload failed", snap.Name)
|
||||
}
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusSuccess})
|
||||
return loadLogByStatus(snapStatus), snap.Name, nil
|
||||
loadLogByStatus(snapStatus, logPath)
|
||||
return snap.Name, nil
|
||||
}
|
||||
|
||||
func (u *SnapshotService) handleDockerDatas(fileOp files.FileOp, operation string, source, target string) error {
|
||||
@@ -778,7 +785,7 @@ func (u *SnapshotService) handlePanelDatas(snapID uint, fileOp files.FileOp, ope
|
||||
return fmt.Errorf("backup panel data failed, err: %v", err)
|
||||
}
|
||||
case "recover":
|
||||
exclusionRules := "./tmp;./log;./cache;"
|
||||
exclusionRules := "./tmp;./log;./cache;./db/1Panel.db-*;"
|
||||
if strings.Contains(backupDir, target) {
|
||||
exclusionRules += ("." + strings.ReplaceAll(backupDir, target, "") + ";")
|
||||
}
|
||||
@@ -787,22 +794,26 @@ func (u *SnapshotService) handlePanelDatas(snapID uint, fileOp files.FileOp, ope
|
||||
}
|
||||
|
||||
_ = snapshotRepo.Update(snapID, map[string]interface{}{"recover_status": ""})
|
||||
checkPointOfWal()
|
||||
if err := handleSnapTar(target, u.OriginalPath, "1panel_data.tar.gz", exclusionRules); err != nil {
|
||||
return fmt.Errorf("restore original panel data failed, err: %v", err)
|
||||
}
|
||||
_ = snapshotRepo.Update(snapID, map[string]interface{}{"recover_status": constant.StatusWaiting})
|
||||
|
||||
_ = fileOp.Fs.RemoveAll(path.Join(target, "apps"))
|
||||
checkPointOfWal()
|
||||
if err := u.handleUnTar(source+"/1panel/1panel_data.tar.gz", target); err != nil {
|
||||
return fmt.Errorf("recover panel data failed, err: %v", err)
|
||||
}
|
||||
case "re-recover":
|
||||
_ = fileOp.Fs.RemoveAll(path.Join(target, "apps"))
|
||||
checkPointOfWal()
|
||||
if err := u.handleUnTar(source+"/1panel/1panel_data.tar.gz", target); err != nil {
|
||||
return fmt.Errorf("retry recover panel data failed, err: %v", err)
|
||||
}
|
||||
case "rollback":
|
||||
_ = fileOp.Fs.RemoveAll(path.Join(target, "apps"))
|
||||
checkPointOfWal()
|
||||
if err := u.handleUnTar(source+"/1panel_data.tar.gz", target); err != nil {
|
||||
return fmt.Errorf("rollback panel data failed, err: %v", err)
|
||||
}
|
||||
@@ -1045,16 +1056,13 @@ func checkIsAllDone(snapID uint) bool {
|
||||
if status.AppData != constant.StatusDone {
|
||||
return false
|
||||
}
|
||||
if status.PanelData != constant.StatusDone {
|
||||
return false
|
||||
}
|
||||
if status.BackupData != constant.StatusDone {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func loadLogByStatus(status model.SnapshotStatus) string {
|
||||
func loadLogByStatus(status model.SnapshotStatus, logPath string) {
|
||||
logs := ""
|
||||
logs += fmt.Sprintf("Write 1Panel basic information: %s \n", status.PanelInfo)
|
||||
logs += fmt.Sprintf("Backup 1Panel system files: %s \n", status.Panel)
|
||||
@@ -1066,5 +1074,11 @@ func loadLogByStatus(status model.SnapshotStatus) string {
|
||||
logs += fmt.Sprintf("Snapshot size: %s \n", status.Size)
|
||||
logs += fmt.Sprintf("Upload snapshot file: %s \n", status.Upload)
|
||||
|
||||
return logs
|
||||
file, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("write snapshot logs failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
_, _ = file.Write([]byte(logs))
|
||||
}
|
||||
|
||||
@@ -129,16 +129,16 @@ func snapBackup(snap snapHelper, localDir, targetDir string) {
|
||||
}
|
||||
|
||||
func snapPanelData(snap snapHelper, localDir, targetDir string) {
|
||||
defer snap.Wg.Done()
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"panel_data": constant.Running})
|
||||
status := constant.StatusDone
|
||||
dataDir := path.Join(global.CONF.System.BaseDir, "1panel")
|
||||
exclusionRules := "./tmp;./log;./cache;"
|
||||
exclusionRules := "./tmp;./log;./cache;./db/1Panel.db-*;"
|
||||
if strings.Contains(localDir, dataDir) {
|
||||
exclusionRules += ("." + strings.ReplaceAll(localDir, dataDir, "") + ";")
|
||||
}
|
||||
|
||||
_ = snapshotRepo.Update(snap.SnapID, map[string]interface{}{"status": "OnSaveData"})
|
||||
checkPointOfWal()
|
||||
if err := handleSnapTar(dataDir, targetDir, "1panel_data.tar.gz", exclusionRules); err != nil {
|
||||
status = err.Error()
|
||||
}
|
||||
@@ -175,7 +175,7 @@ func snapCompress(snap snapHelper, rootDir string) {
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"compress": constant.StatusDone, "size": size})
|
||||
}
|
||||
|
||||
func snapUpload(snap snapHelper, account string, file string) {
|
||||
func snapUpload(snap snapHelper, accounts string, file string) {
|
||||
source := path.Join(global.CONF.System.TmpDir, "system", path.Base(file))
|
||||
defer func() {
|
||||
global.LOG.Debugf("remove snapshot file %s", source)
|
||||
@@ -183,24 +183,22 @@ func snapUpload(snap snapHelper, account string, file string) {
|
||||
}()
|
||||
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"upload": constant.StatusUploading})
|
||||
backup, err := backupRepo.Get(commonRepo.WithByType(account))
|
||||
accountMap, err := loadClientMap(accounts)
|
||||
if err != nil {
|
||||
snap.Status.Upload = err.Error()
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"upload": err.Error()})
|
||||
return
|
||||
}
|
||||
client, err := NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
snap.Status.Upload = err.Error()
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"upload": err.Error()})
|
||||
return
|
||||
}
|
||||
target := path.Join(strings.TrimPrefix(backup.BackupPath, "/"), "system_snapshot", path.Base(file))
|
||||
global.LOG.Debugf("start upload snapshot to %s, dir: %s", backup.Type, target)
|
||||
if _, err := client.Upload(source, target); err != nil {
|
||||
snap.Status.Upload = err.Error()
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"upload": err.Error()})
|
||||
return
|
||||
targetAccounts := strings.Split(accounts, ",")
|
||||
for _, item := range targetAccounts {
|
||||
global.LOG.Debugf("start upload snapshot to %s, path: %s", item, path.Join(accountMap[item].backupPath, "system_snapshot", path.Base(file)))
|
||||
if _, err := accountMap[item].client.Upload(source, path.Join(accountMap[item].backupPath, "system_snapshot", path.Base(file))); err != nil {
|
||||
global.LOG.Debugf("upload to %s failed, err: %v", item, err)
|
||||
snap.Status.Upload = err.Error()
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"upload": err.Error()})
|
||||
return
|
||||
}
|
||||
global.LOG.Debugf("upload to %s successful", item)
|
||||
}
|
||||
snap.Status.Upload = constant.StatusDone
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"upload": constant.StatusDone})
|
||||
@@ -235,3 +233,9 @@ func handleSnapTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkPointOfWal() {
|
||||
if err := global.DB.Exec("PRAGMA wal_checkpoint(TRUNCATE);").Error; err != nil {
|
||||
global.LOG.Errorf("handle check point failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
+81
-230
@@ -32,7 +32,6 @@ type ISSHService interface {
|
||||
UpdateByFile(value string) error
|
||||
Update(req dto.SSHUpdate) error
|
||||
GenerateSSH(req dto.GenerateSSH) error
|
||||
AnalysisLog(req dto.SearchForAnalysis) (*dto.AnalysisRes, error)
|
||||
LoadSSHSecret(mode string) (string, error)
|
||||
LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error)
|
||||
|
||||
@@ -49,7 +48,7 @@ func (u *SSHService) GetSSHInfo() (*dto.SSHInfo, error) {
|
||||
Status: constant.StatusEnable,
|
||||
Message: "",
|
||||
Port: "22",
|
||||
ListenAddress: "0.0.0.0",
|
||||
ListenAddress: "",
|
||||
PasswordAuthentication: "yes",
|
||||
PubkeyAuthentication: "yes",
|
||||
PermitRootLogin: "yes",
|
||||
@@ -91,7 +90,12 @@ func (u *SSHService) GetSSHInfo() (*dto.SSHInfo, error) {
|
||||
data.Port = strings.ReplaceAll(line, "Port ", "")
|
||||
}
|
||||
if strings.HasPrefix(line, "ListenAddress ") {
|
||||
data.ListenAddress = strings.ReplaceAll(line, "ListenAddress ", "")
|
||||
itemAddr := strings.ReplaceAll(line, "ListenAddress ", "")
|
||||
if len(data.ListenAddress) != 0 {
|
||||
data.ListenAddress += ("," + itemAddr)
|
||||
} else {
|
||||
data.ListenAddress = itemAddr
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(line, "PasswordAuthentication ") {
|
||||
data.PasswordAuthentication = strings.ReplaceAll(line, "PasswordAuthentication ", "")
|
||||
@@ -100,7 +104,7 @@ func (u *SSHService) GetSSHInfo() (*dto.SSHInfo, error) {
|
||||
data.PubkeyAuthentication = strings.ReplaceAll(line, "PubkeyAuthentication ", "")
|
||||
}
|
||||
if strings.HasPrefix(line, "PermitRootLogin ") {
|
||||
data.PermitRootLogin = strings.ReplaceAll(line, "PermitRootLogin ", "")
|
||||
data.PermitRootLogin = strings.ReplaceAll(strings.ReplaceAll(line, "PermitRootLogin ", ""), "prohibit-password", "without-password")
|
||||
}
|
||||
if strings.HasPrefix(line, "UseDNS ") {
|
||||
data.UseDNS = strings.ReplaceAll(line, "UseDNS ", "")
|
||||
@@ -310,9 +314,9 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
|
||||
case constant.StatusSuccess:
|
||||
commandItem = fmt.Sprintf("cat %s | grep -a Accepted %s", file.Name, command)
|
||||
case constant.StatusFailed:
|
||||
commandItem = fmt.Sprintf("cat %s | grep -a 'Failed password for' | grep -v 'invalid' %s", file.Name, command)
|
||||
commandItem = fmt.Sprintf("cat %s | grep -a 'Failed password for' %s", file.Name, command)
|
||||
default:
|
||||
commandItem = fmt.Sprintf("cat %s | grep -aE '(Failed password for|Accepted)' | grep -v 'invalid' %s", file.Name, command)
|
||||
commandItem = fmt.Sprintf("cat %s | grep -aE '(Failed password for|Accepted)' %s", file.Name, command)
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(path.Base(file.Name), "auth.log") {
|
||||
@@ -322,7 +326,7 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
|
||||
case constant.StatusFailed:
|
||||
commandItem = fmt.Sprintf("cat %s | grep -aE 'Failed password for|Connection closed by authenticating user' %s", file.Name, command)
|
||||
default:
|
||||
commandItem = fmt.Sprintf("cat %s | grep -aE \"(Failed password for|Connection closed by authenticating user|Accepted)\" | grep -v 'invalid' %s", file.Name, command)
|
||||
commandItem = fmt.Sprintf("cat %s | grep -aE \"(Failed password for|Connection closed by authenticating user|Accepted)\" %s", file.Name, command)
|
||||
}
|
||||
}
|
||||
dataItem, successCount, failedCount := loadSSHData(commandItem, showCountFrom, showCountTo, file.Year, qqWry, nyc)
|
||||
@@ -337,89 +341,6 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func (u *SSHService) AnalysisLog(req dto.SearchForAnalysis) (*dto.AnalysisRes, error) {
|
||||
var fileList []string
|
||||
baseDir := "/var/log"
|
||||
if err := filepath.Walk(baseDir, func(pathItem string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() && (strings.HasPrefix(info.Name(), "secure") || strings.HasPrefix(info.Name(), "auth")) {
|
||||
if !strings.HasSuffix(info.Name(), ".gz") {
|
||||
fileList = append(fileList, pathItem)
|
||||
return nil
|
||||
}
|
||||
itemFileName := strings.TrimSuffix(pathItem, ".gz")
|
||||
if _, err := os.Stat(itemFileName); err != nil && os.IsNotExist(err) {
|
||||
if err := handleGunzip(pathItem); err == nil {
|
||||
fileList = append(fileList, itemFileName)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
command := ""
|
||||
sortMap := make(map[string]dto.SSHLogAnalysis)
|
||||
for _, file := range fileList {
|
||||
commandItem := ""
|
||||
if strings.HasPrefix(path.Base(file), "secure") {
|
||||
commandItem = fmt.Sprintf("cat %s | grep -aE '(Failed password for|Accepted)' | grep -v 'invalid' %s", file, command)
|
||||
}
|
||||
if strings.HasPrefix(path.Base(file), "auth.log") {
|
||||
commandItem = fmt.Sprintf("cat %s | grep -aE \"(Connection closed by authenticating user|Accepted)\" | grep -v 'invalid' %s", file, command)
|
||||
}
|
||||
loadSSHDataForAnalysis(sortMap, commandItem)
|
||||
}
|
||||
var sortSlice []dto.SSHLogAnalysis
|
||||
for key, value := range sortMap {
|
||||
sortSlice = append(sortSlice, dto.SSHLogAnalysis{Address: key, SuccessfulCount: value.SuccessfulCount, FailedCount: value.FailedCount, Status: "accept"})
|
||||
}
|
||||
if req.OrderBy == constant.StatusSuccess {
|
||||
sort.Slice(sortSlice, func(i, j int) bool {
|
||||
return sortSlice[i].SuccessfulCount > sortSlice[j].SuccessfulCount
|
||||
})
|
||||
} else {
|
||||
sort.Slice(sortSlice, func(i, j int) bool {
|
||||
return sortSlice[i].FailedCount > sortSlice[j].FailedCount
|
||||
})
|
||||
}
|
||||
qqWry, _ := qqwry.NewQQwry()
|
||||
rules, _ := listIpRules("drop")
|
||||
for i := 0; i < len(sortSlice); i++ {
|
||||
sortSlice[i].Area = qqWry.Find(sortSlice[i].Address).Area
|
||||
for _, rule := range rules {
|
||||
if sortSlice[i].Address == rule {
|
||||
sortSlice[i].Status = "drop"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var backData dto.AnalysisRes
|
||||
for _, item := range sortSlice {
|
||||
backData.FailedCount += item.FailedCount
|
||||
backData.SuccessfulCount += item.SuccessfulCount
|
||||
}
|
||||
|
||||
var data []dto.SSHLogAnalysis
|
||||
total, start, end := len(sortSlice), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||
if start > total {
|
||||
data = make([]dto.SSHLogAnalysis, 0)
|
||||
} else {
|
||||
if end >= total {
|
||||
end = total
|
||||
}
|
||||
data = sortSlice[start:end]
|
||||
}
|
||||
backData.Items = data
|
||||
backData.Total = int64(total)
|
||||
|
||||
return &backData, nil
|
||||
}
|
||||
|
||||
func (u *SSHService) LoadSSHConf() (string, error) {
|
||||
if _, err := os.Stat("/etc/ssh/sshd_config"); err != nil {
|
||||
return "", buserr.New("ErrHttpReqNotFound")
|
||||
@@ -450,33 +371,34 @@ func sortFileList(fileNames []sshFileItem) []sshFileItem {
|
||||
return fileNames
|
||||
}
|
||||
|
||||
func updateSSHConf(oldFiles []string, param string, value interface{}) []string {
|
||||
hasKey := false
|
||||
func updateSSHConf(oldFiles []string, param string, value string) []string {
|
||||
var valueItems []string
|
||||
if param != "ListenAddress" {
|
||||
valueItems = append(valueItems, value)
|
||||
} else {
|
||||
if value != "" {
|
||||
valueItems = strings.Split(value, ",")
|
||||
}
|
||||
}
|
||||
var newFiles []string
|
||||
for _, line := range oldFiles {
|
||||
if strings.HasPrefix(line, param+" ") {
|
||||
newFiles = append(newFiles, fmt.Sprintf("%s %v", param, value))
|
||||
hasKey = true
|
||||
lineItem := strings.TrimSpace(line)
|
||||
if (strings.HasPrefix(lineItem, param) || strings.HasPrefix(lineItem, fmt.Sprintf("#%s", param))) && len(valueItems) != 0 {
|
||||
newFiles = append(newFiles, fmt.Sprintf("%s %s", param, valueItems[0]))
|
||||
valueItems = valueItems[1:]
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(lineItem, param) && len(valueItems) == 0 {
|
||||
newFiles = append(newFiles, fmt.Sprintf("#%s", line))
|
||||
continue
|
||||
}
|
||||
newFiles = append(newFiles, line)
|
||||
}
|
||||
if !hasKey {
|
||||
newFiles = []string{}
|
||||
for _, line := range oldFiles {
|
||||
if strings.HasPrefix(line, fmt.Sprintf("#%s ", param)) && !hasKey {
|
||||
newFiles = append(newFiles, fmt.Sprintf("%s %v", param, value))
|
||||
hasKey = true
|
||||
continue
|
||||
}
|
||||
newFiles = append(newFiles, line)
|
||||
if len(valueItems) != 0 {
|
||||
for _, item := range valueItems {
|
||||
newFiles = append(newFiles, fmt.Sprintf("%s %s", param, item))
|
||||
}
|
||||
}
|
||||
if !hasKey {
|
||||
newFiles = []string{}
|
||||
newFiles = append(newFiles, oldFiles...)
|
||||
newFiles = append(newFiles, fmt.Sprintf("%s %v", param, value))
|
||||
}
|
||||
return newFiles
|
||||
}
|
||||
|
||||
@@ -529,147 +451,62 @@ func loadSSHData(command string, showCountFrom, showCountTo, currentYear int, qq
|
||||
return datas, successCount, failedCount
|
||||
}
|
||||
|
||||
func loadSSHDataForAnalysis(analysisMap map[string]dto.SSHLogAnalysis, commandItem string) {
|
||||
stdout, err := cmd.Exec(commandItem)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
lines := strings.Split(string(stdout), "\n")
|
||||
for i := len(lines) - 1; i >= 0; i-- {
|
||||
var itemData dto.SSHHistory
|
||||
switch {
|
||||
case strings.Contains(lines[i], "Failed password for"):
|
||||
itemData = loadFailedSecureDatas(lines[i])
|
||||
case strings.Contains(lines[i], "Connection closed by authenticating user"):
|
||||
itemData = loadFailedAuthDatas(lines[i])
|
||||
case strings.Contains(lines[i], "Accepted "):
|
||||
itemData = loadSuccessDatas(lines[i])
|
||||
}
|
||||
if len(itemData.Address) != 0 {
|
||||
if val, ok := analysisMap[itemData.Address]; ok {
|
||||
if itemData.Status == constant.StatusSuccess {
|
||||
val.SuccessfulCount++
|
||||
} else {
|
||||
val.FailedCount++
|
||||
}
|
||||
analysisMap[itemData.Address] = val
|
||||
} else {
|
||||
item := dto.SSHLogAnalysis{
|
||||
Address: itemData.Address,
|
||||
SuccessfulCount: 0,
|
||||
FailedCount: 0,
|
||||
}
|
||||
if itemData.Status == constant.StatusSuccess {
|
||||
item.SuccessfulCount = 1
|
||||
} else {
|
||||
item.FailedCount = 1
|
||||
}
|
||||
analysisMap[itemData.Address] = item
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadSuccessDatas(line string) dto.SSHHistory {
|
||||
var data dto.SSHHistory
|
||||
parts := strings.Fields(line)
|
||||
t, err := time.Parse("2006-01-02T15:04:05.999999-07:00", parts[0])
|
||||
if err != nil {
|
||||
if len(parts) < 14 {
|
||||
return data
|
||||
}
|
||||
data = dto.SSHHistory{
|
||||
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
||||
AuthMode: parts[6],
|
||||
User: parts[8],
|
||||
Address: parts[10],
|
||||
Port: parts[12],
|
||||
Status: constant.StatusSuccess,
|
||||
}
|
||||
} else {
|
||||
if len(parts) < 12 {
|
||||
return data
|
||||
}
|
||||
data = dto.SSHHistory{
|
||||
DateStr: t.Format("2006 Jan 2 15:04:05"),
|
||||
AuthMode: parts[4],
|
||||
User: parts[6],
|
||||
Address: parts[8],
|
||||
Port: parts[10],
|
||||
Status: constant.StatusSuccess,
|
||||
}
|
||||
index, dataStr := analyzeDateStr(parts)
|
||||
if dataStr == "" {
|
||||
return data
|
||||
}
|
||||
data.DateStr = dataStr
|
||||
data.AuthMode = parts[4+index]
|
||||
data.User = parts[6+index]
|
||||
data.Address = parts[8+index]
|
||||
data.Port = parts[10+index]
|
||||
data.Status = constant.StatusSuccess
|
||||
return data
|
||||
}
|
||||
|
||||
func loadFailedAuthDatas(line string) dto.SSHHistory {
|
||||
var data dto.SSHHistory
|
||||
parts := strings.Fields(line)
|
||||
t, err := time.Parse("2006-01-02T15:04:05.999999-07:00", parts[0])
|
||||
if err != nil {
|
||||
if len(parts) < 14 {
|
||||
return data
|
||||
}
|
||||
data = dto.SSHHistory{
|
||||
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
||||
AuthMode: parts[8],
|
||||
User: parts[10],
|
||||
Address: parts[11],
|
||||
Port: parts[13],
|
||||
Status: constant.StatusFailed,
|
||||
}
|
||||
} else {
|
||||
if len(parts) < 12 {
|
||||
return data
|
||||
}
|
||||
data = dto.SSHHistory{
|
||||
DateStr: t.Format("2006 Jan 2 15:04:05"),
|
||||
AuthMode: parts[6],
|
||||
User: parts[7],
|
||||
Address: parts[9],
|
||||
Port: parts[11],
|
||||
Status: constant.StatusFailed,
|
||||
}
|
||||
index, dataStr := analyzeDateStr(parts)
|
||||
if dataStr == "" {
|
||||
return data
|
||||
}
|
||||
data.DateStr = dataStr
|
||||
if index == 2 {
|
||||
data.User = parts[10]
|
||||
} else {
|
||||
data.User = parts[7]
|
||||
}
|
||||
data.AuthMode = parts[6+index]
|
||||
data.Address = parts[9+index]
|
||||
data.Port = parts[11+index]
|
||||
data.Status = constant.StatusFailed
|
||||
if strings.Contains(line, ": ") {
|
||||
data.Message = strings.Split(line, ": ")[1]
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func loadFailedSecureDatas(line string) dto.SSHHistory {
|
||||
var data dto.SSHHistory
|
||||
parts := strings.Fields(line)
|
||||
t, err := time.Parse("2006-01-02T15:04:05.999999-07:00", parts[0])
|
||||
if err != nil {
|
||||
if len(parts) < 14 {
|
||||
return data
|
||||
}
|
||||
data = dto.SSHHistory{
|
||||
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
||||
AuthMode: parts[6],
|
||||
User: parts[8],
|
||||
Address: parts[10],
|
||||
Port: parts[12],
|
||||
Status: constant.StatusFailed,
|
||||
}
|
||||
} else {
|
||||
if len(parts) < 12 {
|
||||
return data
|
||||
}
|
||||
index := 0
|
||||
if strings.Contains(line, " invalid user") {
|
||||
index = 2
|
||||
}
|
||||
data = dto.SSHHistory{
|
||||
DateStr: t.Format("2006 Jan 2 15:04:05"),
|
||||
AuthMode: parts[4],
|
||||
User: parts[index+6],
|
||||
Address: parts[index+8],
|
||||
Port: parts[index+10],
|
||||
Status: constant.StatusFailed,
|
||||
}
|
||||
index, dataStr := analyzeDateStr(parts)
|
||||
if dataStr == "" {
|
||||
return data
|
||||
}
|
||||
data.DateStr = dataStr
|
||||
if strings.Contains(line, " invalid ") {
|
||||
data.AuthMode = parts[4+index]
|
||||
index += 2
|
||||
} else {
|
||||
data.AuthMode = parts[4+index]
|
||||
}
|
||||
data.User = parts[6+index]
|
||||
data.Address = parts[8+index]
|
||||
data.Port = parts[10+index]
|
||||
data.Status = constant.StatusFailed
|
||||
if strings.Contains(line, ": ") {
|
||||
data.Message = strings.Split(line, ": ")[1]
|
||||
}
|
||||
@@ -699,3 +536,17 @@ func loadDate(currentYear int, DateStr string, nyc *time.Location) time.Time {
|
||||
}
|
||||
return itemDate
|
||||
}
|
||||
|
||||
func analyzeDateStr(parts []string) (int, string) {
|
||||
t, err := time.Parse("2006-01-02T15:04:05.999999-07:00", parts[0])
|
||||
if err != nil {
|
||||
if len(parts) < 14 {
|
||||
return 0, ""
|
||||
}
|
||||
return 2, fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2])
|
||||
}
|
||||
if len(parts) < 12 {
|
||||
return 0, ""
|
||||
}
|
||||
return 0, t.Format("2006 Jan 2 15:04:05")
|
||||
}
|
||||
|
||||
@@ -158,6 +158,7 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
|
||||
go writeLogs(req.Version)
|
||||
_ = settingRepo.Update("SystemVersion", req.Version)
|
||||
_ = settingRepo.Update("SystemStatus", "Free")
|
||||
checkPointOfWal()
|
||||
_, _ = cmd.ExecWithTimeOut("systemctl daemon-reload && systemctl restart 1panel.service", 1*time.Minute)
|
||||
}()
|
||||
return nil
|
||||
|
||||
@@ -18,6 +18,9 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/jinzhu/copier"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
@@ -54,7 +57,7 @@ type IWebsiteService interface {
|
||||
GetWebsites() ([]response.WebsiteDTO, error)
|
||||
CreateWebsite(create request.WebsiteCreate) error
|
||||
OpWebsite(req request.WebsiteOp) error
|
||||
GetWebsiteOptions() ([]string, error)
|
||||
GetWebsiteOptions() ([]response.WebsiteOption, error)
|
||||
UpdateWebsite(req request.WebsiteUpdate) error
|
||||
DeleteWebsite(req request.WebsiteDelete) error
|
||||
GetWebsite(id uint) (response.WebsiteDTO, error)
|
||||
@@ -172,8 +175,14 @@ func (w WebsiteService) GetWebsites() ([]response.WebsiteDTO, error) {
|
||||
func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error) {
|
||||
primaryDomainArray := strings.Split(create.PrimaryDomain, ":")
|
||||
primaryDomain := primaryDomainArray[0]
|
||||
|
||||
if exist, _ := websiteRepo.GetBy(websiteRepo.WithAlias(create.Alias)); len(exist) > 0 {
|
||||
alias := create.Alias
|
||||
if common.ContainsChinese(alias) {
|
||||
alias, err = common.PunycodeEncode(alias)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if exist, _ := websiteRepo.GetBy(websiteRepo.WithAlias(alias)); len(exist) > 0 {
|
||||
return buserr.New(constant.ErrAliasIsExist)
|
||||
}
|
||||
|
||||
@@ -201,7 +210,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
||||
website := &model.Website{
|
||||
PrimaryDomain: primaryDomain,
|
||||
Type: create.Type,
|
||||
Alias: create.Alias,
|
||||
Alias: alias,
|
||||
Remark: create.Remark,
|
||||
Status: constant.WebRunning,
|
||||
ExpireDate: defaultDate,
|
||||
@@ -341,14 +350,18 @@ func (w WebsiteService) OpWebsite(req request.WebsiteOp) error {
|
||||
return websiteRepo.Save(context.Background(), &website)
|
||||
}
|
||||
|
||||
func (w WebsiteService) GetWebsiteOptions() ([]string, error) {
|
||||
webs, err := websiteRepo.GetBy()
|
||||
func (w WebsiteService) GetWebsiteOptions() ([]response.WebsiteOption, error) {
|
||||
webs, err := websiteRepo.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var datas []string
|
||||
var datas []response.WebsiteOption
|
||||
for _, web := range webs {
|
||||
datas = append(datas, web.PrimaryDomain)
|
||||
var item response.WebsiteOption
|
||||
if err := copier.Copy(&item, &web); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
datas = append(datas, item)
|
||||
}
|
||||
return datas, nil
|
||||
}
|
||||
@@ -647,6 +660,13 @@ 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.Enable, nginxInstall, website); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.Enable = req.Enable
|
||||
res.SSLProtocol = req.SSLProtocol
|
||||
res.Algorithm = req.Algorithm
|
||||
@@ -758,6 +778,7 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
|
||||
|
||||
res.SSL = websiteSSL
|
||||
}
|
||||
|
||||
website.Protocol = constant.ProtocolHTTPS
|
||||
if err := applySSL(website, websiteSSL, req); err != nil {
|
||||
return nil, err
|
||||
@@ -1413,7 +1434,7 @@ func (w WebsiteService) UpdateSitePermission(req request.WebsiteUpdateDirPermiss
|
||||
if cmd.HasNoPasswordSudo() {
|
||||
chownCmd = fmt.Sprintf("sudo %s", chownCmd)
|
||||
}
|
||||
if out, err := cmd.ExecWithTimeOut(chownCmd, 1*time.Second); err != nil {
|
||||
if out, err := cmd.ExecWithTimeOut(chownCmd, 10*time.Second); err != nil {
|
||||
if out != "" {
|
||||
return errors.New(out)
|
||||
}
|
||||
@@ -1521,6 +1542,9 @@ func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error)
|
||||
}
|
||||
location.UpdateDirective("proxy_pass", []string{req.ProxyPass})
|
||||
location.UpdateDirective("proxy_set_header", []string{"Host", req.ProxyHost})
|
||||
if website.Protocol == constant.ProtocolHTTPS {
|
||||
location.UpdateDirective("add_header", []string{"Strict-Transport-Security", "\"max-age=31536000\""})
|
||||
}
|
||||
location.ChangePath(req.Modifier, req.Match)
|
||||
if req.Cache {
|
||||
location.AddCache(req.CacheTime, req.CacheUnit)
|
||||
|
||||
@@ -35,7 +35,7 @@ func (w WebsiteAcmeAccountService) Page(search dto.PageInfo) (int64, []response.
|
||||
}
|
||||
|
||||
func (w WebsiteAcmeAccountService) Create(create request.WebsiteAcmeAccountCreate) (*response.WebsiteAcmeAccountDTO, error) {
|
||||
exist, _ := websiteAcmeRepo.GetFirst(websiteAcmeRepo.WithEmail(create.Email))
|
||||
exist, _ := websiteAcmeRepo.GetFirst(websiteAcmeRepo.WithEmail(create.Email), websiteAcmeRepo.WithType(create.Type))
|
||||
if exist != nil {
|
||||
return nil, buserr.New(constant.ErrEmailIsExist)
|
||||
}
|
||||
|
||||
@@ -92,9 +92,6 @@ func (w WebsiteCAService) Create(create request.WebsiteCACreate) (*request.Websi
|
||||
MaxPathLenZero: false,
|
||||
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
||||
}
|
||||
var (
|
||||
caPEM = new(bytes.Buffer)
|
||||
)
|
||||
|
||||
interPrivateKey, interPublicKey, privateBytes, err := createPrivateKey(create.KeyType)
|
||||
if err != nil {
|
||||
@@ -102,15 +99,18 @@ func (w WebsiteCAService) Create(create request.WebsiteCACreate) (*request.Websi
|
||||
}
|
||||
ca.PrivateKey = string(privateBytes)
|
||||
|
||||
caBytes, err := x509.CreateCertificate(rand.Reader, rootCA, rootCA, interPublicKey, interPrivateKey)
|
||||
rootDer, err := x509.CreateCertificate(rand.Reader, rootCA, rootCA, interPublicKey, interPrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rootCert, err := x509.ParseCertificate(rootDer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certBlock := &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: caBytes,
|
||||
Bytes: rootCert.Raw,
|
||||
}
|
||||
_ = pem.Encode(caPEM, certBlock)
|
||||
pemData := pem.EncodeToMemory(certBlock)
|
||||
ca.CSR = string(pemData)
|
||||
|
||||
@@ -297,15 +297,26 @@ func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) (*model.Website
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
interCertBlock := &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: interCert.Raw,
|
||||
}
|
||||
_, publicKey, privateKeyBytes, err := createPrivateKey(websiteSSL.KeyType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
commonName := ""
|
||||
if len(domains) > 0 {
|
||||
commonName = domains[0]
|
||||
}
|
||||
if len(ips) > 0 {
|
||||
commonName = ips[0].String()
|
||||
}
|
||||
subject := rootCsr.Subject
|
||||
subject.CommonName = commonName
|
||||
csr := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(time.Now().Unix()),
|
||||
Subject: rootCsr.Subject,
|
||||
Subject: subject,
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: notAfter,
|
||||
BasicConstraintsValid: true,
|
||||
@@ -329,8 +340,7 @@ func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) (*model.Website
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: cert.Raw,
|
||||
}
|
||||
pemData := pem.EncodeToMemory(certBlock)
|
||||
websiteSSL.Pem = string(pemData)
|
||||
websiteSSL.Pem = string(pem.EncodeToMemory(certBlock)) + string(pem.EncodeToMemory(rootCertBlock)) + string(pem.EncodeToMemory(interCertBlock))
|
||||
websiteSSL.PrivateKey = string(privateKeyBytes)
|
||||
websiteSSL.ExpireDate = cert.NotAfter
|
||||
websiteSSL.StartDate = cert.NotBefore
|
||||
|
||||
@@ -191,7 +191,7 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = client.UseDns(ssl.DnsType(dnsAccount.Type), dnsAccount.Authorization); err != nil {
|
||||
if err = client.UseDns(ssl.DnsType(dnsAccount.Type), dnsAccount.Authorization, apply.SkipDNSCheck); err != nil {
|
||||
return err
|
||||
}
|
||||
case constant.Http:
|
||||
@@ -414,14 +414,28 @@ func (w WebsiteSSLService) Upload(req request.WebsiteSSLUpload) error {
|
||||
return buserr.New("ErrSSLKeyFormat")
|
||||
}
|
||||
|
||||
certBlock, _ := pem.Decode([]byte(websiteSSL.Pem))
|
||||
if certBlock == nil {
|
||||
var (
|
||||
cert *x509.Certificate
|
||||
pemData = []byte(websiteSSL.Pem)
|
||||
)
|
||||
for {
|
||||
certBlock, reset := pem.Decode(pemData)
|
||||
if certBlock == nil {
|
||||
break
|
||||
}
|
||||
cert, err = x509.ParseCertificate(certBlock.Bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(cert.DNSNames) > 0 || len(cert.IPAddresses) > 0 {
|
||||
break
|
||||
}
|
||||
pemData = reset
|
||||
}
|
||||
if pemData == nil {
|
||||
return buserr.New("ErrSSLCertificateFormat")
|
||||
}
|
||||
cert, err := x509.ParseCertificate(certBlock.Bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
websiteSSL.ExpireDate = cert.NotAfter
|
||||
websiteSSL.StartDate = cert.NotBefore
|
||||
websiteSSL.Type = cert.Issuer.CommonName
|
||||
|
||||
@@ -10,7 +10,9 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
|
||||
"gopkg.in/yaml.v3"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -30,27 +32,44 @@ import (
|
||||
)
|
||||
|
||||
func getDomain(domainStr string, defaultPort int) (model.WebsiteDomain, error) {
|
||||
domain := model.WebsiteDomain{}
|
||||
var (
|
||||
err error
|
||||
domain = model.WebsiteDomain{}
|
||||
portN int
|
||||
)
|
||||
domainArray := strings.Split(domainStr, ":")
|
||||
if len(domainArray) == 1 {
|
||||
domain.Domain = domainArray[0]
|
||||
domain.Domain, err = handleChineseDomain(domainArray[0])
|
||||
if err != nil {
|
||||
return domain, err
|
||||
}
|
||||
domain.Port = defaultPort
|
||||
return domain, nil
|
||||
}
|
||||
if len(domainArray) > 1 {
|
||||
domain.Domain = domainArray[0]
|
||||
portStr := domainArray[1]
|
||||
portN, err := strconv.Atoi(portStr)
|
||||
domain.Domain, err = handleChineseDomain(domainArray[0])
|
||||
if err != nil {
|
||||
return model.WebsiteDomain{}, buserr.WithName("ErrTypePort", portStr)
|
||||
return domain, err
|
||||
}
|
||||
portStr := domainArray[1]
|
||||
portN, err = strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return domain, buserr.WithName("ErrTypePort", portStr)
|
||||
}
|
||||
if portN <= 0 || portN > 65535 {
|
||||
return model.WebsiteDomain{}, buserr.New("ErrTypePortRange")
|
||||
return domain, buserr.New("ErrTypePortRange")
|
||||
}
|
||||
domain.Port = portN
|
||||
return domain, nil
|
||||
}
|
||||
return model.WebsiteDomain{}, nil
|
||||
return domain, nil
|
||||
}
|
||||
|
||||
func handleChineseDomain(domain string) (string, error) {
|
||||
if common.ContainsChinese(domain) {
|
||||
return common.PunycodeEncode(domain)
|
||||
}
|
||||
return domain, nil
|
||||
}
|
||||
|
||||
func createIndexFile(website *model.Website, runtime *model.Runtime) error {
|
||||
@@ -282,7 +301,7 @@ func delNginxConfig(website model.Website, force bool) error {
|
||||
if err := fileOp.DeleteFile(configPath); err != nil {
|
||||
return err
|
||||
}
|
||||
sitePath := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name, "www", "sites", website.PrimaryDomain)
|
||||
sitePath := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name, "www", "sites", website.Alias)
|
||||
if fileOp.Stat(sitePath) {
|
||||
_ = fileOp.DeleteDir(sitePath)
|
||||
}
|
||||
@@ -537,7 +556,15 @@ func opWebsite(website *model.Website, operate string) error {
|
||||
case constant.Deployment:
|
||||
server.RemoveDirective("location", []string{"/"})
|
||||
case constant.Runtime:
|
||||
server.RemoveDirective("location", []string{"~", "[^/]\\.php(/|$)"})
|
||||
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(website.RuntimeID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if runtime.Type == constant.RuntimePHP {
|
||||
server.RemoveDirective("location", []string{"~", "[^/]\\.php(/|$)"})
|
||||
} else {
|
||||
server.RemoveDirective("location", []string{"/"})
|
||||
}
|
||||
}
|
||||
server.UpdateRoot("/usr/share/nginx/html/stop")
|
||||
website.Status = constant.WebStopped
|
||||
@@ -575,10 +602,19 @@ func opWebsite(website *model.Website, operate string) error {
|
||||
case constant.Runtime:
|
||||
server.UpdateRoot(rootIndex)
|
||||
localPath := ""
|
||||
if website.ProxyType == constant.RuntimeProxyUnix {
|
||||
localPath = path.Join(nginxInstall.Install.GetPath(), rootIndex, "index.php")
|
||||
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(website.RuntimeID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if runtime.Type == constant.RuntimePHP {
|
||||
if website.ProxyType == constant.RuntimeProxyUnix {
|
||||
localPath = path.Join(nginxInstall.Install.GetPath(), rootIndex, "index.php")
|
||||
}
|
||||
server.UpdatePHPProxy([]string{website.Proxy}, localPath)
|
||||
} else {
|
||||
proxy := fmt.Sprintf("http://127.0.0.1:%d", runtime.Port)
|
||||
server.UpdateRootProxy([]string{proxy})
|
||||
}
|
||||
server.UpdatePHPProxy([]string{website.Proxy}, localPath)
|
||||
}
|
||||
website.Status = constant.WebRunning
|
||||
now := time.Now()
|
||||
@@ -819,3 +855,44 @@ func UpdateSSLConfig(websiteSSL model.WebsiteSSL) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ChangeHSTSConfig(enable bool, nginxInstall model.AppInstall, website model.Website) error {
|
||||
includeDir := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "proxy")
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(includeDir) {
|
||||
return nil
|
||||
}
|
||||
err := filepath.Walk(includeDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() {
|
||||
if filepath.Ext(path) == ".conf" {
|
||||
par, err := parser.NewParser(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config := par.Parse()
|
||||
config.FilePath = path
|
||||
directives := config.Directives
|
||||
location, ok := directives[0].(*components.Location)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if enable {
|
||||
location.UpdateDirective("add_header", []string{"Strict-Transport-Security", "\"max-age=31536000\""})
|
||||
} else {
|
||||
location.RemoveDirective("add_header", []string{"Strict-Transport-Security", "\"max-age=31536000\""})
|
||||
}
|
||||
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,10 +19,14 @@ const (
|
||||
AppNormal = "Normal"
|
||||
AppTakeDown = "TakeDown"
|
||||
|
||||
AppOpenresty = "openresty"
|
||||
AppMysql = "mysql"
|
||||
AppMariaDB = "mariadb"
|
||||
AppRedis = "redis"
|
||||
AppOpenresty = "openresty"
|
||||
AppMysql = "mysql"
|
||||
AppMariaDB = "mariadb"
|
||||
AppPostgresql = "postgresql"
|
||||
AppRedis = "redis"
|
||||
AppPostgres = "postgres"
|
||||
AppMongodb = "mongodb"
|
||||
AppMemcached = "memcached"
|
||||
|
||||
AppResourceLocal = "local"
|
||||
AppResourceRemote = "remote"
|
||||
|
||||
@@ -12,6 +12,7 @@ const (
|
||||
Cos = "COS"
|
||||
Kodo = "KODO"
|
||||
WebDAV = "WebDAV"
|
||||
Local = "LOCAL"
|
||||
|
||||
OneDriveRedirectURI = "http://localhost/login/authorized"
|
||||
)
|
||||
|
||||
@@ -116,6 +116,7 @@ var (
|
||||
ErrInUsed = "ErrInUsed"
|
||||
ErrObjectInUsed = "ErrObjectInUsed"
|
||||
ErrPortRules = "ErrPortRules"
|
||||
ErrPgImagePull = "ErrPgImagePull"
|
||||
)
|
||||
|
||||
// runtime
|
||||
|
||||
@@ -44,6 +44,12 @@ func Run() {
|
||||
if _, err := global.Cron.AddJob("@daily", job.NewAppStoreJob()); err != nil {
|
||||
global.LOG.Errorf("can not add appstore corn job: %s", err.Error())
|
||||
}
|
||||
|
||||
var backup model.BackupAccount
|
||||
_ = global.DB.Where("type = ?", "OneDrive").Find(&backup).Error
|
||||
if backup.ID != 0 {
|
||||
service.StartRefreshOneDriveToken()
|
||||
}
|
||||
global.Cron.Start()
|
||||
|
||||
var cronJobs []model.Cronjob
|
||||
@@ -60,11 +66,11 @@ func Run() {
|
||||
global.LOG.Errorf("start my cronjob failed, err: %v", err)
|
||||
}
|
||||
for i := 0; i < len(cronJobs); i++ {
|
||||
entryID, err := service.NewICronjobService().StartJob(&cronJobs[i])
|
||||
entryIDs, err := service.NewICronjobService().StartJob(&cronJobs[i])
|
||||
if err != nil {
|
||||
global.LOG.Errorf("start %s job %s failed, err: %v", cronJobs[i].Type, cronJobs[i].Name, err)
|
||||
}
|
||||
if err := repo.NewICronjobRepo().Update(cronJobs[i].ID, map[string]interface{}{"entry_id": entryID}); err != nil {
|
||||
if err := repo.NewICronjobRepo().Update(cronJobs[i].ID, map[string]interface{}{"entry_ids": entryIDs}); err != nil {
|
||||
global.LOG.Errorf("update cronjob %s %s failed, err: %v", cronJobs[i].Type, cronJobs[i].Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ var (
|
||||
CACHE *badger_db.Cache
|
||||
Viper *viper.Viper
|
||||
|
||||
Cron *cron.Cron
|
||||
MonitorCronID int
|
||||
Cron *cron.Cron
|
||||
MonitorCronID cron.EntryID
|
||||
OneDriveCronID cron.EntryID
|
||||
)
|
||||
|
||||
@@ -125,6 +125,7 @@ ErrTypeOfRedis: "The recovery file type does not match the current persistence m
|
||||
ErrInUsed: "{{ .detail }} is in use and cannot be deleted"
|
||||
ErrObjectInUsed: "This object is in use and cannot be deleted"
|
||||
ErrPortRules: "The number of ports does not match, please re-enter!"
|
||||
ErrPgImagePull: "Image pull timeout. Please configure image acceleration or manually pull the postgres:16.0-alpine image and try again"
|
||||
|
||||
#runtime
|
||||
ErrDirNotFound: "The build folder does not exist! Please check file integrity!"
|
||||
@@ -157,6 +158,8 @@ ErrFirewall: "No firewalld or ufw service is detected. Please check and try agai
|
||||
|
||||
#cronjob
|
||||
ErrBashExecute: "Script execution error, please check the specific information in the task output text area."
|
||||
ErrCutWebsiteLog: "{{ .name }} website log cutting failed, error {{ .err }}"
|
||||
CutWebsiteLogSuccess: "{{ .name }} website log cut successfully, backup path {{ .path }}"
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: "The current user does not exist. Please modify and retry!"
|
||||
|
||||
@@ -126,6 +126,7 @@ ErrTypeOfRedis: "恢復文件類型與當前持久化方式不符,請修改後
|
||||
ErrInUsed: "{{ .detail }} 正被使用,無法刪除"
|
||||
ErrObjectInUsed: "該對象正被使用,無法刪除"
|
||||
ErrPortRules: "端口數目不匹配,請重新輸入!"
|
||||
ErrPgImagePull: "鏡像拉取超時,請配置鏡像加速或手動拉取 postgres:16.0-alpine 鏡像後重試"
|
||||
|
||||
#runtime
|
||||
ErrDirNotFound: "build 文件夾不存在!請檢查文件完整性!"
|
||||
@@ -158,6 +159,8 @@ ErrFirewall: "當前未檢測到系統 firewalld 或 ufw 服務,請檢查後
|
||||
|
||||
#cronjob
|
||||
ErrBashExecute: "腳本執行錯誤,請在任務輸出文本域中查看具體信息。"
|
||||
ErrCutWebsiteLog: "{{ .name }} 網站日誌切割失敗,錯誤 {{ .err }}"
|
||||
CutWebsiteLogSuccess: "{{ .name }} 網站日誌切割成功,備份路徑 {{ .path }}"
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: "當前使用者不存在,請修改後重試!"
|
||||
|
||||
@@ -125,6 +125,7 @@ ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后
|
||||
ErrInUsed: "{{ .detail }} 正被使用,无法删除"
|
||||
ErrObjectInUsed: "该对象正被使用,无法删除"
|
||||
ErrPortRules: "端口数目不匹配,请重新输入!"
|
||||
ErrPgImagePull: "镜像拉取超时,请配置镜像加速或手动拉取 postgres:16.0-alpine 镜像后重试"
|
||||
|
||||
#runtime
|
||||
ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!"
|
||||
@@ -157,6 +158,8 @@ ErrFirewall: "当前未检测到系统 firewalld 或 ufw 服务,请检查后
|
||||
|
||||
#cronjob
|
||||
ErrBashExecute: "脚本执行错误,请在任务输出文本域中查看具体信息。"
|
||||
ErrCutWebsiteLog: "{{ .name }} 网站日志切割失败,错误 {{ .err }}"
|
||||
CutWebsiteLogSuccess: "{{ .name }} 网站日志切割成功,备份路径 {{ .path }}"
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: "当前用户不存在,请修改后重试!"
|
||||
|
||||
+75
-19
@@ -2,7 +2,10 @@ package hook
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
@@ -70,50 +73,103 @@ func Init() {
|
||||
_, _ = cmd.Execf("%s sed -i '/CHANGE_USER_INFO=true/d' /usr/local/bin/1pctl", sudo)
|
||||
}
|
||||
|
||||
handleCronjobStatus()
|
||||
handleSnapStatus()
|
||||
loadLocalDir()
|
||||
}
|
||||
|
||||
func handleSnapStatus() {
|
||||
msgFailed := "the task was interrupted due to the restart of the 1panel service"
|
||||
_ = global.DB.Model(&model.Snapshot{}).Where("status = ?", "OnSaveData").
|
||||
Updates(map[string]interface{}{"status": constant.StatusSuccess}).Error
|
||||
|
||||
_ = global.DB.Model(&model.Snapshot{}).Where("status = ?", constant.StatusWaiting).
|
||||
Updates(map[string]interface{}{
|
||||
"status": constant.StatusFailed,
|
||||
"message": msgFailed,
|
||||
}).Error
|
||||
|
||||
_ = global.DB.Model(&model.Snapshot{}).Where("recover_status = ?", constant.StatusWaiting).
|
||||
Updates(map[string]interface{}{
|
||||
"recover_status": constant.StatusFailed,
|
||||
"recover_message": msgFailed,
|
||||
}).Error
|
||||
|
||||
_ = global.DB.Model(&model.Snapshot{}).Where("rollback_status = ?", constant.StatusWaiting).
|
||||
Updates(map[string]interface{}{
|
||||
"rollback_status": constant.StatusFailed,
|
||||
"rollback_message": msgFailed,
|
||||
}).Error
|
||||
|
||||
snapRepo := repo.NewISnapshotRepo()
|
||||
snaps, _ := snapRepo.GetList()
|
||||
for _, snap := range snaps {
|
||||
if snap.Status == "OnSaveData" {
|
||||
_ = snapRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusSuccess})
|
||||
}
|
||||
if snap.Status == constant.StatusWaiting {
|
||||
_ = snapRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed, "message": "the task was interrupted due to the restart of the 1panel service"})
|
||||
}
|
||||
}
|
||||
|
||||
status, _ := snapRepo.GetStatusList()
|
||||
for _, statu := range status {
|
||||
for _, item := range status {
|
||||
updates := make(map[string]interface{})
|
||||
if statu.Panel == constant.StatusRunning {
|
||||
if item.Panel == constant.StatusRunning {
|
||||
updates["panel"] = constant.StatusFailed
|
||||
}
|
||||
if statu.PanelInfo == constant.StatusRunning {
|
||||
if item.PanelInfo == constant.StatusRunning {
|
||||
updates["panel_info"] = constant.StatusFailed
|
||||
}
|
||||
if statu.DaemonJson == constant.StatusRunning {
|
||||
if item.DaemonJson == constant.StatusRunning {
|
||||
updates["daemon_json"] = constant.StatusFailed
|
||||
}
|
||||
if statu.AppData == constant.StatusRunning {
|
||||
if item.AppData == constant.StatusRunning {
|
||||
updates["app_data"] = constant.StatusFailed
|
||||
}
|
||||
if statu.PanelData == constant.StatusRunning {
|
||||
if item.PanelData == constant.StatusRunning {
|
||||
updates["panel_data"] = constant.StatusFailed
|
||||
}
|
||||
if statu.BackupData == constant.StatusRunning {
|
||||
if item.BackupData == constant.StatusRunning {
|
||||
updates["backup_data"] = constant.StatusFailed
|
||||
}
|
||||
if statu.Compress == constant.StatusRunning {
|
||||
if item.Compress == constant.StatusRunning {
|
||||
updates["compress"] = constant.StatusFailed
|
||||
}
|
||||
if statu.Upload == constant.StatusUploading {
|
||||
if item.Upload == constant.StatusUploading {
|
||||
updates["upload"] = constant.StatusFailed
|
||||
}
|
||||
if len(updates) != 0 {
|
||||
_ = snapRepo.UpdateStatus(statu.ID, updates)
|
||||
_ = snapRepo.UpdateStatus(item.ID, updates)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleCronjobStatus() {
|
||||
_ = global.DB.Model(&model.JobRecords{}).Where("status = ?", constant.StatusWaiting).
|
||||
Updates(map[string]interface{}{
|
||||
"status": constant.StatusFailed,
|
||||
"message": "the task was interrupted due to the restart of the 1panel service",
|
||||
}).Error
|
||||
}
|
||||
|
||||
func loadLocalDir() {
|
||||
var backup model.BackupAccount
|
||||
_ = global.DB.Where("type = ?", "LOCAL").First(&backup).Error
|
||||
if backup.ID == 0 {
|
||||
global.LOG.Errorf("no such backup account `%s` in db", "LOCAL")
|
||||
return
|
||||
}
|
||||
varMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
|
||||
global.LOG.Errorf("json unmarshal backup.Vars: %v failed, err: %v", backup.Vars, err)
|
||||
return
|
||||
}
|
||||
if _, ok := varMap["dir"]; !ok {
|
||||
global.LOG.Error("load local backup dir failed")
|
||||
return
|
||||
}
|
||||
baseDir, ok := varMap["dir"].(string)
|
||||
if ok {
|
||||
if _, err := os.Stat(baseDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(baseDir, os.ModePerm); err != nil {
|
||||
global.LOG.Errorf("mkdir %s failed, err: %v", baseDir, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
global.CONF.System.Backup = baseDir
|
||||
return
|
||||
}
|
||||
global.LOG.Errorf("error type dir: %T", varMap["dir"])
|
||||
}
|
||||
|
||||
@@ -60,6 +60,18 @@ func Init() {
|
||||
migrations.AddDatabaseSSL,
|
||||
migrations.AddDefaultCA,
|
||||
migrations.AddSettingRecycleBin,
|
||||
migrations.UpdateWebsiteBackupRecord,
|
||||
|
||||
migrations.AddTablePHPExtensions,
|
||||
migrations.AddTableDatabasePostgresql,
|
||||
migrations.AddPostgresqlSuperUser,
|
||||
migrations.UpdateCronjobWithWebsite,
|
||||
migrations.UpdateOneDriveToken,
|
||||
migrations.UpdateCronjobSpec,
|
||||
migrations.UpdateBackupRecordPath,
|
||||
migrations.UpdateSnapshotRecords,
|
||||
|
||||
migrations.UpdateWebDavConf,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
||||
@@ -85,7 +85,7 @@ var AddTableSetting = &gormigrate.Migration{
|
||||
if err := tx.Create(&model.Setting{Key: "Language", Value: "zh"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "Theme", Value: "light"}).Error; err != nil {
|
||||
if err := tx.Create(&model.Setting{Key: "Theme", Value: "auto"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -214,7 +214,6 @@ var AddTableDatabaseMysql = &gormigrate.Migration{
|
||||
return tx.AutoMigrate(&model.DatabaseMysql{})
|
||||
},
|
||||
}
|
||||
|
||||
var AddTableWebsite = &gormigrate.Migration{
|
||||
ID: "20201009-add-table-website",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/service"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage/client"
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -87,3 +99,381 @@ var AddSettingRecycleBin = &gormigrate.Migration{
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var UpdateWebsiteBackupRecord = &gormigrate.Migration{
|
||||
ID: "20231218-update-backup-record-for-website",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
backupRepo := repo.NewIBackupRepo()
|
||||
websitesBackups, _ := backupRepo.ListRecord(repo.NewCommonRepo().WithByType("website"))
|
||||
if len(websitesBackups) > 0 {
|
||||
for _, backup := range websitesBackups {
|
||||
backup.DetailName = backup.Name
|
||||
_ = backupRepo.UpdateRecord(&backup)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var AddTablePHPExtensions = &gormigrate.Migration{
|
||||
ID: "20240102-add-php-extensions",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.PHPExtensions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.PHPExtensions{Name: "默认", Extensions: "bcmath,gd,gettext,intl,pcntl,shmop,soap,sockets,sysvsem,xmlrpc,zip"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var AddTableDatabasePostgresql = &gormigrate.Migration{
|
||||
ID: "20231225-add-table-database_postgresql",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.DatabasePostgresql{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.AutoMigrate(&model.Cronjob{}); err != nil {
|
||||
return err
|
||||
}
|
||||
var jobs []model.Cronjob
|
||||
if err := tx.Where("type == ?", "database").Find(&jobs).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
for _, job := range jobs {
|
||||
if job.DBName == "all" {
|
||||
if err := tx.Model(&model.Cronjob{}).Where("id = ?", job.ID).Update("db_type", "mysql").Error; err != nil {
|
||||
global.LOG.Errorf("update db type of cronjob %s failed, err: %v", job.Name, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
var db model.DatabaseMysql
|
||||
if err := tx.Where("id == ?", job.DBName).First(&db).Error; err != nil {
|
||||
continue
|
||||
}
|
||||
var database model.Database
|
||||
if err := tx.Where("name == ?", db.MysqlName).First(&database).Error; err != nil {
|
||||
continue
|
||||
}
|
||||
if err := tx.Model(&model.Cronjob{}).Where("id = ?", job.ID).Update("db_type", database.Type).Error; err != nil {
|
||||
global.LOG.Errorf("update db type of cronjob %s failed, err: %v", job.Name, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var AddPostgresqlSuperUser = &gormigrate.Migration{
|
||||
ID: "20231225-add-postgresql-super_user",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.DatabasePostgresql{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var UpdateCronjobWithWebsite = &gormigrate.Migration{
|
||||
ID: "20230809-update-cronjob-with-website",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
var cronjobs []model.Cronjob
|
||||
if err := tx.Where("(type = ? OR type = ?) AND website != ?", "website", "cutWebsiteLog", "all").Find(&cronjobs).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, job := range cronjobs {
|
||||
var web model.Website
|
||||
if err := tx.Where("primary_domain = ?", job.Website).First(&web).Error; err != nil {
|
||||
continue
|
||||
}
|
||||
if err := tx.Model(&model.Cronjob{}).
|
||||
Where("id = ?", job.ID).
|
||||
Updates(map[string]interface{}{"website": web.ID}).Error; err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var UpdateOneDriveToken = &gormigrate.Migration{
|
||||
ID: "20240117-update-onedrive-token",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
var (
|
||||
backup model.BackupAccount
|
||||
clientSetting model.Setting
|
||||
secretSetting model.Setting
|
||||
)
|
||||
_ = tx.Where("type = ?", "OneDrive").First(&backup).Error
|
||||
if backup.ID == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(backup.Credential) == 0 {
|
||||
global.LOG.Error("OneDrive configuration lacks token information, please rebind.")
|
||||
return nil
|
||||
}
|
||||
|
||||
_ = tx.Where("key = ?", "OneDriveID").First(&clientSetting).Error
|
||||
if clientSetting.ID == 0 {
|
||||
global.LOG.Error("system configuration lacks clientID information, please retry.")
|
||||
return nil
|
||||
}
|
||||
_ = tx.Where("key = ?", "OneDriveSc").First(&secretSetting).Error
|
||||
if secretSetting.ID == 0 {
|
||||
global.LOG.Error("system configuration lacks clientID information, please retry.")
|
||||
return nil
|
||||
}
|
||||
idItem, _ := base64.StdEncoding.DecodeString(clientSetting.Value)
|
||||
global.CONF.System.OneDriveID = string(idItem)
|
||||
scItem, _ := base64.StdEncoding.DecodeString(secretSetting.Value)
|
||||
global.CONF.System.OneDriveSc = string(scItem)
|
||||
|
||||
varMap := make(map[string]interface{})
|
||||
varMap["isCN"] = false
|
||||
varMap["client_id"] = global.CONF.System.OneDriveID
|
||||
varMap["client_secret"] = global.CONF.System.OneDriveSc
|
||||
varMap["redirect_uri"] = constant.OneDriveRedirectURI
|
||||
varMap["refresh_token"] = backup.Credential
|
||||
token, refreshToken, err := client.RefreshToken("refresh_token", varMap)
|
||||
varMap["refresh_status"] = constant.StatusSuccess
|
||||
varMap["refresh_time"] = time.Now().Format("2006-01-02 15:04:05")
|
||||
if err != nil {
|
||||
varMap["refresh_msg"] = err.Error()
|
||||
varMap["refresh_status"] = constant.StatusFailed
|
||||
}
|
||||
varMap["refresh_token"] = refreshToken
|
||||
itemVars, _ := json.Marshal(varMap)
|
||||
if err := tx.Model(&model.BackupAccount{}).
|
||||
Where("id = ?", backup.ID).
|
||||
Updates(map[string]interface{}{
|
||||
"credential": token,
|
||||
"vars": string(itemVars),
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var UpdateCronjobSpec = &gormigrate.Migration{
|
||||
ID: "20240122-update-cronjob-spec",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.Cronjob{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.AutoMigrate(&model.BackupRecord{}); err != nil {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
jobs []model.Cronjob
|
||||
backupAccounts []model.BackupAccount
|
||||
)
|
||||
mapAccount := make(map[uint]model.BackupAccount)
|
||||
if err := tx.Find(&jobs).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
_ = tx.Find(&backupAccounts).Error
|
||||
for _, item := range backupAccounts {
|
||||
mapAccount[item.ID] = item
|
||||
}
|
||||
for _, job := range jobs {
|
||||
if job.KeepLocal && mapAccount[uint(job.TargetDirID)].Type != constant.Local {
|
||||
if err := tx.Model(&model.Cronjob{}).
|
||||
Where("id = ?", job.ID).
|
||||
Updates(map[string]interface{}{
|
||||
"backup_accounts": fmt.Sprintf("%v,%v", mapAccount[uint(job.TargetDirID)].Type, constant.Local),
|
||||
"default_download": constant.Local,
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
job.DefaultDownload = constant.Local
|
||||
} else {
|
||||
if err := tx.Model(&model.Cronjob{}).
|
||||
Where("id = ?", job.ID).
|
||||
Updates(map[string]interface{}{
|
||||
"backup_accounts": mapAccount[uint(job.TargetDirID)].Type,
|
||||
"default_download": mapAccount[uint(job.TargetDirID)].Type,
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
job.DefaultDownload = mapAccount[uint(job.TargetDirID)].Type
|
||||
}
|
||||
if job.Type != "directory" && job.Type != "database" && job.Type != "website" && job.Type != "app" && job.Type != "snapshot" && job.Type != "log" {
|
||||
continue
|
||||
}
|
||||
|
||||
itemPath := mapAccount[uint(job.TargetDirID)].BackupPath
|
||||
if job.DefaultDownload == constant.Local || itemPath == "/" {
|
||||
itemPath = ""
|
||||
} else {
|
||||
itemPath = strings.TrimPrefix(itemPath, "/") + "/"
|
||||
}
|
||||
|
||||
var records []model.JobRecords
|
||||
_ = tx.Where("cronjob_id = ?", job.ID).Find(&records).Error
|
||||
for _, record := range records {
|
||||
if job.Type == "snapshot" && record.Status == constant.StatusSuccess {
|
||||
var snaps []model.Snapshot
|
||||
_ = tx.Where("name like ?", "snapshot_"+"%").Find(&snaps).Error
|
||||
for _, snap := range snaps {
|
||||
item := model.BackupRecord{
|
||||
From: "cronjob",
|
||||
CronjobID: job.ID,
|
||||
Type: "snapshot",
|
||||
Name: job.Name,
|
||||
FileDir: "system_snapshot",
|
||||
FileName: snap.Name + ".tar.gz",
|
||||
Source: snap.From,
|
||||
BackupType: snap.From,
|
||||
BaseModel: model.BaseModel{
|
||||
CreatedAt: job.CreatedAt,
|
||||
},
|
||||
}
|
||||
_ = tx.Create(&item).Error
|
||||
}
|
||||
continue
|
||||
}
|
||||
if job.Type == "log" && record.Status == constant.StatusSuccess {
|
||||
item := model.BackupRecord{
|
||||
From: "cronjob",
|
||||
CronjobID: job.ID,
|
||||
Type: "log",
|
||||
Name: job.Name,
|
||||
FileDir: path.Dir(strings.TrimPrefix(record.File, itemPath)),
|
||||
FileName: path.Base(record.File),
|
||||
Source: mapAccount[uint(job.TargetDirID)].Type,
|
||||
BackupType: mapAccount[uint(job.TargetDirID)].Type,
|
||||
BaseModel: model.BaseModel{
|
||||
CreatedAt: job.CreatedAt,
|
||||
},
|
||||
}
|
||||
_ = tx.Create(&item).Error
|
||||
continue
|
||||
}
|
||||
if job.Type == "directory" && record.Status == constant.StatusSuccess {
|
||||
item := model.BackupRecord{
|
||||
From: "cronjob",
|
||||
CronjobID: job.ID,
|
||||
Type: "directory",
|
||||
Name: job.Name,
|
||||
FileDir: path.Dir(strings.TrimPrefix(record.File, itemPath)),
|
||||
FileName: path.Base(record.File),
|
||||
BackupType: mapAccount[uint(job.TargetDirID)].Type,
|
||||
BaseModel: model.BaseModel{
|
||||
CreatedAt: job.CreatedAt,
|
||||
},
|
||||
}
|
||||
if record.FromLocal {
|
||||
item.Source = constant.Local
|
||||
} else {
|
||||
item.Source = mapAccount[uint(job.TargetDirID)].Type
|
||||
}
|
||||
_ = tx.Create(&item).Error
|
||||
continue
|
||||
}
|
||||
if strings.Contains(record.File, ",") {
|
||||
files := strings.Split(record.File, ",")
|
||||
for _, file := range files {
|
||||
_ = tx.Model(&model.BackupRecord{}).
|
||||
Where("file_dir = ? AND file_name = ?", path.Dir(strings.TrimPrefix(file, itemPath)), path.Base(file)).
|
||||
Updates(map[string]interface{}{"cronjob_id": job.ID, "from": "cronjob"}).Error
|
||||
}
|
||||
} else {
|
||||
_ = tx.Model(&model.BackupRecord{}).
|
||||
Where("file_dir = ? AND file_name = ?", path.Dir(strings.TrimPrefix(record.File, itemPath)), path.Base(record.File)).
|
||||
Updates(map[string]interface{}{"cronjob_id": job.ID, "from": "cronjob"}).Error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ = tx.Exec("ALTER TABLE cronjobs DROP COLUMN spec_type;").Error
|
||||
_ = tx.Exec("ALTER TABLE cronjobs DROP COLUMN week;").Error
|
||||
_ = tx.Exec("ALTER TABLE cronjobs DROP COLUMN day;").Error
|
||||
_ = tx.Exec("ALTER TABLE cronjobs DROP COLUMN hour;").Error
|
||||
_ = tx.Exec("ALTER TABLE cronjobs DROP COLUMN minute;").Error
|
||||
_ = tx.Exec("ALTER TABLE cronjobs DROP COLUMN second;").Error
|
||||
_ = tx.Exec("ALTER TABLE cronjobs DROP COLUMN entry_id;").Error
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var UpdateBackupRecordPath = &gormigrate.Migration{
|
||||
ID: "20240124-update-cronjob-spec",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
var (
|
||||
backupRecords []model.BackupRecord
|
||||
localAccount model.BackupAccount
|
||||
)
|
||||
|
||||
_ = tx.Where("type = ?", "LOCAL").First(&localAccount).Error
|
||||
if localAccount.ID == 0 {
|
||||
return nil
|
||||
}
|
||||
varMap := make(map[string]string)
|
||||
if err := json.Unmarshal([]byte(localAccount.Vars), &varMap); err != nil {
|
||||
return err
|
||||
}
|
||||
dir, ok := varMap["dir"]
|
||||
if !ok {
|
||||
return errors.New("load local backup dir failed")
|
||||
}
|
||||
if dir != "/" {
|
||||
dir += "/"
|
||||
}
|
||||
_ = tx.Where("source = ?", "LOCAL").Find(&backupRecords).Error
|
||||
for _, record := range backupRecords {
|
||||
_ = tx.Model(&model.BackupRecord{}).
|
||||
Where("id = ?", record.ID).
|
||||
Updates(map[string]interface{}{"file_dir": strings.TrimPrefix(record.FileDir, dir)}).Error
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var UpdateSnapshotRecords = &gormigrate.Migration{
|
||||
ID: "20240124-update-snapshot-records",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.Snapshot{}); err != nil {
|
||||
return err
|
||||
}
|
||||
var snaps []model.Snapshot
|
||||
_ = tx.Find(&snaps).Error
|
||||
for _, snap := range snaps {
|
||||
_ = tx.Model(&model.Snapshot{}).
|
||||
Where("id = ?", snap.ID).
|
||||
Updates(map[string]interface{}{"default_download": snap.From}).Error
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var UpdateWebDavConf = &gormigrate.Migration{
|
||||
ID: "20240205-update-webdav-conf",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
var backup model.BackupAccount
|
||||
_ = tx.Where("type = ?", constant.WebDAV).First(&backup).Error
|
||||
if backup.ID == 0 {
|
||||
return nil
|
||||
}
|
||||
varMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
|
||||
return err
|
||||
}
|
||||
delete(varMap, "addressItem")
|
||||
if port, ok := varMap["port"]; ok {
|
||||
varMap["address"] = fmt.Sprintf("%s:%v", varMap["address"], port)
|
||||
delete(varMap, "port")
|
||||
}
|
||||
|
||||
vars, _ := json.Marshal(varMap)
|
||||
if err := tx.Model(&model.BackupAccount{}).Where("id = ?", backup.ID).Updates(map[string]interface{}{"vars": string(vars)}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user